UNPKG

mdui

Version:

a CSS Framework based on material design

1,693 lines (1,598 loc) 209 kB
/*! * mdui 1.0.2 (https://mdui.org) * Copyright 2016-2021 zdhxiong * Licensed under MIT */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.mdui = factory()); }(this, (function () { 'use strict'; !function(){try{return new MouseEvent("test")}catch(e$1){}var e=function(e,t){t=t||{bubbles:!1,cancelable:!1};var n=document.createEvent("MouseEvent");return n.initMouseEvent(e,t.bubbles,t.cancelable,window,0,t.screenX||0,t.screenY||0,t.clientX||0,t.clientY||0,t.ctrlKey||!1,t.altKey||!1,t.shiftKey||!1,t.metaKey||!1,t.button||0,t.relatedTarget||null),n};e.prototype=Event.prototype,window.MouseEvent=e;}(); !function(){function t(t,e){e=e||{bubbles:!1,cancelable:!1,detail:void 0};var n=document.createEvent("CustomEvent");return n.initCustomEvent(t,e.bubbles,e.cancelable,e.detail),n}"function"!=typeof window.CustomEvent&&(t.prototype=window.Event.prototype,window.CustomEvent=t);}(); /** * @this {Promise} */ function finallyConstructor(callback) { var constructor = this.constructor; return this.then( function(value) { // @ts-ignore return constructor.resolve(callback()).then(function() { return value; }); }, function(reason) { // @ts-ignore return constructor.resolve(callback()).then(function() { // @ts-ignore return constructor.reject(reason); }); } ); } function allSettled(arr) { var P = this; return new P(function(resolve, reject) { if (!(arr && typeof arr.length !== 'undefined')) { return reject( new TypeError( typeof arr + ' ' + arr + ' is not iterable(cannot read property Symbol(Symbol.iterator))' ) ); } var args = Array.prototype.slice.call(arr); if (args.length === 0) { return resolve([]); } var remaining = args.length; function res(i, val) { if (val && (typeof val === 'object' || typeof val === 'function')) { var then = val.then; if (typeof then === 'function') { then.call( val, function(val) { res(i, val); }, function(e) { args[i] = { status: 'rejected', reason: e }; if (--remaining === 0) { resolve(args); } } ); return; } } args[i] = { status: 'fulfilled', value: val }; if (--remaining === 0) { resolve(args); } } for (var i = 0; i < args.length; i++) { res(i, args[i]); } }); } // Store setTimeout reference so promise-polyfill will be unaffected by // other code modifying setTimeout (like sinon.useFakeTimers()) var setTimeoutFunc = setTimeout; function isArray(x) { return Boolean(x && typeof x.length !== 'undefined'); } function noop() {} // Polyfill for Function.prototype.bind function bind(fn, thisArg) { return function() { fn.apply(thisArg, arguments); }; } /** * @constructor * @param {Function} fn */ function Promise$1(fn) { if (!(this instanceof Promise$1)) { throw new TypeError('Promises must be constructed via new'); } if (typeof fn !== 'function') { throw new TypeError('not a function'); } /** @type {!number} */ this._state = 0; /** @type {!boolean} */ this._handled = false; /** @type {Promise|undefined} */ this._value = undefined; /** @type {!Array<!Function>} */ this._deferreds = []; doResolve(fn, this); } function handle(self, deferred) { while (self._state === 3) { self = self._value; } if (self._state === 0) { self._deferreds.push(deferred); return; } self._handled = true; Promise$1._immediateFn(function() { var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected; if (cb === null) { (self._state === 1 ? resolve : reject)(deferred.promise, self._value); return; } var ret; try { ret = cb(self._value); } catch (e) { reject(deferred.promise, e); return; } resolve(deferred.promise, ret); }); } function resolve(self, newValue) { try { // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure if (newValue === self) { throw new TypeError('A promise cannot be resolved with itself.'); } if ( newValue && (typeof newValue === 'object' || typeof newValue === 'function') ) { var then = newValue.then; if (newValue instanceof Promise$1) { self._state = 3; self._value = newValue; finale(self); return; } else if (typeof then === 'function') { doResolve(bind(then, newValue), self); return; } } self._state = 1; self._value = newValue; finale(self); } catch (e) { reject(self, e); } } function reject(self, newValue) { self._state = 2; self._value = newValue; finale(self); } function finale(self) { if (self._state === 2 && self._deferreds.length === 0) { Promise$1._immediateFn(function() { if (!self._handled) { Promise$1._unhandledRejectionFn(self._value); } }); } for (var i = 0, len = self._deferreds.length; i < len; i++) { handle(self, self._deferreds[i]); } self._deferreds = null; } /** * @constructor */ function Handler(onFulfilled, onRejected, promise) { this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; this.onRejected = typeof onRejected === 'function' ? onRejected : null; this.promise = promise; } /** * Take a potentially misbehaving resolver function and make sure * onFulfilled and onRejected are only called once. * * Makes no guarantees about asynchrony. */ function doResolve(fn, self) { var done = false; try { fn( function(value) { if (done) { return; } done = true; resolve(self, value); }, function(reason) { if (done) { return; } done = true; reject(self, reason); } ); } catch (ex) { if (done) { return; } done = true; reject(self, ex); } } Promise$1.prototype['catch'] = function(onRejected) { return this.then(null, onRejected); }; Promise$1.prototype.then = function(onFulfilled, onRejected) { // @ts-ignore var prom = new this.constructor(noop); handle(this, new Handler(onFulfilled, onRejected, prom)); return prom; }; Promise$1.prototype['finally'] = finallyConstructor; Promise$1.all = function(arr) { return new Promise$1(function(resolve, reject) { if (!isArray(arr)) { return reject(new TypeError('Promise.all accepts an array')); } var args = Array.prototype.slice.call(arr); if (args.length === 0) { return resolve([]); } var remaining = args.length; function res(i, val) { try { if (val && (typeof val === 'object' || typeof val === 'function')) { var then = val.then; if (typeof then === 'function') { then.call( val, function(val) { res(i, val); }, reject ); return; } } args[i] = val; if (--remaining === 0) { resolve(args); } } catch (ex) { reject(ex); } } for (var i = 0; i < args.length; i++) { res(i, args[i]); } }); }; Promise$1.allSettled = allSettled; Promise$1.resolve = function(value) { if (value && typeof value === 'object' && value.constructor === Promise$1) { return value; } return new Promise$1(function(resolve) { resolve(value); }); }; Promise$1.reject = function(value) { return new Promise$1(function(resolve, reject) { reject(value); }); }; Promise$1.race = function(arr) { return new Promise$1(function(resolve, reject) { if (!isArray(arr)) { return reject(new TypeError('Promise.race accepts an array')); } for (var i = 0, len = arr.length; i < len; i++) { Promise$1.resolve(arr[i]).then(resolve, reject); } }); }; // Use polyfill for setImmediate for performance gains Promise$1._immediateFn = // @ts-ignore (typeof setImmediate === 'function' && function(fn) { // @ts-ignore setImmediate(fn); }) || function(fn) { setTimeoutFunc(fn, 0); }; Promise$1._unhandledRejectionFn = function _unhandledRejectionFn(err) { if (typeof console !== 'undefined' && console) { console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console } }; /** @suppress {undefinedVars} */ var globalNS = (function() { // the only reliable means to get the global object is // `Function('return this')()` // However, this causes CSP violations in Chrome apps. if (typeof self !== 'undefined') { return self; } if (typeof window !== 'undefined') { return window; } if (typeof global !== 'undefined') { return global; } throw new Error('unable to locate global object'); })(); // Expose the polyfill if Promise is undefined or set to a // non-function value. The latter can be due to a named HTMLElement // being exposed by browsers for legacy reasons. // https://github.com/taylorhakes/promise-polyfill/issues/114 if (typeof globalNS['Promise'] !== 'function') { globalNS['Promise'] = Promise$1; } else if (!globalNS.Promise.prototype['finally']) { globalNS.Promise.prototype['finally'] = finallyConstructor; } else if (!globalNS.Promise.allSettled) { globalNS.Promise.allSettled = allSettled; } function isFunction(target) { return typeof target === 'function'; } function isString(target) { return typeof target === 'string'; } function isNumber(target) { return typeof target === 'number'; } function isBoolean(target) { return typeof target === 'boolean'; } function isUndefined(target) { return typeof target === 'undefined'; } function isNull(target) { return target === null; } function isWindow(target) { return target instanceof Window; } function isDocument(target) { return target instanceof Document; } function isElement(target) { return target instanceof Element; } function isNode(target) { return target instanceof Node; } /** * 是否是 IE 浏览器 */ function isIE() { // @ts-ignore return !!window.document.documentMode; } function isArrayLike(target) { if (isFunction(target) || isWindow(target)) { return false; } return isNumber(target.length); } function isObjectLike(target) { return typeof target === 'object' && target !== null; } function toElement(target) { return isDocument(target) ? target.documentElement : target; } /** * 把用 - 分隔的字符串转为驼峰(如 box-sizing 转换为 boxSizing) * @param string */ function toCamelCase(string) { return string .replace(/^-ms-/, 'ms-') .replace(/-([a-z])/g, function (_, letter) { return letter.toUpperCase(); }); } /** * 把驼峰法转为用 - 分隔的字符串(如 boxSizing 转换为 box-sizing) * @param string */ function toKebabCase(string) { return string.replace(/[A-Z]/g, function (replacer) { return '-' + replacer.toLowerCase(); }); } /** * 获取元素的样式值 * @param element * @param name */ function getComputedStyleValue(element, name) { return window.getComputedStyle(element).getPropertyValue(toKebabCase(name)); } /** * 检查元素的 box-sizing 是否是 border-box * @param element */ function isBorderBox(element) { return getComputedStyleValue(element, 'box-sizing') === 'border-box'; } /** * 获取元素的 padding, border, margin 宽度(两侧宽度的和,单位为px) * @param element * @param direction * @param extra */ function getExtraWidth(element, direction, extra) { var position = direction === 'width' ? ['Left', 'Right'] : ['Top', 'Bottom']; return [0, 1].reduce(function (prev, _, index) { var prop = extra + position[index]; if (extra === 'border') { prop += 'Width'; } return prev + parseFloat(getComputedStyleValue(element, prop) || '0'); }, 0); } /** * 获取元素的样式值,对 width 和 height 进行过处理 * @param element * @param name */ function getStyle(element, name) { // width、height 属性使用 getComputedStyle 得到的值不准确,需要使用 getBoundingClientRect 获取 if (name === 'width' || name === 'height') { var valueNumber = element.getBoundingClientRect()[name]; if (isBorderBox(element)) { return (valueNumber + "px"); } return ((valueNumber - getExtraWidth(element, name, 'border') - getExtraWidth(element, name, 'padding')) + "px"); } return getComputedStyleValue(element, name); } /** * 获取子节点组成的数组 * @param target * @param parent */ function getChildNodesArray(target, parent) { var tempParent = document.createElement(parent); tempParent.innerHTML = target; return [].slice.call(tempParent.childNodes); } /** * 始终返回 false 的函数 */ function returnFalse() { return false; } /** * 数值单位的 CSS 属性 */ var cssNumber = [ 'animationIterationCount', 'columnCount', 'fillOpacity', 'flexGrow', 'flexShrink', 'fontWeight', 'gridArea', 'gridColumn', 'gridColumnEnd', 'gridColumnStart', 'gridRow', 'gridRowEnd', 'gridRowStart', 'lineHeight', 'opacity', 'order', 'orphans', 'widows', 'zIndex', 'zoom' ]; function each(target, callback) { if (isArrayLike(target)) { for (var i = 0; i < target.length; i += 1) { if (callback.call(target[i], i, target[i]) === false) { return target; } } } else { var keys = Object.keys(target); for (var i$1 = 0; i$1 < keys.length; i$1 += 1) { if (callback.call(target[keys[i$1]], keys[i$1], target[keys[i$1]]) === false) { return target; } } } return target; } /** * 为了使用模块扩充,这里不能使用默认导出 */ var JQ = function JQ(arr) { var this$1 = this; this.length = 0; if (!arr) { return this; } each(arr, function (i, item) { // @ts-ignore this$1[i] = item; }); this.length = arr.length; return this; }; function get$() { var $ = function (selector) { if (!selector) { return new JQ(); } // JQ if (selector instanceof JQ) { return selector; } // function if (isFunction(selector)) { if (/complete|loaded|interactive/.test(document.readyState) && document.body) { selector.call(document, $); } else { document.addEventListener('DOMContentLoaded', function () { return selector.call(document, $); }, false); } return new JQ([document]); } // String if (isString(selector)) { var html = selector.trim(); // 根据 HTML 字符串创建 JQ 对象 if (html[0] === '<' && html[html.length - 1] === '>') { var toCreate = 'div'; var tags = { li: 'ul', tr: 'tbody', td: 'tr', th: 'tr', tbody: 'table', option: 'select', }; each(tags, function (childTag, parentTag) { if (html.indexOf(("<" + childTag)) === 0) { toCreate = parentTag; return false; } return; }); return new JQ(getChildNodesArray(html, toCreate)); } // 根据 CSS 选择器创建 JQ 对象 var isIdSelector = selector[0] === '#' && !selector.match(/[ .<>:~]/); if (!isIdSelector) { return new JQ(document.querySelectorAll(selector)); } var element = document.getElementById(selector.slice(1)); if (element) { return new JQ([element]); } return new JQ(); } if (isArrayLike(selector) && !isNode(selector)) { return new JQ(selector); } return new JQ([selector]); }; $.fn = JQ.prototype; return $; } var $ = get$(); // 避免页面加载完后直接执行css动画 // https://css-tricks.com/transitions-only-after-page-load/ setTimeout(function () { return $('body').addClass('mdui-loaded'); }); var mdui = { $: $, }; $.fn.each = function (callback) { return each(this, callback); }; /** * 检查 container 元素内是否包含 contains 元素 * @param container 父元素 * @param contains 子元素 * @example ```js contains( document, document.body ); // true contains( document.getElementById('test'), document ); // false contains( $('.container').get(0), $('.contains').get(0) ); // false ``` */ function contains(container, contains) { return container !== contains && toElement(container).contains(contains); } /** * 把第二个数组的元素追加到第一个数组中,并返回合并后的数组 * @param first 第一个数组 * @param second 该数组的元素将被追加到第一个数组中 * @example ```js merge( [ 0, 1, 2 ], [ 2, 3, 4 ] ) // [ 0, 1, 2, 2, 3, 4 ] ``` */ function merge(first, second) { each(second, function (_, value) { first.push(value); }); return first; } $.fn.get = function (index) { return index === undefined ? [].slice.call(this) : this[index >= 0 ? index : index + this.length]; }; $.fn.find = function (selector) { var foundElements = []; this.each(function (_, element) { merge(foundElements, $(element.querySelectorAll(selector)).get()); }); return new JQ(foundElements); }; // 存储事件 var handlers = {}; // 元素ID var mduiElementId = 1; /** * 为元素赋予一个唯一的ID */ function getElementId(element) { var key = '_mduiEventId'; // @ts-ignore if (!element[key]) { // @ts-ignore element[key] = ++mduiElementId; } // @ts-ignore return element[key]; } /** * 解析事件名中的命名空间 */ function parse(type) { var parts = type.split('.'); return { type: parts[0], ns: parts.slice(1).sort().join(' '), }; } /** * 命名空间匹配规则 */ function matcherFor(ns) { return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)'); } /** * 获取匹配的事件 * @param element * @param type * @param func * @param selector */ function getHandlers(element, type, func, selector) { var event = parse(type); return (handlers[getElementId(element)] || []).filter(function (handler) { return handler && (!event.type || handler.type === event.type) && (!event.ns || matcherFor(event.ns).test(handler.ns)) && (!func || getElementId(handler.func) === getElementId(func)) && (!selector || handler.selector === selector); }); } /** * 添加事件监听 * @param element * @param types * @param func * @param data * @param selector */ function add(element, types, func, data, selector) { var elementId = getElementId(element); if (!handlers[elementId]) { handlers[elementId] = []; } // 传入 data.useCapture 来设置 useCapture: true var useCapture = false; if (isObjectLike(data) && data.useCapture) { useCapture = true; } types.split(' ').forEach(function (type) { if (!type) { return; } var event = parse(type); function callFn(e, elem) { // 因为鼠标事件模拟事件的 detail 属性是只读的,因此在 e._detail 中存储参数 var result = func.apply(elem, // @ts-ignore e._detail === undefined ? [e] : [e].concat(e._detail)); if (result === false) { e.preventDefault(); e.stopPropagation(); } } function proxyFn(e) { // @ts-ignore if (e._ns && !matcherFor(e._ns).test(event.ns)) { return; } // @ts-ignore e._data = data; if (selector) { // 事件代理 $(element) .find(selector) .get() .reverse() .forEach(function (elem) { if (elem === e.target || contains(elem, e.target)) { callFn(e, elem); } }); } else { // 不使用事件代理 callFn(e, element); } } var handler = { type: event.type, ns: event.ns, func: func, selector: selector, id: handlers[elementId].length, proxy: proxyFn, }; handlers[elementId].push(handler); element.addEventListener(handler.type, proxyFn, useCapture); }); } /** * 移除事件监听 * @param element * @param types * @param func * @param selector */ function remove(element, types, func, selector) { var handlersInElement = handlers[getElementId(element)] || []; var removeEvent = function (handler) { delete handlersInElement[handler.id]; element.removeEventListener(handler.type, handler.proxy, false); }; if (!types) { handlersInElement.forEach(function (handler) { return removeEvent(handler); }); } else { types.split(' ').forEach(function (type) { if (type) { getHandlers(element, type, func, selector).forEach(function (handler) { return removeEvent(handler); }); } }); } } $.fn.trigger = function (type, extraParameters) { var event = parse(type); var eventObject; var eventParams = { bubbles: true, cancelable: true, }; var isMouseEvent = ['click', 'mousedown', 'mouseup', 'mousemove'].indexOf(event.type) > -1; if (isMouseEvent) { // Note: MouseEvent 无法传入 detail 参数 eventObject = new MouseEvent(event.type, eventParams); } else { eventParams.detail = extraParameters; eventObject = new CustomEvent(event.type, eventParams); } // @ts-ignore eventObject._detail = extraParameters; // @ts-ignore eventObject._ns = event.ns; return this.each(function () { this.dispatchEvent(eventObject); }); }; function extend(target, object1) { var objectN = [], len = arguments.length - 2; while ( len-- > 0 ) objectN[ len ] = arguments[ len + 2 ]; objectN.unshift(object1); each(objectN, function (_, object) { each(object, function (prop, value) { if (!isUndefined(value)) { target[prop] = value; } }); }); return target; } /** * 将数组或对象序列化,序列化后的字符串可作为 URL 查询字符串使用 * * 若传入数组,则格式必须和 serializeArray 方法的返回值一样 * @param obj 对象或数组 * @example ```js param({ width: 1680, height: 1050 }); // width=1680&height=1050 ``` * @example ```js param({ foo: { one: 1, two: 2 }}) // foo[one]=1&foo[two]=2 ``` * @example ```js param({ids: [1, 2, 3]}) // ids[]=1&ids[]=2&ids[]=3 ``` * @example ```js param([ {"name":"name","value":"mdui"}, {"name":"password","value":"123456"} ]) // name=mdui&password=123456 ``` */ function param(obj) { if (!isObjectLike(obj) && !Array.isArray(obj)) { return ''; } var args = []; function destructure(key, value) { var keyTmp; if (isObjectLike(value)) { each(value, function (i, v) { if (Array.isArray(value) && !isObjectLike(v)) { keyTmp = ''; } else { keyTmp = i; } destructure((key + "[" + keyTmp + "]"), v); }); } else { if (value == null || value === '') { keyTmp = '='; } else { keyTmp = "=" + (encodeURIComponent(value)); } args.push(encodeURIComponent(key) + keyTmp); } } if (Array.isArray(obj)) { each(obj, function () { destructure(this.name, this.value); }); } else { each(obj, destructure); } return args.join('&'); } // 全局配置参数 var globalOptions = {}; // 全局事件名 var ajaxEvents = { ajaxStart: 'start.mdui.ajax', ajaxSuccess: 'success.mdui.ajax', ajaxError: 'error.mdui.ajax', ajaxComplete: 'complete.mdui.ajax', }; /** * 判断此请求方法是否通过查询字符串提交参数 * @param method 请求方法,大写 */ function isQueryStringData(method) { return ['GET', 'HEAD'].indexOf(method) >= 0; } /** * 添加参数到 URL 上,且 URL 中不存在 ? 时,自动把第一个 & 替换为 ? * @param url * @param query */ function appendQuery(url, query) { return (url + "&" + query).replace(/[&?]{1,2}/, '?'); } /** * 合并请求参数,参数优先级:options > globalOptions > defaults * @param options */ function mergeOptions(options) { // 默认参数 var defaults = { url: '', method: 'GET', data: '', processData: true, async: true, cache: true, username: '', password: '', headers: {}, xhrFields: {}, statusCode: {}, dataType: 'text', contentType: 'application/x-www-form-urlencoded', timeout: 0, global: true, }; // globalOptions 中的回调函数不合并 each(globalOptions, function (key, value) { var callbacks = [ 'beforeSend', 'success', 'error', 'complete', 'statusCode' ]; // @ts-ignore if (callbacks.indexOf(key) < 0 && !isUndefined(value)) { defaults[key] = value; } }); return extend({}, defaults, options); } /** * 发送 ajax 请求 * @param options * @example ```js ajax({ method: "POST", url: "some.php", data: { name: "John", location: "Boston" } }).then(function( msg ) { alert( "Data Saved: " + msg ); }); ``` */ function ajax(options) { // 是否已取消请求 var isCanceled = false; // 事件参数 var eventParams = {}; // 参数合并 var mergedOptions = mergeOptions(options); var url = mergedOptions.url || window.location.toString(); var method = mergedOptions.method.toUpperCase(); var data = mergedOptions.data; var processData = mergedOptions.processData; var async = mergedOptions.async; var cache = mergedOptions.cache; var username = mergedOptions.username; var password = mergedOptions.password; var headers = mergedOptions.headers; var xhrFields = mergedOptions.xhrFields; var statusCode = mergedOptions.statusCode; var dataType = mergedOptions.dataType; var contentType = mergedOptions.contentType; var timeout = mergedOptions.timeout; var global = mergedOptions.global; // 需要发送的数据 // GET/HEAD 请求和 processData 为 true 时,转换为查询字符串格式,特殊格式不转换 if (data && (isQueryStringData(method) || processData) && !isString(data) && !(data instanceof ArrayBuffer) && !(data instanceof Blob) && !(data instanceof Document) && !(data instanceof FormData)) { data = param(data); } // 对于 GET、HEAD 类型的请求,把 data 数据添加到 URL 中 if (data && isQueryStringData(method)) { // 查询字符串拼接到 URL 中 url = appendQuery(url, data); data = null; } /** * 触发事件和回调函数 * @param event * @param params * @param callback * @param args */ function trigger(event, params, callback) { var args = [], len = arguments.length - 3; while ( len-- > 0 ) args[ len ] = arguments[ len + 3 ]; // 触发全局事件 if (global) { $(document).trigger(event, params); } // 触发 ajax 回调和事件 var result1; var result2; if (callback) { // 全局回调 if (callback in globalOptions) { // @ts-ignore result1 = globalOptions[callback].apply(globalOptions, args); } // 自定义回调 if (mergedOptions[callback]) { // @ts-ignore result2 = mergedOptions[callback].apply(mergedOptions, args); } // beforeSend 回调返回 false 时取消 ajax 请求 if (callback === 'beforeSend' && (result1 === false || result2 === false)) { isCanceled = true; } } } // XMLHttpRequest 请求 function XHR() { var textStatus; return new Promise(function (resolve, reject) { // GET/HEAD 请求的缓存处理 if (isQueryStringData(method) && !cache) { url = appendQuery(url, ("_=" + (Date.now()))); } // 创建 XHR var xhr = new XMLHttpRequest(); xhr.open(method, url, async, username, password); if (contentType || (data && !isQueryStringData(method) && contentType !== false)) { xhr.setRequestHeader('Content-Type', contentType); } // 设置 Accept if (dataType === 'json') { xhr.setRequestHeader('Accept', 'application/json, text/javascript'); } // 添加 headers if (headers) { each(headers, function (key, value) { // undefined 值不发送,string 和 null 需要发送 if (!isUndefined(value)) { xhr.setRequestHeader(key, value + ''); // 把 null 转换成字符串 } }); } // 检查是否是跨域请求,跨域请求时不添加 X-Requested-With var crossDomain = /^([\w-]+:)?\/\/([^/]+)/.test(url) && RegExp.$2 !== window.location.host; if (!crossDomain) { xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); } if (xhrFields) { each(xhrFields, function (key, value) { // @ts-ignore xhr[key] = value; }); } eventParams.xhr = xhr; eventParams.options = mergedOptions; var xhrTimeout; xhr.onload = function () { if (xhrTimeout) { clearTimeout(xhrTimeout); } // AJAX 返回的 HTTP 响应码是否表示成功 var isHttpStatusSuccess = (xhr.status >= 200 && xhr.status < 300) || xhr.status === 304 || xhr.status === 0; var responseData; if (isHttpStatusSuccess) { if (xhr.status === 204 || method === 'HEAD') { textStatus = 'nocontent'; } else if (xhr.status === 304) { textStatus = 'notmodified'; } else { textStatus = 'success'; } if (dataType === 'json') { try { responseData = method === 'HEAD' ? undefined : JSON.parse(xhr.responseText); eventParams.data = responseData; } catch (err) { textStatus = 'parsererror'; trigger(ajaxEvents.ajaxError, eventParams, 'error', xhr, textStatus); reject(new Error(textStatus)); } if (textStatus !== 'parsererror') { trigger(ajaxEvents.ajaxSuccess, eventParams, 'success', responseData, textStatus, xhr); resolve(responseData); } } else { responseData = method === 'HEAD' ? undefined : xhr.responseType === 'text' || xhr.responseType === '' ? xhr.responseText : xhr.response; eventParams.data = responseData; trigger(ajaxEvents.ajaxSuccess, eventParams, 'success', responseData, textStatus, xhr); resolve(responseData); } } else { textStatus = 'error'; trigger(ajaxEvents.ajaxError, eventParams, 'error', xhr, textStatus); reject(new Error(textStatus)); } // statusCode each([globalOptions.statusCode, statusCode], function (_, func) { if (func && func[xhr.status]) { if (isHttpStatusSuccess) { func[xhr.status](responseData, textStatus, xhr); } else { func[xhr.status](xhr, textStatus); } } }); trigger(ajaxEvents.ajaxComplete, eventParams, 'complete', xhr, textStatus); }; xhr.onerror = function () { if (xhrTimeout) { clearTimeout(xhrTimeout); } trigger(ajaxEvents.ajaxError, eventParams, 'error', xhr, xhr.statusText); trigger(ajaxEvents.ajaxComplete, eventParams, 'complete', xhr, 'error'); reject(new Error(xhr.statusText)); }; xhr.onabort = function () { var statusText = 'abort'; if (xhrTimeout) { statusText = 'timeout'; clearTimeout(xhrTimeout); } trigger(ajaxEvents.ajaxError, eventParams, 'error', xhr, statusText); trigger(ajaxEvents.ajaxComplete, eventParams, 'complete', xhr, statusText); reject(new Error(statusText)); }; // ajax start 回调 trigger(ajaxEvents.ajaxStart, eventParams, 'beforeSend', xhr); if (isCanceled) { reject(new Error('cancel')); return; } // Timeout if (timeout > 0) { xhrTimeout = setTimeout(function () { xhr.abort(); }, timeout); } // 发送 XHR xhr.send(data); }); } return XHR(); } $.ajax = ajax; /** * 为 Ajax 请求设置全局配置参数 * @param options 键值对参数 * @example ```js ajaxSetup({ dataType: 'json', method: 'POST', }); ``` */ function ajaxSetup(options) { return extend(globalOptions, options); } $.ajaxSetup = ajaxSetup; $.contains = contains; var dataNS = '_mduiElementDataStorage'; /** * 在元素上设置键值对数据 * @param element * @param object */ function setObjectToElement(element, object) { // @ts-ignore if (!element[dataNS]) { // @ts-ignore element[dataNS] = {}; } each(object, function (key, value) { // @ts-ignore element[dataNS][toCamelCase(key)] = value; }); } function data(element, key, value) { var obj; // 根据键值对设置值 // data(element, { 'key' : 'value' }) if (isObjectLike(key)) { setObjectToElement(element, key); return key; } // 根据 key、value 设置值 // data(element, 'key', 'value') if (!isUndefined(value)) { setObjectToElement(element, ( obj = {}, obj[key] = value, obj )); return value; } // 获取所有值 // data(element) if (isUndefined(key)) { // @ts-ignore return element[dataNS] ? element[dataNS] : {}; } // 从 dataNS 中获取指定值 // data(element, 'key') key = toCamelCase(key); // @ts-ignore if (element[dataNS] && key in element[dataNS]) { // @ts-ignore return element[dataNS][key]; } return undefined; } $.data = data; $.each = each; $.extend = function () { var this$1 = this; var objectN = [], len = arguments.length; while ( len-- ) objectN[ len ] = arguments[ len ]; if (objectN.length === 1) { each(objectN[0], function (prop, value) { this$1[prop] = value; }); return this; } return extend.apply(void 0, [ objectN.shift(), objectN.shift() ].concat( objectN )); }; function map(elements, callback) { var ref; var value; var ret = []; each(elements, function (i, element) { value = callback.call(window, element, i); if (value != null) { ret.push(value); } }); return (ref = []).concat.apply(ref, ret); } $.map = map; $.merge = merge; $.param = param; /** * 移除指定元素上存放的数据 * @param element 存放数据的元素 * @param name * 数据键名 * * 若未指定键名,将移除元素上所有数据 * * 多个键名可以用空格分隔,或者用数组表示多个键名 @example ```js // 移除元素上键名为 name 的数据 removeData(document.body, 'name'); ``` * @example ```js // 移除元素上键名为 name1 和 name2 的数据 removeData(document.body, 'name1 name2'); ``` * @example ```js // 移除元素上键名为 name1 和 name2 的数据 removeData(document.body, ['name1', 'name2']); ``` * @example ```js // 移除元素上所有数据 removeData(document.body); ``` */ function removeData(element, name) { // @ts-ignore if (!element[dataNS]) { return; } var remove = function (nameItem) { nameItem = toCamelCase(nameItem); // @ts-ignore if (element[dataNS][nameItem]) { // @ts-ignore element[dataNS][nameItem] = null; // @ts-ignore delete element[dataNS][nameItem]; } }; if (isUndefined(name)) { // @ts-ignore element[dataNS] = null; // @ts-ignore delete element[dataNS]; // @ts-ignore } else if (isString(name)) { name .split(' ') .filter(function (nameItem) { return nameItem; }) .forEach(function (nameItem) { return remove(nameItem); }); } else { each(name, function (_, nameItem) { return remove(nameItem); }); } } $.removeData = removeData; /** * 过滤掉数组中的重复元素 * @param arr 数组 * @example ```js unique([1, 2, 12, 3, 2, 1, 2, 1, 1]); // [1, 2, 12, 3] ``` */ function unique(arr) { var result = []; each(arr, function (_, val) { if (result.indexOf(val) === -1) { result.push(val); } }); return result; } $.unique = unique; $.fn.add = function (selector) { return new JQ(unique(merge(this.get(), $(selector).get()))); }; each(['add', 'remove', 'toggle'], function (_, name) { $.fn[(name + "Class")] = function (className) { if (name === 'remove' && !arguments.length) { return this.each(function (_, element) { element.setAttribute('class', ''); }); } return this.each(function (i, element) { if (!isElement(element)) { return; } var classes = (isFunction(className) ? className.call(element, i, element.getAttribute('class') || '') : className) .split(' ') .filter(function (name) { return name; }); each(classes, function (_, cls) { element.classList[name](cls); }); }); }; }); each(['insertBefore', 'insertAfter'], function (nameIndex, name) { $.fn[name] = function (target) { var $element = nameIndex ? $(this.get().reverse()) : this; // 顺序和 jQuery 保持一致 var $target = $(target); var result = []; $target.each(function (index, target) { if (!target.parentNode) { return; } $element.each(function (_, element) { var newItem = index ? element.cloneNode(true) : element; var existingItem = nameIndex ? target.nextSibling : target; result.push(newItem); target.parentNode.insertBefore(newItem, existingItem); }); }); return $(nameIndex ? result.reverse() : result); }; }); /** * 是否不是 HTML 字符串(包裹在 <> 中) * @param target */ function isPlainText(target) { return (isString(target) && (target[0] !== '<' || target[target.length - 1] !== '>')); } each(['before', 'after'], function (nameIndex, name) { $.fn[name] = function () { var args = [], len = arguments.length; while ( len-- ) args[ len ] = arguments[ len ]; // after 方法,多个参数需要按参数顺序添加到元素后面,所以需要将参数顺序反向处理 if (nameIndex === 1) { args = args.reverse(); } return this.each(function (index, element) { var targets = isFunction(args[0]) ? [args[0].call(element, index, element.innerHTML)] : args; each(targets, function (_, target) { var $target; if (isPlainText(target)) { $target = $(getChildNodesArray(target, 'div')); } else if (index && isElement(target)) { $target = $(target.cloneNode(true)); } else { $target = $(target); } $target[nameIndex ? 'insertAfter' : 'insertBefore'](element); }); }); }; }); $.fn.off = function (types, selector, callback) { var this$1 = this; // types 是对象 if (isObjectLike(types)) { each(types, function (type, fn) { // this.off('click', undefined, function () {}) // this.off('click', '.box', function () {}) this$1.off(type, selector, fn); }); return this; } // selector 不存在 if (selector === false || isFunction(selector)) { callback = selector; selector = undefined; // this.off('click', undefined, function () {}) } // callback 传入 `false`,相当于 `return false` if (callback === false) { callback = returnFalse; } return this.each(function () { remove(this, types, callback, selector); }); }; $.fn.on = function (types, selector, data, callback, one) { var this$1 = this; // types 可以是 type/func 对象 if (isObjectLike(types)) { // (types-Object, selector, data) if (!isString(selector)) { // (types-Object, data) data = data || selector; selector = undefined; } each(types, function (type, fn) { // selector 和 data 都可能是 undefined // @ts-ignore this$1.on(type, selector, data, fn, one); }); return this; } if (data == null && callback == null) { // (types, fn) callback = selector; data = selector = undefined; } else if (callback == null) { if (isString(selector)) { // (types, selector, fn) callback = data; data = undefined; } else { // (types, data, fn) callback = data; data = selector; selector = undefined; } } if (callback === false) { callback = returnFalse; } else if (!callback) { return this; } // $().one() if (one) { // eslint-disable-next-line @typescript-eslint/no-this-alias var _this = this; var origCallback = callback; callback = function (event) { _this.off(event.type, selector, callback); // eslint-disable-next-line prefer-rest-params return origCallback.apply(this, arguments); }; } return this.each(function () { add(this, types, callback, data, selector); }); }; each(ajaxEvents, function (name, eventName) { $.fn[name] = function (fn) { return this.on(eventName, function (e, params) { fn(e, params.xhr, params.options, params.data); }); }; }); $.fn.map = function (callback) { return new JQ(map(this, function (element, i) { return callback.call(element, i, element); })); }; $.fn.clone = function () { return this.map(function () { return this.cloneNode(true); }); }; $.fn.is = function (selector) { var isMatched = false; if (isFunction(selector)) { this.each(function (index, element) { if (selector.call(element, index, element)) { isMatched = true; } }); return isMatched; } if (isString(selector)) { this.each(function (_, element) { if (isDocument(element) || isWindow(element)) { return; } // @ts-ignore var matches = element.matches || element.msMatchesSelector; if (matches.call(element, selector)) { isMatched = true; } }); return isMatched; } var $compareWith = $(selector); this.each(function (_, element) { $compareWith.each(function (_, compare) { if (element === compare) { isMatched = true; } }); }); return isMatched; }; $.fn.remove = function (selector) { return this.each(function (_, element) { if (element.parentNode && (!selector || $(element).is(selector))) { element.parentNode.removeChild(element); } }); }; each(['prepend', 'append'], function (nameIndex, name) { $.fn[name] = function () { var args = [], len = arguments.length; while ( len-- ) args[ len ] = arguments[ len ]; return this.each(function (index, element) { var ref; var childNodes = element.childNodes; var childLength = childNodes.length; var child = childLength ? childNodes[nameIndex ? childLength - 1 : 0] : document.createElement('div'); if (!childLength) { element.appendChild(child); } var contents = isFunction(args[0]) ? [args[0].call(element, index, element.innerHTML)] : args; // 如果不是字符串,则仅第一个元素使用原始元素,其他的都克隆自第一个元素 if (index) { contents = contents.map(function (content) { return isString(content) ? content : $(content).clone(); }); } (ref = $(child))[nameIndex ? 'after' : 'before'].apply(ref, contents); if (!childLength) { element.removeChild(child); } }); }; }); each(['appendTo', 'prependTo'], function (nameIndex, name) { $.fn[name] = function (target) { var extraChilds = []; var $target = $(target).map(function (_, element) { var childNodes = element.childNodes; var childLength = childNodes.length; if (childLength) {