UNPKG

react-pickers

Version:

datapicker and citypicker base on MUI of picker

1,726 lines (1,631 loc) 45.4 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var mui = function (document, undefined) { var readyRE = /complete|loaded|interactive/; var idSelectorRE = /^#([\w-]+)$/; var classSelectorRE = /^\.([\w-]+)$/; var tagSelectorRE = /^[\w-]+$/; var translateRE = /translate(?:3d)?\((.+?)\)/; var translateMatrixRE = /matrix(3d)?\((.+?)\)/; var $ = function $(selector, context) { context = context || document; if (!selector) return wrap(); if ((typeof selector === 'undefined' ? 'undefined' : _typeof(selector)) === 'object') if ($.isArrayLike(selector)) { return wrap($.slice.call(selector), null); } else { return wrap([selector], null); } if (typeof selector === 'function') return $.ready(selector); if (typeof selector === 'string') { try { selector = selector.trim(); if (idSelectorRE.test(selector)) { var found = document.getElementById(RegExp.$1); return wrap(found ? [found] : []); } return wrap($.qsa(selector, context), selector); } catch (e) {} } return wrap(); }; var wrap = function wrap(dom, selector) { dom = dom || []; dom.__proto__ = $.fn; dom.selector = selector || ''; return dom; }; $.uuid = 0; $.data = {}; /** * extend(simple) * @param {type} target * @param {type} source * @param {type} deep * @returns {unresolved} */ $.extend = function () { //from jquery2 var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false; if (typeof target === "boolean") { deep = target; target = arguments[i] || {}; i++; } if ((typeof target === 'undefined' ? 'undefined' : _typeof(target)) !== "object" && !$.isFunction(target)) { target = {}; } if (i === length) { target = this; i--; } for (; i < length; i++) { if ((options = arguments[i]) != null) { for (name in options) { src = target[name]; copy = options[name]; if (target === copy) { continue; } if (deep && copy && ($.isPlainObject(copy) || (copyIsArray = $.isArray(copy)))) { if (copyIsArray) { copyIsArray = false; clone = src && $.isArray(src) ? src : []; } else { clone = src && $.isPlainObject(src) ? src : {}; } target[name] = $.extend(deep, clone, copy); } else if (copy !== undefined) { target[name] = copy; } } } } return target; }; /** * mui noop(function) */ $.noop = function () {}; /** * mui slice(array) */ $.slice = [].slice; /** * mui filter(array) */ $.filter = [].filter; $.type = function (obj) { return obj == null ? String(obj) : class2type[{}.toString.call(obj)] || "object"; }; /** * mui isArray */ $.isArray = Array.isArray || function (object) { return object instanceof Array; }; /** * mui isArrayLike * @param {Object} obj */ $.isArrayLike = function (obj) { var length = !!obj && "length" in obj && obj.length; var type = $.type(obj); if (type === "function" || $.isWindow(obj)) { return false; } return type === "array" || length === 0 || typeof length === "number" && length > 0 && length - 1 in obj; }; /** * mui isWindow(需考虑obj为undefined的情况) */ $.isWindow = function (obj) { return obj != null && obj === obj.window; }; /** * mui isObject */ $.isObject = function (obj) { return $.type(obj) === "object"; }; /** * mui isPlainObject */ $.isPlainObject = function (obj) { return $.isObject(obj) && !$.isWindow(obj) && Object.getPrototypeOf(obj) === Object.prototype; }; /** * mui isEmptyObject * @param {Object} o */ $.isEmptyObject = function (o) { for (var p in o) { if (p !== undefined) { return false; } } return true; }; /** * mui isFunction */ $.isFunction = function (value) { return $.type(value) === "function"; }; /** * mui querySelectorAll * @param {type} selector * @param {type} context * @returns {Array} */ $.qsa = function (selector, context) { context = context || document; return $.slice.call(classSelectorRE.test(selector) ? context.getElementsByClassName(RegExp.$1) : tagSelectorRE.test(selector) ? context.getElementsByTagName(selector) : context.querySelectorAll(selector)); }; /** * ready(DOMContentLoaded) * @param {type} callback * @returns {_L6.$} */ $.ready = function (callback) { if (readyRE.test(document.readyState)) { callback($); } else { document.addEventListener('DOMContentLoaded', function () { callback($); }, false); } return this; }; /** * 将 fn 缓存一段时间后, 再被调用执行 * 此方法为了避免在 ms 段时间内, 执行 fn 多次. 常用于 resize , scroll , mousemove 等连续性事件中; * 当 ms 设置为 -1, 表示立即执行 fn, 即和直接调用 fn 一样; * 调用返回函数的 stop 停止最后一次的 buffer 效果 * @param {Object} fn * @param {Object} ms * @param {Object} context */ $.buffer = function (fn, ms, context) { var timer; var lastStart = 0; var lastEnd = 0; var ms = ms || 150; function run() { if (timer) { timer.cancel(); timer = 0; } lastStart = $.now(); fn.apply(context || this, arguments); lastEnd = $.now(); } return $.extend(function () { if (!lastStart || // 从未运行过 lastEnd >= lastStart && $.now() - lastEnd > ms || // 上次运行成功后已经超过ms毫秒 lastEnd < lastStart && $.now() - lastStart > ms * 8 // 上次运行或未完成,后8*ms毫秒 ) { run(); } else { if (timer) { timer.cancel(); } timer = $.later(run, ms, null, arguments); } }, { stop: function stop() { if (timer) { timer.cancel(); timer = 0; } } }); }; /** * each * @param {type} elements * @param {type} callback * @returns {_L8.$} */ $.each = function (elements, callback, hasOwnProperty) { if (!elements) { return this; } if (typeof elements.length === 'number') { [].every.call(elements, function (el, idx) { return callback.call(el, idx, el) !== false; }); } else { for (var key in elements) { if (hasOwnProperty) { if (elements.hasOwnProperty(key)) { if (callback.call(elements[key], key, elements[key]) === false) return elements; } } else { if (callback.call(elements[key], key, elements[key]) === false) return elements; } } } return this; }; $.focus = function (element) { if ($.os.ios) { setTimeout(function () { element.focus(); }, 10); } else { element.focus(); } }; /** * trigger event * @param {type} element * @param {type} eventType * @param {type} eventData * @returns {_L8.$} */ $.trigger = function (element, eventType, eventData) { element.dispatchEvent(new CustomEvent(eventType, { detail: eventData, bubbles: true, cancelable: true })); return this; }; /** * getStyles * @param {type} element * @param {type} property * @returns {styles} */ $.getStyles = function (element, property) { var styles = element.ownerDocument.defaultView.getComputedStyle(element, null); if (property) { return styles.getPropertyValue(property) || styles[property]; } return styles; }; /** * parseTranslate * @param {type} translateString * @param {type} position * @returns {Object} */ $.parseTranslate = function (translateString, position) { var result = translateString.match(translateRE || ''); if (!result || !result[1]) { result = ['', '0,0,0']; } result = result[1].split(","); result = { x: parseFloat(result[0]), y: parseFloat(result[1]), z: parseFloat(result[2]) }; if (position && result.hasOwnProperty(position)) { return result[position]; } return result; }; /** * parseTranslateMatrix * @param {type} translateString * @param {type} position * @returns {Object} */ $.parseTranslateMatrix = function (translateString, position) { var matrix = translateString.match(translateMatrixRE); var is3D = matrix && matrix[1]; if (matrix) { matrix = matrix[2].split(","); if (is3D === "3d") matrix = matrix.slice(12, 15);else { matrix.push(0); matrix = matrix.slice(4, 7); } } else { matrix = [0, 0, 0]; } var result = { x: parseFloat(matrix[0]), y: parseFloat(matrix[1]), z: parseFloat(matrix[2]) }; if (position && result.hasOwnProperty(position)) { return result[position]; } return result; }; $.hooks = {}; $.addAction = function (type, hook) { var hooks = $.hooks[type]; if (!hooks) { hooks = []; } hook.index = hook.index || 1000; hooks.push(hook); hooks.sort(function (a, b) { return a.index - b.index; }); $.hooks[type] = hooks; return $.hooks[type]; }; $.doAction = function (type, callback) { if ($.isFunction(callback)) { //指定了callback $.each($.hooks[type], callback); } else { //未指定callback,直接执行 $.each($.hooks[type], function (index, hook) { return !hook.handle(); }); } }; /** * setTimeout封装 * @param {Object} fn * @param {Object} when * @param {Object} context * @param {Object} data */ $.later = function (fn, when, context, data) { when = when || 0; var m = fn; var d = data; var f; var r; if (typeof fn === 'string') { m = context[fn]; } f = function f() { m.apply(context, $.isArray(d) ? d : [d]); }; r = setTimeout(f, when); return { id: r, cancel: function cancel() { clearTimeout(r); } }; }; $.now = Date.now || function () { return +new Date(); }; var class2type = {}; $.each(['Boolean', 'Number', 'String', 'Function', 'Array', 'Date', 'RegExp', 'Object', 'Error'], function (i, name) { class2type["[object " + name + "]"] = name.toLowerCase(); }); if (window.JSON) { $.parseJSON = JSON.parse; } /** * $.fn */ $.fn = { each: function each(callback) { [].every.call(this, function (el, idx) { return callback.call(el, idx, el) !== false; }); return this; } }; return $; }(document); (function ($, window) { function detect(ua) { this.os = {}; var funcs = [function () { //wechat var wechat = ua.match(/(MicroMessenger)\/([\d\.]+)/i); if (wechat) { //wechat this.os.wechat = { version: wechat[2].replace(/_/g, '.') }; } return false; }, function () { //android var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/); if (android) { this.os.android = true; this.os.version = android[2]; this.os.isBadAndroid = !/Chrome\/\d/.test(window.navigator.appVersion); } return this.os.android === true; }, function () { //ios var iphone = ua.match(/(iPhone\sOS)\s([\d_]+)/); if (iphone) { //iphone this.os.ios = this.os.iphone = true; this.os.version = iphone[2].replace(/_/g, '.'); } else { var ipad = ua.match(/(iPad).*OS\s([\d_]+)/); if (ipad) { //ipad this.os.ios = this.os.ipad = true; this.os.version = ipad[2].replace(/_/g, '.'); } } return this.os.ios === true; }]; [].every.call(funcs, function (func) { return !func.call($); }); } detect.call($, navigator.userAgent); })(mui, window); /** * $.os.plus * @param {type} $ * @returns {undefined} */ (function ($, document) { function detect(ua) { this.os = this.os || {}; var plus = ua.match(/Html5Plus/i); //TODO 5\+Browser? if (plus) { this.os.plus = true; $(function () { document.body.classList.add('mui-plus'); }); if (ua.match(/StreamApp/i)) { //TODO 最好有流应用自己的标识 this.os.stream = true; $(function () { document.body.classList.add('mui-plus-stream'); }); } } } detect.call($, navigator.userAgent); })(mui, document); (function ($) { var initializing = false, fnTest = /xyz/.test(function () { xyz; }) ? /\b_super\b/ : /.*/; var Class = function Class() {}; Class.extend = function (prop) { var _super = this.prototype; initializing = true; var prototype = new this(); initializing = false; for (var name in prop) { prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ? function (name, fn) { return function () { var tmp = this._super; this._super = _super[name]; var ret = fn.apply(this, arguments); this._super = tmp; return ret; }; }(name, prop[name]) : prop[name]; } function Class() { if (!initializing && this.init) this.init.apply(this, arguments); } Class.prototype = prototype; Class.prototype.constructor = Class; Class.extend = this; return Class; }; $.Class = Class; })(mui); /** * mui.init * @param {type} $ * @returns {undefined} */ (function ($) { $.global = $.options = { gestureConfig: { tap: true, doubletap: false, longtap: false, hold: false, flick: true, swipe: true, drag: true, pinch: false } }; /** * * @param {type} options * @returns {undefined} */ $.initGlobal = function (options) { $.options = $.extend(true, $.global, options); return this; }; var inits = {}; var isInitialized = false; //TODO 自动调用init?因为用户自己调用init的时机可能不确定,如果晚于自动init,则会有潜在问题 // $.ready(function() { // setTimeout(function() { // if (!isInitialized) { // $.init(); // } // }, 300); // }); /** * 单页配置 初始化 * @param {object} options */ $.init = function (options) { isInitialized = true; $.options = $.extend(true, $.global, options || {}); $.ready(function () { $.doAction('inits', function (index, init) { var isInit = !!(!inits[init.name] || init.repeat); if (isInit) { init.handle.call($); inits[init.name] = true; } }); }); return this; }; /** * 增加初始化执行流程 * @param {function} init */ $.addInit = function (init) { return $.addAction('inits', init); }; })(mui); /** * mui target(action>popover>modal>tab>toggle) */ (function ($, window, document) { /** * targets */ $.targets = {}; /** * target handles */ $.targetHandles = []; /** * register target * @param {type} target * @returns {$.targets} */ $.registerTarget = function (target) { target.index = target.index || 1000; $.targetHandles.push(target); $.targetHandles.sort(function (a, b) { return a.index - b.index; }); return $.targetHandles; }; window.addEventListener($.EVENT_START, function (event) { var target = event.target; var founds = {}; for (; target && target !== document; target = target.parentNode) { var isFound = false; $.each($.targetHandles, function (index, targetHandle) { var name = targetHandle.name; if (!isFound && !founds[name] && targetHandle.hasOwnProperty('handle')) { $.targets[name] = targetHandle.handle(event, target); if ($.targets[name]) { founds[name] = true; if (targetHandle.isContinue !== true) { isFound = true; } } } else { if (!founds[name]) { if (targetHandle.isReset !== false) $.targets[name] = false; } } }); if (isFound) { break; } } }); window.addEventListener('click', function (event) { //解决touch与click的target不一致的问题(比如链接边缘点击时,touch的target为html,而click的target为A) var target = event.target; var isFound = false; for (; target && target !== document; target = target.parentNode) { if (target.tagName === 'A') { $.each($.targetHandles, function (index, targetHandle) { var name = targetHandle.name; if (targetHandle.hasOwnProperty('handle')) { if (targetHandle.handle(event, target)) { isFound = true; event.preventDefault(); return false; } } }); if (isFound) { break; } } } }); })(mui, window, document); /** * Popovers * @param {type} $ * @param {type} window * @param {type} document * @param {type} name * @param {type} undefined * @returns {undefined} */ (function ($, window, document, name) { var CLASS_POPOVER = 'mui-popover'; var CLASS_POPOVER_ARROW = 'mui-popover-arrow'; var CLASS_ACTION_POPOVER = 'mui-popover-action'; var CLASS_BACKDROP = 'mui-backdrop'; var CLASS_BAR_POPOVER = 'mui-bar-popover'; var CLASS_BAR_BACKDROP = 'mui-bar-backdrop'; var CLASS_ACTION_BACKDROP = 'mui-backdrop-action'; var CLASS_ACTIVE = 'mui-active'; var CLASS_BOTTOM = 'mui-bottom'; var handle = function handle(event, target) { if (target.tagName === 'A' && target.hash) { $.targets._popover = document.getElementById(target.hash.replace('#', '')); if ($.targets._popover && $.targets._popover.classList.contains(CLASS_POPOVER)) { return target; } else { $.targets._popover = null; } } return false; }; $.registerTarget({ name: name, index: 60, handle: handle, target: false, isReset: false, isContinue: true }); var fixedPopoverScroll = function fixedPopoverScroll(isPopoverScroll) { // if (isPopoverScroll) { // document.body.setAttribute('style', 'overflow:hidden;'); // } else { // document.body.setAttribute('style', ''); // } }; var onPopoverShown = function onPopoverShown(e) { this.removeEventListener('webkitTransitionEnd', onPopoverShown); this.addEventListener($.EVENT_MOVE, $.preventDefault); $.trigger(this, 'shown', this); }; var onPopoverHidden = function onPopoverHidden(e) { setStyle(this, 'none'); this.removeEventListener('webkitTransitionEnd', onPopoverHidden); this.removeEventListener($.EVENT_MOVE, $.preventDefault); fixedPopoverScroll(false); $.trigger(this, 'hidden', this); }; var backdrop = function () { var element = document.createElement('div'); element.classList.add(CLASS_BACKDROP); element.addEventListener($.EVENT_MOVE, $.preventDefault); element.addEventListener('tap', function (e) { var popover = $.targets._popover; if (popover) { popover.addEventListener('webkitTransitionEnd', onPopoverHidden); popover.classList.remove(CLASS_ACTIVE); removeBackdrop(popover); document.body.setAttribute('style', ''); //webkitTransitionEnd有时候不触发? } }); return element; }(); var removeBackdropTimer; var removeBackdrop = function removeBackdrop(popover) { backdrop.setAttribute('style', 'opacity:0'); $.targets.popover = $.targets._popover = null; //reset removeBackdropTimer = $.later(function () { if (!popover.classList.contains(CLASS_ACTIVE) && backdrop.parentNode && backdrop.parentNode === document.body) { document.body.removeChild(backdrop); } }, 350); }; window.addEventListener('tap', function (e) { if (!$.targets.popover) { return; } var toggle = false; var target = e.target; for (; target && target !== document; target = target.parentNode) { if (target === $.targets.popover) { toggle = true; } } if (toggle) { e.detail.gesture.preventDefault(); //fixed hashchange togglePopover($.targets._popover, $.targets.popover); } }); var togglePopover = function togglePopover(popover, anchor, state) { if (state === 'show' && popover.classList.contains(CLASS_ACTIVE) || state === 'hide' && !popover.classList.contains(CLASS_ACTIVE)) { return; } removeBackdropTimer && removeBackdropTimer.cancel(); //取消remove的timer //remove一遍,以免来回快速切换,导致webkitTransitionEnd不触发,无法remove popover.removeEventListener('webkitTransitionEnd', onPopoverShown); popover.removeEventListener('webkitTransitionEnd', onPopoverHidden); backdrop.classList.remove(CLASS_BAR_BACKDROP); backdrop.classList.remove(CLASS_ACTION_BACKDROP); var _popover = document.querySelector('.mui-popover.mui-active'); if (_popover) { // _popover.setAttribute('style', ''); _popover.addEventListener('webkitTransitionEnd', onPopoverHidden); _popover.classList.remove(CLASS_ACTIVE); // _popover.removeEventListener('webkitTransitionEnd', onPopoverHidden); // fixedPopoverScroll(false); //同一个弹出则直接返回,解决同一个popover的toggle if (popover === _popover) { removeBackdrop(_popover); return; } } var isActionSheet = false; if (popover.classList.contains(CLASS_BAR_POPOVER) || popover.classList.contains(CLASS_ACTION_POPOVER)) { //navBar if (popover.classList.contains(CLASS_ACTION_POPOVER)) { //action sheet popover isActionSheet = true; backdrop.classList.add(CLASS_ACTION_BACKDROP); } else { //bar popover backdrop.classList.add(CLASS_BAR_BACKDROP); // if (anchor) { // if (anchor.parentNode) { // var offsetWidth = anchor.offsetWidth; // var offsetLeft = anchor.offsetLeft; // var innerWidth = window.innerWidth; // popover.style.left = (Math.min(Math.max(offsetLeft, defaultPadding), innerWidth - offsetWidth - defaultPadding)) + "px"; // } else { // //TODO anchor is position:{left,top,bottom,right} // } // } } } setStyle(popover, 'block'); //actionsheet transform popover.offsetHeight; popover.classList.add(CLASS_ACTIVE); backdrop.setAttribute('style', ''); document.body.appendChild(backdrop); fixedPopoverScroll(true); calPosition(popover, anchor, isActionSheet); //position backdrop.classList.add(CLASS_ACTIVE); popover.addEventListener('webkitTransitionEnd', onPopoverShown); }; var setStyle = function setStyle(popover, display, top, left) { var style = popover.style; if (typeof display !== 'undefined') style.display = display; if (typeof top !== 'undefined') style.top = top + 'px'; if (typeof left !== 'undefined') style.left = left + 'px'; }; var calPosition = function calPosition(popover, anchor, isActionSheet) { if (!popover || !anchor) { return; } if (isActionSheet) { //actionsheet setStyle(popover, 'block'); return; } var wWidth = window.innerWidth; var wHeight = window.innerHeight; var pWidth = popover.offsetWidth; var pHeight = popover.offsetHeight; var aWidth = anchor.offsetWidth; var aHeight = anchor.offsetHeight; var offset = $.offset(anchor); var arrow = popover.querySelector('.' + CLASS_POPOVER_ARROW); if (!arrow) { arrow = document.createElement('div'); arrow.className = CLASS_POPOVER_ARROW; popover.appendChild(arrow); } var arrowSize = arrow && arrow.offsetWidth / 2 || 0; var pTop = 0; var pLeft = 0; var diff = 0; var arrowLeft = 0; var defaultPadding = popover.classList.contains(CLASS_ACTION_POPOVER) ? 0 : 5; var position = 'top'; if (pHeight + arrowSize < offset.top - window.pageYOffset) { //top pTop = offset.top - pHeight - arrowSize; } else if (pHeight + arrowSize < wHeight - (offset.top - window.pageYOffset) - aHeight) { //bottom position = 'bottom'; pTop = offset.top + aHeight + arrowSize; } else { //middle position = 'middle'; pTop = Math.max((wHeight - pHeight) / 2 + window.pageYOffset, 0); pLeft = Math.max((wWidth - pWidth) / 2 + window.pageXOffset, 0); } if (position === 'top' || position === 'bottom') { pLeft = aWidth / 2 + offset.left - pWidth / 2; diff = pLeft; if (pLeft < defaultPadding) pLeft = defaultPadding; if (pLeft + pWidth > wWidth) pLeft = wWidth - pWidth - defaultPadding; if (arrow) { if (position === 'top') { arrow.classList.add(CLASS_BOTTOM); } else { arrow.classList.remove(CLASS_BOTTOM); } diff = diff - pLeft; arrowLeft = pWidth / 2 - arrowSize / 2 + diff; arrowLeft = Math.max(Math.min(arrowLeft, pWidth - arrowSize * 2 - 6), 6); arrow.setAttribute('style', 'left:' + arrowLeft + 'px'); } } else if (position === 'middle') { arrow.setAttribute('style', 'display:none'); } setStyle(popover, 'block', pTop, pLeft); }; $.createMask = function (callback) { var element = document.createElement('div'); element.classList.add(CLASS_BACKDROP); element.addEventListener($.EVENT_MOVE, $.preventDefault); element.addEventListener('tap', function () { mask.close(); }); var mask = [element]; mask._show = false; mask.show = function () { mask._show = true; element.setAttribute('style', 'opacity:1'); document.body.appendChild(element); return mask; }; mask._remove = function () { if (mask._show) { mask._show = false; element.setAttribute('style', 'opacity:0'); $.later(function () { var body = document.body; element.parentNode === body && body.removeChild(element); }, 350); } return mask; }; mask.close = function () { if (callback) { if (callback() !== false) { mask._remove(); } } else { mask._remove(); } }; return mask; }; $.fn.popover = function () { var args = arguments; this.each(function () { $.targets._popover = this; if (args[0] === 'show' || args[0] === 'hide' || args[0] === 'toggle') { togglePopover(this, args[1], args[0]); } }); }; })(mui, window, document, 'popover'); /** * mui namespace(optimization) * @param {type} $ * @returns {undefined} */ (function ($) { $.namespace = 'mui'; $.classNamePrefix = $.namespace + '-'; $.classSelectorPrefix = '.' + $.classNamePrefix; /** * 返回正确的className * @param {type} className * @returns {String} */ $.className = function (className) { return $.classNamePrefix + className; }; /** * 返回正确的classSelector * @param {type} classSelector * @returns {String} */ $.classSelector = function (classSelector) { return classSelector.replace(/\./g, $.classSelectorPrefix); }; /** * 返回正确的eventName * @param {type} event * @param {type} module * @returns {String} */ $.eventName = function (event, module) { return event + ($.namespace ? '.' + $.namespace : '') + (module ? '.' + module : ''); }; })(mui); /** * 仅提供简单的on,off(仅支持事件委托,不支持当前元素绑定,当前元素绑定请直接使用addEventListener,removeEventListener) * @param {Object} $ */ (function ($) { if ('ontouchstart' in window) { $.isTouchable = true; $.EVENT_START = 'touchstart'; $.EVENT_MOVE = 'touchmove'; $.EVENT_END = 'touchend'; } else { $.isTouchable = false; $.EVENT_START = 'mousedown'; $.EVENT_MOVE = 'mousemove'; $.EVENT_END = 'mouseup'; } $.EVENT_CANCEL = 'touchcancel'; $.EVENT_CLICK = 'click'; var _mid = 1; var delegates = {}; //需要wrap的函数 var eventMethods = { preventDefault: 'isDefaultPrevented', stopImmediatePropagation: 'isImmediatePropagationStopped', stopPropagation: 'isPropagationStopped' }; //默认true返回函数 var returnTrue = function returnTrue() { return true; }; //默认false返回函数 var returnFalse = function returnFalse() { return false; }; //wrap浏览器事件 var compatible = function compatible(event, target) { if (!event.detail) { event.detail = { currentTarget: target }; } else { event.detail.currentTarget = target; } $.each(eventMethods, function (name, predicate) { var sourceMethod = event[name]; event[name] = function () { this[predicate] = returnTrue; return sourceMethod && sourceMethod.apply(event, arguments); }; event[predicate] = returnFalse; }, true); return event; }; //简单的wrap对象_mid var mid = function mid(obj) { return obj && (obj._mid || (obj._mid = _mid++)); }; //事件委托对象绑定的事件回调列表 var delegateFns = {}; //返回事件委托的wrap事件回调 var delegateFn = function delegateFn(element, event, selector, callback) { return function (e) { //same event var callbackObjs = delegates[element._mid][event]; var handlerQueue = []; var target = e.target; var selectorAlls = {}; for (; target && target !== document; target = target.parentNode) { if (target === element) { break; } if (~['click', 'tap', 'doubletap', 'longtap', 'hold'].indexOf(event) && (target.disabled || target.classList.contains($.className('disabled')))) { break; } var matches = {}; $.each(callbackObjs, function (selector, callbacks) { //same selector selectorAlls[selector] || (selectorAlls[selector] = $.qsa(selector, element)); if (selectorAlls[selector] && ~selectorAlls[selector].indexOf(target)) { if (!matches[selector]) { matches[selector] = callbacks; } } }, true); if (!$.isEmptyObject(matches)) { handlerQueue.push({ element: target, handlers: matches }); } } selectorAlls = null; e = compatible(e); //compatible event $.each(handlerQueue, function (index, handler) { target = handler.element; var tagName = target.tagName; if (event === 'tap' && tagName !== 'INPUT' && tagName !== 'TEXTAREA' && tagName !== 'SELECT') { e.preventDefault(); e.detail && e.detail.gesture && e.detail.gesture.preventDefault(); } $.each(handler.handlers, function (index, handler) { $.each(handler, function (index, callback) { if (callback.call(target, e) === false) { e.preventDefault(); e.stopPropagation(); } }, true); }, true); if (e.isPropagationStopped()) { return false; } }, true); }; }; var findDelegateFn = function findDelegateFn(element, event) { var delegateCallbacks = delegateFns[mid(element)]; var result = []; if (delegateCallbacks) { result = []; if (event) { var filterFn = function filterFn(fn) { return fn.type === event; }; return delegateCallbacks.filter(filterFn); } else { result = delegateCallbacks; } } return result; }; var preventDefaultException = /^(INPUT|TEXTAREA|BUTTON|SELECT)$/; /** * mui delegate events * @param {type} event * @param {type} selector * @param {type} callback * @returns {undefined} */ $.fn.on = function (event, selector, callback) { //仅支持简单的事件委托,主要是tap事件使用,类似mouse,focus之类暂不封装支持 return this.each(function () { var element = this; mid(element); mid(callback); var isAddEventListener = false; var delegateEvents = delegates[element._mid] || (delegates[element._mid] = {}); var delegateCallbackObjs = delegateEvents[event] || (delegateEvents[event] = {}); if ($.isEmptyObject(delegateCallbackObjs)) { isAddEventListener = true; } var delegateCallbacks = delegateCallbackObjs[selector] || (delegateCallbackObjs[selector] = []); delegateCallbacks.push(callback); if (isAddEventListener) { var delegateFnArray = delegateFns[mid(element)]; if (!delegateFnArray) { delegateFnArray = []; } var delegateCallback = delegateFn(element, event, selector, callback); delegateFnArray.push(delegateCallback); delegateCallback.i = delegateFnArray.length - 1; delegateCallback.type = event; delegateFns[mid(element)] = delegateFnArray; element.addEventListener(event, delegateCallback); if (event === 'tap') { //TODO 需要找个更好的解决方案 element.addEventListener('click', function (e) { if (e.target) { var tagName = e.target.tagName; if (!preventDefaultException.test(tagName)) { if (tagName === 'A') { var href = e.target.href; if (!(href && ~href.indexOf('tel:'))) { e.preventDefault(); } } else { e.preventDefault(); } } } }); } } }); }; $.fn.off = function (event, selector, callback) { return this.each(function () { var _mid = mid(this); if (!event) { //mui(selector).off(); delegates[_mid] && delete delegates[_mid]; } else if (!selector) { //mui(selector).off(event); delegates[_mid] && delete delegates[_mid][event]; } else if (!callback) { //mui(selector).off(event,selector); delegates[_mid] && delegates[_mid][event] && delete delegates[_mid][event][selector]; } else { //mui(selector).off(event,selector,callback); var delegateCallbacks = delegates[_mid] && delegates[_mid][event] && delegates[_mid][event][selector]; $.each(delegateCallbacks, function (index, delegateCallback) { if (mid(delegateCallback) === mid(callback)) { delegateCallbacks.splice(index, 1); return false; } }, true); } if (delegates[_mid]) { //如果off掉了所有当前element的指定的event事件,则remove掉当前element的delegate回调 if (!delegates[_mid][event] || $.isEmptyObject(delegates[_mid][event])) { findDelegateFn(this, event).forEach(function (fn) { this.removeEventListener(fn.type, fn); delete delegateFns[_mid][fn.i]; }.bind(this)); } } else { //如果delegates[_mid]已不存在,删除所有 findDelegateFn(this).forEach(function (fn) { this.removeEventListener(fn.type, fn); delete delegateFns[_mid][fn.i]; }.bind(this)); } }); }; })(mui); /** * mui gestures * @param {type} $ * @param {type} window * @returns {undefined} */ (function ($, window) { $.gestures = { session: {} }; /** * Gesture preventDefault * @param {type} e * @returns {undefined} */ $.preventDefault = function (e) { e.preventDefault(); }; /** * Gesture stopPropagation * @param {type} e * @returns {undefined} */ $.stopPropagation = function (e) { e.stopPropagation(); }; /** * register gesture * @param {type} gesture * @returns {$.gestures} */ $.addGesture = function (gesture) { return $.addAction('gestures', gesture); }; var round = Math.round; var abs = Math.abs; var sqrt = Math.sqrt; var atan = Math.atan; var atan2 = Math.atan2; /** * distance * @param {type} p1 * @param {type} p2 * @returns {Number} */ var getDistance = function getDistance(p1, p2, props) { if (!props) { props = ['x', 'y']; } var x = p2[props[0]] - p1[props[0]]; var y = p2[props[1]] - p1[props[1]]; return sqrt(x * x + y * y); }; /** * scale * @param {Object} starts * @param {Object} moves */ var getScale = function getScale(starts, moves) { if (starts.length >= 2 && moves.length >= 2) { var props = ['pageX', 'pageY']; return getDistance(moves[1], moves[0], props) / getDistance(starts[1], starts[0], props); } return 1; }; /** * angle * @param {type} p1 * @param {type} p2 * @returns {Number} */ var getAngle = function getAngle(p1, p2, props) { if (!props) { props = ['x', 'y']; } var x = p2[props[0]] - p1[props[0]]; var y = p2[props[1]] - p1[props[1]]; return atan2(y, x) * 180 / Math.PI; }; /** * direction * @param {Object} x * @param {Object} y */ var getDirection = function getDirection(x, y) { if (x === y) { return ''; } if (abs(x) >= abs(y)) { return x > 0 ? 'left' : 'right'; } return y > 0 ? 'up' : 'down'; }; /** * rotation * @param {Object} start * @param {Object} end */ var getRotation = function getRotation(start, end) { var props = ['pageX', 'pageY']; return getAngle(end[1], end[0], props) - getAngle(start[1], start[0], props); }; /** * px per ms * @param {Object} deltaTime * @param {Object} x * @param {Object} y */ var getVelocity = function getVelocity(deltaTime, x, y) { return { x: x / deltaTime || 0, y: y / deltaTime || 0 }; }; /** * detect gestures * @param {type} event * @param {type} touch * @returns {undefined} */ var detect = function detect(event, touch) { if ($.gestures.stoped) { return; } $.doAction('gestures', function (index, gesture) { if (!$.gestures.stoped) { if ($.options.gestureConfig[gesture.name] !== false) { gesture.handle(event, touch); } } }); }; /** * 暂时无用 * @param {Object} node * @param {Object} parent */ var hasParent = function hasParent(node, parent) { while (node) { if (node == parent) { return true; } node = node.parentNode; } return false; }; var uniqueArray = function uniqueArray(src, key, sort) { var results = []; var values = []; var i = 0; while (i < src.length) { var val = key ? src[i][key] : src[i]; if (values.indexOf(val) < 0) { results.push(src[i]); } values[i] = val; i++; } if (sort) { if (!key) { results = results.sort(); } else { results = results.sort(function sortUniqueArray(a, b) { return a[key] > b[key]; }); } } return results; }; var getMultiCenter = function getMultiCenter(touches) { var touchesLength = touches.length; if (touchesLength === 1) { return { x: round(touches[0].pageX), y: round(touches[0].pageY) }; } var x = 0; var y = 0; var i = 0; while (i < touchesLength) { x += touches[i].pageX; y += touches[i].pageY; i++; } return { x: round(x / touchesLength), y: round(y / touchesLength) }; }; var multiTouch = function multiTouch() { return $.options.gestureConfig.pinch; }; var copySimpleTouchData = function copySimpleTouchData(touch) { var touches = []; var i = 0; while (i < touch.touches.length) { touches[i] = { pageX: round(touch.touches[i].pageX), pageY: round(touch.touches[i].pageY) }; i++; } return { timestamp: $.now(), gesture: touch.gesture, touches: touches, center: getMultiCenter(touch.touches), deltaX: touch.deltaX, deltaY: touch.deltaY }; }; var calDelta = function calDelta(touch) { var session = $.gestures.session; var center = touch.center; var offset = session.offsetDelta || {}; var prevDelta = session.prevDelta || {}; var prevTouch = session.prevTouch || {}; if (touch.gesture.type === $.EVENT_START || touch.gesture.type === $.EVENT_END) { prevDelta = session.prevDelta = { x: prevTouch.deltaX || 0, y: prevTouch.deltaY || 0 }; offset = session.offsetDelta = { x: center.x, y: center.y }; } touch.deltaX = prevDelta.x + (center.x - offset.x); touch.deltaY = prevDelta.y + (center.y - offset.y); }; var calTouchData = function calTouchData(touch) { var session = $.gestures.session; var touches = touch.touches; var touchesLength = touches.length; if (!session.firstTouch) { session.firstTouch = copySimpleTouchData(touch); } if (multiTouch() && touchesLength > 1 && !session.firstMultiTouch) { session.firstMultiTouch = copySimpleTouchData(touch); } else if (touchesLength === 1) { session.firstMultiTouch = false; } var firstTouch = session.firstTouch; var firstMultiTouch = session.firstMultiTouch; var offsetCenter = firstMultiTouch ? firstMultiTouch.center : firstTouch.center; var center = touch.center = getMultiCenter(touches); touch.timestamp = $.now(); touch.deltaTime = touch.timestamp - firstTouch.timestamp; touch.angle = getAngle(offsetCenter, center); touch.distance = getDistance(offsetCenter, center); calDelta(touch); touch.offsetDirection = getDirection(touch.deltaX, touch.deltaY); touch.scale = firstMultiTouch ? getScale(firstMultiTouch.touches, touches) : 1; touch.rotation = firstMultiTouch ? getRotation(firstMultiTouch.touches, touches) : 0; calIntervalTouchData(touch); }; var CAL_INTERVAL = 25; var calIntervalTouchData = function calIntervalTouchData(touch) { var session = $.gestures.session; var last = session.lastInterval || touch; var deltaTime = touch.timestamp - last.timestamp; var velocity; var velocityX; var velocityY; var direction; if (touch.gesture.type != $.EVENT_CANCEL && (deltaTime > CAL_INTERVAL || last.velocity === undefined)) { var deltaX = last.deltaX - touch.deltaX; var deltaY = last.deltaY - touch.deltaY; var v = getVelocity(deltaTime, deltaX, deltaY); velocityX = v.x; velocityY = v.y; velocity = abs(v.x) > abs(v.y) ? v.x : v.y; direction = getDirection(deltaX, deltaY) || last.direction; session.lastInterval = touch; } else { velocity = last.velocity; velocityX = last.velocityX; velocityY = last.velocityY; direction = last.direction; } touch.velocity = velocity; touch.velocityX = velocityX; touch.velocityY = velocityY; touch.direction = direction; }; var targetIds = {}; var convertTouches = function convertTouches(touches) { for (var i = 0; i < touches.length; i++) { !touches['identifier'] && (touches['identifier'] = 0); } return touches; }; var getTouches = function getTouches(event, touch) { var allTouches = convertTouches($.slice.call(event.touches || [event])); var type = event.type; var targetTouches = []; var changedTargetTouches = []; //当touchstart或touchmove且touches长度为1,直接获得all和changed if ((type === $.EVENT_START || type === $.EVENT_MOVE) && allTouches.length === 1) { targetIds[allTouches[0].identifier] = true; targetTouches = allTouches; changedTargetTouches = allTouches; touch.target = event.target; } else { var i = 0; var targetTouches = []; var changedTargetTouches = []; var changedTouches = convertTouches($.slice.call(event.changedTouches || [event])); touch.target = event.target; var sessionTarget = $.gestures.session.target || event.target; targetTouches = allTouches.filter(function (touch) { return hasParent(touch.target, sessionTarget); }); if (type === $.EVENT_START) { i = 0; while (i < targetTouches.length) { targetIds[targetTouches[i].identifier] = true; i++; } } i = 0; while (i < changedTouches.length) { if (targetIds[changedTouches[i].identifier]) { changedTargetTouches.push(changedTouches[i]); } if (type === $.EVENT_END || type === $.EVENT_CANCEL) { delete targetIds[changedTouches[i].identifier]; } i++; } if (!changedTargetTouches.length) { return false; } } targetTouches = uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true); var touchesLength = targetTouches.length; var changedTouchesLength = changedTargetTouches.length; if (type === $.EVENT_START && touchesLength - changedTouchesLength === 0) { //first touch.isFirst = true; $.gestures.touch = $.gestures.session = { target: event.target }; } touch.isFinal = (type === $.EVENT_END || type === $.EVENT_CANCEL) && touchesLength - changedTouchesLength === 0; touch.touches = targetTouches; touch.changedTouches = changedTargetTouches; return true; }; var handleTouchEvent = function handleTouchEvent(event) { var touch = { gesture: event }; var touches = getTouches(event, touch); if (!touches) { return; } calTouchData(touch); detect(event, touch); $.gestures.session.prevTouch = touch; if (event.type === $.EVENT_END && !$.isTouchable) { $.gestures.touch = $.gestures.session = {}; } }; window.addEventListener($.EVENT_START, handleTouchEvent); window.addEventListener($.EVENT_MOVE, handleTouchEvent); window.addEventListener($.EVENT_END, handleTouchEvent); window.addEventListener($.EVENT_CANCEL, handleTouchEvent); //fixed hashchange(android) window.addEventListener($.EVENT_CLICK, function (e) { //TODO 应该判断当前target是不是在targets.popover内部,而不是非要相等 if (($.os.android || $.os.ios) && ($.targets.popover && e.target === $.targets.popover || $.targets.tab || $.targets.offcanvas || $.targets.modal)) { e.preventDefault(); } }, true); //增加原生滚动识别 $.isScrolling = false; var scrollingTimeout = null; window.addEventListener('scroll', function () { $.isScrolling = true; scrollingTimeout && clearTimeout(scrollingTimeout); scrollingTimeout = setTimeout(function () { $.isScrolling = false; }, 250); }); })(mui, window); /** * mui gesture tap and doubleTap * @param {type} $ * @param {type} name * @returns {undefined} */ (function ($, name) { var lastTarget; var lastTapTime; var handle = function handle(event, touch) { var session = $.gestures.session; var options = this.options; switch (event.type) { case $.EVENT_END: if (!touch.isFinal) { return; } var target = session.target; if (!target || target.disabled || target.classList && target.classList.contains($.className('disabled'))) { return; } if (touch.distance < options.tapMaxDistance && touch.deltaTime < options.tapMaxTime) { if ($.options.gestureConfig.doubletap && lastTarget && lastTarget === target) { //same target if (lastTapTime && touch.timestamp - lastTapTime < options.tapMaxInterval) { $.trigger(target, 'doubletap', touch); lastTapTime = $.now(); lastTarget = target; return; } } $.trigger(target, name, touch); lastTapTime = $.now(); lastTarget = target; } break; } }; /** * mui gesture tap */ $.addGesture({ name: name, index: 30, handle: handle, options: { fingers: 1, tapMaxInterval: 300, tapMaxDistance: 5, tapMaxTime: 250 } }); })(mui, 'tap'); exports.default = mui;