diff --git a/public/UEditor/ueditor.all.js b/public/UEditor/ueditor.all.js
deleted file mode 100644
index ec942ca..0000000
--- a/public/UEditor/ueditor.all.js
+++ /dev/null
@@ -1,29559 +0,0 @@
-/*!
- * UEditor
- * version: ueditor
- * build: Wed Dec 26 2018 17:24:52 GMT+0800 (CST)
- */
-
-(function(){
-
-// editor.js
-UEDITOR_CONFIG = window.UEDITOR_CONFIG || {};
-
-var baidu = window.baidu || {};
-
-window.baidu = baidu;
-
-window.UE = baidu.editor = window.UE || {};
-
-UE.plugins = {};
-
-UE.commands = {};
-
-UE.instants = {};
-
-UE.I18N = {};
-
-UE._customizeUI = {};
-
-UE.version = "1.4.3";
-
-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.]+)/.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
- *
- * - IE系列返回值为5,6,7,8,9,10等
- * - gecko系列会返回10900,158900等
- * - webkit系列会返回其build号 (如 522等)
- *
- * @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 = '&';
- *
- * //output: <body>&</body>
- * console.log( UE.utils.unhtml( html ) );
- *
- * ```
- */
- unhtml:function (str, reg) {
- return str ? str.replace(reg || /[&<">'](?:(amp|lt|quot|gt|#39|nbsp|#\d+);)?/g, function (a, b) {
- if (b) {
- return a;
- } else {
- return {
- '<':'<',
- '&':'&',
- '"':'"',
- '>':'>',
- "'":'''
- }[a]
- }
-
- }) : '';
- },
- /**
- * 将url中的html字符转义, 仅转义 ', ", <, > 四个字符
- * @param { String } str 需要转义的字符串
- * @param { RegExp } reg 自定义的正则
- * @return { String } 转义后的字符串
- */
- unhtmlForUrl:function (str, reg) {
- return str ? str.replace(reg || /[<">']/g, function (a) {
- return {
- '<':'<',
- '&':'&',
- '"':'"',
- '>':'>',
- "'":'''
- }[a]
-
- }) : '';
- },
-
- /**
- * 将str中的转义字符还原成html字符
- * @see UE.utils.unhtml(String);
- * @method html
- * @param { String } str 需要逆转义的字符串
- * @return { String } 逆转义后的字符串
- * @example
- * ```javascript
- *
- * var str = '<body>&</body>';
- *
- * //output: &
- * console.log( UE.utils.html( str ) );
- *
- * ```
- */
- html:function (str) {
- return str ? str.replace(/&((g|l|quo)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 ueditor.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 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('');
- }
- }
- };
- }
-
- })()
-
-};
-/**
- * 判断给定的对象是否是字符串
- * @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( _({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,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:1,strike:1,span:1}),
- hr : L,
- dt : L,
- sub : L,
- optgroup : _({option:1}),
- param : {},
- bdo : L,
- 'var' : L,
- div : P,
- object : O,
- sup : L,
- dd : P,
- strike : L,
- area : {},
- dir : Q,
- map : X( _({area:1,form:1,p:1}), A, F, E ),
- applet : O,
- dl : _({dt:1,dd:1}),
- del : L,
- isindex : {},
- fieldset : X( _({legend:1}), K ),
- thead : M,
- ul : Q,
- acronym : L,
- b : L,
- a : X( _({a:1}), J ),
- blockquote :X(_({td:1,tr:1,tbody:1,li:1}),P),
- caption : L,
- i : L,
- u : L,
- tbody : M,
- s : L,
- address : X( D, I ),
- tt : L,
- legend : L,
- q : L,
- pre : X( G, C ),
- p : X(_({'a':1}),L),
- em :L,
- dfn : L
- });
-})();
-
-
-// core/domUtils.js
-/**
- * Dom操作工具包
- * @file
- * @module UE.dom.domUtils
- * @since 1.2.6.1
- */
-
-/**
- * Dom操作工具包
- * @unfile
- * @module UE.dom.domUtils
- */
-function getDomNode(node, start, ltr, startFromChild, fn, guard) {
- var tmpNode = startFromChild && node[start],
- parent;
- !tmpNode && (tmpNode = node[ltr]);
- while (!tmpNode && (parent = (parent || node).parentNode)) {
- if (parent.tagName == 'BODY' || guard && !guard(parent)) {
- return null;
- }
- tmpNode = parent[ltr];
- }
- if (tmpNode && fn && !fn(tmpNode)) {
- return getDomNode(tmpNode, start, ltr, false, fn);
- }
- return tmpNode;
-}
-var attrFix = ie && browser.version < 9 ? {
- tabindex:"tabIndex",
- readonly:"readOnly",
- "for":"htmlFor",
- "class":"className",
- maxlength:"maxLength",
- cellspacing:"cellSpacing",
- cellpadding:"cellPadding",
- rowspan:"rowSpan",
- colspan:"colSpan",
- usemap:"useMap",
- frameborder:"frameBorder"
- } : {
- tabindex:"tabIndex",
- readonly:"readOnly"
- },
- styleBlock = utils.listToMap([
- '-webkit-box', '-moz-box', 'block' ,
- 'list-item' , 'table' , 'table-row-group' ,
- 'table-header-group', 'table-footer-group' ,
- 'table-row' , 'table-column-group' , 'table-column' ,
- 'table-cell' , 'table-caption'
- ]);
-var domUtils = dom.domUtils = {
- //节点常量
- NODE_ELEMENT:1,
- NODE_DOCUMENT:9,
- NODE_TEXT:3,
- NODE_COMMENT:8,
- NODE_DOCUMENT_FRAGMENT:11,
-
- //位置关系
- POSITION_IDENTICAL:0,
- POSITION_DISCONNECTED:1,
- POSITION_FOLLOWING:2,
- POSITION_PRECEDING:4,
- POSITION_IS_CONTAINED:8,
- POSITION_CONTAINS:16,
- //ie6使用其他的会有一段空白出现
- fillChar:ie && browser.version == '6' ? '\ufeff' : '\u200B',
- //-------------------------Node部分--------------------------------
- keys:{
- /*Backspace*/ 8:1, /*Delete*/ 46:1,
- /*Shift*/ 16:1, /*Ctrl*/ 17:1, /*Alt*/ 18:1,
- 37:1, 38:1, 39:1, 40:1,
- 13:1 /*enter*/
- },
- /**
- * 获取节点A相对于节点B的位置关系
- * @method getPosition
- * @param { Node } nodeA 需要查询位置关系的节点A
- * @param { Node } nodeB 需要查询位置关系的节点B
- * @return { Number } 节点A与节点B的关系
- * @example
- * ```javascript
- * //output: 20
- * var position = UE.dom.domUtils.getPosition( document.documentElement, document.body );
- *
- * switch ( position ) {
- *
- * //0
- * case UE.dom.domUtils.POSITION_IDENTICAL:
- * console.log('元素相同');
- * break;
- * //1
- * case UE.dom.domUtils.POSITION_DISCONNECTED:
- * console.log('两个节点在不同的文档中');
- * break;
- * //2
- * case UE.dom.domUtils.POSITION_FOLLOWING:
- * console.log('节点A在节点B之后');
- * break;
- * //4
- * case UE.dom.domUtils.POSITION_PRECEDING;
- * console.log('节点A在节点B之前');
- * break;
- * //8
- * case UE.dom.domUtils.POSITION_IS_CONTAINED:
- * console.log('节点A被节点B包含');
- * break;
- * case 10:
- * console.log('节点A被节点B包含且节点A在节点B之后');
- * break;
- * //16
- * case UE.dom.domUtils.POSITION_CONTAINS:
- * console.log('节点A包含节点B');
- * break;
- * case 20:
- * console.log('节点A包含节点B且节点A在节点B之前');
- * break;
- *
- * }
- * ```
- */
- getPosition:function (nodeA, nodeB) {
- // 如果两个节点是同一个节点
- if (nodeA === nodeB) {
- // domUtils.POSITION_IDENTICAL
- return 0;
- }
- var node,
- parentsA = [nodeA],
- parentsB = [nodeB];
- node = nodeA;
- while (node = node.parentNode) {
- // 如果nodeB是nodeA的祖先节点
- if (node === nodeB) {
- // domUtils.POSITION_IS_CONTAINED + domUtils.POSITION_FOLLOWING
- return 10;
- }
- parentsA.push(node);
- }
- node = nodeB;
- while (node = node.parentNode) {
- // 如果nodeA是nodeB的祖先节点
- if (node === nodeA) {
- // domUtils.POSITION_CONTAINS + domUtils.POSITION_PRECEDING
- return 20;
- }
- parentsB.push(node);
- }
- parentsA.reverse();
- parentsB.reverse();
- if (parentsA[0] !== parentsB[0]) {
- // domUtils.POSITION_DISCONNECTED
- return 1;
- }
- var i = -1;
- while (i++, parentsA[i] === parentsB[i]) {
- }
- nodeA = parentsA[i];
- nodeB = parentsB[i];
- while (nodeA = nodeA.nextSibling) {
- if (nodeA === nodeB) {
- // domUtils.POSITION_PRECEDING
- return 4
- }
- }
- // domUtils.POSITION_FOLLOWING
- return 2;
- },
-
- /**
- * 检测节点node在父节点中的索引位置
- * @method getNodeIndex
- * @param { Node } node 需要检测的节点对象
- * @return { Number } 该节点在父节点中的位置
- * @see UE.dom.domUtils.getNodeIndex(Node,Boolean)
- */
-
- /**
- * 检测节点node在父节点中的索引位置, 根据给定的mergeTextNode参数决定是否要合并多个连续的文本节点为一个节点
- * @method getNodeIndex
- * @param { Node } node 需要检测的节点对象
- * @param { Boolean } mergeTextNode 是否合并多个连续的文本节点为一个节点
- * @return { Number } 该节点在父节点中的位置
- * @example
- * ```javascript
- *
- * var node = document.createElement("div");
- *
- * node.appendChild( document.createTextNode( "hello" ) );
- * node.appendChild( document.createTextNode( "world" ) );
- * node.appendChild( node = document.createElement( "div" ) );
- *
- * //output: 2
- * console.log( UE.dom.domUtils.getNodeIndex( node ) );
- *
- * //output: 1
- * console.log( UE.dom.domUtils.getNodeIndex( node, true ) );
- *
- * ```
- */
- getNodeIndex:function (node, ignoreTextNode) {
- var preNode = node,
- i = 0;
- while (preNode = preNode.previousSibling) {
- if (ignoreTextNode && preNode.nodeType == 3) {
- if(preNode.nodeType != preNode.nextSibling.nodeType ){
- i++;
- }
- continue;
- }
- i++;
- }
- return i;
- },
-
- /**
- * 检测节点node是否在给定的document对象上
- * @method inDoc
- * @param { Node } node 需要检测的节点对象
- * @param { DomDocument } doc 需要检测的document对象
- * @return { Boolean } 该节点node是否在给定的document的dom树上
- * @example
- * ```javascript
- *
- * var node = document.createElement("div");
- *
- * //output: false
- * console.log( UE.do.domUtils.inDoc( node, document ) );
- *
- * document.body.appendChild( node );
- *
- * //output: true
- * console.log( UE.do.domUtils.inDoc( node, document ) );
- *
- * ```
- */
- inDoc:function (node, doc) {
- return domUtils.getPosition(node, doc) == 10;
- },
- /**
- * 根据给定的过滤规则filterFn, 查找符合该过滤规则的node节点的第一个祖先节点,
- * 查找的起点是给定node节点的父节点。
- * @method findParent
- * @param { Node } node 需要查找的节点
- * @param { Function } filterFn 自定义的过滤方法。
- * @warning 查找的终点是到body节点为止
- * @remind 自定义的过滤方法filterFn接受一个Node对象作为参数, 该对象代表当前执行检测的祖先节点。 如果该
- * 节点满足过滤条件, 则要求返回true, 这时将直接返回该节点作为findParent()的结果, 否则, 请返回false。
- * @return { Node | Null } 如果找到符合过滤条件的节点, 就返回该节点, 否则返回NULL
- * @example
- * ```javascript
- * var filterNode = UE.dom.domUtils.findParent( document.body.firstChild, function ( node ) {
- *
- * //由于查找的终点是body节点, 所以永远也不会匹配当前过滤器的条件, 即这里永远会返回false
- * return node.tagName === "HTML";
- *
- * } );
- *
- * //output: true
- * console.log( filterNode === null );
- * ```
- */
-
- /**
- * 根据给定的过滤规则filterFn, 查找符合该过滤规则的node节点的第一个祖先节点,
- * 如果includeSelf的值为true,则查找的起点是给定的节点node, 否则, 起点是node的父节点
- * @method findParent
- * @param { Node } node 需要查找的节点
- * @param { Function } filterFn 自定义的过滤方法。
- * @param { Boolean } includeSelf 查找过程是否包含自身
- * @warning 查找的终点是到body节点为止
- * @remind 自定义的过滤方法filterFn接受一个Node对象作为参数, 该对象代表当前执行检测的祖先节点。 如果该
- * 节点满足过滤条件, 则要求返回true, 这时将直接返回该节点作为findParent()的结果, 否则, 请返回false。
- * @remind 如果includeSelf为true, 则过滤器第一次执行时的参数会是节点本身。
- * 反之, 过滤器第一次执行时的参数将是该节点的父节点。
- * @return { Node | Null } 如果找到符合过滤条件的节点, 就返回该节点, 否则返回NULL
- * @example
- * ```html
- *
- *
- *
- *
- *
- *
- *
- * ```
- */
- findParent:function (node, filterFn, includeSelf) {
- if (node && !domUtils.isBody(node)) {
- node = includeSelf ? node : node.parentNode;
- while (node) {
- if (!filterFn || filterFn(node) || domUtils.isBody(node)) {
- return filterFn && !filterFn(node) && domUtils.isBody(node) ? null : node;
- }
- node = node.parentNode;
- }
- }
- return null;
- },
- /**
- * 查找node的节点名为tagName的第一个祖先节点, 查找的起点是node节点的父节点。
- * @method findParentByTagName
- * @param { Node } node 需要查找的节点对象
- * @param { Array } tagNames 需要查找的父节点的名称数组
- * @warning 查找的终点是到body节点为止
- * @return { Node | NULL } 如果找到符合条件的节点, 则返回该节点, 否则返回NULL
- * @example
- * ```javascript
- * var node = UE.dom.domUtils.findParentByTagName( document.getElementsByTagName("div")[0], [ "BODY" ] );
- * //output: BODY
- * console.log( node.tagName );
- * ```
- */
-
- /**
- * 查找node的节点名为tagName的祖先节点, 如果includeSelf的值为true,则查找的起点是给定的节点node,
- * 否则, 起点是node的父节点。
- * @method findParentByTagName
- * @param { Node } node 需要查找的节点对象
- * @param { Array } tagNames 需要查找的父节点的名称数组
- * @param { Boolean } includeSelf 查找过程是否包含node节点自身
- * @warning 查找的终点是到body节点为止
- * @return { Node | NULL } 如果找到符合条件的节点, 则返回该节点, 否则返回NULL
- * @example
- * ```javascript
- * var queryTarget = document.getElementsByTagName("div")[0];
- * var node = UE.dom.domUtils.findParentByTagName( queryTarget, [ "DIV" ], true );
- * //output: true
- * console.log( queryTarget === node );
- * ```
- */
- findParentByTagName:function (node, tagNames, includeSelf, excludeFn) {
- tagNames = utils.listToMap(utils.isArray(tagNames) ? tagNames : [tagNames]);
- return domUtils.findParent(node, function (node) {
- return tagNames[node.tagName] && !(excludeFn && excludeFn(node));
- }, includeSelf);
- },
- /**
- * 查找节点node的祖先节点集合, 查找的起点是给定节点的父节点,结果集中不包含给定的节点。
- * @method findParents
- * @param { Node } node 需要查找的节点对象
- * @return { Array } 给定节点的祖先节点数组
- * @grammar UE.dom.domUtils.findParents(node) => Array //返回一个祖先节点数组集合,不包含自身
- * @grammar UE.dom.domUtils.findParents(node,includeSelf) => Array //返回一个祖先节点数组集合,includeSelf指定是否包含自身
- * @grammar UE.dom.domUtils.findParents(node,includeSelf,filterFn) => Array //返回一个祖先节点数组集合,filterFn指定过滤条件,返回true的node将被选取
- * @grammar UE.dom.domUtils.findParents(node,includeSelf,filterFn,closerFirst) => Array //返回一个祖先节点数组集合,closerFirst为true的话,node的直接父亲节点是数组的第0个
- */
-
- /**
- * 查找节点node的祖先节点集合, 如果includeSelf的值为true,
- * 则返回的结果集中允许出现当前给定的节点, 否则, 该节点不会出现在其结果集中。
- * @method findParents
- * @param { Node } node 需要查找的节点对象
- * @param { Boolean } includeSelf 查找的结果中是否允许包含当前查找的节点对象
- * @return { Array } 给定节点的祖先节点数组
- */
- findParents:function (node, includeSelf, filterFn, closerFirst) {
- var parents = includeSelf && ( filterFn && filterFn(node) || !filterFn ) ? [node] : [];
- while (node = domUtils.findParent(node, filterFn)) {
- parents.push(node);
- }
- return closerFirst ? parents : parents.reverse();
- },
-
- /**
- * 在节点node后面插入新节点newNode
- * @method insertAfter
- * @param { Node } node 目标节点
- * @param { Node } newNode 新插入的节点, 该节点将置于目标节点之后
- * @return { Node } 新插入的节点
- */
- insertAfter:function (node, newNode) {
- return node.nextSibling ? node.parentNode.insertBefore(newNode, node.nextSibling):
- node.parentNode.appendChild(newNode);
- },
-
- /**
- * 删除节点node及其下属的所有节点
- * @method remove
- * @param { Node } node 需要删除的节点对象
- * @return { Node } 返回刚删除的节点对象
- * @example
- * ```html
- *
- *
- * ```
- */
-
- /**
- * 删除节点node,并根据keepChildren的值决定是否保留子节点
- * @method remove
- * @param { Node } node 需要删除的节点对象
- * @param { Boolean } keepChildren 是否需要保留子节点
- * @return { Node } 返回刚删除的节点对象
- * @example
- * ```html
- *
- *
- * ```
- */
- remove:function (node, keepChildren) {
- var parent = node.parentNode,
- child;
- if (parent) {
- if (keepChildren && node.hasChildNodes()) {
- while (child = node.firstChild) {
- parent.insertBefore(child, node);
- }
- }
- parent.removeChild(node);
- }
- return node;
- },
-
- /**
- * 取得node节点的下一个兄弟节点, 如果该节点其后没有兄弟节点, 则递归查找其父节点之后的第一个兄弟节点,
- * 直到找到满足条件的节点或者递归到BODY节点之后才会结束。
- * @method getNextDomNode
- * @param { Node } node 需要获取其后的兄弟节点的节点对象
- * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL
- * @example
- * ```html
- *
- *
- *
- *
- * xxx
- *
- *
- * ```
- * @example
- * ```html
- *
- *
- *
- * xxx
- *
- * xxx
- *
- *
- * ```
- */
-
- /**
- * 取得node节点的下一个兄弟节点, 如果startFromChild的值为ture,则先获取其子节点,
- * 如果有子节点则直接返回第一个子节点;如果没有子节点或者startFromChild的值为false,
- * 则执行getNextDomNode(Node node)的查找过程。
- * @method getNextDomNode
- * @param { Node } node 需要获取其后的兄弟节点的节点对象
- * @param { Boolean } startFromChild 查找过程是否从其子节点开始
- * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL
- * @see UE.dom.domUtils.getNextDomNode(Node)
- */
- getNextDomNode:function (node, startFromChild, filterFn, guard) {
- return getDomNode(node, 'firstChild', 'nextSibling', startFromChild, filterFn, guard);
- },
- getPreDomNode:function (node, startFromChild, filterFn, guard) {
- return getDomNode(node, 'lastChild', 'previousSibling', startFromChild, filterFn, guard);
- },
- /**
- * 检测节点node是否属是UEditor定义的bookmark节点
- * @method isBookmarkNode
- * @private
- * @param { Node } node 需要检测的节点对象
- * @return { Boolean } 是否是bookmark节点
- * @example
- * ```html
- *
- *
- * ```
- */
- isBookmarkNode:function (node) {
- return node.nodeType == 1 && node.id && /^_baidu_bookmark_/i.test(node.id);
- },
- /**
- * 获取节点node所属的window对象
- * @method getWindow
- * @param { Node } node 节点对象
- * @return { Window } 当前节点所属的window对象
- * @example
- * ```javascript
- * //output: true
- * console.log( UE.dom.domUtils.getWindow( document.body ) === window );
- * ```
- */
- getWindow:function (node) {
- var doc = node.ownerDocument || node;
- return doc.defaultView || doc.parentWindow;
- },
- /**
- * 获取离nodeA与nodeB最近的公共的祖先节点
- * @method getCommonAncestor
- * @param { Node } nodeA 第一个节点
- * @param { Node } nodeB 第二个节点
- * @remind 如果给定的两个节点是同一个节点, 将直接返回该节点。
- * @return { Node | NULL } 如果未找到公共节点, 返回NULL, 否则返回最近的公共祖先节点。
- * @example
- * ```javascript
- * var commonAncestor = UE.dom.domUtils.getCommonAncestor( document.body, document.body.firstChild );
- * //output: true
- * console.log( commonAncestor.tagName.toLowerCase() === 'body' );
- * ```
- */
- getCommonAncestor:function (nodeA, nodeB) {
- if (nodeA === nodeB)
- return nodeA;
- var parentsA = [nodeA] , parentsB = [nodeB], parent = nodeA, i = -1;
- while (parent = parent.parentNode) {
- if (parent === nodeB) {
- return parent;
- }
- parentsA.push(parent);
- }
- parent = nodeB;
- while (parent = parent.parentNode) {
- if (parent === nodeA)
- return parent;
- parentsB.push(parent);
- }
- parentsA.reverse();
- parentsB.reverse();
- while (i++, parentsA[i] === parentsB[i]) {
- }
- return i == 0 ? null : parentsA[i - 1];
-
- },
- /**
- * 清除node节点左右连续为空的兄弟inline节点
- * @method clearEmptySibling
- * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
- * 则这些兄弟节点将被删除
- * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext) //ignoreNext指定是否忽略右边空节点
- * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext,ignorePre) //ignorePre指定是否忽略左边空节点
- * @example
- * ```html
- *
- *
- *
- *
- *
- * xxx
- *
- *
- *
- * ```
- */
-
- /**
- * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true,
- * 则忽略对右边兄弟节点的操作。
- * @method clearEmptySibling
- * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
- * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
- * 则这些兄弟节点将被删除
- * @see UE.dom.domUtils.clearEmptySibling(Node)
- */
-
- /**
- * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true,
- * 则忽略对右边兄弟节点的操作, 如果ignorePre的值为true,则忽略对左边兄弟节点的操作。
- * @method clearEmptySibling
- * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
- * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
- * @param { Boolean } ignorePre 是否忽略忽略对左边的兄弟节点的操作
- * 则这些兄弟节点将被删除
- * @see UE.dom.domUtils.clearEmptySibling(Node)
- */
- clearEmptySibling:function (node, ignoreNext, ignorePre) {
- function clear(next, dir) {
- var tmpNode;
- while (next && !domUtils.isBookmarkNode(next) && (domUtils.isEmptyInlineElement(next)
- //这里不能把空格算进来会吧空格干掉,出现文字间的空格丢掉了
- || !new RegExp('[^\t\n\r' + domUtils.fillChar + ']').test(next.nodeValue) )) {
- tmpNode = next[dir];
- domUtils.remove(next);
- next = tmpNode;
- }
- }
- !ignoreNext && clear(node.nextSibling, 'nextSibling');
- !ignorePre && clear(node.previousSibling, 'previousSibling');
- },
- /**
- * 将一个文本节点textNode拆分成两个文本节点,offset指定拆分位置
- * @method split
- * @param { Node } textNode 需要拆分的文本节点对象
- * @param { int } offset 需要拆分的位置, 位置计算从0开始
- * @return { Node } 拆分后形成的新节点
- * @example
- * ```html
- * abcdef
- *
- * ```
- */
- split:function (node, offset) {
- var doc = node.ownerDocument;
- if (browser.ie && offset == node.nodeValue.length) {
- var next = doc.createTextNode('');
- return domUtils.insertAfter(node, next);
- }
- var retval = node.splitText(offset);
- //ie8下splitText不会跟新childNodes,我们手动触发他的更新
- if (browser.ie8) {
- var tmpNode = doc.createTextNode('');
- domUtils.insertAfter(retval, tmpNode);
- domUtils.remove(tmpNode);
- }
- return retval;
- },
-
- /**
- * 检测文本节点textNode是否为空节点(包括空格、换行、占位符等字符)
- * @method isWhitespace
- * @param { Node } node 需要检测的节点对象
- * @return { Boolean } 检测的节点是否为空
- * @example
- * ```html
- *
- *
- *
- *
- * ```
- */
- isWhitespace:function (node) {
- return !new RegExp('[^ \t\n\r' + domUtils.fillChar + ']').test(node.nodeValue);
- },
- /**
- * 获取元素element相对于viewport的位置坐标
- * @method getXY
- * @param { Node } element 需要计算位置的节点对象
- * @return { Object } 返回形如{x:left,y:top}的一个key-value映射对象, 其中键x代表水平偏移距离,
- * y代表垂直偏移距离。
- *
- * @example
- * ```javascript
- * var location = UE.dom.domUtils.getXY( document.getElementById("test") );
- * //output: test的坐标为: 12, 24
- * console.log( 'test的坐标为: ', location.x, ',', location.y );
- * ```
- */
- getXY:function (element) {
- var x = 0, y = 0;
- while (element.offsetParent) {
- y += element.offsetTop;
- x += element.offsetLeft;
- element = element.offsetParent;
- }
- return { 'x':x, 'y':y};
- },
- /**
- * 为元素element绑定原生DOM事件,type为事件类型,handler为处理函数
- * @method on
- * @param { Node } element 需要绑定事件的节点对象
- * @param { String } type 绑定的事件类型
- * @param { Function } handler 事件处理器
- * @example
- * ```javascript
- * UE.dom.domUtils.on(document.body,"click",function(e){
- * //e为事件对象,this为被点击元素对戏那个
- * });
- * ```
- */
-
- /**
- * 为元素element绑定原生DOM事件,type为事件类型,handler为处理函数
- * @method on
- * @param { Node } element 需要绑定事件的节点对象
- * @param { Array } type 绑定的事件类型数组
- * @param { Function } handler 事件处理器
- * @example
- * ```javascript
- * UE.dom.domUtils.on(document.body,["click","mousedown"],function(evt){
- * //evt为事件对象,this为被点击元素对象
- * });
- * ```
- */
- on:function (element, type, handler) {
-
- var types = utils.isArray(type) ? type : utils.trim(type).split(/\s+/),
- k = types.length;
- if (k) while (k--) {
- type = types[k];
- if (element.addEventListener) {
- element.addEventListener(type, handler, false);
- } else {
- if (!handler._d) {
- handler._d = {
- els : []
- };
- }
- var key = type + handler.toString(),index = utils.indexOf(handler._d.els,element);
- if (!handler._d[key] || index == -1) {
- if(index == -1){
- handler._d.els.push(element);
- }
- if(!handler._d[key]){
- handler._d[key] = function (evt) {
- return handler.call(evt.srcElement, evt || window.event);
- };
- }
-
-
- element.attachEvent('on' + type, handler._d[key]);
- }
- }
- }
- element = null;
- },
- /**
- * 解除DOM事件绑定
- * @method un
- * @param { Node } element 需要解除事件绑定的节点对象
- * @param { String } type 需要接触绑定的事件类型
- * @param { Function } handler 对应的事件处理器
- * @example
- * ```javascript
- * UE.dom.domUtils.un(document.body,"click",function(evt){
- * //evt为事件对象,this为被点击元素对象
- * });
- * ```
- */
-
- /**
- * 解除DOM事件绑定
- * @method un
- * @param { Node } element 需要解除事件绑定的节点对象
- * @param { Array } type 需要接触绑定的事件类型数组
- * @param { Function } handler 对应的事件处理器
- * @example
- * ```javascript
- * UE.dom.domUtils.un(document.body, ["click","mousedown"],function(evt){
- * //evt为事件对象,this为被点击元素对象
- * });
- * ```
- */
- un:function (element, type, handler) {
- var types = utils.isArray(type) ? type : utils.trim(type).split(/\s+/),
- k = types.length;
- if (k) while (k--) {
- type = types[k];
- if (element.removeEventListener) {
- element.removeEventListener(type, handler, false);
- } else {
- var key = type + handler.toString();
- try{
- element.detachEvent('on' + type, handler._d ? handler._d[key] : handler);
- }catch(e){}
- if (handler._d && handler._d[key]) {
- var index = utils.indexOf(handler._d.els,element);
- if(index!=-1){
- handler._d.els.splice(index,1);
- }
- handler._d.els.length == 0 && delete handler._d[key];
- }
- }
- }
- },
-
- /**
- * 比较节点nodeA与节点nodeB是否具有相同的标签名、属性名以及属性值
- * @method isSameElement
- * @param { Node } nodeA 需要比较的节点
- * @param { Node } nodeB 需要比较的节点
- * @return { Boolean } 两个节点是否具有相同的标签名、属性名以及属性值
- * @example
- * ```html
- * ssss
- * bbbbb
- * ssss
- * bbbbb
- *
- *
- * ```
- */
- isSameElement:function (nodeA, nodeB) {
- if (nodeA.tagName != nodeB.tagName) {
- return false;
- }
- var thisAttrs = nodeA.attributes,
- otherAttrs = nodeB.attributes;
- if (!ie && thisAttrs.length != otherAttrs.length) {
- return false;
- }
- var attrA, attrB, al = 0, bl = 0;
- for (var i = 0; attrA = thisAttrs[i++];) {
- if (attrA.nodeName == 'style') {
- if (attrA.specified) {
- al++;
- }
- if (domUtils.isSameStyle(nodeA, nodeB)) {
- continue;
- } else {
- return false;
- }
- }
- if (ie) {
- if (attrA.specified) {
- al++;
- attrB = otherAttrs.getNamedItem(attrA.nodeName);
- } else {
- continue;
- }
- } else {
- attrB = nodeB.attributes[attrA.nodeName];
- }
- if (!attrB.specified || attrA.nodeValue != attrB.nodeValue) {
- return false;
- }
- }
- // 有可能attrB的属性包含了attrA的属性之外还有自己的属性
- if (ie) {
- for (i = 0; attrB = otherAttrs[i++];) {
- if (attrB.specified) {
- bl++;
- }
- }
- if (al != bl) {
- return false;
- }
- }
- return true;
- },
-
- /**
- * 判断节点nodeA与节点nodeB的元素的style属性是否一致
- * @method isSameStyle
- * @param { Node } nodeA 需要比较的节点
- * @param { Node } nodeB 需要比较的节点
- * @return { Boolean } 两个节点是否具有相同的style属性值
- * @example
- * ```html
- * ssss
- * bbbbb
- * ssss
- * bbbbb
- *
- *
- * ```
- */
- isSameStyle:function (nodeA, nodeB) {
- var styleA = nodeA.style.cssText.replace(/( ?; ?)/g, ';').replace(/( ?: ?)/g, ':'),
- styleB = nodeB.style.cssText.replace(/( ?; ?)/g, ';').replace(/( ?: ?)/g, ':');
- if (browser.opera) {
- styleA = nodeA.style;
- styleB = nodeB.style;
- if (styleA.length != styleB.length)
- return false;
- for (var p in styleA) {
- if (/^(\d+|csstext)$/i.test(p)) {
- continue;
- }
- if (styleA[p] != styleB[p]) {
- return false;
- }
- }
- return true;
- }
- if (!styleA || !styleB) {
- return styleA == styleB;
- }
- styleA = styleA.split(';');
- styleB = styleB.split(';');
- if (styleA.length != styleB.length) {
- return false;
- }
- for (var i = 0, ci; ci = styleA[i++];) {
- if (utils.indexOf(styleB, ci) == -1) {
- return false;
- }
- }
- return true;
- },
- /**
- * 检查节点node是否为block元素
- * @method isBlockElm
- * @param { Node } node 需要检测的节点对象
- * @return { Boolean } 是否是block元素节点
- * @warning 该方法的判断规则如下: 如果该元素原本是block元素, 则不论该元素当前的css样式是什么都会返回true;
- * 否则,检测该元素的css样式, 如果该元素当前是block元素, 则返回true。 其余情况下都返回false。
- * @example
- * ```html
- *
- *
- *
- *
- *
- * ```
- */
- isBlockElm:function (node) {
- return node.nodeType == 1 && (dtd.$block[node.tagName] || styleBlock[domUtils.getComputedStyle(node, 'display')]) && !dtd.$nonChild[node.tagName];
- },
- /**
- * 检测node节点是否为body节点
- * @method isBody
- * @param { Element } node 需要检测的dom元素
- * @return { Boolean } 给定的元素是否是body元素
- * @example
- * ```javascript
- * //output: true
- * console.log( UE.dom.domUtils.isBody( document.body ) );
- * ```
- */
- isBody:function (node) {
- return node && node.nodeType == 1 && node.tagName.toLowerCase() == 'body';
- },
- /**
- * 以node节点为分界,将该节点的指定祖先节点parent拆分成两个独立的节点,
- * 拆分形成的两个节点之间是node节点
- * @method breakParent
- * @param { Node } node 作为分界的节点对象
- * @param { Node } parent 该节点必须是node节点的祖先节点, 且是block节点。
- * @return { Node } 给定的node分界节点
- * @example
- * ```javascript
- *
- * var node = document.createElement("span"),
- * wrapNode = document.createElement( "div" ),
- * parent = document.createElement("p");
- *
- * parent.appendChild( node );
- * wrapNode.appendChild( parent );
- *
- * //拆分前
- * //output:
- * console.log( wrapNode.innerHTML );
- *
- *
- * UE.dom.domUtils.breakParent( node, parent );
- * //拆分后
- * //output:
- * console.log( wrapNode.innerHTML );
- *
- * ```
- */
- breakParent:function (node, parent) {
- var tmpNode,
- parentClone = node,
- clone = node,
- leftNodes,
- rightNodes;
- do {
- parentClone = parentClone.parentNode;
- if (leftNodes) {
- tmpNode = parentClone.cloneNode(false);
- tmpNode.appendChild(leftNodes);
- leftNodes = tmpNode;
- tmpNode = parentClone.cloneNode(false);
- tmpNode.appendChild(rightNodes);
- rightNodes = tmpNode;
- } else {
- leftNodes = parentClone.cloneNode(false);
- rightNodes = leftNodes.cloneNode(false);
- }
- while (tmpNode = clone.previousSibling) {
- leftNodes.insertBefore(tmpNode, leftNodes.firstChild);
- }
- while (tmpNode = clone.nextSibling) {
- rightNodes.appendChild(tmpNode);
- }
- clone = parentClone;
- } while (parent !== parentClone);
- tmpNode = parent.parentNode;
- tmpNode.insertBefore(leftNodes, parent);
- tmpNode.insertBefore(rightNodes, parent);
- tmpNode.insertBefore(node, rightNodes);
- domUtils.remove(parent);
- return node;
- },
- /**
- * 检查节点node是否是空inline节点
- * @method isEmptyInlineElement
- * @param { Node } node 需要检测的节点对象
- * @return { Number } 如果给定的节点是空的inline节点, 则返回1, 否则返回0。
- * @example
- * ```html
- * => 1
- * => 1
- * => 1
- * xx => 0
- * ```
- */
- isEmptyInlineElement:function (node) {
- if (node.nodeType != 1 || !dtd.$removeEmpty[ node.tagName ]) {
- return 0;
- }
- node = node.firstChild;
- while (node) {
- //如果是创建的bookmark就跳过
- if (domUtils.isBookmarkNode(node)) {
- return 0;
- }
- if (node.nodeType == 1 && !domUtils.isEmptyInlineElement(node) ||
- node.nodeType == 3 && !domUtils.isWhitespace(node)
- ) {
- return 0;
- }
- node = node.nextSibling;
- }
- return 1;
-
- },
-
- /**
- * 删除node节点下首尾两端的空白文本子节点
- * @method trimWhiteTextNode
- * @param { Element } node 需要执行删除操作的元素对象
- * @example
- * ```javascript
- * var node = document.createElement("div");
- *
- * node.appendChild( document.createTextNode( "" ) );
- *
- * node.appendChild( document.createElement("div") );
- *
- * node.appendChild( document.createTextNode( "" ) );
- *
- * //3
- * console.log( node.childNodes.length );
- *
- * UE.dom.domUtils.trimWhiteTextNode( node );
- *
- * //1
- * console.log( node.childNodes.length );
- * ```
- */
- trimWhiteTextNode:function (node) {
- function remove(dir) {
- var child;
- while ((child = node[dir]) && child.nodeType == 3 && domUtils.isWhitespace(child)) {
- node.removeChild(child);
- }
- }
- remove('firstChild');
- remove('lastChild');
- },
-
- /**
- * 合并node节点下相同的子节点
- * @name mergeChild
- * @desc
- * UE.dom.domUtils.mergeChild(node,tagName) //tagName要合并的子节点的标签
- * @example
- * xxaaxx
- * ==> UE.dom.domUtils.mergeChild(node,'span')
- * xxaaxx
- */
- mergeChild:function (node, tagName, attrs) {
- var list = domUtils.getElementsByTagName(node, node.tagName.toLowerCase());
- for (var i = 0, ci; ci = list[i++];) {
- if (!ci.parentNode || domUtils.isBookmarkNode(ci)) {
- continue;
- }
- //span单独处理
- if (ci.tagName.toLowerCase() == 'span') {
- if (node === ci.parentNode) {
- domUtils.trimWhiteTextNode(node);
- if (node.childNodes.length == 1) {
- node.style.cssText = ci.style.cssText + ";" + node.style.cssText;
- domUtils.remove(ci, true);
- continue;
- }
- }
- ci.style.cssText = node.style.cssText + ';' + ci.style.cssText;
- if (attrs) {
- var style = attrs.style;
- if (style) {
- style = style.split(';');
- for (var j = 0, s; s = style[j++];) {
- ci.style[utils.cssStyleToDomStyle(s.split(':')[0])] = s.split(':')[1];
- }
- }
- }
- if (domUtils.isSameStyle(ci, node)) {
- domUtils.remove(ci, true);
- }
- continue;
- }
- if (domUtils.isSameElement(node, ci)) {
- domUtils.remove(ci, true);
- }
- }
- },
-
- /**
- * 原生方法getElementsByTagName的封装
- * @method getElementsByTagName
- * @param { Node } node 目标节点对象
- * @param { String } tagName 需要查找的节点的tagName, 多个tagName以空格分割
- * @return { Array } 符合条件的节点集合
- */
- getElementsByTagName:function (node, name,filter) {
- if(filter && utils.isString(filter)){
- var className = filter;
- filter = function(node){return domUtils.hasClass(node,className)}
- }
- name = utils.trim(name).replace(/[ ]{2,}/g,' ').split(' ');
- var arr = [];
- for(var n = 0,ni;ni=name[n++];){
- var list = node.getElementsByTagName(ni);
- for (var i = 0, ci; ci = list[i++];) {
- if(!filter || filter(ci))
- arr.push(ci);
- }
- }
-
- return arr;
- },
- /**
- * 将节点node提取到父节点上
- * @method mergeToParent
- * @param { Element } node 需要提取的元素对象
- * @example
- * ```html
- *
- *
- *
- * ```
- */
- mergeToParent:function (node) {
- var parent = node.parentNode;
- while (parent && dtd.$removeEmpty[parent.tagName]) {
- if (parent.tagName == node.tagName || parent.tagName == 'A') {//针对a标签单独处理
- domUtils.trimWhiteTextNode(parent);
- //span需要特殊处理 不处理这样的情况 xxxxxxxxx
- if (parent.tagName == 'SPAN' && !domUtils.isSameStyle(parent, node)
- || (parent.tagName == 'A' && node.tagName == 'SPAN')) {
- if (parent.childNodes.length > 1 || parent !== node.parentNode) {
- node.style.cssText = parent.style.cssText + ";" + node.style.cssText;
- parent = parent.parentNode;
- continue;
- } else {
- parent.style.cssText += ";" + node.style.cssText;
- //trace:952 a标签要保持下划线
- if (parent.tagName == 'A') {
- parent.style.textDecoration = 'underline';
- }
- }
- }
- if (parent.tagName != 'A') {
- parent === node.parentNode && domUtils.remove(node, true);
- break;
- }
- }
- parent = parent.parentNode;
- }
- },
- /**
- * 合并节点node的左右兄弟节点
- * @method mergeSibling
- * @param { Element } node 需要合并的目标节点
- * @example
- * ```html
- * xxxxoooxxxx
- *
- *
- * ```
- */
-
- /**
- * 合并节点node的左右兄弟节点, 可以根据给定的条件选择是否忽略合并左节点。
- * @method mergeSibling
- * @param { Element } node 需要合并的目标节点
- * @param { Boolean } ignorePre 是否忽略合并左节点
- * @example
- * ```html
- * xxxxoooxxxx
- *
- *
- * ```
- */
-
- /**
- * 合并节点node的左右兄弟节点,可以根据给定的条件选择是否忽略合并左右节点。
- * @method mergeSibling
- * @param { Element } node 需要合并的目标节点
- * @param { Boolean } ignorePre 是否忽略合并左节点
- * @param { Boolean } ignoreNext 是否忽略合并右节点
- * @remind 如果同时忽略左右节点, 则该操作什么也不会做
- * @example
- * ```html
- * xxxxoooxxxx
- *
- *
- * ```
- */
- mergeSibling:function (node, ignorePre, ignoreNext) {
- function merge(rtl, start, node) {
- var next;
- if ((next = node[rtl]) && !domUtils.isBookmarkNode(next) && next.nodeType == 1 && domUtils.isSameElement(node, next)) {
- while (next.firstChild) {
- if (start == 'firstChild') {
- node.insertBefore(next.lastChild, node.firstChild);
- } else {
- node.appendChild(next.firstChild);
- }
- }
- domUtils.remove(next);
- }
- }
- !ignorePre && merge('previousSibling', 'firstChild', node);
- !ignoreNext && merge('nextSibling', 'lastChild', node);
- },
-
- /**
- * 设置节点node及其子节点不会被选中
- * @method unSelectable
- * @param { Element } node 需要执行操作的dom元素
- * @remind 执行该操作后的节点, 将不能被鼠标选中
- * @example
- * ```javascript
- * UE.dom.domUtils.unSelectable( document.body );
- * ```
- */
- unSelectable:ie && browser.ie9below || browser.opera ? function (node) {
- //for ie9
- node.onselectstart = function () {
- return false;
- };
- node.onclick = node.onkeyup = node.onkeydown = function () {
- return false;
- };
- node.unselectable = 'on';
- node.setAttribute("unselectable", "on");
- for (var i = 0, ci; ci = node.all[i++];) {
- switch (ci.tagName.toLowerCase()) {
- case 'iframe' :
- case 'textarea' :
- case 'input' :
- case 'select' :
- break;
- default :
- ci.unselectable = 'on';
- node.setAttribute("unselectable", "on");
- }
- }
- } : function (node) {
- node.style.MozUserSelect =
- node.style.webkitUserSelect =
- node.style.msUserSelect =
- node.style.KhtmlUserSelect = 'none';
- },
- /**
- * 删除节点node上的指定属性名称的属性
- * @method removeAttributes
- * @param { Node } node 需要删除属性的节点对象
- * @param { String } attrNames 可以是空格隔开的多个属性名称,该操作将会依次删除相应的属性
- * @example
- * ```html
- *
- * xxxxx
- *
- *
- *
- * ```
- */
-
- /**
- * 删除节点node上的指定属性名称的属性
- * @method removeAttributes
- * @param { Node } node 需要删除属性的节点对象
- * @param { Array } attrNames 需要删除的属性名数组
- * @example
- * ```html
- *
- * xxxxx
- *
- *
- *
- * ```
- */
- removeAttributes:function (node, attrNames) {
- attrNames = utils.isArray(attrNames) ? attrNames : utils.trim(attrNames).replace(/[ ]{2,}/g,' ').split(' ');
- for (var i = 0, ci; ci = attrNames[i++];) {
- ci = attrFix[ci] || ci;
- switch (ci) {
- case 'className':
- node[ci] = '';
- break;
- case 'style':
- node.style.cssText = '';
- var val = node.getAttributeNode('style');
- !browser.ie && val && node.removeAttributeNode(val);
- }
- node.removeAttribute(ci);
- }
- },
- /**
- * 在doc下创建一个标签名为tag,属性为attrs的元素
- * @method createElement
- * @param { DomDocument } doc 新创建的元素属于该document节点创建
- * @param { String } tagName 需要创建的元素的标签名
- * @param { Object } attrs 新创建的元素的属性key-value集合
- * @return { Element } 新创建的元素对象
- * @example
- * ```javascript
- * var ele = UE.dom.domUtils.createElement( document, 'div', {
- * id: 'test'
- * } );
- *
- * //output: DIV
- * console.log( ele.tagName );
- *
- * //output: test
- * console.log( ele.id );
- *
- * ```
- */
- createElement:function (doc, tag, attrs) {
- return domUtils.setAttributes(doc.createElement(tag), attrs)
- },
- /**
- * 为节点node添加属性attrs,attrs为属性键值对
- * @method setAttributes
- * @param { Element } node 需要设置属性的元素对象
- * @param { Object } attrs 需要设置的属性名-值对
- * @return { Element } 设置属性的元素对象
- * @example
- * ```html
- *
- *
- *
- *
- */
- setAttributes:function (node, attrs) {
- for (var attr in attrs) {
- if(attrs.hasOwnProperty(attr)){
- var value = attrs[attr];
- switch (attr) {
- case 'class':
- //ie下要这样赋值,setAttribute不起作用
- node.className = value;
- break;
- case 'style' :
- node.style.cssText = node.style.cssText + ";" + value;
- break;
- case 'innerHTML':
- node[attr] = value;
- break;
- case 'value':
- node.value = value;
- break;
- default:
- node.setAttribute(attrFix[attr] || attr, value);
- }
- }
- }
- return node;
- },
-
- /**
- * 获取元素element经过计算后的样式值
- * @method getComputedStyle
- * @param { Element } element 需要获取样式的元素对象
- * @param { String } styleName 需要获取的样式名
- * @return { String } 获取到的样式值
- * @example
- * ```html
- *
- *
- *
- *
- *
- * ```
- */
- getComputedStyle:function (element, styleName) {
- //一下的属性单独处理
- var pros = 'width height top left';
-
- if(pros.indexOf(styleName) > -1){
- return element['offset' + styleName.replace(/^\w/,function(s){return s.toUpperCase()})] + 'px';
- }
- //忽略文本节点
- if (element.nodeType == 3) {
- element = element.parentNode;
- }
- //ie下font-size若body下定义了font-size,则从currentStyle里会取到这个font-size. 取不到实际值,故此修改.
- if (browser.ie && browser.version < 9 && styleName == 'font-size' && !element.style.fontSize &&
- !dtd.$empty[element.tagName] && !dtd.$nonChild[element.tagName]) {
- var span = element.ownerDocument.createElement('span');
- span.style.cssText = 'padding:0;border:0;font-family:simsun;';
- span.innerHTML = '.';
- element.appendChild(span);
- var result = span.offsetHeight;
- element.removeChild(span);
- span = null;
- return result + 'px';
- }
- try {
- var value = domUtils.getStyle(element, styleName) ||
- (window.getComputedStyle ? domUtils.getWindow(element).getComputedStyle(element, '').getPropertyValue(styleName) :
- ( element.currentStyle || element.style )[utils.cssStyleToDomStyle(styleName)]);
-
- } catch (e) {
- return "";
- }
- return utils.transUnitToPx(utils.fixColor(styleName, value));
- },
- /**
- * 删除元素element指定的className
- * @method removeClasses
- * @param { Element } ele 需要删除class的元素节点
- * @param { String } classNames 需要删除的className, 多个className之间以空格分开
- * @example
- * ```html
- * xxx
- *
- *
- * ```
- */
-
- /**
- * 删除元素element指定的className
- * @method removeClasses
- * @param { Element } ele 需要删除class的元素节点
- * @param { Array } classNames 需要删除的className数组
- * @example
- * ```html
- * xxx
- *
- *
- * ```
- */
- removeClasses:function (elm, classNames) {
- classNames = utils.isArray(classNames) ? classNames :
- utils.trim(classNames).replace(/[ ]{2,}/g,' ').split(' ');
- for(var i = 0,ci,cls = elm.className;ci=classNames[i++];){
- cls = cls.replace(new RegExp('\\b' + ci + '\\b'),'')
- }
- cls = utils.trim(cls).replace(/[ ]{2,}/g,' ');
- if(cls){
- elm.className = cls;
- }else{
- domUtils.removeAttributes(elm,['class']);
- }
- },
- /**
- * 给元素element添加className
- * @method addClass
- * @param { Node } ele 需要增加className的元素
- * @param { String } classNames 需要添加的className, 多个className之间以空格分割
- * @remind 相同的类名不会被重复添加
- * @example
- * ```html
- *
- *
- *
- * ```
- */
-
- /**
- * 判断元素element是否包含给定的样式类名className
- * @method hasClass
- * @param { Node } ele 需要检测的元素
- * @param { Array } classNames 需要检测的className数组
- * @return { Boolean } 元素是否包含所有给定的className
- * @example
- * ```html
- *
- *
- *
- * ```
- */
- hasClass:function (element, className) {
- if(utils.isRegExp(className)){
- return className.test(element.className)
- }
- className = utils.trim(className).replace(/[ ]{2,}/g,' ').split(' ');
- for(var i = 0,ci,cls = element.className;ci=className[i++];){
- if(!new RegExp('\\b' + ci + '\\b','i').test(cls)){
- return false;
- }
- }
- return i - 1 == className.length;
- },
-
- /**
- * 阻止事件默认行为
- * @method preventDefault
- * @param { Event } evt 需要阻止默认行为的事件对象
- * @example
- * ```javascript
- * UE.dom.domUtils.preventDefault( evt );
- * ```
- */
- preventDefault:function (evt) {
- evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
- },
- /**
- * 删除元素element指定的样式
- * @method removeStyle
- * @param { Element } element 需要删除样式的元素
- * @param { String } styleName 需要删除的样式名
- * @example
- * ```html
- *
- *
- *
- * ```
- */
- removeStyle:function (element, name) {
- if(browser.ie ){
- //针对color先单独处理一下
- if(name == 'color'){
- name = '(^|;)' + name;
- }
- element.style.cssText = element.style.cssText.replace(new RegExp(name + '[^:]*:[^;]+;?','ig'),'')
- }else{
- if (element.style.removeProperty) {
- element.style.removeProperty (name);
- }else {
- element.style.removeAttribute (utils.cssStyleToDomStyle(name));
- }
- }
-
-
- if (!element.style.cssText) {
- domUtils.removeAttributes(element, ['style']);
- }
- },
- /**
- * 获取元素element的style属性的指定值
- * @method getStyle
- * @param { Element } element 需要获取属性值的元素
- * @param { String } styleName 需要获取的style的名称
- * @warning 该方法仅获取元素style属性中所标明的值
- * @return { String } 该元素包含指定的style属性值
- * @example
- * ```html
- *
- *
- *
- * ```
- */
- getStyle:function (element, name) {
- var value = element.style[ utils.cssStyleToDomStyle(name) ];
- return utils.fixColor(name, value);
- },
- /**
- * 为元素element设置样式属性值
- * @method setStyle
- * @param { Element } element 需要设置样式的元素
- * @param { String } styleName 样式名
- * @param { String } styleValue 样式值
- * @example
- * ```html
- *
- *
- *
- * ```
- */
- setStyle:function (element, name, value) {
- element.style[utils.cssStyleToDomStyle(name)] = value;
- if(!utils.trim(element.style.cssText)){
- this.removeAttributes(element,'style')
- }
- },
- /**
- * 为元素element设置多个样式属性值
- * @method setStyles
- * @param { Element } element 需要设置样式的元素
- * @param { Object } styles 样式名值对
- * @example
- * ```html
- *
- *
- *
- * ```
- */
- setStyles:function (element, styles) {
- for (var name in styles) {
- if (styles.hasOwnProperty(name)) {
- domUtils.setStyle(element, name, styles[name]);
- }
- }
- },
- /**
- * 删除_moz_dirty属性
- * @private
- * @method removeDirtyAttr
- */
- removeDirtyAttr:function (node) {
- for (var i = 0, ci, nodes = node.getElementsByTagName('*'); ci = nodes[i++];) {
- ci.removeAttribute('_moz_dirty');
- }
- node.removeAttribute('_moz_dirty');
- },
- /**
- * 获取子节点的数量
- * @method getChildCount
- * @param { Element } node 需要检测的元素
- * @return { Number } 给定的node元素的子节点数量
- * @example
- * ```html
- *
- *
- *
- *
- *
- * ```
- */
-
- /**
- * 根据给定的过滤规则, 获取符合条件的子节点的数量
- * @method getChildCount
- * @param { Element } node 需要检测的元素
- * @param { Function } fn 过滤器, 要求对符合条件的子节点返回true, 反之则要求返回false
- * @return { Number } 符合过滤条件的node元素的子节点数量
- * @example
- * ```html
- *
- *
- *
- *
- *
- * ```
- */
- getChildCount:function (node, fn) {
- var count = 0, first = node.firstChild;
- fn = fn || function () {
- return 1;
- };
- while (first) {
- if (fn(first)) {
- count++;
- }
- first = first.nextSibling;
- }
- return count;
- },
-
- /**
- * 判断给定节点是否为空节点
- * @method isEmptyNode
- * @param { Node } node 需要检测的节点对象
- * @return { Boolean } 节点是否为空
- * @example
- * ```javascript
- * UE.dom.domUtils.isEmptyNode( document.body );
- * ```
- */
- isEmptyNode:function (node) {
- return !node.firstChild || domUtils.getChildCount(node, function (node) {
- return !domUtils.isBr(node) && !domUtils.isBookmarkNode(node) && !domUtils.isWhitespace(node)
- }) == 0
- },
- clearSelectedArr:function (nodes) {
- var node;
- while (node = nodes.pop()) {
- domUtils.removeAttributes(node, ['class']);
- }
- },
- /**
- * 将显示区域滚动到指定节点的位置
- * @method scrollToView
- * @param {Node} node 节点
- * @param {window} win window对象
- * @param {Number} offsetTop 距离上方的偏移量
- */
- scrollToView:function (node, win, offsetTop) {
- var getViewPaneSize = function () {
- var doc = win.document,
- mode = doc.compatMode == 'CSS1Compat';
- return {
- width:( mode ? doc.documentElement.clientWidth : doc.body.clientWidth ) || 0,
- height:( mode ? doc.documentElement.clientHeight : doc.body.clientHeight ) || 0
- };
- },
- getScrollPosition = function (win) {
- if ('pageXOffset' in win) {
- return {
- x:win.pageXOffset || 0,
- y:win.pageYOffset || 0
- };
- }
- else {
- var doc = win.document;
- return {
- x:doc.documentElement.scrollLeft || doc.body.scrollLeft || 0,
- y:doc.documentElement.scrollTop || doc.body.scrollTop || 0
- };
- }
- };
- var winHeight = getViewPaneSize().height, offset = winHeight * -1 + offsetTop;
- offset += (node.offsetHeight || 0);
- var elementPosition = domUtils.getXY(node);
- offset += elementPosition.y;
- var currentScroll = getScrollPosition(win).y;
- // offset += 50;
- if (offset > currentScroll || offset < currentScroll - winHeight) {
- win.scrollTo(0, offset + (offset < 0 ? -20 : 20));
- }
- },
- /**
- * 判断给定节点是否为br
- * @method isBr
- * @param { Node } node 需要判断的节点对象
- * @return { Boolean } 给定的节点是否是br节点
- */
- isBr:function (node) {
- return node.nodeType == 1 && node.tagName == 'BR';
- },
- /**
- * 判断给定的节点是否是一个“填充”节点
- * @private
- * @method isFillChar
- * @param { Node } node 需要判断的节点
- * @param { Boolean } isInStart 是否从节点内容的开始位置匹配
- * @returns { Boolean } 节点是否是填充节点
- */
- isFillChar:function (node,isInStart) {
- if(node.nodeType != 3)
- return false;
- var text = node.nodeValue;
- if(isInStart){
- return new RegExp('^' + domUtils.fillChar).test(text)
- }
- return !text.replace(new RegExp(domUtils.fillChar,'g'), '').length
- },
- isStartInblock:function (range) {
- var tmpRange = range.cloneRange(),
- flag = 0,
- start = tmpRange.startContainer,
- tmp;
- if(start.nodeType == 1 && start.childNodes[tmpRange.startOffset]){
- start = start.childNodes[tmpRange.startOffset];
- var pre = start.previousSibling;
- while(pre && domUtils.isFillChar(pre)){
- start = pre;
- pre = pre.previousSibling;
- }
- }
- if(this.isFillChar(start,true) && tmpRange.startOffset == 1){
- tmpRange.setStartBefore(start);
- start = tmpRange.startContainer;
- }
-
- while (start && domUtils.isFillChar(start)) {
- tmp = start;
- start = start.previousSibling
- }
- if (tmp) {
- tmpRange.setStartBefore(tmp);
- start = tmpRange.startContainer;
- }
- if (start.nodeType == 1 && domUtils.isEmptyNode(start) && tmpRange.startOffset == 1) {
- tmpRange.setStart(start, 0).collapse(true);
- }
- while (!tmpRange.startOffset) {
- start = tmpRange.startContainer;
- if (domUtils.isBlockElm(start) || domUtils.isBody(start)) {
- flag = 1;
- break;
- }
- var pre = tmpRange.startContainer.previousSibling,
- tmpNode;
- if (!pre) {
- tmpRange.setStartBefore(tmpRange.startContainer);
- } else {
- while (pre && domUtils.isFillChar(pre)) {
- tmpNode = pre;
- pre = pre.previousSibling;
- }
- if (tmpNode) {
- tmpRange.setStartBefore(tmpNode);
- } else {
- tmpRange.setStartBefore(tmpRange.startContainer);
- }
- }
- }
- return flag && !domUtils.isBody(tmpRange.startContainer) ? 1 : 0;
- },
-
- /**
- * 判断给定的元素是否是一个空元素
- * @method isEmptyBlock
- * @param { Element } node 需要判断的元素
- * @return { Boolean } 是否是空元素
- * @example
- * ```html
- *
- *
- *
- * ```
- */
-
- /**
- * 根据指定的判断规则判断给定的元素是否是一个空元素
- * @method isEmptyBlock
- * @param { Element } node 需要判断的元素
- * @param { RegExp } reg 对内容执行判断的正则表达式对象
- * @return { Boolean } 是否是空元素
- */
- isEmptyBlock:function (node,reg) {
- // HaoChuan9421
- if(!node){
- return;
- }
- if(node.nodeType != 1)
- return 0;
- reg = reg || new RegExp('[ \xa0\t\r\n' + domUtils.fillChar + ']', 'g');
-
- if (node[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').length > 0) {
- return 0;
- }
- for (var n in dtd.$isNotEmpty) {
- if (node.getElementsByTagName(n).length) {
- return 0;
- }
- }
- return 1;
- },
-
- /**
- * 移动元素使得该元素的位置移动指定的偏移量的距离
- * @method setViewportOffset
- * @param { Element } element 需要设置偏移量的元素
- * @param { Object } offset 偏移量, 形如{ left: 100, top: 50 }的一个键值对, 表示该元素将在
- * 现有的位置上向水平方向偏移offset.left的距离, 在竖直方向上偏移
- * offset.top的距离
- * @example
- * ```html
- *
- *
- *
- * ```
- */
- setViewportOffset:function (element, offset) {
- var left = parseInt(element.style.left) | 0;
- var top = parseInt(element.style.top) | 0;
- var rect = element.getBoundingClientRect();
- var offsetLeft = offset.left - rect.left;
- var offsetTop = offset.top - rect.top;
- if (offsetLeft) {
- element.style.left = left + offsetLeft + 'px';
- }
- if (offsetTop) {
- element.style.top = top + offsetTop + 'px';
- }
- },
-
- /**
- * 用“填充字符”填充节点
- * @method fillNode
- * @private
- * @param { DomDocument } doc 填充的节点所在的docment对象
- * @param { Node } node 需要填充的节点对象
- * @example
- * ```html
- *
- *
- *
- * ```
- */
- fillNode:function (doc, node) {
- var tmpNode = browser.ie ? doc.createTextNode(domUtils.fillChar) : doc.createElement('br');
- node.innerHTML = '';
- node.appendChild(tmpNode);
- },
-
- /**
- * 把节点src的所有子节点追加到另一个节点tag上去
- * @method moveChild
- * @param { Node } src 源节点, 该节点下的所有子节点将被移除
- * @param { Node } tag 目标节点, 从源节点移除的子节点将被追加到该节点下
- * @example
- * ```html
- *
- *
- *
- *
- *
- *
- * ```
- */
-
- /**
- * 把节点src的所有子节点移动到另一个节点tag上去, 可以通过dir参数控制附加的行为是“追加”还是“插入顶部”
- * @method moveChild
- * @param { Node } src 源节点, 该节点下的所有子节点将被移除
- * @param { Node } tag 目标节点, 从源节点移除的子节点将被附加到该节点下
- * @param { Boolean } dir 附加方式, 如果为true, 则附加进去的节点将被放到目标节点的顶部, 反之,则放到末尾
- * @example
- * ```html
- *
- *
- *
- *
- *
- *
- * ```
- */
- moveChild:function (src, tag, dir) {
- while (src.firstChild) {
- if (dir && tag.firstChild) {
- tag.insertBefore(src.lastChild, tag.firstChild);
- } else {
- tag.appendChild(src.firstChild);
- }
- }
- },
-
- /**
- * 判断节点的标签上是否不存在任何属性
- * @method hasNoAttributes
- * @private
- * @param { Node } node 需要检测的节点对象
- * @return { Boolean } 节点是否不包含任何属性
- * @example
- * ```html
- * xxxx
- *
- *
- * ```
- */
- hasNoAttributes:function (node) {
- return browser.ie ? /^<\w+\s*?>/.test(node.outerHTML) : node.attributes.length == 0;
- },
-
- /**
- * 检测节点是否是UEditor所使用的辅助节点
- * @method isCustomeNode
- * @private
- * @param { Node } node 需要检测的节点
- * @remind 辅助节点是指编辑器要完成工作临时添加的节点, 在输出的时候将会从编辑器内移除, 不会影响最终的结果。
- * @return { Boolean } 给定的节点是否是一个辅助节点
- */
- isCustomeNode:function (node) {
- return node.nodeType == 1 && node.getAttribute('_ue_custom_node_');
- },
-
- /**
- * 检测节点的标签是否是给定的标签
- * @method isTagNode
- * @param { Node } node 需要检测的节点对象
- * @param { String } tagName 标签
- * @return { Boolean } 节点的标签是否是给定的标签
- * @example
- * ```html
- *
- *
- *
- * ```
- */
- isTagNode:function (node, tagNames) {
- return node.nodeType == 1 && new RegExp('\\b' + node.tagName + '\\b','i').test(tagNames)
- },
-
- /**
- * 给定一个节点数组,在通过指定的过滤器过滤后, 获取其中满足过滤条件的第一个节点
- * @method filterNodeList
- * @param { Array } nodeList 需要过滤的节点数组
- * @param { Function } fn 过滤器, 对符合条件的节点, 执行结果返回true, 反之则返回false
- * @return { Node | NULL } 如果找到符合过滤条件的节点, 则返回该节点, 否则返回NULL
- * @example
- * ```javascript
- * var divNodes = document.getElementsByTagName("div");
- * divNodes = [].slice.call( divNodes, 0 );
- *
- * //output: null
- * console.log( UE.dom.domUtils.filterNodeList( divNodes, function ( node ) {
- * return node.tagName.toLowerCase() !== 'div';
- * } ) );
- * ```
- */
-
- /**
- * 给定一个节点数组nodeList和一组标签名tagNames, 获取其中能够匹配标签名的节点集合中的第一个节点
- * @method filterNodeList
- * @param { Array } nodeList 需要过滤的节点数组
- * @param { String } tagNames 需要匹配的标签名, 多个标签名之间用空格分割
- * @return { Node | NULL } 如果找到标签名匹配的节点, 则返回该节点, 否则返回NULL
- * @example
- * ```javascript
- * var divNodes = document.getElementsByTagName("div");
- * divNodes = [].slice.call( divNodes, 0 );
- *
- * //output: null
- * console.log( UE.dom.domUtils.filterNodeList( divNodes, 'a span' ) );
- * ```
- */
-
- /**
- * 给定一个节点数组,在通过指定的过滤器过滤后, 如果参数forAll为true, 则会返回所有满足过滤
- * 条件的节点集合, 否则, 返回满足条件的节点集合中的第一个节点
- * @method filterNodeList
- * @param { Array } nodeList 需要过滤的节点数组
- * @param { Function } fn 过滤器, 对符合条件的节点, 执行结果返回true, 反之则返回false
- * @param { Boolean } forAll 是否返回整个节点数组, 如果该参数为false, 则返回节点集合中的第一个节点
- * @return { Array | Node | NULL } 如果找到符合过滤条件的节点, 则根据参数forAll的值决定返回满足
- * 过滤条件的节点数组或第一个节点, 否则返回NULL
- * @example
- * ```javascript
- * var divNodes = document.getElementsByTagName("div");
- * divNodes = [].slice.call( divNodes, 0 );
- *
- * //output: 3(假定有3个div)
- * console.log( divNodes.length );
- *
- * var nodes = UE.dom.domUtils.filterNodeList( divNodes, function ( node ) {
- * return node.tagName.toLowerCase() === 'div';
- * }, true );
- *
- * //output: 3
- * console.log( nodes.length );
- *
- * var node = UE.dom.domUtils.filterNodeList( divNodes, function ( node ) {
- * return node.tagName.toLowerCase() === 'div';
- * }, false );
- *
- * //output: div
- * console.log( node.nodeName );
- * ```
- */
- filterNodeList : function(nodelist,filter,forAll){
- var results = [];
- if(!utils .isFunction(filter)){
- var str = filter;
- filter = function(n){
- return utils.indexOf(utils.isArray(str) ? str:str.split(' '), n.tagName.toLowerCase()) != -1
- };
- }
- utils.each(nodelist,function(n){
- filter(n) && results.push(n)
- });
- return results.length == 0 ? null : results.length == 1 || !forAll ? results[0] : results
- },
-
- /**
- * 查询给定的range选区是否在给定的node节点内,且在该节点的最末尾
- * @method isInNodeEndBoundary
- * @param { UE.dom.Range } rng 需要判断的range对象, 该对象的startContainer不能为NULL
- * @param node 需要检测的节点对象
- * @return { Number } 如果给定的选取range对象是在node内部的最末端, 则返回1, 否则返回0
- */
- isInNodeEndBoundary : function (rng,node){
- var start = rng.startContainer;
- if(start.nodeType == 3 && rng.startOffset != start.nodeValue.length){
- return 0;
- }
- if(start.nodeType == 1 && rng.startOffset != start.childNodes.length){
- return 0;
- }
- while(start !== node){
- if(start.nextSibling){
- return 0
- };
- start = start.parentNode;
- }
- return 1;
- },
- isBoundaryNode : function (node,dir){
- var tmp;
- while(!domUtils.isBody(node)){
- tmp = node;
- node = node.parentNode;
- if(tmp !== node[dir]){
- return false;
- }
- }
- return true;
- },
- fillHtml : browser.ie11below ? ' ' : '
'
-};
-var fillCharReg = new RegExp(domUtils.fillChar, 'g');
-
-// core/Range.js
-/**
- * Range封装
- * @file
- * @module UE.dom
- * @class Range
- * @since 1.2.6.1
- */
-
-/**
- * dom操作封装
- * @unfile
- * @module UE.dom
- */
-
-/**
- * Range实现类,本类是UEditor底层核心类,封装不同浏览器之间的Range操作。
- * @unfile
- * @module UE.dom
- * @class Range
- */
-
-
-(function () {
- var guid = 0,
- fillChar = domUtils.fillChar,
- fillData;
-
- /**
- * 更新range的collapse状态
- * @param {Range} range range对象
- */
- function updateCollapse(range) {
- range.collapsed =
- range.startContainer && range.endContainer &&
- range.startContainer === range.endContainer &&
- range.startOffset == range.endOffset;
- }
-
- function selectOneNode(rng){
- return !rng.collapsed && rng.startContainer.nodeType == 1 && rng.startContainer === rng.endContainer && rng.endOffset - rng.startOffset == 1
- }
- function setEndPoint(toStart, node, offset, range) {
- //如果node是自闭合标签要处理
- if (node.nodeType == 1 && (dtd.$empty[node.tagName] || dtd.$nonChild[node.tagName])) {
- offset = domUtils.getNodeIndex(node) + (toStart ? 0 : 1);
- node = node.parentNode;
- }
- if (toStart) {
- range.startContainer = node;
- range.startOffset = offset;
- if (!range.endContainer) {
- range.collapse(true);
- }
- } else {
- range.endContainer = node;
- range.endOffset = offset;
- if (!range.startContainer) {
- range.collapse(false);
- }
- }
- updateCollapse(range);
- return range;
- }
-
- function execContentsAction(range, action) {
- //调整边界
- //range.includeBookmark();
- var start = range.startContainer,
- end = range.endContainer,
- startOffset = range.startOffset,
- endOffset = range.endOffset,
- doc = range.document,
- frag = doc.createDocumentFragment(),
- tmpStart, tmpEnd;
- if (start.nodeType == 1) {
- start = start.childNodes[startOffset] || (tmpStart = start.appendChild(doc.createTextNode('')));
- }
- if (end.nodeType == 1) {
- end = end.childNodes[endOffset] || (tmpEnd = end.appendChild(doc.createTextNode('')));
- }
- if (start === end && start.nodeType == 3) {
- frag.appendChild(doc.createTextNode(start.substringData(startOffset, endOffset - startOffset)));
- //is not clone
- if (action) {
- start.deleteData(startOffset, endOffset - startOffset);
- range.collapse(true);
- }
- return frag;
- }
- var current, currentLevel, clone = frag,
- startParents = domUtils.findParents(start, true), endParents = domUtils.findParents(end, true);
- for (var i = 0; startParents[i] == endParents[i];) {
- i++;
- }
- for (var j = i, si; si = startParents[j]; j++) {
- current = si.nextSibling;
- if (si == start) {
- if (!tmpStart) {
- if (range.startContainer.nodeType == 3) {
- clone.appendChild(doc.createTextNode(start.nodeValue.slice(startOffset)));
- //is not clone
- if (action) {
- start.deleteData(startOffset, start.nodeValue.length - startOffset);
- }
- } else {
- clone.appendChild(!action ? start.cloneNode(true) : start);
- }
- }
- } else {
- currentLevel = si.cloneNode(false);
- clone.appendChild(currentLevel);
- }
- while (current) {
- if (current === end || current === endParents[j]) {
- break;
- }
- si = current.nextSibling;
- clone.appendChild(!action ? current.cloneNode(true) : current);
- current = si;
- }
- clone = currentLevel;
- }
- clone = frag;
- if (!startParents[i]) {
- clone.appendChild(startParents[i - 1].cloneNode(false));
- clone = clone.firstChild;
- }
- for (var j = i, ei; ei = endParents[j]; j++) {
- current = ei.previousSibling;
- if (ei == end) {
- if (!tmpEnd && range.endContainer.nodeType == 3) {
- clone.appendChild(doc.createTextNode(end.substringData(0, endOffset)));
- //is not clone
- if (action) {
- end.deleteData(0, endOffset);
- }
- }
- } else {
- currentLevel = ei.cloneNode(false);
- clone.appendChild(currentLevel);
- }
- //如果两端同级,右边第一次已经被开始做了
- if (j != i || !startParents[i]) {
- while (current) {
- if (current === start) {
- break;
- }
- ei = current.previousSibling;
- clone.insertBefore(!action ? current.cloneNode(true) : current, clone.firstChild);
- current = ei;
- }
- }
- clone = currentLevel;
- }
- if (action) {
- range.setStartBefore(!endParents[i] ? endParents[i - 1] : !startParents[i] ? startParents[i - 1] : endParents[i]).collapse(true);
- }
- tmpStart && domUtils.remove(tmpStart);
- tmpEnd && domUtils.remove(tmpEnd);
- return frag;
- }
-
- /**
- * 创建一个跟document绑定的空的Range实例
- * @constructor
- * @param { Document } document 新建的选区所属的文档对象
- */
-
- /**
- * @property { Node } startContainer 当前Range的开始边界的容器节点, 可以是一个元素节点或者是文本节点
- */
-
- /**
- * @property { Node } startOffset 当前Range的开始边界容器节点的偏移量, 如果是元素节点,
- * 该值就是childNodes中的第几个节点, 如果是文本节点就是文本内容的第几个字符
- */
-
- /**
- * @property { Node } endContainer 当前Range的结束边界的容器节点, 可以是一个元素节点或者是文本节点
- */
-
- /**
- * @property { Node } endOffset 当前Range的结束边界容器节点的偏移量, 如果是元素节点,
- * 该值就是childNodes中的第几个节点, 如果是文本节点就是文本内容的第几个字符
- */
-
- /**
- * @property { Boolean } collapsed 当前Range是否闭合
- * @default true
- * @remind Range是闭合的时候, startContainer === endContainer && startOffset === endOffset
- */
-
- /**
- * @property { Document } document 当前Range所属的Document对象
- * @remind 不同range的的document属性可以是不同的
- */
- var Range = dom.Range = function (document) {
- var me = this;
- me.startContainer =
- me.startOffset =
- me.endContainer =
- me.endOffset = null;
- me.document = document;
- me.collapsed = true;
- };
-
- /**
- * 删除fillData
- * @param doc
- * @param excludeNode
- */
- function removeFillData(doc, excludeNode) {
- try {
- if (fillData && domUtils.inDoc(fillData, doc)) {
- if (!fillData.nodeValue.replace(fillCharReg, '').length) {
- var tmpNode = fillData.parentNode;
- domUtils.remove(fillData);
- while (tmpNode && domUtils.isEmptyInlineElement(tmpNode) &&
- //safari的contains有bug
- (browser.safari ? !(domUtils.getPosition(tmpNode,excludeNode) & domUtils.POSITION_CONTAINS) : !tmpNode.contains(excludeNode))
- ) {
- fillData = tmpNode.parentNode;
- domUtils.remove(tmpNode);
- tmpNode = fillData;
- }
- } else {
- fillData.nodeValue = fillData.nodeValue.replace(fillCharReg, '');
- }
- }
- } catch (e) {
- }
- }
-
- /**
- * @param node
- * @param dir
- */
- function mergeSibling(node, dir) {
- var tmpNode;
- node = node[dir];
- while (node && domUtils.isFillChar(node)) {
- tmpNode = node[dir];
- domUtils.remove(node);
- node = tmpNode;
- }
- }
-
- Range.prototype = {
-
- /**
- * 克隆选区的内容到一个DocumentFragment里
- * @method cloneContents
- * @return { DocumentFragment | NULL } 如果选区是闭合的将返回null, 否则, 返回包含所clone内容的DocumentFragment元素
- * @example
- * ```html
- *
- *
- * xx[xxx]x
- *
- *
- *
- * ```
- */
- cloneContents:function () {
- return this.collapsed ? null : execContentsAction(this, 0);
- },
-
- /**
- * 删除当前选区范围中的所有内容
- * @method deleteContents
- * @remind 执行完该操作后, 当前Range对象变成了闭合状态
- * @return { UE.dom.Range } 当前操作的Range对象
- * @example
- * ```html
- *
- *
- * xx[xxx]x
- *
- *
- *
- * ```
- */
- deleteContents:function () {
- var txt;
- if (!this.collapsed) {
- execContentsAction(this, 1);
- }
- if (browser.webkit) {
- txt = this.startContainer;
- if (txt.nodeType == 3 && !txt.nodeValue.length) {
- this.setStartBefore(txt).collapse(true);
- domUtils.remove(txt);
- }
- }
- return this;
- },
-
- /**
- * 将当前选区的内容提取到一个DocumentFragment里
- * @method extractContents
- * @remind 执行该操作后, 选区将变成闭合状态
- * @warning 执行该操作后, 原来选区所选中的内容将从dom树上剥离出来
- * @return { DocumentFragment } 返回包含所提取内容的DocumentFragment对象
- * @example
- * ```html
- *
- *
- * xx[xxx]x
- *
- *
- *
- */
- extractContents:function () {
- return this.collapsed ? null : execContentsAction(this, 2);
- },
-
- /**
- * 设置Range的开始容器节点和偏移量
- * @method setStart
- * @remind 如果给定的节点是元素节点,那么offset指的是其子元素中索引为offset的元素,
- * 如果是文本节点,那么offset指的是其文本内容的第offset个字符
- * @remind 如果提供的容器节点是一个不能包含子元素的节点, 则该选区的开始容器将被设置
- * 为该节点的父节点, 此时, 其距离开始容器的偏移量也变成了该节点在其父节点
- * 中的索引
- * @param { Node } node 将被设为当前选区开始边界容器的节点对象
- * @param { int } offset 选区的开始位置偏移量
- * @return { UE.dom.Range } 当前range对象
- * @example
- * ```html
- *
- * xxxxxxxxxxxxx[xxx]
- *
- *
- * ```
- * @example
- * ```html
- *
- * xxx[xx]x
- *
- *
- * ```
- */
- setStart:function (node, offset) {
- return setEndPoint(true, node, offset, this);
- },
-
- /**
- * 设置Range的结束容器和偏移量
- * @method setEnd
- * @param { Node } node 作为当前选区结束边界容器的节点对象
- * @param { int } offset 结束边界的偏移量
- * @see UE.dom.Range:setStart(Node,int)
- * @return { UE.dom.Range } 当前range对象
- */
- setEnd:function (node, offset) {
- return setEndPoint(false, node, offset, this);
- },
-
- /**
- * 将Range开始位置设置到node节点之后
- * @method setStartAfter
- * @remind 该操作将会把给定节点的父节点作为range的开始容器, 且偏移量是该节点在其父节点中的位置索引+1
- * @param { Node } node 选区的开始边界将紧接着该节点之后
- * @return { UE.dom.Range } 当前range对象
- * @example
- * ```html
- *
- * xxxxxxx[xxxx]
- *
- *
- * ```
- */
- setStartAfter:function (node) {
- return this.setStart(node.parentNode, domUtils.getNodeIndex(node) + 1);
- },
-
- /**
- * 将Range开始位置设置到node节点之前
- * @method setStartBefore
- * @remind 该操作将会把给定节点的父节点作为range的开始容器, 且偏移量是该节点在其父节点中的位置索引
- * @param { Node } node 新的选区开始位置在该节点之前
- * @see UE.dom.Range:setStartAfter(Node)
- * @return { UE.dom.Range } 当前range对象
- */
- setStartBefore:function (node) {
- return this.setStart(node.parentNode, domUtils.getNodeIndex(node));
- },
-
- /**
- * 将Range结束位置设置到node节点之后
- * @method setEndAfter
- * @remind 该操作将会把给定节点的父节点作为range的结束容器, 且偏移量是该节点在其父节点中的位置索引+1
- * @param { Node } node 目标节点
- * @see UE.dom.Range:setStartAfter(Node)
- * @return { UE.dom.Range } 当前range对象
- * @example
- * ```html
- *
- * [xxxxxxx]xxxx
- *
- *
- * ```
- */
- setEndAfter:function (node) {
- return this.setEnd(node.parentNode, domUtils.getNodeIndex(node) + 1);
- },
-
- /**
- * 将Range结束位置设置到node节点之前
- * @method setEndBefore
- * @remind 该操作将会把给定节点的父节点作为range的结束容器, 且偏移量是该节点在其父节点中的位置索引
- * @param { Node } node 目标节点
- * @see UE.dom.Range:setEndAfter(Node)
- * @return { UE.dom.Range } 当前range对象
- */
- setEndBefore:function (node) {
- return this.setEnd(node.parentNode, domUtils.getNodeIndex(node));
- },
-
- /**
- * 设置Range的开始位置到node节点内的第一个子节点之前
- * @method setStartAtFirst
- * @remind 选区的开始容器将变成给定的节点, 且偏移量为0
- * @remind 如果给定的节点是元素节点, 则该节点必须是允许包含子节点的元素。
- * @param { Node } node 目标节点
- * @see UE.dom.Range:setStartBefore(Node)
- * @return { UE.dom.Range } 当前range对象
- * @example
- * ```html
- *
- * xxxxx[xx]xxxx
- *
- *
- * ```
- */
- setStartAtFirst:function (node) {
- return this.setStart(node, 0);
- },
-
- /**
- * 设置Range的开始位置到node节点内的最后一个节点之后
- * @method setStartAtLast
- * @remind 选区的开始容器将变成给定的节点, 且偏移量为该节点的子节点数
- * @remind 如果给定的节点是元素节点, 则该节点必须是允许包含子节点的元素。
- * @param { Node } node 目标节点
- * @see UE.dom.Range:setStartAtFirst(Node)
- * @return { UE.dom.Range } 当前range对象
- */
- setStartAtLast:function (node) {
- return this.setStart(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length);
- },
-
- /**
- * 设置Range的结束位置到node节点内的第一个节点之前
- * @method setEndAtFirst
- * @param { Node } node 目标节点
- * @remind 选区的结束容器将变成给定的节点, 且偏移量为0
- * @remind node必须是一个元素节点, 且必须是允许包含子节点的元素。
- * @see UE.dom.Range:setStartAtFirst(Node)
- * @return { UE.dom.Range } 当前range对象
- */
- setEndAtFirst:function (node) {
- return this.setEnd(node, 0);
- },
-
- /**
- * 设置Range的结束位置到node节点内的最后一个节点之后
- * @method setEndAtLast
- * @param { Node } node 目标节点
- * @remind 选区的结束容器将变成给定的节点, 且偏移量为该节点的子节点数量
- * @remind node必须是一个元素节点, 且必须是允许包含子节点的元素。
- * @see UE.dom.Range:setStartAtFirst(Node)
- * @return { UE.dom.Range } 当前range对象
- */
- setEndAtLast:function (node) {
- return this.setEnd(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length);
- },
-
- /**
- * 选中给定节点
- * @method selectNode
- * @remind 此时, 选区的开始容器和结束容器都是该节点的父节点, 其startOffset是该节点在父节点中的位置索引,
- * 而endOffset为startOffset+1
- * @param { Node } node 需要选中的节点
- * @return { UE.dom.Range } 当前range对象,此时的range仅包含当前给定的节点对象
- * @example
- * ```html
- *
- * xxxxx[xx]xxxx
- *
- *
- * ```
- */
- selectNode:function (node) {
- return this.setStartBefore(node).setEndAfter(node);
- },
-
- /**
- * 选中给定节点内部的所有节点
- * @method selectNodeContents
- * @remind 此时, 选区的开始容器和结束容器都是该节点, 其startOffset为0,
- * 而endOffset是该节点的子节点数。
- * @param { Node } node 目标节点, 当前range将包含该节点内的所有节点
- * @return { UE.dom.Range } 当前range对象, 此时range仅包含给定节点的所有子节点
- * @example
- * ```html
- *
- * xxxxx[xx]xxxx
- *
- *
- * ```
- */
- selectNodeContents:function (node) {
- return this.setStart(node, 0).setEndAtLast(node);
- },
-
- /**
- * clone当前Range对象
- * @method cloneRange
- * @remind 返回的range是一个全新的range对象, 其内部所有属性与当前被clone的range相同。
- * @return { UE.dom.Range } 当前range对象的一个副本
- */
- cloneRange:function () {
- var me = this;
- return new Range(me.document).setStart(me.startContainer, me.startOffset).setEnd(me.endContainer, me.endOffset);
-
- },
-
- /**
- * 向当前选区的结束处闭合选区
- * @method collapse
- * @return { UE.dom.Range } 当前range对象
- * @example
- * ```html
- *
- * xxxxx[xx]xxxx
- *
- *
- * ```
- */
-
- /**
- * 闭合当前选区,根据给定的toStart参数项决定是向当前选区开始处闭合还是向结束处闭合,
- * 如果toStart的值为true,则向开始位置闭合, 反之,向结束位置闭合。
- * @method collapse
- * @param { Boolean } toStart 是否向选区开始处闭合
- * @return { UE.dom.Range } 当前range对象,此时range对象处于闭合状态
- * @see UE.dom.Range:collapse()
- * @example
- * ```html
- *
- * xxxxx[xx]xxxx
- *
- *
- * ```
- */
- collapse:function (toStart) {
- var me = this;
- if (toStart) {
- me.endContainer = me.startContainer;
- me.endOffset = me.startOffset;
- } else {
- me.startContainer = me.endContainer;
- me.startOffset = me.endOffset;
- }
- me.collapsed = true;
- return me;
- },
-
- /**
- * 调整range的开始位置和结束位置,使其"收缩"到最小的位置
- * @method shrinkBoundary
- * @return { UE.dom.Range } 当前range对象
- * @example
- * ```html
- * xxxx[xxxxx] => xxxx[xxxxx]
- * ```
- *
- * @example
- * ```html
- *
- * x[xx]xxx
- *
- *
- * ```
- *
- * @example
- * ```html
- * [xxxxxxxxxxx] => [xxxxxxxxxxx]
- * ```
- */
-
- /**
- * 调整range的开始位置和结束位置,使其"收缩"到最小的位置,
- * 如果ignoreEnd的值为true,则忽略对结束位置的调整
- * @method shrinkBoundary
- * @param { Boolean } ignoreEnd 是否忽略对结束位置的调整
- * @return { UE.dom.Range } 当前range对象
- * @see UE.dom.domUtils.Range:shrinkBoundary()
- */
- shrinkBoundary:function (ignoreEnd) {
- var me = this, child,
- collapsed = me.collapsed;
- function check(node){
- return node.nodeType == 1 && !domUtils.isBookmarkNode(node) && !dtd.$empty[node.tagName] && !dtd.$nonChild[node.tagName]
- }
- while (me.startContainer.nodeType == 1 //是element
- && (child = me.startContainer.childNodes[me.startOffset]) //子节点也是element
- && check(child)) {
- me.setStart(child, 0);
- }
- if (collapsed) {
- return me.collapse(true);
- }
- if (!ignoreEnd) {
- while (me.endContainer.nodeType == 1//是element
- && me.endOffset > 0 //如果是空元素就退出 endOffset=0那么endOffst-1为负值,childNodes[endOffset]报错
- && (child = me.endContainer.childNodes[me.endOffset - 1]) //子节点也是element
- && check(child)) {
- me.setEnd(child, child.childNodes.length);
- }
- }
- return me;
- },
-
- /**
- * 获取离当前选区内包含的所有节点最近的公共祖先节点,
- * @method getCommonAncestor
- * @remind 返回的公共祖先节点一定不是range自身的容器节点, 但有可能是一个文本节点
- * @return { Node } 当前range对象内所有节点的公共祖先节点
- * @example
- * ```html
- * //选区示例
- * xxxx[xxx]xxxxxx
- *
- * ```
- */
-
- /**
- * 获取当前选区所包含的所有节点的公共祖先节点, 可以根据给定的参数 includeSelf 决定获取到
- * 的公共祖先节点是否可以是当前选区的startContainer或endContainer节点, 如果 includeSelf
- * 的取值为true, 则返回的节点可以是自身的容器节点, 否则, 则不能是容器节点
- * @method getCommonAncestor
- * @param { Boolean } includeSelf 是否允许获取到的公共祖先节点是当前range对象的容器节点
- * @return { Node } 当前range对象内所有节点的公共祖先节点
- * @see UE.dom.Range:getCommonAncestor()
- * @example
- * ```html
- *
- *
- *
- * xxxxxxxxx[xxx]xxxxxxxx
- *
- *
- *
- *
- * ```
- */
-
- /**
- * 获取当前选区所包含的所有节点的公共祖先节点, 可以根据给定的参数 includeSelf 决定获取到
- * 的公共祖先节点是否可以是当前选区的startContainer或endContainer节点, 如果 includeSelf
- * 的取值为true, 则返回的节点可以是自身的容器节点, 否则, 则不能是容器节点; 同时可以根据
- * ignoreTextNode 参数的取值决定是否忽略类型为文本节点的祖先节点。
- * @method getCommonAncestor
- * @param { Boolean } includeSelf 是否允许获取到的公共祖先节点是当前range对象的容器节点
- * @param { Boolean } ignoreTextNode 获取祖先节点的过程中是否忽略类型为文本节点的祖先节点
- * @return { Node } 当前range对象内所有节点的公共祖先节点
- * @see UE.dom.Range:getCommonAncestor()
- * @see UE.dom.Range:getCommonAncestor(Boolean)
- * @example
- * ```html
- *
- *
- *
- * xxxxxxxx[x]xxxxxxxxxxx
- *
- *
- *
- *
- * ```
- */
- getCommonAncestor:function (includeSelf, ignoreTextNode) {
- var me = this,
- start = me.startContainer,
- end = me.endContainer;
- if (start === end) {
- if (includeSelf && selectOneNode(this)) {
- start = start.childNodes[me.startOffset];
- if(start.nodeType == 1)
- return start;
- }
- //只有在上来就相等的情况下才会出现是文本的情况
- return ignoreTextNode && start.nodeType == 3 ? start.parentNode : start;
- }
- return domUtils.getCommonAncestor(start, end);
- },
-
- /**
- * 调整当前Range的开始和结束边界容器,如果是容器节点是文本节点,就调整到包含该文本节点的父节点上
- * @method trimBoundary
- * @remind 该操作有可能会引起文本节点被切开
- * @return { UE.dom.Range } 当前range对象
- * @example
- * ```html
- *
- * //选区示例
- * xxx[xxxxx]xxx
- *
- *
- * ```
- */
-
- /**
- * 调整当前Range的开始和结束边界容器,如果是容器节点是文本节点,就调整到包含该文本节点的父节点上,
- * 可以根据 ignoreEnd 参数的值决定是否调整对结束边界的调整
- * @method trimBoundary
- * @param { Boolean } ignoreEnd 是否忽略对结束边界的调整
- * @return { UE.dom.Range } 当前range对象
- * @example
- * ```html
- *
- * //选区示例
- * xxx[xxxxx]xxx
- *
- *
- * ```
- */
- trimBoundary:function (ignoreEnd) {
- this.txtToElmBoundary();
- var start = this.startContainer,
- offset = this.startOffset,
- collapsed = this.collapsed,
- end = this.endContainer;
- if (start.nodeType == 3) {
- if (offset == 0) {
- this.setStartBefore(start);
- } else {
- if (offset >= start.nodeValue.length) {
- this.setStartAfter(start);
- } else {
- var textNode = domUtils.split(start, offset);
- //跟新结束边界
- if (start === end) {
- this.setEnd(textNode, this.endOffset - offset);
- } else if (start.parentNode === end) {
- this.endOffset += 1;
- }
- this.setStartBefore(textNode);
- }
- }
- if (collapsed) {
- return this.collapse(true);
- }
- }
- if (!ignoreEnd) {
- offset = this.endOffset;
- end = this.endContainer;
- if (end.nodeType == 3) {
- if (offset == 0) {
- this.setEndBefore(end);
- } else {
- offset < end.nodeValue.length && domUtils.split(end, offset);
- this.setEndAfter(end);
- }
- }
- }
- return this;
- },
-
- /**
- * 如果选区在文本的边界上,就扩展选区到文本的父节点上, 如果当前选区是闭合的, 则什么也不做
- * @method txtToElmBoundary
- * @remind 该操作不会修改dom节点
- * @return { UE.dom.Range } 当前range对象
- */
-
- /**
- * 如果选区在文本的边界上,就扩展选区到文本的父节点上, 如果当前选区是闭合的, 则根据参数项
- * ignoreCollapsed 的值决定是否执行该调整
- * @method txtToElmBoundary
- * @param { Boolean } ignoreCollapsed 是否忽略选区的闭合状态, 如果该参数取值为true, 则
- * 不论选区是否闭合, 都会执行该操作, 反之, 则不会对闭合的选区执行该操作
- * @return { UE.dom.Range } 当前range对象
- */
- txtToElmBoundary:function (ignoreCollapsed) {
- function adjust(r, c) {
- var container = r[c + 'Container'],
- offset = r[c + 'Offset'];
- if (container.nodeType == 3) {
- if (!offset) {
- r['set' + c.replace(/(\w)/, function (a) {
- return a.toUpperCase();
- }) + 'Before'](container);
- } else if (offset >= container.nodeValue.length) {
- r['set' + c.replace(/(\w)/, function (a) {
- return a.toUpperCase();
- }) + 'After' ](container);
- }
- }
- }
-
- if (ignoreCollapsed || !this.collapsed) {
- adjust(this, 'start');
- adjust(this, 'end');
- }
- return this;
- },
-
- /**
- * 在当前选区的开始位置前插入节点,新插入的节点会被该range包含
- * @method insertNode
- * @param { Node } node 需要插入的节点
- * @remind 插入的节点可以是一个DocumentFragment依次插入多个节点
- * @return { UE.dom.Range } 当前range对象
- */
- insertNode:function (node) {
- var first = node, length = 1;
- if (node.nodeType == 11) {
- first = node.firstChild;
- length = node.childNodes.length;
- }
- this.trimBoundary(true);
- var start = this.startContainer,
- offset = this.startOffset;
- var nextNode = start.childNodes[ offset ];
- if (nextNode) {
- start.insertBefore(node, nextNode);
- } else {
- start.appendChild(node);
- }
- if (first.parentNode === this.endContainer) {
- this.endOffset = this.endOffset + length;
- }
- return this.setStartBefore(first);
- },
-
- /**
- * 闭合选区到当前选区的开始位置, 并且定位光标到闭合后的位置
- * @method setCursor
- * @return { UE.dom.Range } 当前range对象
- * @see UE.dom.Range:collapse()
- */
-
- /**
- * 闭合选区,可以根据参数toEnd的值控制选区是向前闭合还是向后闭合, 并且定位光标到闭合后的位置。
- * @method setCursor
- * @param { Boolean } toEnd 是否向后闭合, 如果为true, 则闭合选区时, 将向结束容器方向闭合,
- * 反之,则向开始容器方向闭合
- * @return { UE.dom.Range } 当前range对象
- * @see UE.dom.Range:collapse(Boolean)
- */
- setCursor:function (toEnd, noFillData) {
- return this.collapse(!toEnd).select(noFillData);
- },
-
- /**
- * 创建当前range的一个书签,记录下当前range的位置,方便当dom树改变时,还能找回原来的选区位置
- * @method createBookmark
- * @param { Boolean } serialize 控制返回的标记位置是对当前位置的引用还是ID,如果该值为true,则
- * 返回标记位置的ID, 反之则返回标记位置节点的引用
- * @return { Object } 返回一个书签记录键值对, 其包含的key有: start => 开始标记的ID或者引用,
- * end => 结束标记的ID或引用, id => 当前标记的类型, 如果为true,则表示
- * 返回的记录的类型为ID, 反之则为引用
- */
- createBookmark:function (serialize, same) {
- var endNode,
- startNode = this.document.createElement('span');
- startNode.style.cssText = 'display:none;line-height:0px;';
- startNode.appendChild(this.document.createTextNode('\u200D'));
- startNode.id = '_baidu_bookmark_start_' + (same ? '' : guid++);
-
- if (!this.collapsed) {
- endNode = startNode.cloneNode(true);
- endNode.id = '_baidu_bookmark_end_' + (same ? '' : guid++);
- }
- this.insertNode(startNode);
- if (endNode) {
- this.collapse().insertNode(endNode).setEndBefore(endNode);
- }
- this.setStartAfter(startNode);
- return {
- start:serialize ? startNode.id : startNode,
- end:endNode ? serialize ? endNode.id : endNode : null,
- id:serialize
- }
- },
-
- /**
- * 调整当前range的边界到书签位置,并删除该书签对象所标记的位置内的节点
- * @method moveToBookmark
- * @param { BookMark } bookmark createBookmark所创建的标签对象
- * @return { UE.dom.Range } 当前range对象
- * @see UE.dom.Range:createBookmark(Boolean)
- */
- moveToBookmark:function (bookmark) {
- var start = bookmark.id ? this.document.getElementById(bookmark.start) : bookmark.start,
- end = bookmark.end && bookmark.id ? this.document.getElementById(bookmark.end) : bookmark.end;
- this.setStartBefore(start);
- domUtils.remove(start);
- if (end) {
- this.setEndBefore(end);
- domUtils.remove(end);
- } else {
- this.collapse(true);
- }
- return this;
- },
-
- /**
- * 调整range的边界,使其"放大"到最近的父节点
- * @method enlarge
- * @remind 会引起选区的变化
- * @return { UE.dom.Range } 当前range对象
- */
-
- /**
- * 调整range的边界,使其"放大"到最近的父节点,根据参数 toBlock 的取值, 可以
- * 要求扩大之后的父节点是block节点
- * @method enlarge
- * @param { Boolean } toBlock 是否要求扩大之后的父节点必须是block节点
- * @return { UE.dom.Range } 当前range对象
- */
- enlarge:function (toBlock, stopFn) {
- var isBody = domUtils.isBody,
- pre, node, tmp = this.document.createTextNode('');
- if (toBlock) {
- node = this.startContainer;
- if (node.nodeType == 1) {
- if (node.childNodes[this.startOffset]) {
- pre = node = node.childNodes[this.startOffset]
- } else {
- node.appendChild(tmp);
- pre = node = tmp;
- }
- } else {
- pre = node;
- }
- while (1) {
- if (domUtils.isBlockElm(node)) {
- node = pre;
- while ((pre = node.previousSibling) && !domUtils.isBlockElm(pre)) {
- node = pre;
- }
- this.setStartBefore(node);
- break;
- }
- pre = node;
- node = node.parentNode;
- }
- node = this.endContainer;
- if (node.nodeType == 1) {
- if (pre = node.childNodes[this.endOffset]) {
- node.insertBefore(tmp, pre);
- } else {
- node.appendChild(tmp);
- }
- pre = node = tmp;
- } else {
- pre = node;
- }
- while (1) {
- if (domUtils.isBlockElm(node)) {
- node = pre;
- while ((pre = node.nextSibling) && !domUtils.isBlockElm(pre)) {
- node = pre;
- }
- this.setEndAfter(node);
- break;
- }
- pre = node;
- node = node.parentNode;
- }
- if (tmp.parentNode === this.endContainer) {
- this.endOffset--;
- }
- domUtils.remove(tmp);
- }
-
- // 扩展边界到最大
- if (!this.collapsed) {
- while (this.startOffset == 0) {
- if (stopFn && stopFn(this.startContainer)) {
- break;
- }
- if (isBody(this.startContainer)) {
- break;
- }
- this.setStartBefore(this.startContainer);
- }
- while (this.endOffset == (this.endContainer.nodeType == 1 ? this.endContainer.childNodes.length : this.endContainer.nodeValue.length)) {
- if (stopFn && stopFn(this.endContainer)) {
- break;
- }
- if (isBody(this.endContainer)) {
- break;
- }
- this.setEndAfter(this.endContainer);
- }
- }
- return this;
- },
- enlargeToBlockElm:function(ignoreEnd){
- while(!domUtils.isBlockElm(this.startContainer)){
- this.setStartBefore(this.startContainer);
- }
- if(!ignoreEnd){
- while(!domUtils.isBlockElm(this.endContainer)){
- this.setEndAfter(this.endContainer);
- }
- }
- return this;
- },
- /**
- * 调整Range的边界,使其"缩小"到最合适的位置
- * @method adjustmentBoundary
- * @return { UE.dom.Range } 当前range对象
- * @see UE.dom.Range:shrinkBoundary()
- */
- adjustmentBoundary:function () {
- if (!this.collapsed) {
- while (!domUtils.isBody(this.startContainer) &&
- this.startOffset == this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length &&
- this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
- ) {
-
- this.setStartAfter(this.startContainer);
- }
- while (!domUtils.isBody(this.endContainer) && !this.endOffset &&
- this.endContainer[this.endContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
- ) {
- this.setEndBefore(this.endContainer);
- }
- }
- return this;
- },
-
- /**
- * 给range选区中的内容添加给定的inline标签
- * @method applyInlineStyle
- * @param { String } tagName 需要添加的标签名
- * @example
- * ```html
- * xxxx[xxxx]x
==> range.applyInlineStyle("strong") ==> xxxx[xxxx]x
- * ```
- */
-
- /**
- * 给range选区中的内容添加给定的inline标签, 并且为标签附加上一些初始化属性。
- * @method applyInlineStyle
- * @param { String } tagName 需要添加的标签名
- * @param { Object } attrs 跟随新添加的标签的属性
- * @return { UE.dom.Range } 当前选区
- * @example
- * ```html
- * xxxx[xxxx]x
- *
- * ==>
- *
- *
- * range.applyInlineStyle("strong",{"style":"font-size:12px"})
- *
- * ==>
- *
- * xxxx[xxxx]x
- * ```
- */
- applyInlineStyle:function (tagName, attrs, list) {
- if (this.collapsed)return this;
- this.trimBoundary().enlarge(false,
- function (node) {
- return node.nodeType == 1 && domUtils.isBlockElm(node)
- }).adjustmentBoundary();
- var bookmark = this.createBookmark(),
- end = bookmark.end,
- filterFn = function (node) {
- return node.nodeType == 1 ? node.tagName.toLowerCase() != 'br' : !domUtils.isWhitespace(node);
- },
- current = domUtils.getNextDomNode(bookmark.start, false, filterFn),
- node,
- pre,
- range = this.cloneRange();
- while (current && (domUtils.getPosition(current, end) & domUtils.POSITION_PRECEDING)) {
- if (current.nodeType == 3 || dtd[tagName][current.tagName]) {
- range.setStartBefore(current);
- node = current;
- while (node && (node.nodeType == 3 || dtd[tagName][node.tagName]) && node !== end) {
- pre = node;
- node = domUtils.getNextDomNode(node, node.nodeType == 1, null, function (parent) {
- return dtd[tagName][parent.tagName];
- });
- }
- var frag = range.setEndAfter(pre).extractContents(), elm;
- if (list && list.length > 0) {
- var level, top;
- top = level = list[0].cloneNode(false);
- for (var i = 1, ci; ci = list[i++];) {
- level.appendChild(ci.cloneNode(false));
- level = level.firstChild;
- }
- elm = level;
- } else {
- elm = range.document.createElement(tagName);
- }
- if (attrs) {
- domUtils.setAttributes(elm, attrs);
- }
- elm.appendChild(frag);
- range.insertNode(list ? top : elm);
- //处理下滑线在a上的情况
- var aNode;
- if (tagName == 'span' && attrs.style && /text\-decoration/.test(attrs.style) && (aNode = domUtils.findParentByTagName(elm, 'a', true))) {
- domUtils.setAttributes(aNode, attrs);
- domUtils.remove(elm, true);
- elm = aNode;
- } else {
- domUtils.mergeSibling(elm);
- domUtils.clearEmptySibling(elm);
- }
- //去除子节点相同的
- domUtils.mergeChild(elm, attrs);
- current = domUtils.getNextDomNode(elm, false, filterFn);
- domUtils.mergeToParent(elm);
- if (node === end) {
- break;
- }
- } else {
- current = domUtils.getNextDomNode(current, true, filterFn);
- }
- }
- return this.moveToBookmark(bookmark);
- },
-
- /**
- * 移除当前选区内指定的inline标签,但保留其中的内容
- * @method removeInlineStyle
- * @param { String } tagName 需要移除的标签名
- * @return { UE.dom.Range } 当前的range对象
- * @example
- * ```html
- * xx[xxxxyyyzz]z => range.removeInlineStyle(["em"]) => xx[xxxxyyyzz]z
- * ```
- */
-
- /**
- * 移除当前选区内指定的一组inline标签,但保留其中的内容
- * @method removeInlineStyle
- * @param { Array } tagNameArr 需要移除的标签名的数组
- * @return { UE.dom.Range } 当前的range对象
- * @see UE.dom.Range:removeInlineStyle(String)
- */
- removeInlineStyle:function (tagNames) {
- if (this.collapsed)return this;
- tagNames = utils.isArray(tagNames) ? tagNames : [tagNames];
- this.shrinkBoundary().adjustmentBoundary();
- var start = this.startContainer, end = this.endContainer;
- while (1) {
- if (start.nodeType == 1) {
- if (utils.indexOf(tagNames, start.tagName.toLowerCase()) > -1) {
- break;
- }
- if (start.tagName.toLowerCase() == 'body') {
- start = null;
- break;
- }
- }
- start = start.parentNode;
- }
- while (1) {
- if (end.nodeType == 1) {
- if (utils.indexOf(tagNames, end.tagName.toLowerCase()) > -1) {
- break;
- }
- if (end.tagName.toLowerCase() == 'body') {
- end = null;
- break;
- }
- }
- end = end.parentNode;
- }
- var bookmark = this.createBookmark(),
- frag,
- tmpRange;
- if (start) {
- tmpRange = this.cloneRange().setEndBefore(bookmark.start).setStartBefore(start);
- frag = tmpRange.extractContents();
- tmpRange.insertNode(frag);
- domUtils.clearEmptySibling(start, true);
- start.parentNode.insertBefore(bookmark.start, start);
- }
- if (end) {
- tmpRange = this.cloneRange().setStartAfter(bookmark.end).setEndAfter(end);
- frag = tmpRange.extractContents();
- tmpRange.insertNode(frag);
- domUtils.clearEmptySibling(end, false, true);
- end.parentNode.insertBefore(bookmark.end, end.nextSibling);
- }
- var current = domUtils.getNextDomNode(bookmark.start, false, function (node) {
- return node.nodeType == 1;
- }), next;
- while (current && current !== bookmark.end) {
- next = domUtils.getNextDomNode(current, true, function (node) {
- return node.nodeType == 1;
- });
- if (utils.indexOf(tagNames, current.tagName.toLowerCase()) > -1) {
- domUtils.remove(current, true);
- }
- current = next;
- }
- return this.moveToBookmark(bookmark);
- },
-
- /**
- * 获取当前选中的自闭合的节点
- * @method getClosedNode
- * @return { Node | NULL } 如果当前选中的是自闭合节点, 则返回该节点, 否则返回NULL
- */
- getClosedNode:function () {
- var node;
- if (!this.collapsed) {
- var range = this.cloneRange().adjustmentBoundary().shrinkBoundary();
- if (selectOneNode(range)) {
- var child = range.startContainer.childNodes[range.startOffset];
- if (child && child.nodeType == 1 && (dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName])) {
- node = child;
- }
- }
- }
- return node;
- },
-
- /**
- * 在页面上高亮range所表示的选区
- * @method select
- * @return { UE.dom.Range } 返回当前Range对象
- */
- //这里不区分ie9以上,trace:3824
- select:browser.ie ? function (noFillData, textRange) {
- var nativeRange;
- if (!this.collapsed)
- this.shrinkBoundary();
- var node = this.getClosedNode();
- if (node && !textRange) {
- try {
- nativeRange = this.document.body.createControlRange();
- nativeRange.addElement(node);
- nativeRange.select();
- } catch (e) {}
- return this;
- }
- var bookmark = this.createBookmark(),
- start = bookmark.start,
- end;
- nativeRange = this.document.body.createTextRange();
- nativeRange.moveToElementText(start);
- nativeRange.moveStart('character', 1);
- if (!this.collapsed) {
- var nativeRangeEnd = this.document.body.createTextRange();
- end = bookmark.end;
- nativeRangeEnd.moveToElementText(end);
- nativeRange.setEndPoint('EndToEnd', nativeRangeEnd);
- } else {
- if (!noFillData && this.startContainer.nodeType != 3) {
- //使用|x固定住光标
- var tmpText = this.document.createTextNode(fillChar),
- tmp = this.document.createElement('span');
- tmp.appendChild(this.document.createTextNode(fillChar));
- start.parentNode.insertBefore(tmp, start);
- start.parentNode.insertBefore(tmpText, start);
- //当点b,i,u时,不能清除i上边的b
- removeFillData(this.document, tmpText);
- fillData = tmpText;
- mergeSibling(tmp, 'previousSibling');
- mergeSibling(start, 'nextSibling');
- nativeRange.moveStart('character', -1);
- nativeRange.collapse(true);
- }
- }
- this.moveToBookmark(bookmark);
- tmp && domUtils.remove(tmp);
- //IE在隐藏状态下不支持range操作,catch一下
- try {
- nativeRange.select();
- } catch (e) {
- }
- return this;
- } : function (notInsertFillData) {
- function checkOffset(rng){
-
- function check(node,offset,dir){
- if(node.nodeType == 3 && node.nodeValue.length < offset){
- rng[dir + 'Offset'] = node.nodeValue.length
- }
- }
- check(rng.startContainer,rng.startOffset,'start');
- check(rng.endContainer,rng.endOffset,'end');
- }
- var win = domUtils.getWindow(this.document),
- sel = win.getSelection(),
- txtNode;
- //FF下关闭自动长高时滚动条在关闭dialog时会跳
- //ff下如果不body.focus将不能定位闭合光标到编辑器内
- browser.gecko ? this.document.body.focus() : win.focus();
- if (sel) {
- sel.removeAllRanges();
- // trace:870 chrome/safari后边是br对于闭合得range不能定位 所以去掉了判断
- // this.startContainer.nodeType != 3 &&! ((child = this.startContainer.childNodes[this.startOffset]) && child.nodeType == 1 && child.tagName == 'BR'
- if (this.collapsed && !notInsertFillData) {
-// //opear如果没有节点接着,原生的不能够定位,不能在body的第一级插入空白节点
-// if (notInsertFillData && browser.opera && !domUtils.isBody(this.startContainer) && this.startContainer.nodeType == 1) {
-// var tmp = this.document.createTextNode('');
-// this.insertNode(tmp).setStart(tmp, 0).collapse(true);
-// }
-//
- //处理光标落在文本节点的情况
- //处理以下的情况
- //|xxxx
- //xxxx|xxxx
- //xxxx|
- var start = this.startContainer,child = start;
- if(start.nodeType == 1){
- child = start.childNodes[this.startOffset];
-
- }
- if( !(start.nodeType == 3 && this.startOffset) &&
- (child ?
- (!child.previousSibling || child.previousSibling.nodeType != 3)
- :
- (!start.lastChild || start.lastChild.nodeType != 3)
- )
- ){
- txtNode = this.document.createTextNode(fillChar);
- //跟着前边走
- this.insertNode(txtNode);
- removeFillData(this.document, txtNode);
- mergeSibling(txtNode, 'previousSibling');
- mergeSibling(txtNode, 'nextSibling');
- fillData = txtNode;
- this.setStart(txtNode, browser.webkit ? 1 : 0).collapse(true);
- }
- }
- var nativeRange = this.document.createRange();
- if(this.collapsed && browser.opera && this.startContainer.nodeType == 1){
- var child = this.startContainer.childNodes[this.startOffset];
- if(!child){
- //往前靠拢
- child = this.startContainer.lastChild;
- if( child && domUtils.isBr(child)){
- this.setStartBefore(child).collapse(true);
- }
- }else{
- //向后靠拢
- while(child && domUtils.isBlockElm(child)){
- if(child.nodeType == 1 && child.childNodes[0]){
- child = child.childNodes[0]
- }else{
- break;
- }
- }
- child && this.setStartBefore(child).collapse(true)
- }
-
- }
- //是createAddress最后一位算的不准,现在这里进行微调
- checkOffset(this);
- nativeRange.setStart(this.startContainer, this.startOffset);
- nativeRange.setEnd(this.endContainer, this.endOffset);
- sel.addRange(nativeRange);
- }
- return this;
- },
-
- /**
- * 滚动到当前range开始的位置
- * @method scrollToView
- * @param { Window } win 当前range对象所属的window对象
- * @return { UE.dom.Range } 当前Range对象
- */
-
- /**
- * 滚动到距离当前range开始位置 offset 的位置处
- * @method scrollToView
- * @param { Window } win 当前range对象所属的window对象
- * @param { Number } offset 距离range开始位置处的偏移量, 如果为正数, 则向下偏移, 反之, 则向上偏移
- * @return { UE.dom.Range } 当前Range对象
- */
- scrollToView:function (win, offset) {
- win = win ? window : domUtils.getWindow(this.document);
- var me = this,
- span = me.document.createElement('span');
- //trace:717
- span.innerHTML = ' ';
- me.cloneRange().insertNode(span);
- domUtils.scrollToView(span, win, offset);
- domUtils.remove(span);
- return me;
- },
-
- /**
- * 判断当前选区内容是否占位符
- * @private
- * @method inFillChar
- * @return { Boolean } 如果是占位符返回true,否则返回false
- */
- inFillChar : function(){
- var start = this.startContainer;
- if(this.collapsed && start.nodeType == 3
- && start.nodeValue.replace(new RegExp('^' + domUtils.fillChar),'').length + 1 == start.nodeValue.length
- ){
- return true;
- }
- return false;
- },
-
- /**
- * 保存
- * @method createAddress
- * @private
- * @return { Boolean } 返回开始和结束的位置
- * @example
- * ```html
- *
- *
- * aaaa
- *
- *
- * bbbb
- *
- *
- *
- *
- *
- *
- * ```
- */
- createAddress : function(ignoreEnd,ignoreTxt){
- var addr = {},me = this;
-
- function getAddress(isStart){
- var node = isStart ? me.startContainer : me.endContainer;
- var parents = domUtils.findParents(node,true,function(node){return !domUtils.isBody(node)}),
- addrs = [];
- for(var i = 0,ci;ci = parents[i++];){
- addrs.push(domUtils.getNodeIndex(ci,ignoreTxt));
- }
- var firstIndex = 0;
-
- if(ignoreTxt){
- if(node.nodeType == 3){
- var tmpNode = node.previousSibling;
- while(tmpNode && tmpNode.nodeType == 3){
- firstIndex += tmpNode.nodeValue.replace(fillCharReg,'').length;
- tmpNode = tmpNode.previousSibling;
- }
- firstIndex += (isStart ? me.startOffset : me.endOffset)// - (fillCharReg.test(node.nodeValue) ? 1 : 0 )
- }else{
- node = node.childNodes[ isStart ? me.startOffset : me.endOffset];
- if(node){
- firstIndex = domUtils.getNodeIndex(node,ignoreTxt);
- }else{
- node = isStart ? me.startContainer : me.endContainer;
- var first = node.firstChild;
- while(first){
- if(domUtils.isFillChar(first)){
- first = first.nextSibling;
- continue;
- }
- firstIndex++;
- if(first.nodeType == 3){
- while( first && first.nodeType == 3){
- first = first.nextSibling;
- }
- }else{
- first = first.nextSibling;
- }
- }
- }
- }
-
- }else{
- firstIndex = isStart ? domUtils.isFillChar(node) ? 0 : me.startOffset : me.endOffset
- }
- if(firstIndex < 0){
- firstIndex = 0;
- }
- addrs.push(firstIndex);
- return addrs;
- }
- addr.startAddress = getAddress(true);
- if(!ignoreEnd){
- addr.endAddress = me.collapsed ? [].concat(addr.startAddress) : getAddress();
- }
- return addr;
- },
-
- /**
- * 保存
- * @method createAddress
- * @private
- * @return { Boolean } 返回开始和结束的位置
- * @example
- * ```html
- *
- *
- * aaaa
- *
- *
- * bbbb
- *
- *
- *
- *
- *
- *
- * ```
- */
- moveToAddress : function(addr,ignoreEnd){
- var me = this;
- function getNode(address,isStart){
- var tmpNode = me.document.body,
- parentNode,offset;
- for(var i= 0,ci,l=address.length;i
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- *
- * ```
- */
-
- /**
- * 遍历range内的节点。
- * 每当遍历一个节点时, 都会执行参数项 doFn 指定的函数, 该函数的接受当前遍历的节点
- * 作为其参数。
- * 可以通过参数项 filterFn 来指定一个过滤器, 只有符合该过滤器过滤规则的节点才会触
- * 发doFn函数的执行
- * @method traversal
- * @param { Function } doFn 对每个遍历的节点要执行的方法, 该方法接受当前遍历的节点作为其参数
- * @param { Function } filterFn 过滤器, 该函数接受当前遍历的节点作为参数, 如果该节点满足过滤
- * 规则, 请返回true, 该节点会触发doFn, 否则, 请返回false, 则该节点不
- * 会触发doFn。
- * @return { UE.dom.Range } 当前range对象
- * @see UE.dom.Range:traversal(Function)
- * @example
- * ```html
- *
- *
- *
- *
- * ```
- */
- traversal:function(doFn,filterFn){
- if (this.collapsed)
- return this;
- var bookmark = this.createBookmark(),
- end = bookmark.end,
- current = domUtils.getNextDomNode(bookmark.start, false, filterFn);
- while (current && current !== end && (domUtils.getPosition(current, end) & domUtils.POSITION_PRECEDING)) {
- var tmpNode = domUtils.getNextDomNode(current,false,filterFn);
- doFn(current);
- current = tmpNode;
- }
- return this.moveToBookmark(bookmark);
- }
- };
-})();
-
-// core/Selection.js
-/**
- * 选集
- * @file
- * @module UE.dom
- * @class Selection
- * @since 1.2.6.1
- */
-
-/**
- * 选区集合
- * @unfile
- * @module UE.dom
- * @class Selection
- */
-(function () {
-
- function getBoundaryInformation( range, start ) {
- var getIndex = domUtils.getNodeIndex;
- range = range.duplicate();
- range.collapse( start );
- var parent = range.parentElement();
- //如果节点里没有子节点,直接退出
- if ( !parent.hasChildNodes() ) {
- return {container:parent, offset:0};
- }
- var siblings = parent.children,
- child,
- testRange = range.duplicate(),
- startIndex = 0, endIndex = siblings.length - 1, index = -1,
- distance;
- while ( startIndex <= endIndex ) {
- index = Math.floor( (startIndex + endIndex) / 2 );
- child = siblings[index];
- testRange.moveToElementText( child );
- var position = testRange.compareEndPoints( 'StartToStart', range );
- if ( position > 0 ) {
- endIndex = index - 1;
- } else if ( position < 0 ) {
- startIndex = index + 1;
- } else {
- //trace:1043
- return {container:parent, offset:getIndex( child )};
- }
- }
- if ( index == -1 ) {
- testRange.moveToElementText( parent );
- testRange.setEndPoint( 'StartToStart', range );
- distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length;
- siblings = parent.childNodes;
- if ( !distance ) {
- child = siblings[siblings.length - 1];
- return {container:child, offset:child.nodeValue.length};
- }
-
- var i = siblings.length;
- while ( distance > 0 ){
- distance -= siblings[ --i ].nodeValue.length;
- }
- return {container:siblings[i], offset:-distance};
- }
- testRange.collapse( position > 0 );
- testRange.setEndPoint( position > 0 ? 'StartToStart' : 'EndToStart', range );
- distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length;
- if ( !distance ) {
- return dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName] ?
- {container:parent, offset:getIndex( child ) + (position > 0 ? 0 : 1)} :
- {container:child, offset:position > 0 ? 0 : child.childNodes.length}
- }
- while ( distance > 0 ) {
- try {
- var pre = child;
- child = child[position > 0 ? 'previousSibling' : 'nextSibling'];
- distance -= child.nodeValue.length;
- } catch ( e ) {
- return {container:parent, offset:getIndex( pre )};
- }
- }
- return {container:child, offset:position > 0 ? -distance : child.nodeValue.length + distance}
- }
-
- /**
- * 将ieRange转换为Range对象
- * @param {Range} ieRange ieRange对象
- * @param {Range} range Range对象
- * @return {Range} range 返回转换后的Range对象
- */
- function transformIERangeToRange( ieRange, range ) {
- if ( ieRange.item ) {
- range.selectNode( ieRange.item( 0 ) );
- } else {
- var bi = getBoundaryInformation( ieRange, true );
- range.setStart( bi.container, bi.offset );
- if ( ieRange.compareEndPoints( 'StartToEnd', ieRange ) != 0 ) {
- bi = getBoundaryInformation( ieRange, false );
- range.setEnd( bi.container, bi.offset );
- }
- }
- return range;
- }
-
- /**
- * 获得ieRange
- * @param {Selection} sel Selection对象
- * @return {ieRange} 得到ieRange
- */
- function _getIERange( sel ) {
- var ieRange;
- //ie下有可能报错
- try {
- ieRange = sel.getNative().createRange();
- } catch ( e ) {
- return null;
- }
- var el = ieRange.item ? ieRange.item( 0 ) : ieRange.parentElement();
- if ( ( el.ownerDocument || el ) === sel.document ) {
- return ieRange;
- }
- return null;
- }
-
- var Selection = dom.Selection = function ( doc ) {
- var me = this, iframe;
- me.document = doc;
- if ( browser.ie9below ) {
- iframe = domUtils.getWindow( doc ).frameElement;
- domUtils.on( iframe, 'beforedeactivate', function () {
- me._bakIERange = me.getIERange();
- } );
- domUtils.on( iframe, 'activate', function () {
- try {
- if ( !_getIERange( me ) && me._bakIERange ) {
- me._bakIERange.select();
- }
- } catch ( ex ) {
- }
- me._bakIERange = null;
- } );
- }
- iframe = doc = null;
- };
-
- Selection.prototype = {
-
- rangeInBody : function(rng,txtRange){
- var node = browser.ie9below || txtRange ? rng.item ? rng.item() : rng.parentElement() : rng.startContainer;
-
- return node === this.document.body || domUtils.inDoc(node,this.document);
- },
-
- /**
- * 获取原生seleciton对象
- * @method getNative
- * @return { Object } 获得selection对象
- * @example
- * ```javascript
- * editor.selection.getNative();
- * ```
- */
- getNative:function () {
- var doc = this.document;
- try {
- return !doc ? null : browser.ie9below ? doc.selection : domUtils.getWindow( doc ).getSelection();
- } catch ( e ) {
- return null;
- }
- },
-
- /**
- * 获得ieRange
- * @method getIERange
- * @return { Object } 返回ie原生的Range
- * @example
- * ```javascript
- * editor.selection.getIERange();
- * ```
- */
- getIERange:function () {
- var ieRange = _getIERange( this );
- if ( !ieRange ) {
- if ( this._bakIERange ) {
- return this._bakIERange;
- }
- }
- return ieRange;
- },
-
- /**
- * 缓存当前选区的range和选区的开始节点
- * @method cache
- */
- cache:function () {
- this.clear();
- this._cachedRange = this.getRange();
- this._cachedStartElement = this.getStart();
- this._cachedStartElementPath = this.getStartElementPath();
- },
-
- /**
- * 获取选区开始位置的父节点到body
- * @method getStartElementPath
- * @return { Array } 返回父节点集合
- * @example
- * ```javascript
- * editor.selection.getStartElementPath();
- * ```
- */
- getStartElementPath:function () {
- if ( this._cachedStartElementPath ) {
- return this._cachedStartElementPath;
- }
- var start = this.getStart();
- if ( start ) {
- return domUtils.findParents( start, true, null, true )
- }
- return [];
- },
-
- /**
- * 清空缓存
- * @method clear
- */
- clear:function () {
- this._cachedStartElementPath = this._cachedRange = this._cachedStartElement = null;
- },
-
- /**
- * 编辑器是否得到了选区
- * @method isFocus
- */
- isFocus:function () {
- try {
- if(browser.ie9below){
-
- var nativeRange = _getIERange(this);
- return !!(nativeRange && this.rangeInBody(nativeRange));
- }else{
- return !!this.getNative().rangeCount;
- }
- } catch ( e ) {
- return false;
- }
-
- },
-
- /**
- * 获取选区对应的Range
- * @method getRange
- * @return { Object } 得到Range对象
- * @example
- * ```javascript
- * editor.selection.getRange();
- * ```
- */
- getRange:function () {
- var me = this;
- function optimze( range ) {
- var child = me.document.body.firstChild,
- collapsed = range.collapsed;
- while ( child && child.firstChild ) {
- range.setStart( child, 0 );
- child = child.firstChild;
- }
- if ( !range.startContainer ) {
- range.setStart( me.document.body, 0 )
- }
- if ( collapsed ) {
- range.collapse( true );
- }
- }
-
- if ( me._cachedRange != null ) {
- return this._cachedRange;
- }
- var range = new baidu.editor.dom.Range( me.document );
-
- if ( browser.ie9below ) {
- var nativeRange = me.getIERange();
- if ( nativeRange ) {
- //备份的_bakIERange可能已经实效了,dom树发生了变化比如从源码模式切回来,所以try一下,实效就放到body开始位置
- try{
- transformIERangeToRange( nativeRange, range );
- }catch(e){
- optimze( range );
- }
-
- } else {
- optimze( range );
- }
- } else {
- var sel = me.getNative();
- if ( sel && sel.rangeCount ) {
- var firstRange = sel.getRangeAt( 0 );
- var lastRange = sel.getRangeAt( sel.rangeCount - 1 );
- range.setStart( firstRange.startContainer, firstRange.startOffset ).setEnd( lastRange.endContainer, lastRange.endOffset );
- if ( range.collapsed && domUtils.isBody( range.startContainer ) && !range.startOffset ) {
- optimze( range );
- }
- } else {
- //trace:1734 有可能已经不在dom树上了,标识的节点
- if ( this._bakRange && domUtils.inDoc( this._bakRange.startContainer, this.document ) ){
- return this._bakRange;
- }
- optimze( range );
- }
- }
- return this._bakRange = range;
- },
-
- /**
- * 获取开始元素,用于状态反射
- * @method getStart
- * @return { Element } 获得开始元素
- * @example
- * ```javascript
- * editor.selection.getStart();
- * ```
- */
- getStart:function () {
- if ( this._cachedStartElement ) {
- return this._cachedStartElement;
- }
- var range = browser.ie9below ? this.getIERange() : this.getRange(),
- tmpRange,
- start, tmp, parent;
- if ( browser.ie9below ) {
- if ( !range ) {
- //todo 给第一个值可能会有问题
- return this.document.body.firstChild;
- }
- //control元素
- if ( range.item ){
- return range.item( 0 );
- }
- tmpRange = range.duplicate();
- //修正ie下x[xx] 闭合后 x|xx
- tmpRange.text.length > 0 && tmpRange.moveStart( 'character', 1 );
- tmpRange.collapse( 1 );
- start = tmpRange.parentElement();
- parent = tmp = range.parentElement();
- while ( tmp = tmp.parentNode ) {
- if ( tmp == start ) {
- start = parent;
- break;
- }
- }
- } else {
- range.shrinkBoundary();
- start = range.startContainer;
- if ( start.nodeType == 1 && start.hasChildNodes() ){
- start = start.childNodes[Math.min( start.childNodes.length - 1, range.startOffset )];
- }
- if ( start.nodeType == 3 ){
- return start.parentNode;
- }
- }
- return start;
- },
-
- /**
- * 得到选区中的文本
- * @method getText
- * @return { String } 选区中包含的文本
- * @example
- * ```javascript
- * editor.selection.getText();
- * ```
- */
- getText:function () {
- var nativeSel, nativeRange;
- if ( this.isFocus() && (nativeSel = this.getNative()) ) {
- nativeRange = browser.ie9below ? nativeSel.createRange() : nativeSel.getRangeAt( 0 );
- return browser.ie9below ? nativeRange.text : nativeRange.toString();
- }
- return '';
- },
-
- /**
- * 清除选区
- * @method clearRange
- * @example
- * ```javascript
- * editor.selection.clearRange();
- * ```
- */
- clearRange : function(){
- this.getNative()[browser.ie9below ? 'empty' : 'removeAllRanges']();
- }
- };
-})();
-
-// core/Editor.js
-/**
- * 编辑器主类,包含编辑器提供的大部分公用接口
- * @file
- * @module UE
- * @class Editor
- * @since 1.2.6.1
- */
-
-/**
- * UEditor公用空间,UEditor所有的功能都挂载在该空间下
- * @unfile
- * @module UE
- */
-
-/**
- * UEditor的核心类,为用户提供与编辑器交互的接口。
- * @unfile
- * @module UE
- * @class Editor
- */
-
-(function () {
- var uid = 0, _selectionChangeTimer;
-
- /**
- * 获取编辑器的html内容,赋值到编辑器所在表单的textarea文本域里面
- * @private
- * @method setValue
- * @param { UE.Editor } editor 编辑器事例
- */
- function setValue(form, editor) {
- var textarea;
- if (editor.textarea) {
- if (utils.isString(editor.textarea)) {
- for (var i = 0, ti, tis = domUtils.getElementsByTagName(form, 'textarea'); ti = tis[i++];) {
- if (ti.id == 'ueditor_textarea_' + editor.options.textarea) {
- textarea = ti;
- break;
- }
- }
- } else {
- textarea = editor.textarea;
- }
- }
- if (!textarea) {
- form.appendChild(textarea = domUtils.createElement(document, 'textarea', {
- 'name': editor.options.textarea,
- 'id': 'ueditor_textarea_' + editor.options.textarea,
- 'style': "display:none"
- }));
- //不要产生多个textarea
- editor.textarea = textarea;
- }
- !textarea.getAttribute('name') && textarea.setAttribute('name', editor.options.textarea );
- textarea.value = editor.hasContents() ?
- (editor.options.allHtmlEnabled ? editor.getAllHtml() : editor.getContent(null, null, true)) :
- ''
- }
- function loadPlugins(me){
- //初始化插件
- for (var pi in UE.plugins) {
- UE.plugins[pi].call(me);
- }
-
- }
- function checkCurLang(I18N){
- for(var lang in I18N){
- return lang
- }
- }
-
- function langReadied(me){
- me.langIsReady = true;
-
- me.fireEvent("langReady");
- }
-
- /**
- * 编辑器准备就绪后会触发该事件
- * @module UE
- * @class Editor
- * @event ready
- * @remind render方法执行完成之后,会触发该事件
- * @remind
- * @example
- * ```javascript
- * editor.addListener( 'ready', function( editor ) {
- * editor.execCommand( 'focus' ); //编辑器家在完成后,让编辑器拿到焦点
- * } );
- * ```
- */
- /**
- * 执行destroy方法,会触发该事件
- * @module UE
- * @class Editor
- * @event destroy
- * @see UE.Editor:destroy()
- */
- /**
- * 执行reset方法,会触发该事件
- * @module UE
- * @class Editor
- * @event reset
- * @see UE.Editor:reset()
- */
- /**
- * 执行focus方法,会触发该事件
- * @module UE
- * @class Editor
- * @event focus
- * @see UE.Editor:focus(Boolean)
- */
- /**
- * 语言加载完成会触发该事件
- * @module UE
- * @class Editor
- * @event langReady
- */
- /**
- * 运行命令之后会触发该命令
- * @module UE
- * @class Editor
- * @event beforeExecCommand
- */
- /**
- * 运行命令之后会触发该命令
- * @module UE
- * @class Editor
- * @event afterExecCommand
- */
- /**
- * 运行命令之前会触发该命令
- * @module UE
- * @class Editor
- * @event firstBeforeExecCommand
- */
- /**
- * 在getContent方法执行之前会触发该事件
- * @module UE
- * @class Editor
- * @event beforeGetContent
- * @see UE.Editor:getContent()
- */
- /**
- * 在getContent方法执行之后会触发该事件
- * @module UE
- * @class Editor
- * @event afterGetContent
- * @see UE.Editor:getContent()
- */
- /**
- * 在getAllHtml方法执行时会触发该事件
- * @module UE
- * @class Editor
- * @event getAllHtml
- * @see UE.Editor:getAllHtml()
- */
- /**
- * 在setContent方法执行之前会触发该事件
- * @module UE
- * @class Editor
- * @event beforeSetContent
- * @see UE.Editor:setContent(String)
- */
- /**
- * 在setContent方法执行之后会触发该事件
- * @module UE
- * @class Editor
- * @event afterSetContent
- * @see UE.Editor:setContent(String)
- */
- /**
- * 每当编辑器内部选区发生改变时,将触发该事件
- * @event selectionchange
- * @warning 该事件的触发非常频繁,不建议在该事件的处理过程中做重量级的处理
- * @example
- * ```javascript
- * editor.addListener( 'selectionchange', function( editor ) {
- * console.log('选区发生改变');
- * }
- */
- /**
- * 在所有selectionchange的监听函数执行之前,会触发该事件
- * @module UE
- * @class Editor
- * @event beforeSelectionChange
- * @see UE.Editor:selectionchange
- */
- /**
- * 在所有selectionchange的监听函数执行完之后,会触发该事件
- * @module UE
- * @class Editor
- * @event afterSelectionChange
- * @see UE.Editor:selectionchange
- */
- /**
- * 编辑器内容发生改变时会触发该事件
- * @module UE
- * @class Editor
- * @event contentChange
- */
-
-
- /**
- * 以默认参数构建一个编辑器实例
- * @constructor
- * @remind 通过 改构造方法实例化的编辑器,不带ui层.需要render到一个容器,编辑器实例才能正常渲染到页面
- * @example
- * ```javascript
- * var editor = new UE.Editor();
- * editor.execCommand('blod');
- * ```
- * @see UE.Config
- */
-
- /**
- * 以给定的参数集合创建一个编辑器实例,对于未指定的参数,将应用默认参数。
- * @constructor
- * @remind 通过 改构造方法实例化的编辑器,不带ui层.需要render到一个容器,编辑器实例才能正常渲染到页面
- * @param { Object } setting 创建编辑器的参数
- * @example
- * ```javascript
- * var editor = new UE.Editor();
- * editor.execCommand('blod');
- * ```
- * @see UE.Config
- */
- var Editor = UE.Editor = function (options) {
- var me = this;
- me.uid = uid++;
- EventBase.call(me);
- me.commands = {};
- me.options = utils.extend(utils.clone(options || {}), UEDITOR_CONFIG, true);
- me.shortcutkeys = {};
- me.inputRules = [];
- me.outputRules = [];
- //设置默认的常用属性
- me.setOpt(Editor.defaultOptions(me));
-
- /* 尝试异步加载后台配置 */
- me.loadServerConfig();
-
- if(!utils.isEmptyObject(UE.I18N)){
- //修改默认的语言类型
- me.options.lang = checkCurLang(UE.I18N);
- UE.plugin.load(me);
- langReadied(me);
-
- }else{
- utils.loadFile(document, {
- src: me.options.langPath + me.options.lang + "/" + me.options.lang + ".js",
- tag: "script",
- type: "text/javascript",
- defer: "defer"
- }, function () {
- UE.plugin.load(me);
- langReadied(me);
- });
- }
-
- UE.instants['ueditorInstant' + me.uid] = me;
- };
- Editor.prototype = {
- registerCommand : function(name,obj){
- this.commands[name] = obj;
- },
- /**
- * 编辑器对外提供的监听ready事件的接口, 通过调用该方法,达到的效果与监听ready事件是一致的
- * @method ready
- * @param { Function } fn 编辑器ready之后所执行的回调, 如果在注册事件之前编辑器已经ready,将会
- * 立即触发该回调。
- * @remind 需要等待编辑器加载完成后才能执行的代码,可以使用该方法传入
- * @example
- * ```javascript
- * editor.ready( function( editor ) {
- * editor.setContent('初始化完毕');
- * } );
- * ```
- * @see UE.Editor.event:ready
- */
- ready: function (fn) {
- var me = this;
- if (fn) {
- me.isReady ? fn.apply(me) : me.addListener('ready', fn);
- }
- },
-
- /**
- * 该方法是提供给插件里面使用,设置配置项默认值
- * @method setOpt
- * @warning 三处设置配置项的优先级: 实例化时传入参数 > setOpt()设置 > config文件里设置
- * @warning 该方法仅供编辑器插件内部和编辑器初始化时调用,其他地方不能调用。
- * @param { String } key 编辑器的可接受的选项名称
- * @param { * } val 该选项可接受的值
- * @example
- * ```javascript
- * editor.setOpt( 'initContent', '欢迎使用编辑器' );
- * ```
- */
-
- /**
- * 该方法是提供给插件里面使用,以{key:value}集合的方式设置插件内用到的配置项默认值
- * @method setOpt
- * @warning 三处设置配置项的优先级: 实例化时传入参数 > setOpt()设置 > config文件里设置
- * @warning 该方法仅供编辑器插件内部和编辑器初始化时调用,其他地方不能调用。
- * @param { Object } options 将要设置的选项的键值对对象
- * @example
- * ```javascript
- * editor.setOpt( {
- * 'initContent': '欢迎使用编辑器'
- * } );
- * ```
- */
- setOpt: function (key, val) {
- var obj = {};
- if (utils.isString(key)) {
- obj[key] = val
- } else {
- obj = key;
- }
- utils.extend(this.options, obj, true);
- },
- getOpt:function(key){
- return this.options[key]
- },
- /**
- * 销毁编辑器实例,使用textarea代替
- * @method destroy
- * @example
- * ```javascript
- * editor.destroy();
- * ```
- */
- destroy: function () {
-
- var me = this;
- me.fireEvent('destroy');
- var container = me.container.parentNode;
- var textarea = me.textarea;
- if (!textarea) {
- textarea = document.createElement('textarea');
- container.parentNode.insertBefore(textarea, container);
- } else {
- textarea.style.display = ''
- }
-
- textarea.style.width = me.iframe.offsetWidth + 'px';
- textarea.style.height = me.iframe.offsetHeight + 'px';
- textarea.value = me.getContent();
- textarea.id = me.key;
- container.innerHTML = '';
- domUtils.remove(container);
- var key = me.key;
- //trace:2004
- for (var p in me) {
- if (me.hasOwnProperty(p)) {
- delete this[p];
- }
- }
- UE.delEditor(key);
- },
-
- /**
- * 渲染编辑器的DOM到指定容器
- * @method render
- * @param { String } containerId 指定一个容器ID
- * @remind 执行该方法,会触发ready事件
- * @warning 必须且只能调用一次
- */
-
- /**
- * 渲染编辑器的DOM到指定容器
- * @method render
- * @param { Element } containerDom 直接指定容器对象
- * @remind 执行该方法,会触发ready事件
- * @warning 必须且只能调用一次
- */
- render: function (container) {
- var me = this,
- options = me.options,
- getStyleValue=function(attr){
- return parseInt(domUtils.getComputedStyle(container,attr));
- };
- if (utils.isString(container)) {
- container = document.getElementById(container);
- }
- if (container) {
- if(options.initialFrameWidth){
- options.minFrameWidth = options.initialFrameWidth
- }else{
- options.minFrameWidth = options.initialFrameWidth = container.offsetWidth;
- }
- if(options.initialFrameHeight){
- options.minFrameHeight = options.initialFrameHeight
- }else{
- options.initialFrameHeight = options.minFrameHeight = container.offsetHeight;
- }
-
- container.style.width = /%$/.test(options.initialFrameWidth) ? '100%' : options.initialFrameWidth-
- getStyleValue("padding-left")- getStyleValue("padding-right") +'px';
- container.style.height = /%$/.test(options.initialFrameHeight) ? '100%' : options.initialFrameHeight -
- getStyleValue("padding-top")- getStyleValue("padding-bottom") +'px';
-
- container.style.zIndex = options.zIndex;
-
- var html = ( ie && browser.version < 9 ? '' : '') +
- '
' +
- '' +
- ( options.iframeCssUrl ? '' : '' ) +
- (options.initialStyle ? '' : '') +
- '
' +
- '