')
.appendTo($body)
.reflow()
.css('z-index', zIndex);
}
var level = $overlay.data('overlay-level') || 0;
return $overlay
.data('overlay-level', ++level)
.addClass('mdui-overlay-show');
},
/**
* 隐藏遮罩层
* @param force 是否强制隐藏遮罩
*/
hideOverlay: function (force) {
var $overlay = $('.mdui-overlay');
if (!$overlay.length) {
return;
}
var level = force ? 1 : $overlay.data('overlay-level');
if (level > 1) {
$overlay.data('overlay-level', --level);
return;
}
$overlay
.data('overlay-level', 0)
.removeClass('mdui-overlay-show')
.data('isDeleted', 1)
.transitionEnd(function () {
if ($overlay.data('isDeleted')) {
$overlay.remove();
}
});
},
/**
* 锁定屏幕
*/
lockScreen: function () {
// 不直接把 body 设为 box-sizing: border-box,避免污染全局样式
var newBodyWidth = $body.width();
$body
.addClass('mdui-locked')
.width(newBodyWidth);
var level = $body.data('lockscreen-level') || 0;
$body.data('lockscreen-level', ++level);
},
/**
* 解除屏幕锁定
* @param force 是否强制解锁屏幕
*/
unlockScreen: function (force) {
var level = force ? 1 : $body.data('lockscreen-level');
if (level > 1) {
$body.data('lockscreen-level', --level);
return;
}
$body
.data('lockscreen-level', 0)
.removeClass('mdui-locked')
.width('');
},
/**
* 函数节流
* @param fn
* @param delay
* @returns {Function}
*/
throttle: function (fn, delay) {
var timer = null;
if (!delay || delay < 16) {
delay = 16;
}
return function () {
var _this = this;
var args = arguments;
if (timer === null) {
timer = setTimeout(function () {
fn.apply(_this, args);
timer = null;
}, delay);
}
};
},
/**
* 生成唯一 id
* @param pluginName 插件名,若传入该参数,guid 将以该参数作为前缀
* @returns {string}
*/
guid: function (pluginName) {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
var guid = s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
if (pluginName) {
guid = 'mdui-' + pluginName + '-' + guid;
}
return guid;
},
});
/**
* =============================================================================
* ************ Headroom.js ************
* =============================================================================
*/
mdui.Headroom = (function () {
/**
* 默认参数
* @type {{}}
*/
var DEFAULT = {
tolerance: 5, // 滚动条滚动多少距离开始隐藏或显示元素,{down: num, up: num},或数字
offset: 0, // 在页面顶部多少距离内滚动不会隐藏元素
initialClass: 'mdui-headroom', // 初始化时添加的类
pinnedClass: 'mdui-headroom-pinned-top', // 元素固定时添加的类
unpinnedClass: 'mdui-headroom-unpinned-top', // 元素隐藏时添加的类
};
/**
* Headroom
* @param selector
* @param opts
* @constructor
*/
function Headroom(selector, opts) {
var _this = this;
_this.$headroom = $(selector).eq(0);
if (!_this.$headroom.length) {
return;
}
// 已通过自定义属性实例化过,不再重复实例化
var oldInst = _this.$headroom.data('mdui.headroom');
if (oldInst) {
return oldInst;
}
_this.options = $.extend({}, DEFAULT, (opts || {}));
// 数值转为 {down: bum, up: num}
var tolerance = _this.options.tolerance;
if (tolerance !== Object(tolerance)) {
_this.options.tolerance = {
down: tolerance,
up: tolerance,
};
}
_this._init();
}
/**
* 初始化
* @private
*/
Headroom.prototype._init = function () {
var _this = this;
_this.state = 'pinned';
_this.$headroom
.addClass(_this.options.initialClass)
.removeClass(_this.options.pinnedClass + ' ' + _this.options.unpinnedClass);
_this.inited = false;
_this.lastScrollY = 0;
_this._attachEvent();
};
/**
* 监听滚动事件
* @private
*/
Headroom.prototype._attachEvent = function () {
var _this = this;
if (!_this.inited) {
_this.lastScrollY = window.pageYOffset;
_this.inited = true;
$window.on('scroll', function () {
_this._scroll();
});
}
};
/**
* 滚动时的处理
* @private
*/
Headroom.prototype._scroll = function () {
var _this = this;
_this.rafId = window.requestAnimationFrame(function () {
var currentScrollY = window.pageYOffset;
var direction = currentScrollY > _this.lastScrollY ? 'down' : 'up';
var toleranceExceeded =
Math.abs(currentScrollY - _this.lastScrollY) >=
_this.options.tolerance[direction];
if (
currentScrollY > _this.lastScrollY &&
currentScrollY >= _this.options.offset &&
toleranceExceeded) {
_this.unpin();
} else if (
(currentScrollY < _this.lastScrollY && toleranceExceeded) ||
currentScrollY <= _this.options.offset
) {
_this.pin();
}
_this.lastScrollY = currentScrollY;
});
};
/**
* 动画结束回调
* @param inst
*/
var transitionEnd = function (inst) {
if (inst.state === 'pinning') {
inst.state = 'pinned';
componentEvent('pinned', 'headroom', inst, inst.$headroom);
}
if (inst.state === 'unpinning') {
inst.state = 'unpinned';
componentEvent('unpinned', 'headroom', inst, inst.$headroom);
}
};
/**
* 固定住
*/
Headroom.prototype.pin = function () {
var _this = this;
if (
_this.state === 'pinning' ||
_this.state === 'pinned' ||
!_this.$headroom.hasClass(_this.options.initialClass)
) {
return;
}
componentEvent('pin', 'headroom', _this, _this.$headroom);
_this.state = 'pinning';
_this.$headroom
.removeClass(_this.options.unpinnedClass)
.addClass(_this.options.pinnedClass)
.transitionEnd(function () {
transitionEnd(_this);
});
};
/**
* 不固定住
*/
Headroom.prototype.unpin = function () {
var _this = this;
if (
_this.state === 'unpinning' ||
_this.state === 'unpinned' ||
!_this.$headroom.hasClass(_this.options.initialClass)
) {
return;
}
componentEvent('unpin', 'headroom', _this, _this.$headroom);
_this.state = 'unpinning';
_this.$headroom
.removeClass(_this.options.pinnedClass)
.addClass(_this.options.unpinnedClass)
.transitionEnd(function () {
transitionEnd(_this);
});
};
/**
* 启用
*/
Headroom.prototype.enable = function () {
var _this = this;
if (!_this.inited) {
_this._init();
}
};
/**
* 禁用
*/
Headroom.prototype.disable = function () {
var _this = this;
if (_this.inited) {
_this.inited = false;
_this.$headroom
.removeClass([
_this.options.initialClass,
_this.options.pinnedClass,
_this.options.unpinnedClass,
].join(' '));
$window.off('scroll', function () {
_this._scroll();
});
window.cancelAnimationFrame(_this.rafId);
}
};
/**
* 获取当前状态 pinning | pinned | unpinning | unpinned
*/
Headroom.prototype.getState = function () {
return this.state;
};
return Headroom;
})();
/**
* =============================================================================
* ************ Headroom 自定义属性 API ************
* =============================================================================
*/
$(function () {
$('[mdui-headroom]').each(function () {
var $this = $(this);
var options = parseOptions($this.attr('mdui-headroom'));
var inst = $this.data('mdui.headroom');
if (!inst) {
inst = new mdui.Headroom($this, options);
$this.data('mdui.headroom', inst);
}
});
});
/**
* =============================================================================
* ************ 供 Collapse、 Panel 调用的折叠内容块插件 ************
* =============================================================================
*/
var CollapsePrivate = (function () {
/**
* 默认参数
*/
var DEFAULT = {
accordion: false, // 是否使用手风琴效果
};
// 类名
var CLASS = {
item: 'mdui-collapse-item', // item 类名
itemOpen: 'mdui-collapse-item-open', // 打开状态的 item
header: 'mdui-collapse-item-header', // item 中的 header 类名
body: 'mdui-collapse-item-body', // item 中的 body 类名
};
// 命名空间
var NAMESPACE = 'collapse';
/**
* 折叠内容块
* @param selector
* @param opts
* @param classes
* @param namespace
* @constructor
*/
function Collapse(selector, opts, classes, namespace) {
var _this = this;
_this.classes = $.extend({}, CLASS, classes || {});
_this.namespace = namespace ? namespace : NAMESPACE;
// 折叠面板元素
_this.$collapse = $(selector).eq(0);
if (!_this.$collapse.length) {
return;
}
// 已通过自定义属性实例化过,不再重复实例化
var oldInst = _this.$collapse.data('mdui.' + _this.namespace);
if (oldInst) {
return oldInst;
}
_this.options = $.extend({}, DEFAULT, (opts || {}));
_this.$collapse.on('click', '.' + _this.classes.header, function () {
var $item = $(this).parent('.' + _this.classes.item);
if (_this.$collapse.children($item).length) {
_this.toggle($item);
}
});
}
/**
* 指定 item 是否处于打开状态
* @param $item
* @returns {boolean}
* @private
*/
Collapse.prototype._isOpen = function ($item) {
return $item.hasClass(this.classes.itemOpen);
};
/**
* 获取指定 item
* @param item
* @returns {*}
* @private
*/
Collapse.prototype._getItem = function (item) {
var _this = this;
if (parseInt(item) === item) {
// item 是索引号
return _this.$collapse.children('.' + _this.classes.item).eq(item);
}
return $(item).eq(0);
};
/**
* 动画结束回调
* @param inst
* @param $content
* @param $item
*/
var transitionEnd = function (inst, $content, $item) {
if (inst._isOpen($item)) {
$content
.transition(0)
.height('auto')
.reflow()
.transition('');
componentEvent('opened', inst.namespace, inst, $item[0]);
} else {
$content.height('');
componentEvent('closed', inst.namespace, inst, $item[0]);
}
};
/**
* 打开指定面板项
* @param item 面板项的索引号或 DOM 元素或 CSS 选择器
*/
Collapse.prototype.open = function (item) {
var _this = this;
var $item = _this._getItem(item);
if (_this._isOpen($item)) {
return;
}
// 关闭其他项
if (_this.options.accordion) {
_this.$collapse.children('.' + _this.classes.itemOpen).each(function () {
var $tmpItem = $(this);
if ($tmpItem !== $item) {
_this.close($tmpItem);
}
});
}
var $content = $item.children('.' + _this.classes.body);
$content
.height($content[0].scrollHeight)
.transitionEnd(function () {
transitionEnd(_this, $content, $item);
});
componentEvent('open', _this.namespace, _this, $item[0]);
$item.addClass(_this.classes.itemOpen);
};
/**
* 关闭指定项
* @param item 面板项的索引号或 DOM 元素或 CSS 选择器
*/
Collapse.prototype.close = function (item) {
var _this = this;
var $item = _this._getItem(item);
if (!_this._isOpen($item)) {
return;
}
var $content = $item.children('.' + _this.classes.body);
componentEvent('close', _this.namespace, _this, $item[0]);
$item.removeClass(_this.classes.itemOpen);
$content
.transition(0)
.height($content[0].scrollHeight)
.reflow()
.transition('')
.height('')
.transitionEnd(function () {
transitionEnd(_this, $content, $item);
});
};
/**
* 切换指定项的状态
* @param item 面板项的索引号或 DOM 元素或 CSS 选择器或 JQ 对象
*/
Collapse.prototype.toggle = function (item) {
var _this = this;
var $item = _this._getItem(item);
if (_this._isOpen($item)) {
_this.close($item);
} else {
_this.open($item);
}
};
/**
* 打开所有项
*/
Collapse.prototype.openAll = function () {
var _this = this;
_this.$collapse.children('.' + _this.classes.item).each(function () {
var $tmpItem = $(this);
if (!_this._isOpen($tmpItem)) {
_this.open($tmpItem);
}
});
};
/**
* 关闭所有项
*/
Collapse.prototype.closeAll = function () {
var _this = this;
_this.$collapse.children('.' + _this.classes.item).each(function () {
var $tmpItem = $(this);
if (_this._isOpen($tmpItem)) {
_this.close($tmpItem);
}
});
};
return Collapse;
})();
/**
* =============================================================================
* ************ Collapse 折叠内容块插件 ************
* =============================================================================
*/
mdui.Collapse = (function () {
function Collapse(selector, opts) {
return new CollapsePrivate(selector, opts);
}
return Collapse;
})();
/**
* =============================================================================
* ************ Collapse 自定义属性 ************
* =============================================================================
*/
$(function () {
$('[mdui-collapse]').each(function () {
var $this = $(this);
var options = parseOptions($this.attr('mdui-collapse'));
var inst = $this.data('mdui.collapse');
if (!inst) {
inst = new mdui.Collapse($this, options);
$this.data('mdui.collapse', inst);
}
});
});
/**
* =============================================================================
* ************ Table 表格 ************
* =============================================================================
*/
(function () {
/**
* 生成 checkbox 的 HTML 结构
* @param tag
* @returns {string}
*/
var checkboxHTML = function (tag) {
return '<' + tag + ' class="mdui-table-cell-checkbox">' +
'
' +
'' + tag + '>';
};
/**
* Table 表格
* @param selector
* @constructor
*/
function Table(selector) {
var _this = this;
_this.$table = $(selector).eq(0);
if (!_this.$table.length) {
return;
}
_this.init();
}
/**
* 初始化
*/
Table.prototype.init = function () {
var _this = this;
_this.$thRow = _this.$table.find('thead tr');
_this.$tdRows = _this.$table.find('tbody tr');
_this.$tdCheckboxs = $();
_this.selectable = _this.$table.hasClass('mdui-table-selectable');
_this.selectedRow = 0;
_this._updateThCheckbox();
_this._updateTdCheckbox();
_this._updateNumericCol();
};
/**
* 更新表格行的 checkbox
*/
Table.prototype._updateTdCheckbox = function () {
var _this = this;
_this.$tdRows.each(function () {
var $tdRow = $(this);
// 移除旧的 checkbox
$tdRow.find('.mdui-table-cell-checkbox').remove();
if (!_this.selectable) {
return;
}
// 创建 DOM
var $checkbox = $(checkboxHTML('td'))
.prependTo($tdRow)
.find('input[type="checkbox"]');
// 默认选中的行
if ($tdRow.hasClass('mdui-table-row-selected')) {
$checkbox[0].checked = true;
_this.selectedRow++;
}
// 所有行都选中后,选中表头;否则,不选中表头
_this.$thCheckbox[0].checked = _this.selectedRow === _this.$tdRows.length;
// 绑定事件
$checkbox.on('change', function () {
if ($checkbox[0].checked) {
$tdRow.addClass('mdui-table-row-selected');
_this.selectedRow++;
} else {
$tdRow.removeClass('mdui-table-row-selected');
_this.selectedRow--;
}
// 所有行都选中后,选中表头;否则,不选中表头
_this.$thCheckbox[0].checked = _this.selectedRow === _this.$tdRows.length;
});
_this.$tdCheckboxs = _this.$tdCheckboxs.add($checkbox);
});
};
/**
* 更新表头的 checkbox
*/
Table.prototype._updateThCheckbox = function () {
var _this = this;
// 移除旧的 checkbox
_this.$thRow.find('.mdui-table-cell-checkbox').remove();
if (!_this.selectable) {
return;
}
_this.$thCheckbox = $(checkboxHTML('th'))
.prependTo(_this.$thRow)
.find('input[type="checkbox"]')
.on('change', function () {
var isCheckedAll = _this.$thCheckbox[0].checked;
_this.selectedRow = isCheckedAll ? _this.$tdRows.length : 0;
_this.$tdCheckboxs.each(function (i, checkbox) {
checkbox.checked = isCheckedAll;
});
_this.$tdRows.each(function (i, row) {
$(row)[isCheckedAll ? 'addClass' : 'removeClass']('mdui-table-row-selected');
});
});
};
/**
* 更新数值列
*/
Table.prototype._updateNumericCol = function () {
var _this = this;
var $th;
var $tdRow;
_this.$thRow.find('th').each(function (i, th) {
$th = $(th);
_this.$tdRows.each(function () {
$tdRow = $(this);
var method = $th.hasClass('mdui-table-col-numeric') ? 'addClass' : 'removeClass';
$tdRow.find('td').eq(i)[method]('mdui-table-col-numeric');
});
});
};
$(function () {
// 实例化表格
$('.mdui-table').each(function () {
var $table = $(this);
if (!$table.data('mdui.table')) {
$table.data('mdui.table', new Table($table));
}
});
});
/**
* 更新表格
*/
mdui.updateTables = function () {
$(arguments.length ? arguments[0] : '.mdui-table').each(function () {
var $table = $(this);
var inst = $table.data('mdui.table');
if (inst) {
inst.init();
} else {
$table.data('mdui.table', new Table($table));
}
});
};
})();
/**
* =============================================================================
* ************ 涟漪 ************
* =============================================================================
*
* Inspired by https://github.com/nolimits4web/Framework7/blob/master/src/js/fast-clicks.js
* https://github.com/nolimits4web/Framework7/blob/master/LICENSE
*
* Inspired by https://github.com/fians/Waves
*/
(function () {
var Ripple = {
/**
* 显示涟漪动画
* @param e
* @param $ripple
*/
show: function (e, $ripple) {
// 鼠标右键不产生涟漪
if (e.button === 2) {
return;
}
// 点击位置坐标
var tmp;
if ('touches' in e && e.touches.length) {
tmp = e.touches[0];
} else {
tmp = e;
}
var touchStartX = tmp.pageX;
var touchStartY = tmp.pageY;
// 涟漪位置
var offset = $ripple.offset();
var center = {
x: touchStartX - offset.left,
y: touchStartY - offset.top,
};
var height = $ripple.innerHeight();
var width = $ripple.innerWidth();
var diameter = Math.max(
Math.pow((Math.pow(height, 2) + Math.pow(width, 2)), 0.5), 48
);
// 涟漪扩散动画
var translate =
'translate3d(' + (-center.x + width / 2) + 'px, ' + (-center.y + height / 2) + 'px, 0) ' +
'scale(1)';
// 涟漪的 DOM 结构
$('
' +
'
')
// 缓存动画效果
.data('translate', translate)
.prependTo($ripple)
.reflow()
.transform(translate);
},
/**
* 隐藏涟漪动画
*/
hide: function () {
var $ripple = $(this);
$ripple.children('.mdui-ripple-wave').each(function () {
removeRipple($(this));
});
$ripple.off('touchmove touchend touchcancel mousemove mouseup mouseleave', Ripple.hide);
},
};
/**
* 隐藏并移除涟漪
* @param $wave
*/
function removeRipple($wave) {
if (!$wave.length || $wave.data('isRemoved')) {
return;
}
$wave.data('isRemoved', true);
var removeTimeout = setTimeout(function () {
$wave.remove();
}, 400);
var translate = $wave.data('translate');
$wave
.addClass('mdui-ripple-wave-fill')
.transform(translate.replace('scale(1)', 'scale(1.01)'))
.transitionEnd(function () {
clearTimeout(removeTimeout);
$wave
.addClass('mdui-ripple-wave-out')
.transform(translate.replace('scale(1)', 'scale(1.01)'));
removeTimeout = setTimeout(function () {
$wave.remove();
}, 700);
setTimeout(function () {
$wave.transitionEnd(function () {
clearTimeout(removeTimeout);
$wave.remove();
});
}, 0);
});
}
/**
* 显示涟漪,并绑定 touchend 等事件
* @param e
*/
function showRipple(e) {
if (!TouchHandler.isAllow(e)) {
return;
}
TouchHandler.register(e);
var $ripple;
var $target = $(e.target);
// 获取含 .mdui-ripple 类的元素
if ($target.hasClass('mdui-ripple')) {
$ripple = $target;
} else {
$ripple = $target.parents('.mdui-ripple').eq(0);
}
if ($ripple.length) {
// 禁用状态的元素上不产生涟漪效果
if ($ripple[0].disabled || $ripple.attr('disabled') !== null) {
return;
}
Ripple.show(e, $ripple);
$ripple.on('touchmove touchend touchcancel mousemove mouseup mouseleave', Ripple.hide);
}
}
// 初始化绑定的事件
$document
.on(TouchHandler.start, showRipple)
.on(TouchHandler.unlock, TouchHandler.register);
})();
/**
* =============================================================================
* ************ Text Field 文本框 ************
* =============================================================================
*/
(function () {
var getProp = function (obj, prop) {
return (
typeof obj === 'object' &&
obj !== null &&
typeof obj[prop] !== 'undefined' &&
obj[prop]
) ? obj[prop] : false;
};
/**
* 输入框事件
* @param e
*/
var inputEvent = function (e) {
var input = e.target;
var $input = $(input);
var event = e.type;
var value = $input.val();
// reInit 为 true 时,需要重新初始化文本框
var reInit = getProp(e.detail, 'reInit');
// domLoadedEvent 为 true 时,为 DOM 加载完毕后自动触发的事件
var domLoadedEvent = getProp(e.detail, 'domLoadedEvent');
// 文本框类型
var type = $input.attr('type') || '';
if (['checkbox', 'button', 'submit', 'range', 'radio', 'image'].indexOf(type) >= 0) {
return;
}
var $textField = $input.parent('.mdui-textfield');
// 输入框是否聚焦
if (event === 'focus') {
$textField.addClass('mdui-textfield-focus');
}
if (event === 'blur') {
$textField.removeClass('mdui-textfield-focus');
}
// 输入框是否为空
if (event === 'blur' || event === 'input') {
$textField[(value && value !== '') ? 'addClass' : 'removeClass']('mdui-textfield-not-empty');
}
// 输入框是否禁用
$textField[input.disabled ? 'addClass' : 'removeClass']('mdui-textfield-disabled');
// 表单验证
if ((event === 'input' || event === 'blur') && !domLoadedEvent) {
if (input.validity) {
$textField[input.validity.valid ? 'removeClass' : 'addClass']('mdui-textfield-invalid');
}
}
// textarea 高度自动调整
if (e.target.nodeName.toLowerCase() === 'textarea') {
$input.height('');
var height = input.offsetHeight;
var diff = height - input.clientHeight;
var scrollHeight = input.scrollHeight;
if (scrollHeight + diff > height) {
var newAreaHeight = scrollHeight + diff;
$input.height(newAreaHeight);
}
}
// 实时字数统计
if (reInit) {
$textField
.removeClass('mdui-textfield-has-counter')
.find('.mdui-textfield-counter')
.remove();
}
var maxlength = $input.attr('maxlength');
if (maxlength) {
if (reInit || domLoadedEvent) {
$('
' +
' / ' + maxlength +
'
').appendTo($textField);
// 如果没有 .mdui-textfield-error 作为占位,需要增加 .mdui-textfield 的下边距,
// 使 .mdui-textfield-counter 不会覆盖在文本框上
if (!$textField.find('.mdui-textfield-error').length) {
$textField.addClass('mdui-textfield-has-counter');
}
}
// 字符长度,确保统计方式和 maxlength 一致
var inputed = value.length + value.split('\n').length - 1;
$textField.find('.mdui-textfield-counter-inputed').text(inputed.toString());
}
};
// 绑定事件
$document.on('input focus blur', '.mdui-textfield-input', { useCapture: true }, inputEvent);
// 可展开文本框展开
$document.on('click', '.mdui-textfield-expandable .mdui-textfield-icon', function () {
$(this)
// 展开文本框
.parents('.mdui-textfield')
.addClass('mdui-textfield-expanded')
// 聚焦到输入框
.find('.mdui-textfield-input')[0].focus();
});
// 可展开文本框关闭
$document.on('click', '.mdui-textfield-expanded .mdui-textfield-close', function () {
$(this)
// 关闭文本框
.parents('.mdui-textfield')
.removeClass('mdui-textfield-expanded')
// 清空输入框
.find('.mdui-textfield-input')
.val('');
});
/**
* 通过 JS 更新了表单内容,需要重新进行表单处理
* @param- 如果传入了 .mdui-textfield 所在的 DOM 元素,则更新该文本框;否则,更新所有文本框
*/
mdui.updateTextFields = function () {
$(arguments.length ? arguments[0] : '.mdui-textfield').each(function () {
$(this)
.find('.mdui-textfield-input')
.trigger('input', {
reInit: true,
});
});
};
$(function () {
// DOM 加载完后自动执行
$('.mdui-textfield-input').each(function () {
$(this).trigger('input', {
domLoadedEvent: true,
});
});
});
})();
/**
* =============================================================================
* ************ Slider 滑块 ************
* =============================================================================
*/
(function () {
/**
* 滑块的值变更后修改滑块样式
* @param $slider
*/
var updateValueStyle = function ($slider) {
var data = $slider.data();
var $track = data.$track;
var $fill = data.$fill;
var $thumb = data.$thumb;
var $input = data.$input;
var min = data.min;
var max = data.max;
var isDisabled = data.disabled;
var isDiscrete = data.discrete;
var $thumbText = data.$thumbText;
var value = $input.val();
var percent = (value - min) / (max - min) * 100;
$fill.width(percent + '%');
$track.width((100 - percent) + '%');
if (isDisabled) {
$fill.css('padding-right', '6px');
$track.css('padding-left', '6px');
}
$thumb.css('left', percent + '%');
if (isDiscrete) {
$thumbText.text(value);
}
$slider[parseFloat(percent) === 0 ? 'addClass' : 'removeClass']('mdui-slider-zero');
};
/**
* 重新初始化
* @param $slider
*/
var reInit = function ($slider) {
var $track = $('
');
var $fill = $('
');
var $thumb = $('
');
var $input = $slider.find('input[type="range"]');
// 禁用状态
var isDisabled = $input[0].disabled;
$slider[isDisabled ? 'addClass' : 'removeClass']('mdui-slider-disabled');
// 重新填充 HTML
$slider.find('.mdui-slider-track').remove();
$slider.find('.mdui-slider-fill').remove();
$slider.find('.mdui-slider-thumb').remove();
$slider.append($track).append($fill).append($thumb);
// 间续型滑块
var isDiscrete = $slider.hasClass('mdui-slider-discrete');
var $thumbText;
if (isDiscrete) {
$thumbText = $('
');
$thumb.empty().append($thumbText);
}
$slider.data({
$track: $track,
$fill: $fill,
$thumb: $thumb,
$input: $input,
min: $input.attr('min'), // 滑块最小值
max: $input.attr('max'), // 滑块最大值
disabled: isDisabled, // 是否禁用状态
discrete: isDiscrete, // 是否是间续型滑块
$thumbText: $thumbText, // 间续型滑块的数值
});
// 设置默认值
updateValueStyle($slider);
};
var rangeSelector = '.mdui-slider input[type="range"]';
$document
// 滑动滑块事件
.on('input change', rangeSelector, function () {
var $slider = $(this).parent();
updateValueStyle($slider);
})
// 开始触摸滑块事件
.on(TouchHandler.start, rangeSelector, function (e) {
if (!TouchHandler.isAllow(e)) {
return;
}
TouchHandler.register(e);
if (!this.disabled) {
var $slider = $(this).parent();
$slider.addClass('mdui-slider-focus');
}
})
// 结束触摸滑块事件
.on(TouchHandler.end, rangeSelector, function (e) {
if (!TouchHandler.isAllow(e)) {
return;
}
if (!this.disabled) {
var $slider = $(this).parent();
$slider.removeClass('mdui-slider-focus');
}
})
.on(TouchHandler.unlock, rangeSelector, TouchHandler.register);
/**
* 页面加载完后自动初始化
*/
$(function () {
$('.mdui-slider').each(function () {
reInit($(this));
});
});
/**
* 重新初始化滑块
*/
mdui.updateSliders = function () {
$(arguments.length ? arguments[0] : '.mdui-slider').each(function () {
reInit($(this));
});
};
})();
/**
* =============================================================================
* ************ Fab 浮动操作按钮 ************
* =============================================================================
*/
mdui.Fab = (function () {
/**
* 默认参数
* @type {{}}
*/
var DEFAULT = {
trigger: 'hover', // 触发方式 ['hover', 'click']
};
/**
* 浮动操作按钮实例
* @param selector 选择器或 HTML 字符串或 DOM 元素或 JQ 对象
* @param opts
* @constructor
*/
function Fab(selector, opts) {
var _this = this;
_this.$fab = $(selector).eq(0);
if (!_this.$fab.length) {
return;
}
// 已通过 data 属性实例化过,不再重复实例化
var oldInst = _this.$fab.data('mdui.fab');
if (oldInst) {
return oldInst;
}
_this.options = $.extend({}, DEFAULT, (opts || {}));
_this.state = 'closed';
_this.$btn = _this.$fab.find('.mdui-fab');
_this.$dial = _this.$fab.find('.mdui-fab-dial');
_this.$dialBtns = _this.$dial.find('.mdui-fab');
if (_this.options.trigger === 'hover') {
_this.$btn
.on('touchstart mouseenter', function () {
_this.open();
});
_this.$fab
.on('mouseleave', function () {
_this.close();
});
}
if (_this.options.trigger === 'click') {
_this.$btn
.on(TouchHandler.start, function () {
_this.open();
});
}
// 触摸屏幕其他地方关闭快速拨号
$document.on(TouchHandler.start, function (e) {
if (!$(e.target).parents('.mdui-fab-wrapper').length) {
_this.close();
}
});
}
/**
* 打开菜单
*/
Fab.prototype.open = function () {
var _this = this;
if (_this.state === 'opening' || _this.state === 'opened') {
return;
}
// 为菜单中的按钮添加不同的 transition-delay
_this.$dialBtns.each(function (index, btn) {
btn.style['transition-delay'] = btn.style['-webkit-transition-delay'] =
15 * (_this.$dialBtns.length - index) + 'ms';
});
_this.$dial.addClass('mdui-fab-dial-show');
// 如果按钮中存在 .mdui-fab-opened 的图标,则进行图标切换
if (_this.$btn.find('.mdui-fab-opened').length) {
_this.$btn.addClass('mdui-fab-opened');
}
_this.state = 'opening';
componentEvent('open', 'fab', _this, _this.$fab);
// 打开顺序为从下到上逐个打开,最上面的打开后才表示动画完成
_this.$dialBtns.eq(0).transitionEnd(function () {
if (_this.$btn.hasClass('mdui-fab-opened')) {
_this.state = 'opened';
componentEvent('opened', 'fab', _this, _this.$fab);
}
});
};
/**
* 关闭菜单
*/
Fab.prototype.close = function () {
var _this = this;
if (_this.state === 'closing' || _this.state === 'closed') {
return;
}
// 为菜单中的按钮添加不同的 transition-delay
_this.$dialBtns.each(function (index, btn) {
btn.style['transition-delay'] = btn.style['-webkit-transition-delay'] = 15 * index + 'ms';
});
_this.$dial.removeClass('mdui-fab-dial-show');
_this.$btn.removeClass('mdui-fab-opened');
_this.state = 'closing';
componentEvent('close', 'fab', _this, _this.$fab);
// 从上往下依次关闭,最后一个关闭后才表示动画完成
_this.$dialBtns.eq(-1).transitionEnd(function () {
if (!_this.$btn.hasClass('mdui-fab-opened')) {
_this.state = 'closed';
componentEvent('closed', 'fab', _this, _this.$fab);
}
});
};
/**
* 切换菜单的打开状态
*/
Fab.prototype.toggle = function () {
var _this = this;
if (_this.state === 'opening' || _this.state === 'opened') {
_this.close();
} else if (_this.state === 'closing' || _this.state === 'closed') {
_this.open();
}
};
/**
* 获取当前菜单状态
* @returns {'opening'|'opened'|'closing'|'closed'}
*/
Fab.prototype.getState = function () {
return this.state;
};
/**
* 以动画的形式显示浮动操作按钮
*/
Fab.prototype.show = function () {
this.$fab.removeClass('mdui-fab-hide');
};
/**
* 以动画的形式隐藏浮动操作按钮
*/
Fab.prototype.hide = function () {
this.$fab.addClass('mdui-fab-hide');
};
return Fab;
})();
/**
* =============================================================================
* ************ Fab DATA API ************
* =============================================================================
*/
$(function () {
// mouseenter 不冒泡,无法进行事件委托,这里用 mouseover 代替。
// 不管是 click 、 mouseover 还是 touchstart ,都先初始化。
$document.on('touchstart mousedown mouseover', '[mdui-fab]', function (e) {
var $this = $(this);
var inst = $this.data('mdui.fab');
if (!inst) {
var options = parseOptions($this.attr('mdui-fab'));
inst = new mdui.Fab($this, options);
$this.data('mdui.fab', inst);
}
});
});
/**
* =============================================================================
* ************ Appbar ************
* =============================================================================
* 滚动时自动隐藏应用栏
* mdui-appbar-scroll-hide
* mdui-appbar-scroll-toolbar-hide
*/
$(function () {
// 滚动时隐藏应用栏
$('.mdui-appbar-scroll-hide').each(function () {
var $this = $(this);
$this.data('mdui.headroom', new mdui.Headroom($this));
});
// 滚动时只隐藏应用栏中的工具栏
$('.mdui-appbar-scroll-toolbar-hide').each(function () {
var $this = $(this);
var inst = new mdui.Headroom($this, {
pinnedClass: 'mdui-headroom-pinned-toolbar',
unpinnedClass: 'mdui-headroom-unpinned-toolbar',
});
$this.data('mdui.headroom', inst);
});
});
/**
* =============================================================================
* ************ Tab ************
* =============================================================================
*/
mdui.Tab = (function () {
var DEFAULT = {
trigger: 'click', // 触发方式 click: 鼠标点击切换 hover: 鼠标悬浮切换
//animation: false, // 切换时是否显示动画
loop: false, // 为true时,在最后一个选项卡时调用 next() 方法会回到第一个选项卡
};
// 元素是否已禁用
var isDisabled = function ($ele) {
return $ele[0].disabled || $ele.attr('disabled') !== null;
};
/**
* 选项卡
* @param selector
* @param opts
* @returns {*}
* @constructor
*/
function Tab(selector, opts) {
var _this = this;
_this.$tab = $(selector).eq(0);
if (!_this.$tab.length) {
return;
}
// 已通过自定义属性实例化过,不再重复实例化
var oldInst = _this.$tab.data('mdui.tab');
if (oldInst) {
return oldInst;
}
_this.options = $.extend({}, DEFAULT, (opts || {}));
_this.$tabs = _this.$tab.children('a');
_this.$indicator = $('
').appendTo(_this.$tab);
_this.activeIndex = false;
// 根据 url hash 获取默认激活的选项卡
var hash = location.hash;
if (hash) {
_this.$tabs.each(function (i, tab) {
if ($(tab).attr('href') === hash) {
_this.activeIndex = i;
return false;
}
});
}
// 含 mdui-tab-active 的元素默认激活
if (_this.activeIndex === false) {
_this.$tabs.each(function (i, tab) {
if ($(tab).hasClass('mdui-tab-active')) {
_this.activeIndex = i;
return false;
}
});
}
// 默认激活第一个选项卡
if (_this.activeIndex === false) {
_this.activeIndex = 0;
}
// 设置激活状态选项卡
_this._setActive();
// 监听窗口大小变化事件,调整指示器位置
$window.on('resize', $.throttle(function () {
_this._setIndicatorPosition();
}, 100));
// 监听点击选项卡事件
_this.$tabs.each(function (i, tab) {
var $tab = $(tab);
// 点击或鼠标移入触发的事件
var clickEvent = function (e) {
// 禁用状态的选项无法选中
if (isDisabled($tab)) {
e.preventDefault();
return;
}
_this.activeIndex = i;
_this._setActive();
};
// 无论 trigger 是 click 还是 hover,都会响应 click 事件
$tab.on('click', clickEvent);
// trigger 为 hover 时,额外响应 mouseenter 事件
if (_this.options.trigger === 'hover') {
$tab.on('mouseenter', clickEvent);
}
$tab.on('click', function (e) {
// 阻止链接的默认点击动作
if ($tab.attr('href').indexOf('#') === 0) {
e.preventDefault();
}
});
});
}
/**
* 设置激活状态的选项卡
*/
Tab.prototype._setActive = function () {
var _this = this;
_this.$tabs.each(function (i, tab) {
var $tab = $(tab);
var targetId = $tab.attr('href');
// 设置选项卡激活状态
if (i === _this.activeIndex && !isDisabled($tab)) {
if (!$tab.hasClass('mdui-tab-active')) {
componentEvent('change', 'tab', _this, _this.$tab, {
index: _this.activeIndex,
target: tab,
});
componentEvent('show', 'tab', _this, $tab);
$tab.addClass('mdui-tab-active');
}
$(targetId).show();
_this._setIndicatorPosition();
} else {
$tab.removeClass('mdui-tab-active');
$(targetId).hide();
}
});
};
/**
* 设置选项卡指示器的位置
*/
Tab.prototype._setIndicatorPosition = function () {
var _this = this;
var $activeTab = _this.$tabs.eq(_this.activeIndex);
if (isDisabled($activeTab)) {
return;
}
var activeTabOffset = $activeTab.offset();
_this.$indicator.css({
left: activeTabOffset.left + _this.$tab[0].scrollLeft -
_this.$tab[0].getBoundingClientRect().left + 'px',
width: $activeTab.width() + 'px',
});
};
/**
* 切换到下一个选项卡
*/
Tab.prototype.next = function () {
var _this = this;
if (_this.$tabs.length > _this.activeIndex + 1) {
_this.activeIndex++;
} else if (_this.options.loop) {
_this.activeIndex = 0;
}
_this._setActive();
};
/**
* 切换到上一个选项卡
*/
Tab.prototype.prev = function () {
var _this = this;
if (_this.activeIndex > 0) {
_this.activeIndex--;
} else if (_this.options.loop) {
_this.activeIndex = _this.$tabs.length - 1;
}
_this._setActive();
};
/**
* 显示指定序号或指定id的选项卡
* @param index 从0开始的序号,或以#开头的id
*/
Tab.prototype.show = function (index) {
var _this = this;
if (parseInt(index) === index) {
_this.activeIndex = index;
} else {
_this.$tabs.each(function (i, tab) {
if (tab.id === index) {
_this.activeIndex = i;
return false;
}
});
}
_this._setActive();
};
/**
* 在父元素的宽度变化时,需要调用该方法重新调整指示器位置
*/
Tab.prototype.handleUpdate = function () {
this._setIndicatorPosition();
};
return Tab;
})();
/**
* =============================================================================
* ************ Tab 自定义属性 API ************
* =============================================================================
*/
$(function () {
$('[mdui-tab]').each(function () {
var $this = $(this);
var inst = $this.data('mdui.tab');
if (!inst) {
inst = new mdui.Tab($this, parseOptions($this.attr('mdui-tab')));
$this.data('mdui.tab', inst);
}
});
});
/**
* =============================================================================
* ************ Drawer 抽屉栏 ************
* =============================================================================
*
* 在桌面设备上默认显示抽屉栏,不显示遮罩层
* 在手机和平板设备上默认不显示抽屉栏,始终显示遮罩层,且覆盖导航栏
*/
mdui.Drawer = (function () {
/**
* 默认参数
* @type {{}}
*/
var DEFAULT = {
// 在桌面设备上是否显示遮罩层。手机和平板不受这个参数影响,始终会显示遮罩层
overlay: false,
};
var isDesktop = function () {
return $window.width() >= 1024;
};
/**
* 抽屉栏实例
* @param selector 选择器或 HTML 字符串或 DOM 元素
* @param opts
* @constructor
*/
function Drawer(selector, opts) {
var _this = this;
_this.$drawer = $(selector).eq(0);
if (!_this.$drawer.length) {
return;
}
var oldInst = _this.$drawer.data('mdui.drawer');
if (oldInst) {
return oldInst;
}
_this.options = $.extend({}, DEFAULT, (opts || {}));
_this.overlay = false; // 是否显示着遮罩层
_this.position = _this.$drawer.hasClass('mdui-drawer-right') ? 'right' : 'left';
if (_this.$drawer.hasClass('mdui-drawer-close')) {
_this.state = 'closed';
} else if (_this.$drawer.hasClass('mdui-drawer-open')) {
_this.state = 'opened';
} else if (isDesktop()) {
_this.state = 'opened';
} else {
_this.state = 'closed';
}
// 浏览器窗口大小调整时
$window.on('resize', $.throttle(function () {
// 由手机平板切换到桌面时
if (isDesktop()) {
// 如果显示着遮罩,则隐藏遮罩
if (_this.overlay && !_this.options.overlay) {
$.hideOverlay();
_this.overlay = false;
$.unlockScreen();
}
// 没有强制关闭,则状态为打开状态
if (!_this.$drawer.hasClass('mdui-drawer-close')) {
_this.state = 'opened';
}
}
// 由桌面切换到手机平板时。如果抽屉栏是打开着的且没有遮罩层,则关闭抽屉栏
else {
if (!_this.overlay && _this.state === 'opened') {
// 抽屉栏处于强制打开状态,添加遮罩
if (_this.$drawer.hasClass('mdui-drawer-open')) {
$.showOverlay();
_this.overlay = true;
$.lockScreen();
$('.mdui-overlay').one('click', function () {
_this.close();
});
} else {
_this.state = 'closed';
}
}
}
}, 100));
// 绑定关闭按钮事件
_this.$drawer.find('[mdui-drawer-close]').each(function () {
$(this).on('click', function () {
_this.close();
});
});
}
/**
* 动画结束回调
* @param inst
*/
var transitionEnd = function (inst) {
if (inst.$drawer.hasClass('mdui-drawer-open')) {
inst.state = 'opened';
componentEvent('opened', 'drawer', inst, inst.$drawer);
} else {
inst.state = 'closed';
componentEvent('closed', 'drawer', inst, inst.$drawer);
}
};
/**
* 打开抽屉栏
*/
Drawer.prototype.open = function () {
var _this = this;
if (_this.state === 'opening' || _this.state === 'opened') {
return;
}
_this.state = 'opening';
componentEvent('open', 'drawer', _this, _this.$drawer);
if (!_this.options.overlay) {
$body.addClass('mdui-drawer-body-' + _this.position);
}
_this.$drawer
.removeClass('mdui-drawer-close')
.addClass('mdui-drawer-open')
.transitionEnd(function () {
transitionEnd(_this);
});
if (!isDesktop() || _this.options.overlay) {
_this.overlay = true;
$.showOverlay().one('click', function () {
_this.close();
});
$.lockScreen();
}
};
/**
* 关闭抽屉栏
*/
Drawer.prototype.close = function () {
var _this = this;
if (_this.state === 'closing' || _this.state === 'closed') {
return;
}
_this.state = 'closing';
componentEvent('close', 'drawer', _this, _this.$drawer);
if (!_this.options.overlay) {
$body.removeClass('mdui-drawer-body-' + _this.position);
}
_this.$drawer
.addClass('mdui-drawer-close')
.removeClass('mdui-drawer-open')
.transitionEnd(function () {
transitionEnd(_this);
});
if (_this.overlay) {
$.hideOverlay();
_this.overlay = false;
$.unlockScreen();
}
};
/**
* 切换抽屉栏打开/关闭状态
*/
Drawer.prototype.toggle = function () {
var _this = this;
if (_this.state === 'opening' || _this.state === 'opened') {
_this.close();
} else if (_this.state === 'closing' || _this.state === 'closed') {
_this.open();
}
};
/**
* 获取抽屉栏状态
* @returns {'opening'|'opened'|'closing'|'closed'}
*/
Drawer.prototype.getState = function () {
return this.state;
};
return Drawer;
})();
/**
* =============================================================================
* ************ Drawer 自定义属性 API ************
* =============================================================================
*/
$(function () {
$('[mdui-drawer]').each(function () {
var $this = $(this);
var options = parseOptions($this.attr('mdui-drawer'));
var selector = options.target;
delete options.target;
var $drawer = $(selector).eq(0);
var inst = $drawer.data('mdui.drawer');
if (!inst) {
inst = new mdui.Drawer($drawer, options);
$drawer.data('mdui.drawer', inst);
}
$this.on('click', function () {
inst.toggle();
});
});
});
/**
* =============================================================================
* ************ Dialog 对话框 ************
* =============================================================================
*/
mdui.Dialog = (function () {
/**
* 默认参数
*/
var DEFAULT = {
history: true, // 监听 hashchange 事件
overlay: true, // 打开对话框时是否显示遮罩
modal: false, // 是否模态化对话框,为 false 时点击对话框外面区域关闭对话框,为 true 时不关闭
closeOnEsc: true, // 按下 esc 关闭对话框
closeOnCancel: true, // 按下取消按钮时关闭对话框
closeOnConfirm: true, // 按下确认按钮时关闭对话框
destroyOnClosed: false, // 关闭后销毁
};
/**
* 遮罩层元素
*/
var $overlay;
/**
* 窗口是否已锁定
*/
var isLockScreen;
/**
* 当前对话框实例
*/
var currentInst;
/**
* 队列名
* @type {string}
*/
var queueName = '__md_dialog';
/**
* 窗口宽度变化,或对话框内容变化时,调整对话框位置和对话框内的滚动条
*/
var readjust = function () {
if (!currentInst) {
return;
}
var $dialog = currentInst.$dialog;
var $dialogTitle = $dialog.children('.mdui-dialog-title');
var $dialogContent = $dialog.children('.mdui-dialog-content');
var $dialogActions = $dialog.children('.mdui-dialog-actions');
// 调整 dialog 的 top 和 height 值
$dialog.height('');
$dialogContent.height('');
var dialogHeight = $dialog.height();
$dialog.css({
top: (($window.height() - dialogHeight) / 2) + 'px',
height: dialogHeight + 'px',
});
// 调整 mdui-dialog-content 的高度
$dialogContent.height(
dialogHeight -
($dialogTitle.height() || 0) -
($dialogActions.height() || 0)
);
};
/**
* hashchange 事件触发时关闭对话框
*/
var hashchangeEvent = function () {
if (location.hash.substring(1).indexOf('&mdui-dialog') < 0) {
currentInst.close(true);
}
};
/**
* 点击遮罩层关闭对话框
* @param e
*/
var overlayClick = function (e) {
if ($(e.target).hasClass('mdui-overlay')) {
currentInst.close();
}
};
/**
* 对话框实例
* @param selector 选择器或 HTML 字符串或 DOM 元素
* @param opts
* @constructor
*/
function Dialog(selector, opts) {
var _this = this;
// 对话框元素
_this.$dialog = $(selector).eq(0);
if (!_this.$dialog.length) {
return;
}
// 已通过 data 属性实例化过,不再重复实例化
var oldInst = _this.$dialog.data('mdui.dialog');
if (oldInst) {
return oldInst;
}
// 如果对话框元素没有在当前文档中,则需要添加
if (!$.contains($body[0], _this.$dialog[0])) {
_this.append = true;
$body.append(_this.$dialog);
}
_this.options = $.extend({}, DEFAULT, (opts || {}));
_this.state = 'closed';
// 绑定取消按钮事件
_this.$dialog.find('[mdui-dialog-cancel]').each(function () {
$(this).on('click', function () {
componentEvent('cancel', 'dialog', _this, _this.$dialog);
if (_this.options.closeOnCancel) {
_this.close();
}
});
});
// 绑定确认按钮事件
_this.$dialog.find('[mdui-dialog-confirm]').each(function () {
$(this).on('click', function () {
componentEvent('confirm', 'dialog', _this, _this.$dialog);
if (_this.options.closeOnConfirm) {
_this.close();
}
});
});
// 绑定关闭按钮事件
_this.$dialog.find('[mdui-dialog-close]').each(function () {
$(this).on('click', function () {
_this.close();
});
});
}
/**
* 打开指定对话框
* @private
*/
Dialog.prototype._doOpen = function () {
var _this = this;
currentInst = _this;
if (!isLockScreen) {
$.lockScreen();
isLockScreen = true;
}
_this.$dialog.show();
readjust();
$window.on('resize', $.throttle(function () {
readjust();
}, 100));
// 打开消息框
_this.state = 'opening';
componentEvent('open', 'dialog', _this, _this.$dialog);
_this.$dialog
.addClass('mdui-dialog-open')
.transitionEnd(function () {
if (_this.$dialog.hasClass('mdui-dialog-open')) {
_this.state = 'opened';
componentEvent('opened', 'dialog', _this, _this.$dialog);
} else {
_this.state = 'closed';
componentEvent('closed', 'dialog', _this, _this.$dialog);
}
});
// 不存在遮罩层元素时,添加遮罩层
if (!$overlay) {
$overlay = $.showOverlay(5100);
}
$overlay
// 点击遮罩层时是否关闭对话框
[_this.options.modal ? 'off' : 'on']('click', overlayClick)
// 是否显示遮罩层,不显示时,把遮罩层背景透明
.css('opacity', _this.options.overlay ? '' : 0);
if (_this.options.history) {
// 如果 hash 中原来就有 &mdui-dialog,先删除,避免后退历史纪录后仍然有 &mdui-dialog 导致无法关闭
var hash = location.hash.substring(1);
if (hash.indexOf('&mdui-dialog') > -1) {
hash = hash.replace(/&mdui-dialog/g, '');
}
// 后退按钮关闭对话框
location.hash = hash + '&mdui-dialog';
$window.on('hashchange', hashchangeEvent);
}
};
/**
* 打开对话框
*/
Dialog.prototype.open = function () {
var _this = this;
if (_this.state === 'opening' || _this.state === 'opened') {
return;
}
// 如果当前有正在打开或已经打开的对话框,或队列不为空,则先加入队列,等旧对话框开始关闭时再打开
if (
(currentInst && (currentInst.state === 'opening' || currentInst.state === 'opened')) ||
queue.queue(queueName).length
) {
queue.queue(queueName, function () {
_this._doOpen();
});
return;
}
_this._doOpen();
};
/**
* 关闭对话框
*/
Dialog.prototype.close = function () {
var _this = this;
if (_this.state === 'closing' || _this.state === 'closed') {
return;
}
currentInst = null;
_this.state = 'closing';
componentEvent('close', 'dialog', _this, _this.$dialog);
// 所有对话框都关闭,且当前没有打开的对话框时,隐藏遮罩
if (queue.queue(queueName).length === 0 && $overlay) {
$.hideOverlay();
$overlay = null;
}
_this.$dialog
.removeClass('mdui-dialog-open')
.transitionEnd(function () {
if (!_this.$dialog.hasClass('mdui-dialog-open')) {
_this.state = 'closed';
componentEvent('closed', 'dialog', _this, _this.$dialog);
_this.$dialog.hide();
// 所有对话框都关闭,且当前没有打开的对话框时,解锁屏幕
if (queue.queue(queueName).length === 0 && !currentInst && isLockScreen) {
$.unlockScreen();
isLockScreen = false;
}
$window.off('resize', $.throttle(function () {
readjust();
}, 100));
if (_this.options.destroyOnClosed) {
_this.destroy();
}
} else {
_this.state = 'opened';
componentEvent('opened', 'dialog', _this, _this.$dialog);
}
});
if (_this.options.history && queue.queue(queueName).length === 0) {
// 是否需要后退历史纪录,默认为 false。
// 为 false 时是通过 js 关闭,需要后退一个历史记录
// 为 true 时是通过后退按钮关闭,不需要后退历史记录
if (!arguments[0]) {
window.history.back();
}
$window.off('hashchange', hashchangeEvent);
}
// 关闭旧对话框,打开新对话框。
// 加一点延迟,仅仅为了视觉效果更好。不加延时也不影响功能
setTimeout(function () {
queue.dequeue(queueName);
}, 100);
};
/**
* 切换对话框打开/关闭状态
*/
Dialog.prototype.toggle = function () {
var _this = this;
if (_this.state === 'opening' || _this.state === 'opened') {
_this.close();
} else if (_this.state === 'closing' || _this.state === 'closed') {
_this.open();
}
};
/**
* 获取对话框状态
* @returns {'opening'|'opened'|'closing'|'closed'}
*/
Dialog.prototype.getState = function () {
return this.state;
};
/**
* 销毁对话框
*/
Dialog.prototype.destroy = function () {
var _this = this;
if (_this.append) {
_this.$dialog.remove();
}
_this.$dialog.removeData('mdui.dialog');
if (queue.queue(queueName).length === 0 && !currentInst) {
if ($overlay) {
$.hideOverlay();
$overlay = null;
}
if (isLockScreen) {
$.unlockScreen();
isLockScreen = false;
}
}
};
/**
* 对话框内容变化时,需要调用该方法来调整对话框位置和滚动条高度
*/
Dialog.prototype.handleUpdate = function () {
readjust();
};
// esc 按下时关闭对话框
$document.on('keydown', function (e) {
if (
currentInst &&
currentInst.options.closeOnEsc &&
currentInst.state === 'opened' &&
e.keyCode === 27
) {
currentInst.close();
}
});
return Dialog;
})();
/**
* =============================================================================
* ************ Dialog DATA API ************
* =============================================================================
*/
$(function () {
$document.on('click', '[mdui-dialog]', function () {
var $this = $(this);
var options = parseOptions($this.attr('mdui-dialog'));
var selector = options.target;
delete options.target;
var $dialog = $(selector).eq(0);
var inst = $dialog.data('mdui.dialog');
if (!inst) {
inst = new mdui.Dialog($dialog, options);
$dialog.data('mdui.dialog', inst);
}
inst.open();
});
});
/**
* =============================================================================
* ************ mdui.dialog(options) ************
* =============================================================================
*/
mdui.dialog = function (options) {
/**
* 默认参数
*/
var DEFAULT = {
title: '', // 标题
content: '', // 文本
buttons: [], // 按钮
stackedButtons: false, // 垂直排列按钮
cssClass: '', // 在 Dialog 上添加的 CSS 类
history: true, // 监听 hashchange 事件
overlay: true, // 是否显示遮罩
modal: false, // 是否模态化对话框
closeOnEsc: true, // 按下 esc 时关闭对话框
destroyOnClosed: true, // 关闭后销毁
onOpen: function () { // 打开动画开始时的回调
},
onOpened: function () { // 打开动画结束后的回调
},
onClose: function () { // 关闭动画开始时的回调
},
onClosed: function () { // 关闭动画结束时的回调
},
};
/**
* 按钮的默认参数
*/
var DEFAULT_BUTTON = {
text: '', // 按钮文本
bold: false, // 按钮文本是否加粗
close: true, // 点击按钮后关闭对话框
onClick: function (inst) { // 点击按钮的回调
},
};
// 合并参数
options = $.extend({}, DEFAULT, (options || {}));
$.each(options.buttons, function (i, button) {
options.buttons[i] = $.extend({}, DEFAULT_BUTTON, button);
});
// 按钮的 HTML
var buttonsHTML = '';
if (options.buttons.length) {
buttonsHTML =
'
';
}
// Dialog 的 HTML
var HTML =
'
' +
(options.title ? '
' + options.title + '
' : '') +
(options.content ? '
' + options.content + '
' : '') +
buttonsHTML +
'
';
// 实例化 Dialog
var inst = new mdui.Dialog(HTML, {
history: options.history,
overlay: options.overlay,
modal: options.modal,
closeOnEsc: options.closeOnEsc,
destroyOnClosed: options.destroyOnClosed,
});
// 绑定按钮事件
if (options.buttons.length) {
inst.$dialog.find('.mdui-dialog-actions .mdui-btn').each(function (i, button) {
$(button).on('click', function () {
if (typeof options.buttons[i].onClick === 'function') {
options.buttons[i].onClick(inst);
}
if (options.buttons[i].close) {
inst.close();
}
});
});
}
// 绑定打开关闭事件
if (typeof options.onOpen === 'function') {
inst.$dialog
.on('open.mdui.dialog', function () {
options.onOpen(inst);
})
.on('opened.mdui.dialog', function () {
options.onOpened(inst);
})
.on('close.mdui.dialog', function () {
options.onClose(inst);
})
.on('closed.mdui.dialog', function () {
options.onClosed(inst);
});
}
inst.open();
return inst;
};
/**
* =============================================================================
* ************ mdui.alert(text, title, onConfirm, options) ************
* ************ mdui.alert(text, onConfirm, options) ************
* =============================================================================
*/
mdui.alert = function (text, title, onConfirm, options) {
// title 参数可选
if (typeof title === 'function') {
title = '';
onConfirm = arguments[1];
options = arguments[2];
}
if (onConfirm === undefined) {
onConfirm = function () {};
}
if (options === undefined) {
options = {};
}
/**
* 默认参数
*/
var DEFAULT = {
confirmText: 'ok', // 按钮上的文本
history: true, // 监听 hashchange 事件
modal: false, // 是否模态化对话框,为 false 时点击对话框外面区域关闭对话框,为 true 时不关闭
closeOnEsc: true, // 按下 esc 关闭对话框
};
options = $.extend({}, DEFAULT, options);
return mdui.dialog({
title: title,
content: text,
buttons: [
{
text: options.confirmText,
bold: false,
close: true,
onClick: onConfirm,
},
],
cssClass: 'mdui-dialog-alert',
history: options.history,
modal: options.modal,
closeOnEsc: options.closeOnEsc,
});
};
/**
* =============================================================================
* ************ mdui.confirm(text, title, onConfirm, onCancel, options) ************
* ************ mdui.confirm(text, onConfirm, onCancel, options) ************
* =============================================================================
*/
mdui.confirm = function (text, title, onConfirm, onCancel, options) {
// title 参数可选
if (typeof title === 'function') {
title = '';
onConfirm = arguments[1];
onCancel = arguments[2];
options = arguments[3];
}
if (onConfirm === undefined) {
onConfirm = function () {};
}
if (onCancel === undefined) {
onCancel = function () {};
}
if (options === undefined) {
options = {};
}
/**
* 默认参数
*/
var DEFAULT = {
confirmText: 'ok', // 确认按钮的文本
cancelText: 'cancel', // 取消按钮的文本
history: true, // 监听 hashchange 事件
modal: false, // 是否模态化对话框,为 false 时点击对话框外面区域关闭对话框,为 true 时不关闭
closeOnEsc: true, // 按下 esc 关闭对话框
};
options = $.extend({}, DEFAULT, options);
return mdui.dialog({
title: title,
content: text,
buttons: [
{
text: options.cancelText,
bold: false,
close: true,
onClick: onCancel,
},
{
text: options.confirmText,
bold: false,
close: true,
onClick: onConfirm,
},
],
cssClass: 'mdui-dialog-confirm',
history: options.history,
modal: options.modal,
closeOnEsc: options.closeOnEsc,
});
};
/**
* =============================================================================
* ************ mdui.prompt(label, title, onConfirm, onCancel, options) ************
* ************ mdui.prompt(label, onConfirm, onCancel, options) ************
* =============================================================================
*/
mdui.prompt = function (label, title, onConfirm, onCancel, options) {
// title 参数可选
if (typeof title === 'function') {
title = '';
onConfirm = arguments[1];
onCancel = arguments[2];
options = arguments[3];
}
if (onConfirm === undefined) {
onConfirm = function () {};
}
if (onCancel === undefined) {
onCancel = function () {};
}
if (options === undefined) {
options = {};
}
/**
* 默认参数
*/
var DEFAULT = {
confirmText: 'ok', // 确认按钮的文本
cancelText: 'cancel', // 取消按钮的文本
history: true, // 监听 hashchange 事件
modal: false, // 是否模态化对话框,为 false 时点击对话框外面区域关闭对话框,为 true 时不关闭
closeOnEsc: true, // 按下 esc 关闭对话框
type: 'text', // 输入框类型,text: 单行文本框 textarea: 多行文本框
maxlength: '', // 最大输入字符数
defaultValue: '', // 输入框中的默认文本
};
options = $.extend({}, DEFAULT, options);
var content =
'
' +
(label ? '' : '') +
(options.type === 'text' ?
'' :
'') +
(options.type === 'textarea' ?
'' :
'') +
'
';
return mdui.dialog({
title: title,
content: content,
buttons: [
{
text: options.cancelText,
bold: false,
close: true,
onClick: function (inst) {
var value = inst.$dialog.find('.mdui-textfield-input').val();
onCancel(value, inst);
},
},
{
text: options.confirmText,
bold: false,
close: true,
onClick: function (inst) {
var value = inst.$dialog.find('.mdui-textfield-input').val();
onConfirm(value, inst);
},
},
],
cssClass: 'mdui-dialog-prompt',
history: options.history,
modal: options.modal,
closeOnEsc: options.closeOnEsc,
onOpen: function (inst) {
// 初始化输入框
var $input = inst.$dialog.find('.mdui-textfield-input');
mdui.updateTextFields($input);
// 聚焦到输入框
$input[0].focus();
// 如果是多行输入框,监听输入框的 input 事件,更新对话框高度
if (options.type === 'textarea') {
$input.on('input', function () {
inst.handleUpdate();
});
}
// 有字符数限制时,加载完文本框后 DOM 会变化,需要更新对话框高度
if (options.maxlength) {
inst.handleUpdate();
}
},
});
};
/**
* =============================================================================
* ************ ToolTip 工具提示 ************
* =============================================================================
*/
mdui.Tooltip = (function () {
/**
* 默认参数
*/
var DEFAULT = {
position: 'auto', // 提示所在位置
delay: 0, // 延迟,单位毫秒
content: '', // 提示文本,允许包含 HTML
};
/**
* 是否是桌面设备
* @returns {boolean}
*/
var isDesktop = function () {
return $window.width() > 1024;
};
/**
* 设置 Tooltip 的位置
* @param inst
*/
function setPosition(inst) {
var marginLeft;
var marginTop;
var position;
// 触发的元素
var targetProps = inst.$target[0].getBoundingClientRect();
// 触发的元素和 Tooltip 之间的距离
var targetMargin = (isDesktop() ? 14 : 24);
// Tooltip 的宽度和高度
var tooltipWidth = inst.$tooltip[0].offsetWidth;
var tooltipHeight = inst.$tooltip[0].offsetHeight;
// Tooltip 的方向
position = inst.options.position;
// 自动判断位置,加 2px,使 Tooltip 距离窗口边框至少有 2px 的间距
if (['bottom', 'top', 'left', 'right'].indexOf(position) === -1) {
if (
targetProps.top + targetProps.height + targetMargin + tooltipHeight + 2 <
$window.height()
) {
position = 'bottom';
} else if (targetMargin + tooltipHeight + 2 < targetProps.top) {
position = 'top';
} else if (targetMargin + tooltipWidth + 2 < targetProps.left) {
position = 'left';
} else if (
targetProps.width + targetMargin + tooltipWidth + 2 <
$window.width() - targetProps.left
) {
position = 'right';
} else {
position = 'bottom';
}
}
// 设置位置
switch (position) {
case 'bottom':
marginLeft = -1 * (tooltipWidth / 2);
marginTop = (targetProps.height / 2) + targetMargin;
inst.$tooltip.transformOrigin('top center');
break;
case 'top':
marginLeft = -1 * (tooltipWidth / 2);
marginTop = -1 * (tooltipHeight + (targetProps.height / 2) + targetMargin);
inst.$tooltip.transformOrigin('bottom center');
break;
case 'left':
marginLeft = -1 * (tooltipWidth + (targetProps.width / 2) + targetMargin);
marginTop = -1 * (tooltipHeight / 2);
inst.$tooltip.transformOrigin('center right');
break;
case 'right':
marginLeft = (targetProps.width / 2) + targetMargin;
marginTop = -1 * (tooltipHeight / 2);
inst.$tooltip.transformOrigin('center left');
break;
}
var targetOffset = inst.$target.offset();
inst.$tooltip.css({
top: targetOffset.top + (targetProps.height / 2) + 'px',
left: targetOffset.left + (targetProps.width / 2) + 'px',
'margin-left': marginLeft + 'px',
'margin-top': marginTop + 'px',
});
}
/**
* Tooltip 实例
* @param selector
* @param opts
* @constructor
*/
function Tooltip(selector, opts) {
var _this = this;
_this.$target = $(selector).eq(0);
if (!_this.$target.length) {
return;
}
// 已通过 data 属性实例化过,不再重复实例化
var oldInst = _this.$target.data('mdui.tooltip');
if (oldInst) {
return oldInst;
}
_this.options = $.extend({}, DEFAULT, (opts || {}));
_this.state = 'closed';
// 创建 Tooltip HTML
var guid = $.guid('tooltip');
_this.$tooltip = $(
'
' +
_this.options.content +
'
'
).appendTo($body);
// 绑定事件
_this.$target
.on('touchstart mouseenter', function (e) {
if (!TouchHandler.isAllow(e)) {
return;
}
TouchHandler.register(e);
_this.open();
})
.on('touchend mouseleave', function (e) {
if (!TouchHandler.isAllow(e)) {
return;
}
_this.close();
})
.on(TouchHandler.unlock, TouchHandler.register);
}
/**
* 动画结束回调
* @private
*/
var transitionEnd = function (inst) {
if (inst.$tooltip.hasClass('mdui-tooltip-open')) {
inst.state = 'opened';
componentEvent('opened', 'tooltip', inst, inst.$target);
} else {
inst.state = 'closed';
componentEvent('closed', 'tooltip', inst, inst.$target);
}
};
/**
* 执行打开 Tooltip
* @private
*/
Tooltip.prototype._doOpen = function () {
var _this = this;
_this.state = 'opening';
componentEvent('open', 'tooltip', _this, _this.$target);
_this.$tooltip
.addClass('mdui-tooltip-open')
.transitionEnd(function () {
transitionEnd(_this);
});
};
/**
* 打开 Tooltip
* @param opts 允许每次打开时设置不同的参数
*/
Tooltip.prototype.open = function (opts) {
var _this = this;
if (_this.state === 'opening' || _this.state === 'opened') {
return;
}
var oldOpts = _this.options;
// 合并 data 属性参数
$.extend(_this.options, parseOptions(_this.$target.attr('mdui-tooltip')));
if (opts) {
$.extend(_this.options, opts);
}
// tooltip 的内容有更新
if (oldOpts.content !== _this.options.content) {
_this.$tooltip.html(_this.options.content);
}
setPosition(_this);
if (_this.options.delay) {
_this.timeoutId = setTimeout(function () {
_this._doOpen();
}, _this.options.delay);
} else {
_this.timeoutId = false;
_this._doOpen();
}
};
/**
* 关闭 Tooltip
*/
Tooltip.prototype.close = function () {
var _this = this;
if (_this.timeoutId) {
clearTimeout(_this.timeoutId);
_this.timeoutId = false;
}
if (_this.state === 'closing' || _this.state === 'closed') {
return;
}
_this.state = 'closing';
componentEvent('close', 'tooltip', _this, _this.$target);
_this.$tooltip
.removeClass('mdui-tooltip-open')
.transitionEnd(function () {
transitionEnd(_this);
});
};
/**
* 切换 Tooltip 状态
*/
Tooltip.prototype.toggle = function () {
var _this = this;
if (_this.state === 'opening' || _this.state === 'opened') {
_this.close();
} else if (_this.state === 'closing' || _this.state === 'closed') {
_this.open();
}
};
/**
* 获取 Tooltip 状态
* @returns {'opening'|'opened'|'closing'|'closed'}
*/
Tooltip.prototype.getState = function () {
return this.state;
};
/**
* 销毁 Tooltip
*/
/*Tooltip.prototype.destroy = function () {
var _this = this;
clearTimeout(_this.timeoutId);
$.data(_this.target, 'mdui.tooltip', null);
$.remove(_this.tooltip);
};*/
return Tooltip;
})();
/**
* =============================================================================
* ************ Tooltip DATA API ************
* =============================================================================
*/
$(function () {
// mouseenter 不能冒泡,所以这里用 mouseover 代替
$document.on('touchstart mouseover', '[mdui-tooltip]', function () {
var $this = $(this);
var inst = $this.data('mdui.tooltip');
if (!inst) {
var options = parseOptions($this.attr('mdui-tooltip'));
inst = new mdui.Tooltip($this, options);
$this.data('mdui.tooltip', inst);
}
});
});
/**
* =============================================================================
* ************ Snackbar ************
* =============================================================================
*/
(function () {
/**
* 当前打开着的 Snackbar
*/
var currentInst;
/**
* 对列名
* @type {string}
*/
var queueName = '__md_snackbar';
var DEFAULT = {
message: '', // 文本内容
timeout: 4000, // 在用户没有操作时多长时间自动隐藏
buttonText: '', // 按钮的文本
buttonColor: '', // 按钮的颜色,支持 blue #90caf9 rgba(...)
closeOnButtonClick: true, // 点击按钮时关闭
closeOnOutsideClick: true, // 触摸或点击屏幕其他地方时关闭
onClick: function () { // 在 Snackbar 上点击的回调
},
onButtonClick: function () { // 点击按钮的回调
},
onClose: function () { // 关闭动画开始时的回调
},
};
/**
* 点击 Snackbar 外面的区域关闭
* @param e
*/
var closeOnOutsideClick = function (e) {
var $target = $(e.target);
if (!$target.hasClass('mdui-snackbar') && !$target.parents('.mdui-snackbar').length) {
currentInst.close();
}
};
/**
* Snackbar 实例
* @param opts
* @constructor
*/
function Snackbar(opts) {
var _this = this;
_this.options = $.extend({}, DEFAULT, (opts || {}));
// message 参数必须
if (!_this.options.message) {
return;
}
_this.state = 'closed';
_this.timeoutId = false;
// 按钮颜色
var buttonColorStyle = '';
var buttonColorClass = '';
if (
_this.options.buttonColor.indexOf('#') === 0 ||
_this.options.buttonColor.indexOf('rgb') === 0
) {
buttonColorStyle = 'style="color:' + _this.options.buttonColor + '"';
} else if (_this.options.buttonColor !== '') {
buttonColorClass = 'mdui-text-color-' + _this.options.buttonColor;
}
// 添加 HTML
_this.$snackbar = $(
'
')
.appendTo($body);
// 设置位置
_this.$snackbar
.transform('translateY(' + _this.$snackbar[0].clientHeight + 'px)')
.css('left', (document.body.clientWidth - _this.$snackbar[0].clientWidth) / 2 + 'px')
.addClass('mdui-snackbar-transition');
}
/**
* 打开 Snackbar
*/
Snackbar.prototype.open = function () {
var _this = this;
if (_this.state === 'opening' || _this.state === 'opened') {
return;
}
// 如果当前有正在显示的 Snackbar,则先加入队列,等旧 Snackbar 关闭后再打开
if (currentInst) {
queue.queue(queueName, function () {
_this.open();
});
return;
}
currentInst = _this;
// 开始打开
_this.state = 'opening';
_this.$snackbar
.transform('translateY(0)')
.transitionEnd(function () {
if (_this.state !== 'opening') {
return;
}
_this.state = 'opened';
// 有按钮时绑定事件
if (_this.options.buttonText) {
_this.$snackbar
.find('.mdui-snackbar-action')
.on('click', function () {
_this.options.onButtonClick();
if (_this.options.closeOnButtonClick) {
_this.close();
}
});
}
// 点击 snackbar 的事件
_this.$snackbar.on('click', function (e) {
if (!$(e.target).hasClass('mdui-snackbar-action')) {
_this.options.onClick();
}
});
// 点击 Snackbar 外面的区域关闭
if (_this.options.closeOnOutsideClick) {
$document.on(TouchHandler.start, closeOnOutsideClick);
}
// 超时后自动关闭
_this.timeoutId = setTimeout(function () {
_this.close();
}, _this.options.timeout);
});
};
/**
* 关闭 Snackbar
*/
Snackbar.prototype.close = function () {
var _this = this;
if (_this.state === 'closing' || _this.state === 'closed') {
return;
}
if (_this.timeoutId) {
clearTimeout(_this.timeoutId);
}
if (_this.options.closeOnOutsideClick) {
$document.off(TouchHandler.start, closeOnOutsideClick);
}
_this.state = 'closing';
_this.options.onClose();
_this.$snackbar
.transform('translateY(' + _this.$snackbar[0].clientHeight + 'px)')
.transitionEnd(function () {
if (_this.state !== 'closing') {
return;
}
currentInst = null;
_this.state = 'closed';
_this.$snackbar.remove();
queue.dequeue(queueName);
});
};
/**
* 打开 Snackbar
* @param params
*/
mdui.snackbar = function (params) {
var inst = new Snackbar(params);
inst.open();
return inst;
};
})();
/**
* =============================================================================
* ************ Bottom navigation 底部导航栏 ************
* =============================================================================
*/
(function () {
// 切换导航项
$document.on('click', '.mdui-bottom-nav>a', function () {
var $this = $(this);
var $bottomNav = $this.parent();
var isThis;
$bottomNav.children('a').each(function (i, item) {
isThis = $this.is(item);
if (isThis) {
componentEvent('change', 'bottomNav', null, $bottomNav, {
index: i,
});
}
$(item)[isThis ? 'addClass' : 'removeClass']('mdui-bottom-nav-active');
});
});
// 滚动时隐藏 mdui-bottom-nav-scroll-hide
$('.mdui-bottom-nav-scroll-hide').each(function () {
var $this = $(this);
var inst = new mdui.Headroom($this, {
pinnedClass: 'mdui-headroom-pinned-down',
unpinnedClass: 'mdui-headroom-unpinned-down',
});
$this.data('mdui.headroom', inst);
});
})();
/**
* =============================================================================
* ************ Spinner 圆形进度条 ************
* =============================================================================
*/
(function () {
/**
* layer 的 HTML 结构
*/
var layerHTML = function () {
var i = arguments.length ? arguments[0] : false;
return '
';
};
/**
* 填充 HTML
* @param spinner
*/
var fillHTML = function (spinner) {
var $spinner = $(spinner);
var layer;
if ($spinner.hasClass('mdui-spinner-colorful')) {
layer = layerHTML('1') + layerHTML('2') + layerHTML('3') + layerHTML('4');
} else {
layer = layerHTML();
}
$spinner.html(layer);
};
/**
* 页面加载完后自动填充 HTML 结构
*/
$(function () {
$('.mdui-spinner').each(function () {
fillHTML(this);
});
});
/**
* 更新圆形进度条
*/
mdui.updateSpinners = function () {
$(arguments.length ? arguments[0] : '.mdui-spinner').each(function () {
fillHTML(this);
});
};
})();
/**
* =============================================================================
* ************ Expansion panel 可扩展面板 ************
* =============================================================================
*/
mdui.Panel = (function () {
function Panel(selector, opts) {
return new CollapsePrivate(selector, opts, {
item: 'mdui-panel-item',
itemOpen: 'mdui-panel-item-open',
header: 'mdui-panel-item-header',
body: 'mdui-panel-item-body',
}, 'panel');
}
return Panel;
})();
/**
* =============================================================================
* ************ Expansion panel 自定义属性 ************
* =============================================================================
*/
$(function () {
$('[mdui-panel]').each(function () {
var $target = $(this);
var inst = $target.data('mdui.panel');
if (!inst) {
var options = parseOptions($target.attr('mdui-panel'));
inst = new mdui.Panel($target, options);
$target.data('mdui.panel', inst);
}
});
});
/**
* =============================================================================
* ************ Menu 菜单 ************
* =============================================================================
*/
mdui.Menu = (function () {
/**
* 默认参数
*/
var DEFAULT = {
position: 'auto', // 菜单位置 top、bottom、center、auto
align: 'auto', // 菜单和触发它的元素的对齐方式 left、right、center、auto
gutter: 16, // 菜单距离窗口边缘的最小距离,单位 px
fixed: false, // 是否使菜单固定在窗口,不随滚动条滚动
covered: 'auto', // 菜单是否覆盖在触发它的元素上,true、false。auto 时简单菜单覆盖,级联菜单不覆盖
subMenuTrigger: 'hover', // 子菜单的触发方式 hover、click
subMenuDelay: 200, // 子菜单的触发延时,仅在 submenuTrigger 为 hover 有效
};
/**
* 调整主菜单位置
* @param _this 实例
*/
var readjust = function (_this) {
var menuLeft;
var menuTop;
// 菜单位置和方向
var position;
var align;
// window 窗口的宽度和高度
var windowHeight = $window.height();
var windowWidth = $window.width();
// 配置参数
var gutter = _this.options.gutter;
var isCovered = _this.isCovered;
var isFixed = _this.options.fixed;
// 动画方向参数
var transformOriginX;
var transformOriginY;
// 菜单的原始宽度和高度
var menuWidth = _this.$menu.width();
var menuHeight = _this.$menu.height();
var $anchor = _this.$anchor;
// 触发菜单的元素在窗口中的位置
var anchorTmp = $anchor[0].getBoundingClientRect();
var anchorTop = anchorTmp.top;
var anchorLeft = anchorTmp.left;
var anchorHeight = anchorTmp.height;
var anchorWidth = anchorTmp.width;
var anchorBottom = windowHeight - anchorTop - anchorHeight;
var anchorRight = windowWidth - anchorLeft - anchorWidth;
// 触发元素相对其拥有定位属性的父元素的位置
var anchorOffsetTop = $anchor[0].offsetTop;
var anchorOffsetLeft = $anchor[0].offsetLeft;
// ===============================
// ================= 自动判断菜单位置
// ===============================
if (_this.options.position === 'auto') {
// 判断下方是否放得下菜单
if (anchorBottom + (isCovered ? anchorHeight : 0) > menuHeight + gutter) {
position = 'bottom';
}
// 判断上方是否放得下菜单
else if (anchorTop + (isCovered ? anchorHeight : 0) > menuHeight + gutter) {
position = 'top';
}
// 上下都放不下,居中显示
else {
position = 'center';
}
} else {
position = _this.options.position;
}
// ===============================
// ============== 自动判断菜单对齐方式
// ===============================
if (_this.options.align === 'auto') {
// 判断右侧是否放得下菜单
if (anchorRight + anchorWidth > menuWidth + gutter) {
align = 'left';
}
// 判断左侧是否放得下菜单
else if (anchorLeft + anchorWidth > menuWidth + gutter) {
align = 'right';
}
// 左右都放不下,居中显示
else {
align = 'center';
}
} else {
align = _this.options.align;
}
// ===============================
// ==================== 设置菜单位置
// ===============================
if (position === 'bottom') {
transformOriginY = '0';
menuTop =
(isCovered ? 0 : anchorHeight) +
(isFixed ? anchorTop : anchorOffsetTop);
} else if (position === 'top') {
transformOriginY = '100%';
menuTop =
(isCovered ? anchorHeight : 0) +
(isFixed ? (anchorTop - menuHeight) : (anchorOffsetTop - menuHeight));
} else {
transformOriginY = '50%';
// =====================在窗口中居中
// 显示的菜单的高度,简单菜单高度不超过窗口高度,若超过了则在菜单内部显示滚动条
// 级联菜单内部不允许出现滚动条
var menuHeightTemp = menuHeight;
// 简单菜单比窗口高时,限制菜单高度
if (!_this.$menu.hasClass('mdui-menu-cascade')) {
if (menuHeight + gutter * 2 > windowHeight) {
menuHeightTemp = windowHeight - gutter * 2;
_this.$menu.height(menuHeightTemp);
}
}
menuTop =
(windowHeight - menuHeightTemp) / 2 +
(isFixed ? 0 : (anchorOffsetTop - anchorTop));
}
_this.$menu.css('top', menuTop + 'px');
// ===============================
// ================= 设置菜单对齐方式
// ===============================
if (align === 'left') {
transformOriginX = '0';
menuLeft = isFixed ? anchorLeft : anchorOffsetLeft;
} else if (align === 'right') {
transformOriginX = '100%';
menuLeft = isFixed ?
(anchorLeft + anchorWidth - menuWidth) :
(anchorOffsetLeft + anchorWidth - menuWidth);
} else {
transformOriginX = '50%';
//=======================在窗口中居中
// 显示的菜单的宽度,菜单宽度不能超过窗口宽度
var menuWidthTemp = menuWidth;
// 菜单比窗口宽,限制菜单宽度
if (menuWidth + gutter * 2 > windowWidth) {
menuWidthTemp = windowWidth - gutter * 2;
_this.$menu.width(menuWidthTemp);
}
menuLeft =
(windowWidth - menuWidthTemp) / 2 +
(isFixed ? 0 : anchorOffsetLeft - anchorLeft);
}
_this.$menu.css('left', menuLeft + 'px');
// 设置菜单动画方向
_this.$menu.transformOrigin(transformOriginX + ' ' + transformOriginY);
};
/**
* 调整子菜单的位置
* @param $submenu
*/
var readjustSubmenu = function ($submenu) {
var $item = $submenu.parent('.mdui-menu-item');
var submenuTop;
var submenuLeft;
// 子菜单位置和方向
var position; // top、bottom
var align; // left、right
// window 窗口的宽度和高度
var windowHeight = $window.height();
var windowWidth = $window.width();
// 动画方向参数
var transformOriginX;
var transformOriginY;
// 子菜单的原始宽度和高度
var submenuWidth = $submenu.width();
var submenuHeight = $submenu.height();
// 触发子菜单的菜单项的宽度高度
var itemTmp = $item[0].getBoundingClientRect();
var itemWidth = itemTmp.width;
var itemHeight = itemTmp.height;
var itemLeft = itemTmp.left;
var itemTop = itemTmp.top;
// ===================================
// ===================== 判断菜单上下位置
// ===================================
// 判断下方是否放得下菜单
if (windowHeight - itemTop > submenuHeight) {
position = 'bottom';
}
// 判断上方是否放得下菜单
else if (itemTop + itemHeight > submenuHeight) {
position = 'top';
}
// 默认放在下方
else {
position = 'bottom';
}
// ====================================
// ====================== 判断菜单左右位置
// ====================================
// 判断右侧是否放得下菜单
if (windowWidth - itemLeft - itemWidth > submenuWidth) {
align = 'left';
}
// 判断左侧是否放得下菜单
else if (itemLeft > submenuWidth) {
align = 'right';
}
// 默认放在右侧
else {
align = 'left';
}
// ===================================
// ======================== 设置菜单位置
// ===================================
if (position === 'bottom') {
transformOriginY = '0';
submenuTop = '0';
} else if (position === 'top') {
transformOriginY = '100%';
submenuTop = -submenuHeight + itemHeight;
}
$submenu.css('top', submenuTop + 'px');
// ===================================
// ===================== 设置菜单对齐方式
// ===================================
if (align === 'left') {
transformOriginX = '0';
submenuLeft = itemWidth;
} else if (align === 'right') {
transformOriginX = '100%';
submenuLeft = -submenuWidth;
}
$submenu.css('left', submenuLeft + 'px');
// 设置菜单动画方向
$submenu.transformOrigin(transformOriginX + ' ' + transformOriginY);
};
/**
* 打开子菜单
* @param $submenu
*/
var openSubMenu = function ($submenu) {
readjustSubmenu($submenu);
$submenu
.addClass('mdui-menu-open')
.parent('.mdui-menu-item')
.addClass('mdui-menu-item-active');
};
/**
* 关闭子菜单,及其嵌套的子菜单
* @param $submenu
*/
var closeSubMenu = function ($submenu) {
// 关闭子菜单
$submenu
.removeClass('mdui-menu-open')
// 移除激活状态的样式
.parent('.mdui-menu-item')
.removeClass('mdui-menu-item-active');
// 循环关闭嵌套的子菜单
$submenu.find('.mdui-menu').each(function () {
$(this)
.removeClass('mdui-menu-open')
.parent('.mdui-menu-item')
.removeClass('mdui-menu-item-active');
});
};
/**
* 切换子菜单状态
* @param $submenu
*/
var toggleSubMenu = function ($submenu) {
if ($submenu.hasClass('mdui-menu-open')) {
closeSubMenu($submenu);
} else {
openSubMenu($submenu);
}
};
/**
* 绑定子菜单事件
* @param inst 实例
*/
var bindSubMenuEvent = function (inst) {
// 点击打开子菜单
inst.$menu.on('click', '.mdui-menu-item', function (e) {
var $this = $(this);
var $target = $(e.target);
// 禁用状态菜单不操作
if ($this.attr('disabled') !== null) {
return;
}
// 没有点击在子菜单的菜单项上时,不操作(点在了子菜单的空白区域、或分隔线上)
if ($target.is('.mdui-menu') || $target.is('.mdui-divider')) {
return;
}
// 阻止冒泡,点击菜单项时只在最后一级的 mdui-menu-item 上生效,不向上冒泡
if (!$target.parents('.mdui-menu-item').eq(0).is($this)) {
return;
}
// 当前菜单的子菜单
var $submenu = $this.children('.mdui-menu');
// 先关闭除当前子菜单外的所有同级子菜单
$this.parent('.mdui-menu').children('.mdui-menu-item').each(function () {
var $tmpSubmenu = $(this).children('.mdui-menu');
if (
$tmpSubmenu.length &&
(!$submenu.length || !$tmpSubmenu.is($submenu))
) {
closeSubMenu($tmpSubmenu);
}
});
// 切换当前子菜单
if ($submenu.length) {
toggleSubMenu($submenu);
}
});
if (inst.options.subMenuTrigger === 'hover') {
// 临时存储 setTimeout 对象
var timeout;
var timeoutOpen;
var timeoutClose;
inst.$menu.on('mouseover mouseout', '.mdui-menu-item', function (e) {
var $this = $(this);
var eventType = e.type;
var $relatedTarget = $(e.relatedTarget);
// 禁用状态的菜单不操作
if ($this.attr('disabled') !== null) {
return;
}
// 用 mouseover 模拟 mouseenter
if (eventType === 'mouseover') {
if (!$this.is($relatedTarget) && $.contains($this[0], $relatedTarget[0])) {
return;
}
}
// 用 mouseout 模拟 mouseleave
else if (eventType === 'mouseout') {
if ($this.is($relatedTarget) || $.contains($this[0], $relatedTarget[0])) {
return;
}
}
// 当前菜单项下的子菜单,未必存在
var $submenu = $this.children('.mdui-menu');
// 鼠标移入菜单项时,显示菜单项下的子菜单
if (eventType === 'mouseover') {
if ($submenu.length) {
// 当前子菜单准备打开时,如果当前子菜单正准备着关闭,不用再关闭了
var tmpClose = $submenu.data('timeoutClose.mdui.menu');
if (tmpClose) {
clearTimeout(tmpClose);
}
// 如果当前子菜单已经打开,不操作
if ($submenu.hasClass('mdui-menu-open')) {
return;
}
// 当前子菜单准备打开时,其他准备打开的子菜单不用再打开了
clearTimeout(timeoutOpen);
// 准备打开当前子菜单
timeout = timeoutOpen = setTimeout(function () {
openSubMenu($submenu);
}, inst.options.subMenuDelay);
$submenu.data('timeoutOpen.mdui.menu', timeout);
}
}
// 鼠标移出菜单项时,关闭菜单项下的子菜单
else if (eventType === 'mouseout') {
if ($submenu.length) {
// 鼠标移出菜单项时,如果当前菜单项下的子菜单正准备打开,不用再打开了
var tmpOpen = $submenu.data('timeoutOpen.mdui.menu');
if (tmpOpen) {
clearTimeout(tmpOpen);
}
// 准备关闭当前子菜单
timeout = timeoutClose = setTimeout(function () {
closeSubMenu($submenu);
}, inst.options.subMenuDelay);
$submenu.data('timeoutClose.mdui.menu', timeout);
}
}
});
}
};
/**
* 菜单
* @param anchorSelector 点击该元素触发菜单
* @param menuSelector 菜单
* @param opts 配置项
* @constructor
*/
function Menu(anchorSelector, menuSelector, opts) {
var _this = this;
// 触发菜单的元素
_this.$anchor = $(anchorSelector).eq(0);
if (!_this.$anchor.length) {
return;
}
// 已通过自定义属性实例化过,不再重复实例化
var oldInst = _this.$anchor.data('mdui.menu');
if (oldInst) {
return oldInst;
}
_this.$menu = $(menuSelector).eq(0);
// 触发菜单的元素 和 菜单必须是同级的元素,否则菜单可能不能定位
if (!_this.$anchor.siblings(_this.$menu).length) {
return;
}
_this.options = $.extend({}, DEFAULT, (opts || {}));
_this.state = 'closed';
// 是否是级联菜单
_this.isCascade = _this.$menu.hasClass('mdui-menu-cascade');
// covered 参数处理
if (_this.options.covered === 'auto') {
_this.isCovered = !_this.isCascade;
} else {
_this.isCovered = _this.options.covered;
}
// 点击触发菜单切换
_this.$anchor.on('click', function () {
_this.toggle();
});
// 点击菜单外面区域关闭菜单
$document.on('click touchstart', function (e) {
var $target = $(e.target);
if (
(_this.state === 'opening' || _this.state === 'opened') &&
!$target.is(_this.$menu) &&
!$.contains(_this.$menu[0], $target[0]) &&
!$target.is(_this.$anchor) &&
!$.contains(_this.$anchor[0], $target[0])
) {
_this.close();
}
});
// 点击不含子菜单的菜单条目关闭菜单
$document.on('click', '.mdui-menu-item', function (e) {
var $this = $(this);
if (!$this.find('.mdui-menu').length && $this.attr('disabled') === null) {
_this.close();
}
});
// 绑定点击或鼠标移入含子菜单的条目的事件
bindSubMenuEvent(_this);
// 窗口大小变化时,重新调整菜单位置
$window.on('resize', $.throttle(function () {
readjust(_this);
}, 100));
}
/**
* 切换菜单状态
*/
Menu.prototype.toggle = function () {
var _this = this;
if (_this.state === 'opening' || _this.state === 'opened') {
_this.close();
} else if (_this.state === 'closing' || _this.state === 'closed') {
_this.open();
}
};
/**
* 动画结束回调
* @param inst
*/
var transitionEnd = function (inst) {
if (inst.state === 'opening') {
inst.state = 'opened';
componentEvent('opened', 'menu', inst, inst.$menu);
}
if (inst.state === 'closing') {
inst.state = 'closed';
componentEvent('closed', 'menu', inst, inst.$menu);
// 关闭后,恢复菜单样式到默认状态,并恢复 fixed 定位
inst.$menu.css({
top: '',
left: '',
width: '',
position: 'fixed',
});
}
};
/**
* 打开菜单
*/
Menu.prototype.open = function () {
var _this = this;
if (_this.state === 'opening' || _this.state === 'opened') {
return;
}
_this.state = 'opening';
componentEvent('open', 'menu', _this, _this.$menu);
// 调整菜单位置
readjust(_this);
_this.$menu
// 菜单隐藏状态使用使用 fixed 定位。
.css('position', _this.options.fixed ? 'fixed' : 'absolute')
// 打开菜单
.addClass('mdui-menu-open')
// 打开动画完成后
.transitionEnd(function () {
transitionEnd(_this);
});
};
/**
* 关闭菜单
*/
Menu.prototype.close = function () {
var _this = this;
if (_this.state === 'closing' || _this.state === 'closed') {
return;
}
_this.state = 'closing';
componentEvent('close', 'menu', _this, _this.$menu);
// 菜单开始关闭时,关闭所有子菜单
_this.$menu.find('.mdui-menu').each(function () {
closeSubMenu($(this));
});
_this.$menu
.removeClass('mdui-menu-open')
.transitionEnd(function () {
transitionEnd(_this);
});
};
return Menu;
})();
/**
* =============================================================================
* ************ Menu 自定义属性 API ************
* =============================================================================
*/
$(function () {
$document.on('click', '[mdui-menu]', function () {
var $this = $(this);
var inst = $this.data('mdui.menu');
if (!inst) {
var options = parseOptions($this.attr('mdui-menu'));
var menuSelector = options.target;
delete options.target;
inst = new mdui.Menu($this, menuSelector, options);
$this.data('mdui.menu', inst);
inst.toggle();
}
});
});
/* jshint ignore:start */
mdui.JQ = $;
window.mdui = mdui;
})(window, document);
/* jshint ignore:end */