mdui.editor
Version:
Material Design 样式的富文本编辑器
1,675 lines (1,576 loc) • 149 kB
JavaScript
/*!
* mdui.editor 1.0.2 (https://github.com/zdhxiong/mdui.editor#readme)
* Copyright 2019-2020 zdhxiong
* Licensed under MIT
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = global || self, global.Editor = 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);
});
}
);
}
// 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.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');
})();
if (!('Promise' in globalNS)) {
globalNS['Promise'] = Promise$1;
} else if (!globalNS.Promise.prototype['finally']) {
globalNS.Promise.prototype['finally'] = finallyConstructor;
}
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$();
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;
}
$.fn.each = function (callback) {
return each(this, callback);
};
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);
});
});
};
});
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);
}
$.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(['attr', 'prop', 'css'], function (nameIndex, name) {
function set(element, key, value) {
// 值为 undefined 时,不修改
if (isUndefined(value)) {
return;
}
switch (nameIndex) {
// attr
case 0:
if (isNull(value)) {
element.removeAttribute(key);
}
else {
element.setAttribute(key, value);
}
break;
// prop
case 1:
// @ts-ignore
element[key] = value;
break;
// css
default:
key = toCamelCase(key);
// @ts-ignore
element.style[key] = isNumber(value)
? ("" + value + (cssNumber.indexOf(key) > -1 ? '' : 'px'))
: value;
break;
}
}
function get(element, key) {
switch (nameIndex) {
// attr
case 0:
// 属性不存在时,原生 getAttribute 方法返回 null,而 jquery 返回 undefined。这里和 jquery 保持一致
var value = element.getAttribute(key);
return isNull(value) ? undefined : value;
// prop
case 1:
// @ts-ignore
return element[key];
// css
default:
return getStyle(element, key);
}
}
$.fn[name] = function (key, value) {
var this$1 = this;
if (isObjectLike(key)) {
each(key, function (k, v) {
// @ts-ignore
this$1[name](k, v);
});
return this;
}
if (arguments.length === 1) {
var element = this[0];
return isElement(element) ? get(element, key) : undefined;
}
return this.each(function (i, element) {
set(element, key, isFunction(value) ? value.call(element, i, get(element, key)) : value);
});
};
});
/**
* 过滤掉数组中的重复元素
* @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;
}
$.fn.children = function (selector) {
var children = [];
this.each(function (_, element) {
each(element.childNodes, function (__, childNode) {
if (!isElement(childNode)) {
return;
}
if (!selector || $(childNode).is(selector)) {
children.push(childNode);
}
});
});
return new JQ(unique(children));
};
$.fn.slice = function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return new JQ([].slice.apply(this, args));
};
$.fn.eq = function (index) {
var ret = index === -1 ? this.slice(index) : this.slice(index, +index + 1);
return new JQ(ret);
};
$.fn.first = function () {
return this.eq(0);
};
each(['val', 'html', 'text'], function (nameIndex, name) {
var props = {
0: 'value',
1: 'innerHTML',
2: 'textContent',
};
var propName = props[nameIndex];
function get($elements) {
// text() 获取所有元素的文本
if (nameIndex === 2) {
// @ts-ignore
return map($elements, function (element) { return toElement(element)[propName]; }).join('');
}
// 空集合时,val() 和 html() 返回 undefined
if (!$elements.length) {
return undefined;
}
// val() 和 html() 仅获取第一个元素的内容
var firstElement = $elements[0];
// select multiple 返回数组
if (nameIndex === 0 && $(firstElement).is('select[multiple]')) {
return map($(firstElement).find('option:checked'), function (element) { return element.value; });
}
// @ts-ignore
return firstElement[propName];
}
function set(element, value) {
// text() 和 html() 赋值为 undefined,则保持原内容不变
// val() 赋值为 undefined 则赋值为空
if (isUndefined(value)) {
if (nameIndex !== 0) {
return;
}
value = '';
}
if (nameIndex === 1 && isElement(value)) {
value = value.outerHTML;
}
// @ts-ignore
element[propName] = value;
}
$.fn[name] = function (value) {
// 获取值
if (!arguments.length) {
return get(this);
}
// 设置值
return this.each(function (i, element) {
var computedValue = isFunction(value)
? value.call(element, i, get($(element)))
: value;
// value 是数组,则选中数组中的元素,反选不在数组中的元素
if (nameIndex === 0 && Array.isArray(computedValue)) {
// select[multiple]
if ($(element).is('select[multiple]')) {
map($(element).find('option'), function (option) { return (option.selected =
computedValue.indexOf(option.value) >
-1); });
}
// 其他 checkbox, radio 等元素
else {
element.checked =
computedValue.indexOf(element.value) > -1;
}
}
else {
set(element, computedValue);
}
});
};
});
$.fn.last = function () {
return this.eq(-1);
};
/**
* 检查 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.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(['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) {
return childNodes[nameIndex ? 0 : childLength - 1];
}
var child = document.createElement('div');
element.appendChild(child);
extraChilds.push(child);
return child;
});
var $result = this[nameIndex ? 'insertBefore' : 'insertAfter']($target);
$(extraChilds).remove();
return $result;
};
});
var CommonAbstract = function CommonAbstract(editor) {
this.editor = editor;
};
var prototypeAccessors = { $toolbar: { configurable: true },$container: { configurable: true },selection: { configurable: true },command: { configurable: true } };
/**
* 工具栏 JQ 对象
*/
prototypeAccessors.$toolbar.get = function () {
return this.editor.$toolbar;
};
/**
* 内容区域 JQ 对象
*/
prototypeAccessors.$container.get = function () {
return this.editor.$container;
};
/**
* 选区实例
*/
prototypeAccessors.selection.get = function () {
return this.editor.selection;
};
/**
* 命令实例
*/
prototypeAccessors.command.get = function () {
return this.editor.command;
};
Object.defineProperties( CommonAbstract.prototype, prototypeAccessors );
/**
* 封装 document.execCommand 命令
*/
var Command = /*@__PURE__*/(function (CommonAbstract) {
function Command () {
CommonAbstract.apply(this, arguments);
}
if ( CommonAbstract ) Command.__proto__ = CommonAbstract;
Command.prototype = Object.create( CommonAbstract && CommonAbstract.prototype );
Command.prototype.constructor = Command;
Command.prototype.do = function do$1 (name, value) {
// 如果无选区,忽略
if (!this.selection.getRange()) {
return;
}
// 恢复选区
this.selection.restore();
var customName = name;
// 执行命令
// @ts-ignore
if (this[customName]) {
// @ts-ignore
this[customName](value);
}
else {
document.execCommand(name, false, value);
}
// 修改菜单状态
this.editor.menus.changeStatus();
// 最后,恢复选区保证光标在原来的位置闪烁
this.selection.saveRange();
this.selection.restore();
// 触发 onchange
if (this.editor.change) {
this.editor.change();
}
};
/**
* 自定义 insertHTML 事件,在当前选区中插入指定 HTML
* @param html
*/
// @ts-ignore
Command.prototype.insertHTML = function insertHTML (html) {
// W3C
if (document.queryCommandSupported('insertHTML')) {
document.execCommand('insertHTML', false, html);
return;
}
var range = this.selection.getRange();
if (range.insertNode) {
// IE
range.deleteContents();
range.insertNode($(html)[0]);
// @ts-ignore
}
else if (range.pasteHTML) {
// IE <= 10
// @ts-ignore
range.pasteHTML(html);
}
};
/**
* 用指定 HTML 替换当前选区的 root 元素
* @param html
*/
// @ts-ignore
Command.prototype.replaceRoot = function replaceRoot (html) {
var $oldElem = this.selection.getRootElem();
var $newElem = $(html).insertAfter($oldElem);
$oldElem.remove();
this.selection.createRangeByElem($newElem, false, true);
this.selection.restore();
};
/**
* 在当前选区的 root 元素后面插入指定 html
* @param html
*/
// @ts-ignore
Command.prototype.insertAfterRoot = function insertAfterRoot (html) {
var $oldElem = this.selection.getRootElem();
var $newElem = $(html).insertAfter($oldElem);
this.selection.createRangeByElem($newElem, false, true);
this.selection.restore();
};
/**
* 在当前 $content 的最后追加 html
* @param html
*/
// @ts-ignore
Command.prototype.appendHTML = function appendHTML (html) {
var $newElem = $(html).appendTo(this.$container);
this.selection.createRangeByElem($newElem, false, true);
this.selection.restore();
};
/**
* 插入 elem
* @param $elem
*/
// @ts-ignore
Command.prototype.insertElem = function insertElem ($elem) {
var range = this.selection.getRange();
if (range.insertNode) {
range.deleteContents();
range.insertNode($elem[0]);
}
};
return Command;
}(CommonAbstract));
var MenuAbstract = /*@__PURE__*/(function (CommonAbstract) {
function MenuAbstract(editor, $button) {
CommonAbstract.call(this, editor);
this.$button = $button;
}
if ( CommonAbstract ) MenuAbstract.__proto__ = CommonAbstract;
MenuAbstract.prototype = Object.create( CommonAbstract && CommonAbstract.prototype );
MenuAbstract.prototype.constructor = MenuAbstract;
/**
* 按钮是否激活
*/
MenuAbstract.prototype.isActive = function isActive () {
return false;
};
return MenuAbstract;
}(CommonAbstract));
/**
* 按钮图标
*/
MenuAbstract.icon = '';
/**
* 按钮名称
*/
MenuAbstract.title = '';
/**
* 激活按钮时,需要禁用的其他按钮
*/
MenuAbstract.disable = [];
/**
* 原生命令抽象类
*/
var MenuNativeAbstract = /*@__PURE__*/(function (MenuAbstract) {
function MenuNativeAbstract () {
MenuAbstract.apply(this, arguments);
}
if ( MenuAbstract ) MenuNativeAbstract.__proto__ = MenuAbstract;
MenuNativeAbstract.prototype = Object.create( MenuAbstract && MenuAbstract.prototype );
MenuNativeAbstract.prototype.constructor = MenuNativeAbstract;
MenuNativeAbstract.prototype.onclick = function onclick () {
var isSelectionEmpty = this.selection.isEmpty();
if (isSelectionEmpty) {
// 选区是空的,插入并选中一个“空白”
this.selection.createEmptyRange(this.getElementName());
}
// 执行 bold 命令
this.command.do(this.getCommandName());
if (isSelectionEmpty) {
// 需要将选区折叠起来
this.selection.collapseRange();
this.selection.restore();
}
};
MenuNativeAbstract.prototype.isActive = function isActive () {
return document.queryCommandState(this.getCommandName());
};
return MenuNativeAbstract;
}(MenuAbstract));
/**
* 加粗
*/
var Bold = /*@__PURE__*/(function (MenuNativeAbstract) {
function Bold () {
MenuNativeAbstract.apply(this, arguments);
}
if ( MenuNativeAbstract ) Bold.__proto__ = MenuNativeAbstract;
Bold.prototype = Object.create( MenuNativeAbstract && MenuNativeAbstract.prototype );
Bold.prototype.constructor = Bold;
Bold.prototype.getCommandName = function getCommandName () {
return 'bold';
};
Bold.prototype.getElementName = function getElementName () {
return 'strong';
};
return Bold;
}(MenuNativeAbstract));
Bold.icon = 'format_bold';
Bold.title = '粗体';
Bold.disable = ['image'];
var $document = $(document);
var $window = $(window);
var $body = $('body');
// 避免页面加载完后直接执行css动画
// https://css-tricks.com/transitions-only-after-page-load/
setTimeout(function () { return $body.addClass('mdui-loaded'); });
var mdui = {
$: $,
};
$.fn.hasClass = function (className) {
return this[0].classList.contains(className);
};
/**
* 值上面的 padding、border、margin 处理
* @param element
* @param name
* @param value
* @param funcIndex
* @param includeMargin
* @param multiply
*/
function handleExtraWidth(element, name, value, funcIndex, includeMargin, multiply) {
// 获取元素的 padding, border, margin 宽度(两侧宽度的和)
var getExtraWidthValue = function (extra) {
return (getExtraWidth(element, name.toLowerCase(), extra) *
multiply);
};
if (funcIndex === 2 && includeMargin) {
value += getExtraWidthValue('margin');
}
if (isBorderBox(element)) {
// IE 为 box-sizing: border-box 时,得到的值不含 border 和 padding,这里先修复
// 仅获取时需要处理,multiply === 1 为 get
if (isIE() && multiply === 1) {
value += getExtraWidthValue('border');
value += getExtraWidthValue('padding');
}
if (funcIndex === 0) {
value -= getExtraWidthValue('border');
}
if (funcIndex === 1) {
value -= getExtraWidthValue('border');
value -= getExtraWidthValue('padding');
}
}
else {
if (funcIndex === 0) {
value += getExtraWidthValue('padding');
}
if (funcIndex === 2) {
value += getExtraWidthValue('border');
value += getExtraWidthValue('padding');
}
}
return value;
}
/**
* 获取元素的样式值
* @param element
* @param name
* @param funcIndex 0: innerWidth, innerHeight; 1: width, height; 2: outerWidth, outerHeight
* @param includeMargin
*/
function get(element, name, funcIndex, includeMargin) {
var clientProp = "client" + name;
var scrollProp = "scroll" + name;
var offsetProp = "offset" + name;
var innerProp = "inner" + name;
// $(window).width()
if (isWindow(element)) {
// outerWidth, outerHeight 需要包含滚动条的宽度
return funcIndex === 2
? element[innerProp]
: toElement(document)[clientProp];
}
// $(document).width()
if (isDocument(element)) {
var doc = toElement(element);
return Math.max(
// @ts-ignore
element.body[scrollProp], doc[scrollProp],
// @ts-ignore
element.body[offsetProp], doc[offsetProp], doc[clientProp]);
}
var value = parseFloat(getComputedStyleValue(element, name.toLowerCase()) || '0');
return handleExtraWidth(element, name, value, funcIndex, includeMargin, 1);
}
/**
* 设置元素的样式值
* @param element
* @param elementIndex
* @param name
* @param funcIndex 0: innerWidth, innerHeight; 1: width, height; 2: outerWidth, outerHeight
* @param includeMargin
* @param value
*/
function set(element, elementIndex, name, funcIndex, includeMargin, value) {
var computedValue = isFunction(value)
? value.call(element, elementIndex, get(element, name, funcIndex, includeMargin))
: value;
if (computedValue == null) {
return;
}
var $element = $(element);
var dimension = name.toLowerCase();
// 特殊的值,不需要计算 padding、border、margin
if (['auto', 'inherit', ''].indexOf(computedValue) > -1) {
$element.css(dimension, computedValue);
return;
}
// 其他值保留原始单位。注意:如果不使用 px 作为单位,则算出的值一般是不准确的
var suffix = computedValue.toString().replace(/\b[0-9.]*/, '');
var numerical = parseFloat(computedValue);
computedValue =
handleExtraWidth(element, name, numerical, funcIndex, includeMargin, -1) +
(suffix || 'px');
$element.css(dimension, computedValue);
}
each(['Width', 'Height'], function (_, name) {
each([("inner" + name), name.toLowerCase(), ("outer" + name)], function (funcIndex, funcName) {
$.fn[funcName] = function (margin, value) {
// 是否是赋值操作
var isSet = arguments.length && (funcIndex < 2 || !isBoolean(margin));
var includeMargin = margin === true || value === true;
// 获取第一个元素的值
if (!isSet) {
return this.length
? get(this[0], name, funcIndex, includeMargin)
: undefined;
}
// 设置每个元素的值
return this.each(function (index, element) { return set(element, index, name, funcIndex, includeMargin, margin); });
};
});
});
$.fn.hide = function () {
return this.each(function () {
this.style.display = 'none';
});
};
var elementDisplay = {};
/**
* 获取元素的初始 display 值,用于 .show() 方法
* @param nodeName
*/
function defaultDisplay(nodeName) {
var element;
var display;
if (!elementDisplay[nodeName]) {
element = document.createElement(nodeName);
document.body.appendChild(element);
display = getStyle(element, 'display');
element.parentNode.removeChild(element);
if (display === 'none') {
display = 'block';
}
elementDisplay[nodeName] = display;
}
return elementDisplay[nodeName];
}
/**
* 显示指定元素
* @returns {JQ}
*/
$.fn.show = function () {
return this.each(function () {
if (this.style.display === 'none') {
this.style.display = '';
}
if (getStyle(this, 'display') === 'none') {
this.style.display = defaultDisplay(this.nodeName);
}
});
};
$.fn.transitionEnd = function (callback) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
var that = this;
var events = ['webkitTransitionEnd', 'transitionend'];
function fireCallback(e) {
if (e.target !== this) {
return;
}
// @ts-ignore
callback.cal