@notadd/neditor
Version:
Neditor富文本web编辑器
2,137 lines (2,022 loc) • 1.1 MB
JavaScript
/*!
* neditor
* version: 2.1.20
* build: Mon Sep 09 2019 13:00:57 GMT+0800 (GMT+08:00)
*/
(function(){
// editor.js
UEDITOR_CONFIG = window.UEDITOR_CONFIG || {};
var baidu = window.baidu || {};
window.baidu = baidu;
window.UE = baidu.editor = {
plugins: {},
commands: {},
instants: {},
I18N: {},
_customizeUI: {},
version: "1.5.0"
};
var dom = (UE.dom = {});
// core/browser.js
/**
* 浏览器判断模块
* @file
* @module UE.browser
* @since 1.2.6.1
*/
/**
* 提供浏览器检测的模块
* @unfile
* @module UE.browser
*/
var browser = (UE.browser = (function() {
var agent = navigator.userAgent.toLowerCase(),
opera = window.opera,
browser = {
/**
* @property {boolean} ie 检测当前浏览器是否为IE
* @example
* ```javascript
* if ( UE.browser.ie ) {
* console.log( '当前浏览器是IE' );
* }
* ```
*/
ie: /(msie\s|trident.*rv:)([\w.]+)/i.test(agent),
/**
* @property {boolean} opera 检测当前浏览器是否为Opera
* @example
* ```javascript
* if ( UE.browser.opera ) {
* console.log( '当前浏览器是Opera' );
* }
* ```
*/
opera: !!opera && opera.version,
/**
* @property {boolean} webkit 检测当前浏览器是否是webkit内核的浏览器
* @example
* ```javascript
* if ( UE.browser.webkit ) {
* console.log( '当前浏览器是webkit内核浏览器' );
* }
* ```
*/
webkit: agent.indexOf(" applewebkit/") > -1,
/**
* @property {boolean} mac 检测当前浏览器是否是运行在mac平台下
* @example
* ```javascript
* if ( UE.browser.mac ) {
* console.log( '当前浏览器运行在mac平台下' );
* }
* ```
*/
mac: agent.indexOf("macintosh") > -1,
/**
* @property {boolean} quirks 检测当前浏览器是否处于“怪异模式”下
* @example
* ```javascript
* if ( UE.browser.quirks ) {
* console.log( '当前浏览器运行处于“怪异模式”' );
* }
* ```
*/
quirks: document.compatMode == "BackCompat"
};
/**
* @property {boolean} gecko 检测当前浏览器内核是否是gecko内核
* @example
* ```javascript
* if ( UE.browser.gecko ) {
* console.log( '当前浏览器内核是gecko内核' );
* }
* ```
*/
browser.gecko =
navigator.product == "Gecko" &&
!browser.webkit &&
!browser.opera &&
!browser.ie;
var version = 0;
// Internet Explorer 6.0+
if (browser.ie) {
var v1 = agent.match(/(?:msie\s([\w.]+))/);
var v2 = agent.match(/(?:trident.*rv:([\w.]+))/);
if (v1 && v2 && v1[1] && v2[1]) {
version = Math.max(v1[1] * 1, v2[1] * 1);
} else if (v1 && v1[1]) {
version = v1[1] * 1;
} else if (v2 && v2[1]) {
version = v2[1] * 1;
} else {
version = 0;
}
browser.ie11Compat = document.documentMode == 11;
/**
* @property { boolean } ie9Compat 检测浏览器模式是否为 IE9 兼容模式
* @warning 如果浏览器不是IE, 则该值为undefined
* @example
* ```javascript
* if ( UE.browser.ie9Compat ) {
* console.log( '当前浏览器运行在IE9兼容模式下' );
* }
* ```
*/
browser.ie9Compat = document.documentMode == 9;
/**
* @property { boolean } ie8 检测浏览器是否是IE8浏览器
* @warning 如果浏览器不是IE, 则该值为undefined
* @example
* ```javascript
* if ( UE.browser.ie8 ) {
* console.log( '当前浏览器是IE8浏览器' );
* }
* ```
*/
browser.ie8 = !!document.documentMode;
/**
* @property { boolean } ie8Compat 检测浏览器模式是否为 IE8 兼容模式
* @warning 如果浏览器不是IE, 则该值为undefined
* @example
* ```javascript
* if ( UE.browser.ie8Compat ) {
* console.log( '当前浏览器运行在IE8兼容模式下' );
* }
* ```
*/
browser.ie8Compat = document.documentMode == 8;
/**
* @property { boolean } ie7Compat 检测浏览器模式是否为 IE7 兼容模式
* @warning 如果浏览器不是IE, 则该值为undefined
* @example
* ```javascript
* if ( UE.browser.ie7Compat ) {
* console.log( '当前浏览器运行在IE7兼容模式下' );
* }
* ```
*/
browser.ie7Compat =
(version == 7 && !document.documentMode) || document.documentMode == 7;
/**
* @property { boolean } ie6Compat 检测浏览器模式是否为 IE6 模式 或者怪异模式
* @warning 如果浏览器不是IE, 则该值为undefined
* @example
* ```javascript
* if ( UE.browser.ie6Compat ) {
* console.log( '当前浏览器运行在IE6模式或者怪异模式下' );
* }
* ```
*/
browser.ie6Compat = version < 7 || browser.quirks;
browser.ie9above = version > 8;
browser.ie9below = version < 9;
browser.ie11above = version > 10;
browser.ie11below = version < 11;
}
// Gecko.
if (browser.gecko) {
var geckoRelease = agent.match(/rv:([\d\.]+)/);
if (geckoRelease) {
geckoRelease = geckoRelease[1].split(".");
version =
geckoRelease[0] * 10000 +
(geckoRelease[1] || 0) * 100 +
(geckoRelease[2] || 0) * 1;
}
}
/**
* @property { Number } chrome 检测当前浏览器是否为Chrome, 如果是,则返回Chrome的大版本号
* @warning 如果浏览器不是chrome, 则该值为undefined
* @example
* ```javascript
* if ( UE.browser.chrome ) {
* console.log( '当前浏览器是Chrome' );
* }
* ```
*/
if (/chrome\/(\d+\.\d)/i.test(agent)) {
browser.chrome = +RegExp["\x241"];
}
/**
* @property { Number } safari 检测当前浏览器是否为Safari, 如果是,则返回Safari的大版本号
* @warning 如果浏览器不是safari, 则该值为undefined
* @example
* ```javascript
* if ( UE.browser.safari ) {
* console.log( '当前浏览器是Safari' );
* }
* ```
*/
if (
/(\d+\.\d)?(?:\.\d)?\s+safari\/?(\d+\.\d+)?/i.test(agent) &&
!/chrome/i.test(agent)
) {
browser.safari = +(RegExp["\x241"] || RegExp["\x242"]);
}
// Opera 9.50+
if (browser.opera) version = parseFloat(opera.version());
// WebKit 522+ (Safari 3+)
if (browser.webkit)
version = parseFloat(agent.match(/ applewebkit\/(\d+)/)[1]);
/**
* @property { Number } version 检测当前浏览器版本号
* @remind
* <ul>
* <li>IE系列返回值为5,6,7,8,9,10等</li>
* <li>gecko系列会返回10900,158900等</li>
* <li>webkit系列会返回其build号 (如 522等)</li>
* </ul>
* @example
* ```javascript
* console.log( '当前浏览器版本号是: ' + UE.browser.version );
* ```
*/
browser.version = version;
/**
* @property { boolean } isCompatible 检测当前浏览器是否能够与UEditor良好兼容
* @example
* ```javascript
* if ( UE.browser.isCompatible ) {
* console.log( '浏览器与UEditor能够良好兼容' );
* }
* ```
*/
browser.isCompatible =
!browser.mobile &&
((browser.ie && version >= 6) ||
(browser.gecko && version >= 10801) ||
(browser.opera && version >= 9.5) ||
(browser.air && version >= 1) ||
(browser.webkit && version >= 522) ||
false);
return browser;
})());
//快捷方式
var ie = browser.ie,
webkit = browser.webkit,
gecko = browser.gecko,
opera = browser.opera;
// core/utils.js
/**
* 工具函数包
* @file
* @module UE.utils
* @since 1.2.6.1
*/
/**
* UEditor封装使用的静态工具函数
* @module UE.utils
* @unfile
*/
var utils = (UE.utils = {
/**
* 用给定的迭代器遍历对象
* @method each
* @param { Object } obj 需要遍历的对象
* @param { Function } iterator 迭代器, 该方法接受两个参数, 第一个参数是当前所处理的value, 第二个参数是当前遍历对象的key
* @example
* ```javascript
* var demoObj = {
* key1: 1,
* key2: 2
* };
*
* //output: key1: 1, key2: 2
* UE.utils.each( demoObj, funciton ( value, key ) {
*
* console.log( key + ":" + value );
*
* } );
* ```
*/
/**
* 用给定的迭代器遍历数组或类数组对象
* @method each
* @param { Array } array 需要遍历的数组或者类数组
* @param { Function } iterator 迭代器, 该方法接受两个参数, 第一个参数是当前所处理的value, 第二个参数是当前遍历对象的key
* @example
* ```javascript
* var divs = document.getElmentByTagNames( "div" );
*
* //output: 0: DIV, 1: DIV ...
* UE.utils.each( divs, funciton ( value, key ) {
*
* console.log( key + ":" + value.tagName );
*
* } );
* ```
*/
each: function(obj, iterator, context) {
if (obj == null) return;
if (obj.length === +obj.length) {
for (var i = 0, l = obj.length; i < l; i++) {
if (iterator.call(context, obj[i], i, obj) === false) return false;
}
} else {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if (iterator.call(context, obj[key], key, obj) === false)
return false;
}
}
}
},
/**
* 以给定对象作为原型创建一个新对象
* @method makeInstance
* @param { Object } protoObject 该对象将作为新创建对象的原型
* @return { Object } 新的对象, 该对象的原型是给定的protoObject对象
* @example
* ```javascript
*
* var protoObject = { sayHello: function () { console.log('Hello UEditor!'); } };
*
* var newObject = UE.utils.makeInstance( protoObject );
* //output: Hello UEditor!
* newObject.sayHello();
* ```
*/
makeInstance: function(obj) {
var noop = new Function();
noop.prototype = obj;
obj = new noop();
noop.prototype = null;
return obj;
},
/**
* 将source对象中的属性扩展到target对象上
* @method extend
* @remind 该方法将强制把source对象上的属性复制到target对象上
* @see UE.utils.extend(Object,Object,Boolean)
* @param { Object } target 目标对象, 新的属性将附加到该对象上
* @param { Object } source 源对象, 该对象的属性会被附加到target对象上
* @return { Object } 返回target对象
* @example
* ```javascript
*
* var target = { name: 'target', sex: 1 },
* source = { name: 'source', age: 17 };
*
* UE.utils.extend( target, source );
*
* //output: { name: 'source', sex: 1, age: 17 }
* console.log( target );
*
* ```
*/
/**
* 将source对象中的属性扩展到target对象上, 根据指定的isKeepTarget值决定是否保留目标对象中与
* 源对象属性名相同的属性值。
* @method extend
* @param { Object } target 目标对象, 新的属性将附加到该对象上
* @param { Object } source 源对象, 该对象的属性会被附加到target对象上
* @param { Boolean } isKeepTarget 是否保留目标对象中与源对象中属性名相同的属性
* @return { Object } 返回target对象
* @example
* ```javascript
*
* var target = { name: 'target', sex: 1 },
* source = { name: 'source', age: 17 };
*
* UE.utils.extend( target, source, true );
*
* //output: { name: 'target', sex: 1, age: 17 }
* console.log( target );
*
* ```
*/
extend: function(t, s, b) {
if (s) {
for (var k in s) {
if (!b || !t.hasOwnProperty(k)) {
t[k] = s[k];
}
}
}
return t;
},
/**
* 将给定的多个对象的属性复制到目标对象target上
* @method extend2
* @remind 该方法将强制把源对象上的属性复制到target对象上
* @remind 该方法支持两个及以上的参数, 从第二个参数开始, 其属性都会被复制到第一个参数上。 如果遇到同名的属性,
* 将会覆盖掉之前的值。
* @param { Object } target 目标对象, 新的属性将附加到该对象上
* @param { Object... } source 源对象, 支持多个对象, 该对象的属性会被附加到target对象上
* @return { Object } 返回target对象
* @example
* ```javascript
*
* var target = {},
* source1 = { name: 'source', age: 17 },
* source2 = { title: 'dev' };
*
* UE.utils.extend2( target, source1, source2 );
*
* //output: { name: 'source', age: 17, title: 'dev' }
* console.log( target );
*
* ```
*/
extend2: function(t) {
var a = arguments;
for (var i = 1; i < a.length; i++) {
var x = a[i];
for (var k in x) {
if (!t.hasOwnProperty(k)) {
t[k] = x[k];
}
}
}
return t;
},
/**
* 模拟继承机制, 使得subClass继承自superClass
* @method inherits
* @param { Object } subClass 子类对象
* @param { Object } superClass 超类对象
* @warning 该方法只能让subClass继承超类的原型, subClass对象自身的属性和方法不会被继承
* @return { Object } 继承superClass后的子类对象
* @example
* ```javascript
* function SuperClass(){
* this.name = "小李";
* }
*
* SuperClass.prototype = {
* hello:function(str){
* console.log(this.name + str);
* }
* }
*
* function SubClass(){
* this.name = "小张";
* }
*
* UE.utils.inherits(SubClass,SuperClass);
*
* var sub = new SubClass();
* //output: '小张早上好!
* sub.hello("早上好!");
* ```
*/
inherits: function(subClass, superClass) {
var oldP = subClass.prototype,
newP = utils.makeInstance(superClass.prototype);
utils.extend(newP, oldP, true);
subClass.prototype = newP;
return (newP.constructor = subClass);
},
/**
* 用指定的context对象作为函数fn的上下文
* @method bind
* @param { Function } fn 需要绑定上下文的函数对象
* @param { Object } content 函数fn新的上下文对象
* @return { Function } 一个新的函数, 该函数作为原始函数fn的代理, 将完成fn的上下文调换工作。
* @example
* ```javascript
*
* var name = 'window',
* newTest = null;
*
* function test () {
* console.log( this.name );
* }
*
* newTest = UE.utils.bind( test, { name: 'object' } );
*
* //output: object
* newTest();
*
* //output: window
* test();
*
* ```
*/
bind: function(fn, context) {
return function() {
return fn.apply(context, arguments);
};
},
/**
* 创建延迟指定时间后执行的函数fn
* @method defer
* @param { Function } fn 需要延迟执行的函数对象
* @param { int } delay 延迟的时间, 单位是毫秒
* @warning 该方法的时间控制是不精确的,仅仅只能保证函数的执行是在给定的时间之后,
* 而不能保证刚好到达延迟时间时执行。
* @return { Function } 目标函数fn的代理函数, 只有执行该函数才能起到延时效果
* @example
* ```javascript
* var start = 0;
*
* function test(){
* console.log( new Date() - start );
* }
*
* var testDefer = UE.utils.defer( test, 1000 );
* //
* start = new Date();
* //output: (大约在1000毫秒之后输出) 1000
* testDefer();
* ```
*/
/**
* 创建延迟指定时间后执行的函数fn, 如果在延迟时间内再次执行该方法, 将会根据指定的exclusion的值,
* 决定是否取消前一次函数的执行, 如果exclusion的值为true, 则取消执行,反之,将继续执行前一个方法。
* @method defer
* @param { Function } fn 需要延迟执行的函数对象
* @param { int } delay 延迟的时间, 单位是毫秒
* @param { Boolean } exclusion 如果在延迟时间内再次执行该函数,该值将决定是否取消执行前一次函数的执行,
* 值为true表示取消执行, 反之则将在执行前一次函数之后才执行本次函数调用。
* @warning 该方法的时间控制是不精确的,仅仅只能保证函数的执行是在给定的时间之后,
* 而不能保证刚好到达延迟时间时执行。
* @return { Function } 目标函数fn的代理函数, 只有执行该函数才能起到延时效果
* @example
* ```javascript
*
* function test(){
* console.log(1);
* }
*
* var testDefer = UE.utils.defer( test, 1000, true );
*
* //output: (两次调用仅有一次输出) 1
* testDefer();
* testDefer();
* ```
*/
defer: function(fn, delay, exclusion) {
var timerID;
return function() {
if (exclusion) {
clearTimeout(timerID);
}
timerID = setTimeout(fn, delay);
};
},
/**
* 获取元素item在数组array中首次出现的位置, 如果未找到item, 则返回-1
* @method indexOf
* @remind 该方法的匹配过程使用的是恒等“===”
* @param { Array } array 需要查找的数组对象
* @param { * } item 需要在目标数组中查找的值
* @return { int } 返回item在目标数组array中首次出现的位置, 如果在数组中未找到item, 则返回-1
* @example
* ```javascript
* var item = 1,
* arr = [ 3, 4, 6, 8, 1, 1, 2 ];
*
* //output: 4
* console.log( UE.utils.indexOf( arr, item ) );
* ```
*/
/**
* 获取元素item数组array中首次出现的位置, 如果未找到item, 则返回-1。通过start的值可以指定搜索的起始位置。
* @method indexOf
* @remind 该方法的匹配过程使用的是恒等“===”
* @param { Array } array 需要查找的数组对象
* @param { * } item 需要在目标数组中查找的值
* @param { int } start 搜索的起始位置
* @return { int } 返回item在目标数组array中的start位置之后首次出现的位置, 如果在数组中未找到item, 则返回-1
* @example
* ```javascript
* var item = 1,
* arr = [ 3, 4, 6, 8, 1, 2, 8, 3, 2, 1, 1, 4 ];
*
* //output: 9
* console.log( UE.utils.indexOf( arr, item, 5 ) );
* ```
*/
indexOf: function(array, item, start) {
var index = -1;
start = this.isNumber(start) ? start : 0;
this.each(array, function(v, i) {
if (i >= start && v === item) {
index = i;
return false;
}
});
return index;
},
/**
* 移除数组array中所有的元素item
* @method removeItem
* @param { Array } array 要移除元素的目标数组
* @param { * } item 将要被移除的元素
* @remind 该方法的匹配过程使用的是恒等“===”
* @example
* ```javascript
* var arr = [ 4, 5, 7, 1, 3, 4, 6 ];
*
* UE.utils.removeItem( arr, 4 );
* //output: [ 5, 7, 1, 3, 6 ]
* console.log( arr );
*
* ```
*/
removeItem: function(array, item) {
for (var i = 0, l = array.length; i < l; i++) {
if (array[i] === item) {
array.splice(i, 1);
i--;
}
}
},
/**
* 删除字符串str的首尾空格
* @method trim
* @param { String } str 需要删除首尾空格的字符串
* @return { String } 删除了首尾的空格后的字符串
* @example
* ```javascript
*
* var str = " UEdtior ";
*
* //output: 9
* console.log( str.length );
*
* //output: 7
* console.log( UE.utils.trim( " UEdtior " ).length );
*
* //output: 9
* console.log( str.length );
*
* ```
*/
trim: function(str) {
return str.replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, "");
},
/**
* 将字符串str以','分隔成数组后,将该数组转换成哈希对象, 其生成的hash对象的key为数组中的元素, value为1
* @method listToMap
* @warning 该方法在生成的hash对象中,会为每一个key同时生成一个另一个全大写的key。
* @param { String } str 该字符串将被以','分割为数组, 然后进行转化
* @return { Object } 转化之后的hash对象
* @example
* ```javascript
*
* //output: Object {UEdtior: 1, UEDTIOR: 1, Hello: 1, HELLO: 1}
* console.log( UE.utils.listToMap( 'UEdtior,Hello' ) );
*
* ```
*/
/**
* 将字符串数组转换成哈希对象, 其生成的hash对象的key为数组中的元素, value为1
* @method listToMap
* @warning 该方法在生成的hash对象中,会为每一个key同时生成一个另一个全大写的key。
* @param { Array } arr 字符串数组
* @return { Object } 转化之后的hash对象
* @example
* ```javascript
*
* //output: Object {UEdtior: 1, UEDTIOR: 1, Hello: 1, HELLO: 1}
* console.log( UE.utils.listToMap( [ 'UEdtior', 'Hello' ] ) );
*
* ```
*/
listToMap: function(list) {
if (!list) return {};
list = utils.isArray(list) ? list : list.split(",");
for (var i = 0, ci, obj = {}; (ci = list[i++]); ) {
obj[ci.toUpperCase()] = obj[ci] = 1;
}
return obj;
},
/**
* 将str中的html符号转义,将转义“',&,<,",>,”,“”七个字符
* @method unhtml
* @param { String } str 需要转义的字符串
* @return { String } 转义后的字符串
* @example
* ```javascript
* var html = '<body>&</body>';
*
* //output: <body>&</body>
* console.log( UE.utils.unhtml( html ) );
*
* ```
*/
unhtml: function(str, reg) {
return str
? str.replace(
reg || /[&<">'](?:(amp|lt|ldquo|rdquo|quot|gt|#39|nbsp|#\d+);)?/g,
function(a, b) {
if (b) {
return a;
} else {
return {
"<": "<",
"&": "&",
'"': """,
"“": "“",
"”": "”",
">": ">",
"'": "'"
}[a];
}
}
)
: "";
},
/**
* 将str中的转义字符还原成html字符
* @see UE.utils.unhtml(String);
* @method html
* @param { String } str 需要逆转义的字符串
* @return { String } 逆转义后的字符串
* @example
* ```javascript
*
* var str = '<body>&</body>';
*
* //output: <body>&</body>
* console.log( UE.utils.html( str ) );
*
* ```
*/
html: function(str) {
return str
? str.replace(/&((g|l|quo|ldquo|rdquo)t|amp|#39|nbsp);/g, function(m) {
return {
"<": "<",
"&": "&",
""": '"',
"“": "“",
"”": "”",
">": ">",
"'": "'",
" ": " "
}[m];
})
: "";
},
/**
* 将css样式转换为驼峰的形式
* @method cssStyleToDomStyle
* @param { String } cssName 需要转换的css样式名
* @return { String } 转换成驼峰形式后的css样式名
* @example
* ```javascript
*
* var str = 'border-top';
*
* //output: borderTop
* console.log( UE.utils.cssStyleToDomStyle( str ) );
*
* ```
*/
cssStyleToDomStyle: (function() {
var test = document.createElement("div").style,
cache = {
float: test.cssFloat != undefined
? "cssFloat"
: test.styleFloat != undefined ? "styleFloat" : "float"
};
return function(cssName) {
return (
cache[cssName] ||
(cache[cssName] = cssName.toLowerCase().replace(/-./g, function(match) {
return match.charAt(1).toUpperCase();
}))
);
};
})(),
/**
* 动态加载文件到doc中
* @method loadFile
* @param { DomDocument } document 需要加载资源文件的文档对象
* @param { Object } options 加载资源文件的属性集合, 取值请参考代码示例
* @example
* ```javascript
*
* UE.utils.loadFile( document, {
* src:"test.js",
* tag:"script",
* type:"text/javascript",
* defer:"defer"
* } );
*
* ```
*/
/**
* 动态加载文件到doc中,加载成功后执行的回调函数fn
* @method loadFile
* @param { DomDocument } document 需要加载资源文件的文档对象
* @param { Object } options 加载资源文件的属性集合, 该集合支持的值是script标签和style标签支持的所有属性。
* @param { Function } fn 资源文件加载成功之后执行的回调
* @warning 对于在同一个文档中多次加载同一URL的文件, 该方法会在第一次加载之后缓存该请求,
* 在此之后的所有同一URL的请求, 将会直接触发回调。
* @example
* ```javascript
*
* UE.utils.loadFile( document, {
* src:"test.js",
* tag:"script",
* type:"text/javascript",
* defer:"defer"
* }, function () {
* console.log('加载成功');
* } );
*
* ```
*/
loadFile: (function() {
var tmpList = [];
function getItem(doc, obj) {
try {
for (var i = 0, ci; (ci = tmpList[i++]); ) {
if (ci.doc === doc && ci.url == (obj.src || obj.href)) {
return ci;
}
}
} catch (e) {
return null;
}
}
return function(doc, obj, fn) {
var item = getItem(doc, obj);
if (item) {
if (item.ready) {
fn && fn();
} else {
item.funs.push(fn);
}
return;
}
tmpList.push({
doc: doc,
url: obj.src || obj.href,
funs: [fn]
});
if (!doc.body) {
var html = [];
for (var p in obj) {
if (p == "tag") continue;
html.push(p + '="' + obj[p] + '"');
}
doc.write(
"<" + obj.tag + " " + html.join(" ") + " ></" + obj.tag + ">"
);
return;
}
if (obj.id && doc.getElementById(obj.id)) {
return;
}
var element = doc.createElement(obj.tag);
delete obj.tag;
for (var p in obj) {
element.setAttribute(p, obj[p]);
}
element.onload = element.onreadystatechange = function() {
if (!this.readyState || /loaded|complete/.test(this.readyState)) {
item = getItem(doc, obj);
if (item.funs.length > 0) {
item.ready = 1;
for (var fi; (fi = item.funs.pop()); ) {
fi();
}
}
element.onload = element.onreadystatechange = null;
}
};
element.onerror = function() {
throw Error(
"The load " +
(obj.href || obj.src) +
" fails,check the url settings of file neditor.config.js "
);
};
doc.getElementsByTagName("head")[0].appendChild(element);
};
})(),
/**
* 判断obj对象是否为空
* @method isEmptyObject
* @param { * } obj 需要判断的对象
* @remind 如果判断的对象是NULL, 将直接返回true, 如果是数组且为空, 返回true, 如果是字符串, 且字符串为空,
* 返回true, 如果是普通对象, 且该对象没有任何实例属性, 返回true
* @return { Boolean } 对象是否为空
* @example
* ```javascript
*
* //output: true
* console.log( UE.utils.isEmptyObject( {} ) );
*
* //output: true
* console.log( UE.utils.isEmptyObject( [] ) );
*
* //output: true
* console.log( UE.utils.isEmptyObject( "" ) );
*
* //output: false
* console.log( UE.utils.isEmptyObject( { key: 1 } ) );
*
* //output: false
* console.log( UE.utils.isEmptyObject( [1] ) );
*
* //output: false
* console.log( UE.utils.isEmptyObject( "1" ) );
*
* ```
*/
isEmptyObject: function(obj) {
if (obj == null) return true;
if (this.isArray(obj) || this.isString(obj)) return obj.length === 0;
for (var key in obj) if (obj.hasOwnProperty(key)) return false;
return true;
},
/**
* 把rgb格式的颜色值转换成16进制格式
* @method fixColor
* @param { String } rgb格式的颜色值
* @param { String }
* @example
* rgb(255,255,255) => "#ffffff"
*/
fixColor: function(name, value) {
if (/color/i.test(name) && /rgba?/.test(value)) {
var array = value.split(",");
if (array.length > 3) return "";
value = "#";
for (var i = 0, color; (color = array[i++]); ) {
color = parseInt(color.replace(/[^\d]/gi, ""), 10).toString(16);
value += color.length == 1 ? "0" + color : color;
}
value = value.toUpperCase();
}
return value;
},
/**
* 只针对border,padding,margin做了处理,因为性能问题
* @public
* @function
* @param {String} val style字符串
*/
optCss: function(val) {
var padding, margin, border;
val = val.replace(/(padding|margin|border)\-([^:]+):([^;]+);?/gi, function(
str,
key,
name,
val
) {
if (val.split(" ").length == 1) {
switch (key) {
case "padding":
!padding && (padding = {});
padding[name] = val;
return "";
case "margin":
!margin && (margin = {});
margin[name] = val;
return "";
case "border":
return val == "initial" ? "" : str;
}
}
return str;
});
function opt(obj, name) {
if (!obj) {
return "";
}
var t = obj.top,
b = obj.bottom,
l = obj.left,
r = obj.right,
val = "";
if (!t || !l || !b || !r) {
for (var p in obj) {
val += ";" + name + "-" + p + ":" + obj[p] + ";";
}
} else {
val +=
";" +
name +
":" +
(t == b && b == l && l == r
? t
: t == b && l == r
? t + " " + l
: l == r
? t + " " + l + " " + b
: t + " " + r + " " + b + " " + l) +
";";
}
return val;
}
val += opt(padding, "padding") + opt(margin, "margin");
return val
.replace(/^[ \n\r\t;]*|[ \n\r\t]*$/, "")
.replace(/;([ \n\r\t]+)|\1;/g, ";")
.replace(/(&((l|g)t|quot|#39))?;{2,}/g, function(a, b) {
return b ? b + ";;" : ";";
});
},
/**
* 克隆对象
* @method clone
* @param { Object } source 源对象
* @return { Object } source的一个副本
*/
/**
* 深度克隆对象,将source的属性克隆到target对象, 会覆盖target重名的属性。
* @method clone
* @param { Object } source 源对象
* @param { Object } target 目标对象
* @return { Object } 附加了source对象所有属性的target对象
*/
clone: function(source, target) {
var tmp;
target = target || {};
for (var i in source) {
if (source.hasOwnProperty(i)) {
tmp = source[i];
if (typeof tmp == "object") {
target[i] = utils.isArray(tmp) ? [] : {};
utils.clone(source[i], target[i]);
} else {
target[i] = tmp;
}
}
}
return target;
},
/**
* 把cm/pt为单位的值转换为px为单位的值
* @method transUnitToPx
* @param { String } 待转换的带单位的字符串
* @return { String } 转换为px为计量单位的值的字符串
* @example
* ```javascript
*
* //output: 500px
* console.log( UE.utils.transUnitToPx( '20cm' ) );
*
* //output: 27px
* console.log( UE.utils.transUnitToPx( '20pt' ) );
*
* ```
*/
transUnitToPx: function(val) {
if (!/(pt|cm)/.test(val)) {
return val;
}
var unit;
val.replace(/([\d.]+)(\w+)/, function(str, v, u) {
val = v;
unit = u;
});
switch (unit) {
case "cm":
val = parseFloat(val) * 25;
break;
case "pt":
val = Math.round(parseFloat(val) * 96 / 72);
}
return val + (val ? "px" : "");
},
/**
* 在dom树ready之后执行给定的回调函数
* @method domReady
* @remind 如果在执行该方法的时候, dom树已经ready, 那么回调函数将立刻执行
* @param { Function } fn dom树ready之后的回调函数
* @example
* ```javascript
*
* UE.utils.domReady( function () {
*
* console.log('123');
*
* } );
*
* ```
*/
domReady: (function() {
var fnArr = [];
function doReady(doc) {
//确保onready只执行一次
doc.isReady = true;
for (var ci; (ci = fnArr.pop()); ci()) {}
}
return function(onready, win) {
win = win || window;
var doc = win.document;
onready && fnArr.push(onready);
if (doc.readyState === "complete") {
doReady(doc);
} else {
doc.isReady && doReady(doc);
if (browser.ie && browser.version != 11) {
(function() {
if (doc.isReady) return;
try {
doc.documentElement.doScroll("left");
} catch (error) {
setTimeout(arguments.callee, 0);
return;
}
doReady(doc);
})();
win.attachEvent("onload", function() {
doReady(doc);
});
} else {
doc.addEventListener(
"DOMContentLoaded",
function() {
doc.removeEventListener(
"DOMContentLoaded",
arguments.callee,
false
);
doReady(doc);
},
false
);
win.addEventListener(
"load",
function() {
doReady(doc);
},
false
);
}
}
};
})(),
/**
* 动态添加css样式
* @method cssRule
* @param { String } 节点名称
* @grammar UE.utils.cssRule('添加的样式的节点名称',['样式','放到哪个document上'])
* @grammar UE.utils.cssRule('body','body{background:#ccc}') => null //给body添加背景颜色
* @grammar UE.utils.cssRule('body') =>样式的字符串 //取得key值为body的样式的内容,如果没有找到key值先关的样式将返回空,例如刚才那个背景颜色,将返回 body{background:#ccc}
* @grammar UE.utils.cssRule('body',document) => 返回指定key的样式,并且指定是哪个document
* @grammar UE.utils.cssRule('body','') =>null //清空给定的key值的背景颜色
*/
cssRule: browser.ie && browser.version != 11
? function(key, style, doc) {
var indexList, index;
if (
style === undefined ||
(style && style.nodeType && style.nodeType == 9)
) {
//获取样式
doc = style && style.nodeType && style.nodeType == 9
? style
: doc || document;
indexList = doc.indexList || (doc.indexList = {});
index = indexList[key];
if (index !== undefined) {
return doc.styleSheets[index].cssText;
}
return undefined;
}
doc = doc || document;
indexList = doc.indexList || (doc.indexList = {});
index = indexList[key];
//清除样式
if (style === "") {
if (index !== undefined) {
doc.styleSheets[index].cssText = "";
delete indexList[key];
return true;
}
return false;
}
//添加样式
if (index !== undefined) {
sheetStyle = doc.styleSheets[index];
} else {
sheetStyle = doc.createStyleSheet(
"",
(index = doc.styleSheets.length)
);
indexList[key] = index;
}
sheetStyle.cssText = style;
}
: function(key, style, doc) {
var head, node;
if (
style === undefined ||
(style && style.nodeType && style.nodeType == 9)
) {
//获取样式
doc = style && style.nodeType && style.nodeType == 9
? style
: doc || document;
node = doc.getElementById(key);
return node ? node.innerHTML : undefined;
}
doc = doc || document;
node = doc.getElementById(key);
//清除样式
if (style === "") {
if (node) {
node.parentNode.removeChild(node);
return true;
}
return false;
}
//添加样式
if (node) {
node.innerHTML = style;
} else {
node = doc.createElement("style");
node.id = key;
node.innerHTML = style;
doc.getElementsByTagName("head")[0].appendChild(node);
}
},
sort: function(array, compareFn) {
compareFn =
compareFn ||
function(item1, item2) {
return item1.localeCompare(item2);
};
for (var i = 0, len = array.length; i < len; i++) {
for (var j = i, length = array.length; j < length; j++) {
if (compareFn(array[i], array[j]) > 0) {
var t = array[i];
array[i] = array[j];
array[j] = t;
}
}
}
return array;
},
serializeParam: function(json) {
var strArr = [];
for (var i in json) {
//忽略默认的几个参数
if (i == "method" || i == "timeout" || i == "async") continue;
//传递过来的对象和函数不在提交之列
if (
!(
(typeof json[i]).toLowerCase() == "function" ||
(typeof json[i]).toLowerCase() == "object"
)
) {
strArr.push(encodeURIComponent(i) + "=" + encodeURIComponent(json[i]));
} else if (utils.isArray(json[i])) {
//支持传数组内容
for (var j = 0; j < json[i].length; j++) {
strArr.push(
encodeURIComponent(i) + "[]=" + encodeURIComponent(json[i][j])
);
}
}
}
return strArr.join("&");
},
formatUrl: function(url) {
var u = url.replace(/&&/g, "&");
u = u.replace(/\?&/g, "?");
u = u.replace(/&$/g, "");
u = u.replace(/&#/g, "#");
u = u.replace(/&+/g, "&");
return u;
},
isCrossDomainUrl: function(url) {
var a = document.createElement("a");
a.href = url;
if (browser.ie) {
a.href = a.href;
}
return !(
a.protocol == location.protocol &&
a.hostname == location.hostname &&
(a.port == location.port ||
(a.port == "80" && location.port == "") ||
(a.port == "" && location.port == "80"))
);
},
clearEmptyAttrs: function(obj) {
for (var p in obj) {
if (obj[p] === "") {
delete obj[p];
}
}
return obj;
},
str2json: function(s) {
if (!utils.isString(s)) return null;
if (window.JSON) {
return JSON.parse(s);
} else {
return new Function("return " + utils.trim(s || ""))();
}
},
json2str: (function() {
if (window.JSON) {
return JSON.stringify;
} else {
var escapeMap = {
"\b": "\\b",
"\t": "\\t",
"\n": "\\n",
"\f": "\\f",
"\r": "\\r",
'"': '\\"',
"\\": "\\\\"
};
function encodeString(source) {
if (/["\\\x00-\x1f]/.test(source)) {
source = source.replace(/["\\\x00-\x1f]/g, function(match) {
var c = escapeMap[match];
if (c) {
return c;
}
c = match.charCodeAt();
return (
"\\u00" + Math.floor(c / 16).toString(16) + (c % 16).toString(16)
);
});
}
return '"' + source + '"';
}
function encodeArray(source) {
var result = ["["],
l = source.length,
preComma,
i,
item;
for (i = 0; i < l; i++) {
item = source[i];
switch (typeof item) {
case "undefined":
case "function":
case "unknown":
break;
default:
if (preComma) {
result.push(",");
}
result.push(utils.json2str(item));
preComma = 1;
}
}
result.push("]");
return result.join("");
}
function pad(source) {
return source < 10 ? "0" + source : source;
}
function encodeDate(source) {
return (
'"' +
source.getFullYear() +
"-" +
pad(source.getMonth() + 1) +
"-" +
pad(source.getDate()) +
"T" +
pad(source.getHours()) +
":" +
pad(source.getMinutes()) +
":" +
pad(source.getSeconds()) +
'"'
);
}
return function(value) {
switch (typeof value) {
case "undefined":
return "undefined";
case "number":
return isFinite(value) ? String(value) : "null";
case "string":
return encodeString(value);
case "boolean":
return String(value);
default:
if (value === null) {
return "null";
} else if (utils.isArray(value)) {
return encodeArray(value);
} else if (utils.isDate(value)) {
return encodeDate(value);
} else {
var result = ["{"],
encode = utils.json2str,
preComma,
item;
for (var key in value) {
if (Object.prototype.hasOwnProperty.call(value, key)) {
item = value[key];
switch (typeof item) {
case "undefined":
case "unknown":
case "function":
break;
default:
if (preComma) {
result.push(",");
}
preComma = 1;
result.push(encode(key) + ":" + encode(item));
}
}
}
result.push("}");
return result.join("");
}
}
};
}
})(),
renderTplstr: function(tpl, data) {
return tpl.replace(/\$\{\s*(\w*?)\s*\}/g, function (match, variable) {
if (data.hasOwnProperty(variable)) {
return data[variable];
}
});
}
});
/**
* 判断给定的对象是否是字符串
* @method isString
* @param { * } object 需要判断的对象
* @return { Boolean } 给定的对象是否是字符串
*/
/**
* 判断给定的对象是否是数组
* @method isArray
* @param { * } object 需要判断的对象
* @return { Boolean } 给定的对象是否是数组
*/
/**
* 判断给定的对象是否是一个Function
* @method isFunction
* @param { * } object 需要判断的对象
* @return { Boolean } 给定的对象是否是Function
*/
/**
* 判断给定的对象是否是Number
* @method isNumber
* @param { * } object 需要判断的对象
* @return { Boolean } 给定的对象是否是Number
*/
/**
* 判断给定的对象是否是一个正则表达式
* @method isRegExp
* @param { * } object 需要判断的对象
* @return { Boolean } 给定的对象是否是正则表达式
*/
/**
* 判断给定的对象是否是一个普通对象
* @method isObject
* @param { * } object 需要判断的对象
* @return { Boolean } 给定的对象是否是普通对象
*/
utils.each(
["String", "Function", "Array", "Number", "RegExp", "Object", "Date"],
function(v) {
UE.utils["is" + v] = function(obj) {
return Object.prototype.toString.apply(obj) == "[object " + v + "]";
};
}
);
// core/EventBase.js
/**
* UE采用的事件基类
* @file
* @module UE
* @class EventBase
* @since 1.2.6.1
*/
/**
* UEditor公用空间,UEditor所有的功能都挂载在该空间下
* @unfile
* @module UE
*/
/**
* UE采用的事件基类,继承此类的对应类将获取addListener,removeListener,fireEvent方法。
* 在UE中,Editor以及所有ui实例都继承了该类,故可以在对应的ui对象以及editor对象上使用上述方法。
* @unfile
* @module UE
* @class EventBase
*/
/**
* 通过此构造器,子类可以继承EventBase获取事件监听的方法
* @constructor
* @example
* ```javascript
* UE.EventBase.call(editor);
* ```
*/
var EventBase = (UE.EventBase = function() {});
EventBase.prototype = {
/**
* 注册事件监听器
* @method addListener
* @param { String } types 监听的事件名称,同时监听多个事件使用空格分隔
* @param { Function } fn 监听的事件被触发时,会执行该回调函数
* @waining 事件被触发时,监听的函数假如返回的值恒等于true,回调函数的队列中后面的函数将不执行
* @example
* ```javascript
* editor.addListener('selectionchange',function(){
* console.log("选区已经变化!");
* })
* editor.addListener('beforegetcontent aftergetcontent',function(type){
* if(type == 'beforegetcontent'){
* //do something
* }else{
* //do something
* }
* console.log(this.getContent) // this是注册的事件的编辑器实例
* })
* ```
* @see UE.EventBase:fireEvent(String)
*/
addListener: function(types, listener) {
types = utils.trim(types).split(/\s+/);
for (var i = 0, ti; (ti = types[i++]); ) {
getListener(this, ti, true).push(listener);
}
},
on: function(types, listener) {
return this.addListener(types, listener);
},
off: function(types, listener) {
return this.removeListener(types, listener);
},
trigger: function() {
return this.fireEvent.apply(this, arguments);
},
/**
* 移除事件监听器
* @method removeListener
* @param { String } types 移除的事件名称,同时移除多个事件使用空格分隔
* @param { Function } fn 移除监听事件的函数引用
* @example
* ```javascript
* //changeCallback为方法体
* editor.removeListener("selectionchange",changeCallback);
* ```
*/
removeListener: function(types, listener) {
types = utils.trim(types).split(/\s+/);
for (var i = 0, ti; (ti = types[i++]); ) {
utils.removeItem(getListener(this, ti) || [], listener);
}
},
/**
* 触发事件
* @method fireEvent
* @param { String } types 触发的事件名称,同时触发多个事件使用空格分隔
* @remind 该方法会触发addListener
* @return { * } 返回触发事件的队列中,最后执行的回调函数的返回值
* @example
* ```javascript
* editor.fireEvent("selectionchange");
* ```
*/
/**
* 触发事件
* @method fireEvent
* @param { String } types 触发的事件名称,同时触发多个事件使用空格分隔
* @param { *... } options 可选参数,可以传入一个或多个参数,会传给事件触发的回调函数
* @return { * } 返回触发事件的队列中,最后执行的回调函数的返回值
* @example
* ```javascript
*
* editor.addListener( "selectionchange", function ( type, arg1, arg2 ) {
*
* console.log( arg1 + " " + arg2 );
*
* } );
*
* //触发selectionchange事件, 会执行上面的事件监听器
* //output: Hello World
* editor.fireEvent("selectionchange", "Hello", "World");
* ```
*/
fireEvent: function() {
var types = arguments[0];
types = utils.trim(types).split(" ");
for (var i = 0, ti; (ti = types[i++]); ) {
var listeners = getListener(this, ti),
r,
t,
k;
if (listeners) {
k = listeners.length;
while (k--) {
if (!listeners[k]) continue;
t = listeners[k].apply(this, arguments);
if (t === true) {
return t;
}
if (t !== undefined) {
r = t;
}
}
}
if ((t = this["on" + ti.toLowerCase()])) {
r = t.apply(this, arguments);
}
}
return r;
}
};
/**
* 获得对象所拥有监听类型的所有监听器
* @unfile
* @module UE
* @since 1.2.6.1
* @method getListener
* @public
* @param { Object } obj 查询监听器的对象
* @param { String } type 事件类型
* @param { Boolean } force 为true且当前所有type类型的侦听器不存在时,创建一个空监听器数组
* @return { Array } 监听器数组
*/
function getListener(obj, type, force) {
var allListeners;
type = type.toLowerCase();
return (
(allListeners =
obj.__allListeners || (force && (obj.__allListeners = {}))) &&
(allListeners[type] || (force && (allListeners[type] = [])))
);
}
// core/dtd.js
///import editor.js
///import core/dom/dom.js
///import core/utils.js
/**
* dtd html语义化的体现类
* @constructor
* @namespace dtd
*/
var dtd = (dom.dtd = (function() {
function _(s) {
for (var k in s) {
s[k.toUpperCase()] = s[k];
}
return s;
}
var X = utils.extend2;
var A = _({ isindex: 1, fieldset: 1 }),
B = _({ input: 1, button: 1, select: 1, textarea: 1, label: 1 }),
C = X(_({ a: 1 }), B),
D = X({ iframe: 1 }, C),
E = _({
hr: 1,
ul: 1,
menu: 1,
div: 1,
blockquote: 1,
noscript: 1,
table: 1,
center: 1,
address: 1,
dir: 1,
pre: 1,
h5: 1,
dl: 1,
h4: 1,
noframes: 1,
h6: 1,
ol: 1,
h1: 1,
h3: 1,
h2: 1
}),
F = _({ ins: 1, del: 1, script: 1, style: 1 }),
G = X(
_({
mark: 1,
b: 1,
acronym: 1,
bdo: 1,
var: 1,
"#": 1,
abbr: 1,
code: 1,
br: 1,
i: 1,
cite: 1,
kbd: 1,
u: 1,
strike: 1,
s: 1,
tt: 1,
strong: 1,
q: 1,
samp: 1,
em: 1,
dfn: 1,
span: 1
}),
F
),
H = X(
_({
sub: 1,
img: 1,
embed: 1,
object: 1,
sup: 1,
basefont: 1,
map: 1,
applet: 1,
font: 1,
big: 1,
small: 1
}),
G
),
I = X(_({ p: 1 }), H),
J = X(_({ iframe: 1 }), H, B),
K = _({
img: 1,
embed: 1,
noscript: 1,
br: 1,
kbd: 1,
center: 1,
button: 1,
basefont: 1,
h5: 1,
h4: 1,
samp: 1,
h6: 1,
ol: 1,
h1: 1,
h3: 1,
h2: 1,
form: 1,
font: 1,
"#": 1,
select: 1,
menu: 1,
ins: 1,
abbr: 1,
label: 1,
code: 1,
table: 1,
script: 1,
cite: 1,
input: 1,
iframe: 1,
strong: 1,
textarea: 1,
noframes: 1,
big: 1,
small: 1,
span: 1,
hr: 1,
sub: 1,
bdo: 1,
var: 1,
div: 1,
object: 1,
sup: 1,
strike: 1,
dir: 1,
map: 1,
dl: 1,
applet: 1,
del: 1,
isindex: 1,
fieldset: 1,
ul: 1,
b: 1,
acronym: 1,
a: 1,
blockquote: 1,
i: 1,
u: 1,
s: 1,
tt: 1,
address: 1,
q: 1,
pre: 1,
p: 1,
em: 1,
dfn: 1
}),
L = X(_({ a: 0 }), J), //a不能被切开,所以把他
M = _({ tr: 1 }),
N = _({ "#": 1 }),
O = X(_({ param: 1 }), K),
P = X(_({ form: 1 }), A, D, E, I),
Q = _({ li: 1, ol: 1, ul: 1 }),
R = _({ style: 1, script: 1 }),
S = _({ base: 1, link: 1, meta: 1, title: 1 }),
T = X(S, R),
U = _({ head: 1, body: 1 }),
V = _({ html: 1 });
var block = _({
address: 1,
blockquote: 1,
center: 1,
dir: 1,
div: 1,
dl: 1,
fieldset: 1,
form: 1,
h1: 1,
h2: 1,
h3: 1,
h4: 1,
h5: 1,
h6: 1,
hr: 1,
isindex: 1,
menu: 1,
noframes: 1,
ol: 1,
p: 1,
pre: 1,
table: 1,
ul: 1
}),
empty = _({
area: 1,
base: 1,
basefont: 1,
br: 1,
col: 1,
command: 1,
dialog: 1,
embed: 1,
hr: 1,
img: 1,
input: 1,
isindex: 1,
keygen: 1,
link: 1,
meta: 1,
param: 1,
source: 1,
track: 1,
wbr: 1
});
return _({
// $ 表示自定的属性
// body外的元素列表.
$nonBodyContent: X(V, U, S),
//块结构元素列表
$block: block,
//内联元素列表
$inline: L,
$inlineWithA: X(_({ a: 1 }), L),
$body: X(_({ script: 1, style: 1 }), block),
$cdata: _({ script: 1, style: 1 }),
//自闭和元素
$empty: empty,
//不是自闭合,但不能让range选中里边
$nonChild: _({ iframe: 1, textarea: 1 }),
//列表元素列表
$listItem: _({ dd: 1, dt: 1, li: 1 }),
//列表根元素列表
$list: _({ ul: 1, ol: 1, dl: 1 }),
//不能认为是空的元素
$isNotEmpty: _({
table: 1,
ul: 1,
ol: 1,
dl: 1,
iframe: 1,
area: 1,
base: 1,
col: 1,
hr: 1,
img: 1,
embed: 1,
input: 1,
textarea: 1,
link: 1,
meta: 1,
param: 1,
h1: 1,
h2: 1,
h3: 1,
h4: 1,
h5: 1,
h6: 1
}),
//如果没有子节点就可以删除的元素列表,像span,a
$removeEmpty: _({
a: 1,
abbr: 1,
acronym: 1,
address: 1,
b: 1,
bdo: 1,
big: 1,
cite: 1,
code: 1,
del: 1,
dfn: 1,
em: 1,
font: 1,
i: 1,
ins: 1,
label: 1,
kbd: 1,
q: 1,
s: 1,
samp: 1,
small: 1,
span: 1,
strike: 1,
strong: 1,
sub: 1,
sup: 1,
tt: 1,
u: 1,
var: 1
}),
$removeEmptyBlock: _({ p: 1, div: 1 }),
//在table元素里的元素列表
$tableContent: _({
caption: 1,
col: 1,
colgroup: 1,
tbody: 1,
td: 1,
tfoot: 1,
th: 1,
thead: 1,
tr: 1,
table: 1
}),
//不转换的标签
$notTransContent: _({ pre: 1, script: 1, style: 1, textarea: 1 }),
html: U,
head: T,
style: N,
script: N,
body: P,
base: {},
link: {},
meta: {},
title: N,
col: {},
tr: _({ td: 1, th: 1 }),
img: {},
embed: {},
colgroup: _({ thead: 1, col: 1, tbody: 1, tr: 1, tfoot: 1 }),
noscript: P,
td: P,
br: {},
th: P,
center: P,
kbd: L,
button: X(I, E),
basefont: {},
h5: L,
h4: L,
samp: L,
h6: L,
ol: Q,
h1: L,
h3: L,
option: N,
h2: L,
form: X(A, D, E, I),
select: _({ optgroup: 1, option: 1 }),
font: L,
ins: L,
menu: Q,
abbr: L,
label: L,
table: _({
thead: 1,
col: 1,
tbody: 1,
tr: 1,
colgroup: 1,
caption: 1,
tfoot: 1
}),
code: L,
tfoot: M,
cite: L,
li: P,
input: {},
iframe: P,
strong: L,
textarea: N,
noframes: P,
big: L,
small: L,
//trace:
span: _({
"#": 1,
br: 1,
b: 1,
strong: 1,
u: 1,
i: 1,
em: 1,
sub: 1,
sup: