/*!
 * UEditorPlus
 * version: 2.0.0
*/
(function(){

// editor.js
UEDITOR_CONFIG = window.UEDITOR_CONFIG || {};

var baidu = window.baidu || {};

window.baidu = baidu;

window.UE = baidu.editor = {
    plugins: {},
    commands: {},
    instants: {},
    I18N: {},
    _customizeUI: {},
    version: "3.9.0",
    constants: {
        STATEFUL: {
            DISABLED: -1,
            OFF: 0,
            ON: 1,
        },
    }
};
var dom = (UE.dom = {});


// core/browser.js
/**
 * 浏览器判断模块
 * @file
 * @module UE.browser
 * @since 1.2.6.1
 */

/**
 * 提供浏览器检测的模块
 * @unfile
 * @module UE.browser
 */
var browser = (UE.browser = (function () {
    var agent = navigator.userAgent.toLowerCase(),
        opera = window.opera,
        browser = {
            /**
             * @property {boolean} ie 检测当前浏览器是否为IE
             * @example
             * ```javascript
             * if ( UE.browser.ie ) {
             *     console.log( '当前浏览器是IE' );
             * }
             * ```
             */
            ie: /(msie\s|trident.*rv:)([\w.]+)/i.test(agent),

            /**
             * @property {boolean} opera 检测当前浏览器是否为Opera
             * @example
             * ```javascript
             * if ( UE.browser.opera ) {
             *     console.log( '当前浏览器是Opera' );
             * }
             * ```
             */
            opera: !!opera && opera.version,

            /**
             * @property {boolean} webkit 检测当前浏览器是否是webkit内核的浏览器
             * @example
             * ```javascript
             * if ( UE.browser.webkit ) {
             *     console.log( '当前浏览器是webkit内核浏览器' );
             * }
             * ```
             */
            webkit: agent.indexOf(" applewebkit/") > -1,

            /**
             * @property {boolean} mac 检测当前浏览器是否是运行在mac平台下
             * @example
             * ```javascript
             * if ( UE.browser.mac ) {
             *     console.log( '当前浏览器运行在mac平台下' );
             * }
             * ```
             */
            mac: agent.indexOf("macintosh") > -1,

            /**
             * @property {boolean} quirks 检测当前浏览器是否处于“怪异模式”下
             * @example
             * ```javascript
             * if ( UE.browser.quirks ) {
             *     console.log( '当前浏览器运行处于“怪异模式”' );
             * }
             * ```
             */
            quirks: document.compatMode == "BackCompat"
        };

    /**
     * @property {boolean} gecko 检测当前浏览器内核是否是gecko内核
     * @example
     * ```javascript
     * if ( UE.browser.gecko ) {
     *     console.log( '当前浏览器内核是gecko内核' );
     * }
     * ```
     */
    browser.gecko =
        navigator.product == "Gecko" &&
        !browser.webkit &&
        !browser.opera &&
        !browser.ie;

    var version = 0;

    // Internet Explorer 6.0+
    if (browser.ie) {
        var v1 = agent.match(/(?:msie\s([\w.]+))/);
        var v2 = agent.match(/(?:trident.*rv:([\w.]+))/);
        if (v1 && v2 && v1[1] && v2[1]) {
            version = Math.max(v1[1] * 1, v2[1] * 1);
        } else if (v1 && v1[1]) {
            version = v1[1] * 1;
        } else if (v2 && v2[1]) {
            version = v2[1] * 1;
        } else {
            version = 0;
        }

        browser.ie11Compat = document.documentMode == 11;
        /**
         * @property { boolean } ie9Compat 检测浏览器模式是否为 IE9 兼容模式
         * @warning 如果浏览器不是IE, 则该值为undefined
         * @example
         * ```javascript
         * if ( UE.browser.ie9Compat ) {
         *     console.log( '当前浏览器运行在IE9兼容模式下' );
         * }
         * ```
         */
        browser.ie9Compat = document.documentMode == 9;

        /**
         * @property { boolean } ie8 检测浏览器是否是IE8浏览器
         * @warning 如果浏览器不是IE, 则该值为undefined
         * @example
         * ```javascript
         * if ( UE.browser.ie8 ) {
         *     console.log( '当前浏览器是IE8浏览器' );
         * }
         * ```
         */
        browser.ie8 = !!document.documentMode;

        /**
         * @property { boolean } ie8Compat 检测浏览器模式是否为 IE8 兼容模式
         * @warning 如果浏览器不是IE, 则该值为undefined
         * @example
         * ```javascript
         * if ( UE.browser.ie8Compat ) {
         *     console.log( '当前浏览器运行在IE8兼容模式下' );
         * }
         * ```
         */
        browser.ie8Compat = document.documentMode == 8;

        /**
         * @property { boolean } ie7Compat 检测浏览器模式是否为 IE7 兼容模式
         * @warning 如果浏览器不是IE, 则该值为undefined
         * @example
         * ```javascript
         * if ( UE.browser.ie7Compat ) {
         *     console.log( '当前浏览器运行在IE7兼容模式下' );
         * }
         * ```
         */
        browser.ie7Compat =
            (version == 7 && !document.documentMode) || document.documentMode == 7;

        /**
         * @property { boolean } ie6Compat 检测浏览器模式是否为 IE6 模式 或者怪异模式
         * @warning 如果浏览器不是IE, 则该值为undefined
         * @example
         * ```javascript
         * if ( UE.browser.ie6Compat ) {
         *     console.log( '当前浏览器运行在IE6模式或者怪异模式下' );
         * }
         * ```
         */
        browser.ie6Compat = version < 7 || browser.quirks;

        browser.ie9above = version > 8;

        browser.ie9below = version < 9;

        browser.ie11above = version > 10;

        browser.ie11below = version < 11;
    }

    // Gecko.
    if (browser.gecko) {
        var geckoRelease = agent.match(/rv:([\d\.]+)/);
        if (geckoRelease) {
            geckoRelease = geckoRelease[1].split(".");
            version =
                geckoRelease[0] * 10000 +
                (geckoRelease[1] || 0) * 100 +
                (geckoRelease[2] || 0) * 1;
        }
    }

    /**
     * @property { Number } chrome 检测当前浏览器是否为Chrome, 如果是,则返回Chrome的大版本号
     * @warning 如果浏览器不是chrome, 则该值为undefined
     * @example
     * ```javascript
     * if ( UE.browser.chrome ) {
     *     console.log( '当前浏览器是Chrome' );
     * }
     * ```
     */
    if (/chrome\/(\d+\.\d)/i.test(agent)) {
        browser.chrome = +RegExp["\x241"];
    }

    /**
     * @property { Number } safari 检测当前浏览器是否为Safari, 如果是,则返回Safari的大版本号
     * @warning 如果浏览器不是safari, 则该值为undefined
     * @example
     * ```javascript
     * if ( UE.browser.safari ) {
     *     console.log( '当前浏览器是Safari' );
     * }
     * ```
     */
    if (
        /(\d+\.\d)?(?:\.\d)?\s+safari\/?(\d+\.\d+)?/i.test(agent) &&
        !/chrome/i.test(agent)
    ) {
        browser.safari = +(RegExp["\x241"] || RegExp["\x242"]);
    }

    // Opera 9.50+
    if (browser.opera) version = parseFloat(opera.version());

    // WebKit 522+ (Safari 3+)
    if (browser.webkit)
        version = parseFloat(agent.match(/ applewebkit\/(\d+)/)[1]);

    /**
     * @property { Number } version 检测当前浏览器版本号
     * @remind
     * <ul>
     *     <li>IE系列返回值为5,6,7,8,9,10等</li>
     *     <li>gecko系列会返回10900,158900等</li>
     *     <li>webkit系列会返回其build号 (如 522等)</li>
     * </ul>
     * @example
     * ```javascript
     * console.log( '当前浏览器版本号是: ' + UE.browser.version );
     * ```
     */
    browser.version = version;

    /**
     * @property { boolean } isCompatible 检测当前浏览器是否能够与UEditor良好兼容
     * @example
     * ```javascript
     * if ( UE.browser.isCompatible ) {
     *     console.log( '浏览器与UEditor能够良好兼容' );
     * }
     * ```
     */
    browser.isCompatible =
        !browser.mobile &&
        ((browser.ie && version >= 6) ||
            (browser.gecko && version >= 10801) ||
            (browser.opera && version >= 9.5) ||
            (browser.air && version >= 1) ||
            (browser.webkit && version >= 522) ||
            false);
    return browser;
})());
//快捷方式
var ie = browser.ie,
    webkit = browser.webkit,
    gecko = browser.gecko,
    opera = browser.opera;


// core/utils.js
/**
 * 工具函数包
 * @file
 * @module UE.utils
 * @since 1.2.6.1
 */

/**
 * UEditor封装使用的静态工具函数
 * @module UE.utils
 * @unfile
 */

var utils = (UE.utils = {
    /**
     * 用给定的迭代器遍历对象
     * @method each
     * @param { Object } obj 需要遍历的对象
     * @param { Function } iterator 迭代器, 该方法接受两个参数, 第一个参数是当前所处理的value, 第二个参数是当前遍历对象的key
     * @example
     * ```javascript
     * var demoObj = {
     *     key1: 1,
     *     key2: 2
     * };
     *
     * //output: key1: 1, key2: 2
     * UE.utils.each( demoObj, funciton ( value, key ) {
     *
     *     console.log( key + ":" + value );
     *
     * } );
     * ```
     */

    /**
     * 用给定的迭代器遍历数组或类数组对象
     * @method each
     * @param { Array } array 需要遍历的数组或者类数组
     * @param { Function } iterator 迭代器, 该方法接受两个参数, 第一个参数是当前所处理的value, 第二个参数是当前遍历对象的key
     * @example
     * ```javascript
     * var divs = document.getElmentByTagNames( "div" );
     *
     * //output: 0: DIV, 1: DIV ...
     * UE.utils.each( divs, funciton ( value, key ) {
     *
     *     console.log( key + ":" + value.tagName );
     *
     * } );
     * ```
     */
    each: function (obj, iterator, context) {
        if (obj == null) return;
        if (obj.length === +obj.length) {
            for (var i = 0, l = obj.length; i < l; i++) {
                if (iterator.call(context, obj[i], i, obj) === false) return false;
            }
        } else {
            for (var key in obj) {
                if (obj.hasOwnProperty(key)) {
                    if (iterator.call(context, obj[key], key, obj) === false)
                        return false;
                }
            }
        }
    },

    /**
     * 以给定对象作为原型创建一个新对象
     * @method makeInstance
     * @param { Object } protoObject 该对象将作为新创建对象的原型
     * @return { Object } 新的对象, 该对象的原型是给定的protoObject对象
     * @example
     * ```javascript
     *
     * var protoObject = { sayHello: function () { console.log('Hello UEditorPlus!'); } };
     *
     * var newObject = UE.utils.makeInstance( protoObject );
     * //output: Hello UEditorPlus!
     * newObject.sayHello();
     * ```
     */
    makeInstance: function (obj) {
        var noop = new Function();
        noop.prototype = obj;
        obj = new noop();
        noop.prototype = null;
        return obj;
    },

    isObject: function (item) {
        return (item && typeof item === 'object' && !Array.isArray(item));
    },

    merge: function (target, source) {
        var output = Object.assign({}, target);
        if (this.isObject(target) && this.isObject(source)) {
            Object.keys(source).forEach(key => {
                if (this.isObject(source[key])) {
                    if (!(key in target)) {
                        Object.assign(output, {[key]: source[key]});
                    } else {
                        output[key] = this.merge(target[key], source[key]);
                    }
                } else {
                    Object.assign(output, {[key]: source[key]});
                }
            });
        }
        return output;
    },

    /**
     * 将source对象中的属性扩展到target对象上, 根据指定的isKeepTarget值决定是否保留目标对象中与
     * 源对象属性名相同的属性值。
     * @method extend
     * @param { Object } target 目标对象, 新的属性将附加到该对象上
     * @param { Object } source 源对象, 该对象的属性会被附加到target对象上
     * @param { Boolean } isKeepTarget 是否保留目标对象中与源对象中属性名相同的属性
     * @return { Object } 返回target对象
     * @example
     * ```javascript
     *
     * var target = { name: 'target', sex: 1 },
     *      source = { name: 'source', age: 17 };
     *
     * UE.utils.extend( target, source, true );
     *
     * //output: { name: 'target', sex: 1, age: 17 }
     * console.log( target );
     *
     * ```
     */
    extend: function (t, s, b) {
        if (s) {
            for (var k in s) {
                if (!b || !t.hasOwnProperty(k)) {
                    t[k] = s[k];
                }
            }
        }
        return t;
    },

    /**
     * 将给定的多个对象的属性复制到目标对象target上
     * @method extend2
     * @remind 该方法将强制把源对象上的属性复制到target对象上
     * @remind 该方法支持两个及以上的参数, 从第二个参数开始, 其属性都会被复制到第一个参数上。 如果遇到同名的属性,
     *          将会覆盖掉之前的值。
     * @param { Object } target 目标对象, 新的属性将附加到该对象上
     * @param { Object... } source 源对象, 支持多个对象, 该对象的属性会被附加到target对象上
     * @return { Object } 返回target对象
     * @example
     * ```javascript
     *
     * var target = {},
     *     source1 = { name: 'source', age: 17 },
     *     source2 = { title: 'dev' };
     *
     * UE.utils.extend2( target, source1, source2 );
     *
     * //output: { name: 'source', age: 17, title: 'dev' }
     * console.log( target );
     *
     * ```
     */
    extend2: function (t) {
        var a = arguments;
        for (var i = 1; i < a.length; i++) {
            var x = a[i];
            for (var k in x) {
                if (!t.hasOwnProperty(k)) {
                    t[k] = x[k];
                }
            }
        }
        return t;
    },

    /**
     * 模拟继承机制, 使得subClass继承自superClass
     * @method inherits
     * @param { Object } subClass 子类对象
     * @param { Object } superClass 超类对象
     * @warning 该方法只能让subClass继承超类的原型, subClass对象自身的属性和方法不会被继承
     * @return { Object } 继承superClass后的子类对象
     * @example
     * ```javascript
     * function SuperClass(){
     *     this.name = "小李";
     * }
     *
     * SuperClass.prototype = {
     *     hello:function(str){
     *         console.log(this.name + str);
     *     }
     * }
     *
     * function SubClass(){
     *     this.name = "小张";
     * }
     *
     * UE.utils.inherits(SubClass,SuperClass);
     *
     * var sub = new SubClass();
     * //output: '小张早上好!
     * sub.hello("早上好!");
     * ```
     */
    inherits: function (subClass, superClass) {
        var oldP = subClass.prototype,
            newP = utils.makeInstance(superClass.prototype);
        utils.extend(newP, oldP, true);
        subClass.prototype = newP;
        return (newP.constructor = subClass);
    },

    /**
     * 用指定的context对象作为函数fn的上下文
     * @method bind
     * @param { Function } fn 需要绑定上下文的函数对象
     * @param { Object } content 函数fn新的上下文对象
     * @return { Function } 一个新的函数, 该函数作为原始函数fn的代理, 将完成fn的上下文调换工作。
     * @example
     * ```javascript
     *
     * var name = 'window',
     *     newTest = null;
     *
     * function test () {
     *     console.log( this.name );
     * }
     *
     * newTest = UE.utils.bind( test, { name: 'object' } );
     *
     * //output: object
     * newTest();
     *
     * //output: window
     * test();
     *
     * ```
     */
    bind: function (fn, context) {
        return function () {
            return fn.apply(context, arguments);
        };
    },

    /**
     * 创建延迟指定时间后执行的函数fn
     * @method defer
     * @param { Function } fn 需要延迟执行的函数对象
     * @param { int } delay 延迟的时间, 单位是毫秒
     * @warning 该方法的时间控制是不精确的,仅仅只能保证函数的执行是在给定的时间之后,
     *           而不能保证刚好到达延迟时间时执行。
     * @return { Function } 目标函数fn的代理函数, 只有执行该函数才能起到延时效果
     * @example
     * ```javascript
     * var start = 0;
     *
     * function test(){
     *     console.log( new Date() - start );
     * }
     *
     * var testDefer = UE.utils.defer( test, 1000 );
     * //
     * start = new Date();
     * //output: (大约在1000毫秒之后输出) 1000
     * testDefer();
     * ```
     */

    /**
     * 创建延迟指定时间后执行的函数fn, 如果在延迟时间内再次执行该方法, 将会根据指定的exclusion的值,
     * 决定是否取消前一次函数的执行, 如果exclusion的值为true, 则取消执行,反之,将继续执行前一个方法。
     * @method defer
     * @param { Function } fn 需要延迟执行的函数对象
     * @param { int } delay 延迟的时间, 单位是毫秒
     * @param { Boolean } exclusion 如果在延迟时间内再次执行该函数,该值将决定是否取消执行前一次函数的执行,
     *                     值为true表示取消执行, 反之则将在执行前一次函数之后才执行本次函数调用。
     * @warning 该方法的时间控制是不精确的,仅仅只能保证函数的执行是在给定的时间之后,
     *           而不能保证刚好到达延迟时间时执行。
     * @return { Function } 目标函数fn的代理函数, 只有执行该函数才能起到延时效果
     * @example
     * ```javascript
     *
     * function test(){
     *     console.log(1);
     * }
     *
     * var testDefer = UE.utils.defer( test, 1000, true );
     *
     * //output: (两次调用仅有一次输出) 1
     * testDefer();
     * testDefer();
     * ```
     */
    defer: function (fn, delay, exclusion) {
        var timerID;
        return function () {
            if (exclusion) {
                clearTimeout(timerID);
            }
            timerID = setTimeout(fn, delay);
        };
    },

    /**
     * 获取元素item在数组array中首次出现的位置, 如果未找到item, 则返回-1
     * @method indexOf
     * @remind 该方法的匹配过程使用的是恒等“===”
     * @param { Array } array 需要查找的数组对象
     * @param { * } item 需要在目标数组中查找的值
     * @return { int } 返回item在目标数组array中首次出现的位置, 如果在数组中未找到item, 则返回-1
     * @example
     * ```javascript
     * var item = 1,
     *     arr = [ 3, 4, 6, 8, 1, 1, 2 ];
     *
     * //output: 4
     * console.log( UE.utils.indexOf( arr, item ) );
     * ```
     */

    /**
     * 获取元素item数组array中首次出现的位置, 如果未找到item, 则返回-1。通过start的值可以指定搜索的起始位置。
     * @method indexOf
     * @remind 该方法的匹配过程使用的是恒等“===”
     * @param { Array } array 需要查找的数组对象
     * @param { * } item 需要在目标数组中查找的值
     * @param { int } start 搜索的起始位置
     * @return { int } 返回item在目标数组array中的start位置之后首次出现的位置, 如果在数组中未找到item, 则返回-1
     * @example
     * ```javascript
     * var item = 1,
     *     arr = [ 3, 4, 6, 8, 1, 2, 8, 3, 2, 1, 1, 4 ];
     *
     * //output: 9
     * console.log( UE.utils.indexOf( arr, item, 5 ) );
     * ```
     */
    indexOf: function (array, item, start) {
        var index = -1;
        start = this.isNumber(start) ? start : 0;
        this.each(array, function (v, i) {
            if (i >= start && v === item) {
                index = i;
                return false;
            }
        });
        return index;
    },

    /**
     * 移除数组array中所有的元素item
     * @method removeItem
     * @param { Array } array 要移除元素的目标数组
     * @param { * } item 将要被移除的元素
     * @remind 该方法的匹配过程使用的是恒等“===”
     * @example
     * ```javascript
     * var arr = [ 4, 5, 7, 1, 3, 4, 6 ];
     *
     * UE.utils.removeItem( arr, 4 );
     * //output: [ 5, 7, 1, 3, 6 ]
     * console.log( arr );
     *
     * ```
     */
    removeItem: function (array, item) {
        for (var i = 0, l = array.length; i < l; i++) {
            if (array[i] === item) {
                array.splice(i, 1);
                i--;
            }
        }
    },

    /**
     * 删除字符串str的首尾空格
     * @method trim
     * @param { String } str 需要删除首尾空格的字符串
     * @return { String } 删除了首尾的空格后的字符串
     * @example
     * ```javascript
     *
     * var str = " UEdtior ";
     *
     * //output: 9
     * console.log( str.length );
     *
     * //output: 7
     * console.log( UE.utils.trim( " UEdtior " ).length );
     *
     * //output: 9
     * console.log( str.length );
     *
     *  ```
     */
    trim: function (str) {
        return str.replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, "");
    },

    /**
     * 将字符串str以','分隔成数组后,将该数组转换成哈希对象, 其生成的hash对象的key为数组中的元素, value为1
     * @method listToMap
     * @warning 该方法在生成的hash对象中,会为每一个key同时生成一个另一个全大写的key。
     * @param { String } str 该字符串将被以','分割为数组, 然后进行转化
     * @return { Object } 转化之后的hash对象
     * @example
     * ```javascript
     *
     * //output: Object {UEdtior: 1, UEDTIOR: 1, Hello: 1, HELLO: 1}
     * console.log( UE.utils.listToMap( 'UEdtior,Hello' ) );
     *
     * ```
     */

    /**
     * 将字符串数组转换成哈希对象, 其生成的hash对象的key为数组中的元素, value为1
     * @method listToMap
     * @warning 该方法在生成的hash对象中,会为每一个key同时生成一个另一个全大写的key。
     * @param { Array } arr 字符串数组
     * @return { Object } 转化之后的hash对象
     * @example
     * ```javascript
     *
     * //output: Object {UEdtior: 1, UEDTIOR: 1, Hello: 1, HELLO: 1}
     * console.log( UE.utils.listToMap( [ 'UEdtior', 'Hello' ] ) );
     *
     * ```
     */
    listToMap: function (list) {
        if (!list) return {};
        list = utils.isArray(list) ? list : list.split(",");
        for (var i = 0, ci, obj = {}; (ci = list[i++]);) {
            obj[ci.toUpperCase()] = obj[ci] = 1;
        }
        return obj;
    },

    /**
     * 将str中的html符号转义,将转义“',&,<,",>,”,“”七个字符
     * @method unhtml
     * @param { String } str 需要转义的字符串
     * @return { String } 转义后的字符串
     * @example
     * ```javascript
     * var html = '<body>&</body>';
     *
     * //output: &lt;body&gt;&amp;&lt;/body&gt;
     * console.log( UE.utils.unhtml( html ) );
     *
     * ```
     */
    unhtml: function (str, reg) {
        return str
            ? str.replace(
                reg || /[&<">'](?:(amp|lt|ldquo|rdquo|quot|gt|#39|nbsp|#\d+);)?/g,
                function (a, b) {
                    if (b) {
                        return a;
                    } else {
                        return {
                            "<": "&lt;",
                            "&": "&amp;",
                            '"': "&quot;",
                            "“": "&ldquo;",
                            "”": "&rdquo;",
                            ">": "&gt;",
                            "'": "&#39;"
                        }[a];
                    }
                }
            )
            : "";
    },

    /**
     * 将str中的转义字符还原成html字符
     * @see UE.utils.unhtml(String);
     * @method html
     * @param { String } str 需要逆转义的字符串
     * @return { String } 逆转义后的字符串
     * @example
     * ```javascript
     *
     * var str = '&lt;body&gt;&amp;&lt;/body&gt;';
     *
     * //output: <body>&</body>
     * console.log( UE.utils.html( str ) );
     *
     * ```
     */
    html: function (str) {
        return str
            ? str.replace(/&((g|l|quo|ldquo|rdquo)t|amp|#39|nbsp);/g, function (m) {
                return {
                    "&lt;": "<",
                    "&amp;": "&",
                    "&quot;": '"',
                    "&ldquo;": "“",
                    "&rdquo;": "”",
                    "&gt;": ">",
                    "&#39;": "'",
                    "&nbsp;": " "
                }[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 < len; i++) {
            for (var j = i, length = array.length; j < length; j++) {
                if (compareFn(array[i], array[j]) > 0) {
                    var t = array[i];
                    array[i] = array[j];
                    array[j] = t;
                }
            }
        }
        return array;
    },
    serializeParam: function (json) {
        var strArr = [];
        for (var i in json) {
            //忽略默认的几个参数
            if (i == "method" || i == "timeout" || i == "async") continue;
            //传递过来的对象和函数不在提交之列
            if (
                !(
                    (typeof json[i]).toLowerCase() == "function" ||
                    (typeof json[i]).toLowerCase() == "object"
                )
            ) {
                strArr.push(encodeURIComponent(i) + "=" + encodeURIComponent(json[i]));
            } else if (utils.isArray(json[i])) {
                //支持传数组内容
                for (var j = 0; j < json[i].length; j++) {
                    strArr.push(
                        encodeURIComponent(i) + "[]=" + encodeURIComponent(json[i][j])
                    );
                }
            }
        }
        return strArr.join("&");
    },
    formatUrl: function (url) {
        var u = url.replace(/&&/g, "&");
        u = u.replace(/\?&/g, "?");
        u = u.replace(/&$/g, "");
        u = u.replace(/&#/g, "#");
        u = u.replace(/&+/g, "&");
        return u;
    },
    addStyleContent: function (cssContent) {
        var style = document.createElement("style");
        style.innerHTML = cssContent;
        document.head.appendChild(style);
    },
    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(
            _({
                mark: 1,
                b: 1,
                acronym: 1,
                bdo: 1,
                var: 1,
                "#": 1,
                abbr: 1,
                code: 1,
                br: 1,
                i: 1,
                cite: 1,
                kbd: 1,
                u: 1,
                strike: 1,
                s: 1,
                tt: 1,
                strong: 1,
                q: 1,
                samp: 1,
                em: 1,
                dfn: 1,
                span: 1
            }),
            F
        ),
        H = X(
            _({
                sub: 1,
                img: 1,
                embed: 1,
                object: 1,
                sup: 1,
                basefont: 1,
                map: 1,
                applet: 1,
                font: 1,
                big: 1,
                small: 1
            }),
            G
        ),
        I = X(_({p: 1}), H),
        J = X(_({iframe: 1}), H, B),
        K = _({
            img: 1,
            embed: 1,
            noscript: 1,
            br: 1,
            kbd: 1,
            center: 1,
            button: 1,
            basefont: 1,
            h5: 1,
            h4: 1,
            samp: 1,
            h6: 1,
            ol: 1,
            h1: 1,
            h3: 1,
            h2: 1,
            form: 1,
            font: 1,
            "#": 1,
            select: 1,
            menu: 1,
            ins: 1,
            abbr: 1,
            label: 1,
            code: 1,
            table: 1,
            script: 1,
            cite: 1,
            input: 1,
            iframe: 1,
            strong: 1,
            textarea: 1,
            noframes: 1,
            big: 1,
            small: 1,
            span: 1,
            hr: 1,
            sub: 1,
            bdo: 1,
            var: 1,
            div: 1,
            object: 1,
            sup: 1,
            strike: 1,
            dir: 1,
            map: 1,
            dl: 1,
            applet: 1,
            del: 1,
            isindex: 1,
            fieldset: 1,
            ul: 1,
            b: 1,
            acronym: 1,
            a: 1,
            blockquote: 1,
            i: 1,
            u: 1,
            s: 1,
            tt: 1,
            address: 1,
            q: 1,
            pre: 1,
            p: 1,
            em: 1,
            dfn: 1
        }),
        L = X(_({a: 0}), J), //a不能被切开,所以把他
        M = _({tr: 1}),
        N = _({"#": 1}),
        O = X(_({param: 1}), K),
        P = X(_({form: 1}), A, D, E, I),
        Q = _({li: 1, ol: 1, ul: 1}),
        R = _({style: 1, script: 1}),
        S = _({base: 1, link: 1, meta: 1, title: 1}),
        T = X(S, R),
        U = _({head: 1, body: 1}),
        V = _({html: 1});

    var block = _({
            address: 1,
            blockquote: 1,
            center: 1,
            dir: 1,
            div: 1,
            dl: 1,
            fieldset: 1,
            form: 1,
            h1: 1,
            h2: 1,
            h3: 1,
            h4: 1,
            h5: 1,
            h6: 1,
            hr: 1,
            isindex: 1,
            menu: 1,
            noframes: 1,
            ol: 1,
            p: 1,
            pre: 1,
            table: 1,
            ul: 1
        }),
        empty = _({
            area: 1,
            base: 1,
            basefont: 1,
            br: 1,
            col: 1,
            command: 1,
            dialog: 1,
            embed: 1,
            hr: 1,
            img: 1,
            input: 1,
            isindex: 1,
            keygen: 1,
            link: 1,
            meta: 1,
            param: 1,
            source: 1,
            track: 1,
            wbr: 1
        });

    return _({
        // $ 表示自定的属性

        // body外的元素列表.
        $nonBodyContent: X(V, U, S),

        //块结构元素列表
        $block: block,

        //内联元素列表
        $inline: L,

        $inlineWithA: X(_({a: 1}), L),

        $body: X(_({script: 1, style: 1}), block),

        $cdata: _({script: 1, style: 1}),

        //自闭和元素
        $empty: empty,

        //不是自闭合,但不能让range选中里边
        $nonChild: _({iframe: 1, textarea: 1}),
        //列表元素列表
        $listItem: _({dd: 1, dt: 1, li: 1}),

        //列表根元素列表
        $list: _({ul: 1, ol: 1, dl: 1}),

        //不能认为是空的元素
        $isNotEmpty: _({
            table: 1,
            ul: 1,
            ol: 1,
            dl: 1,
            iframe: 1,
            area: 1,
            base: 1,
            col: 1,
            hr: 1,
            img: 1,
            embed: 1,
            input: 1,
            textarea: 1,
            link: 1,
            meta: 1,
            param: 1,
            h1: 1,
            h2: 1,
            h3: 1,
            h4: 1,
            h5: 1,
            h6: 1
        }),

        //如果没有子节点就可以删除的元素列表,像span,a
        $removeEmpty: _({
            a: 1,
            abbr: 1,
            acronym: 1,
            address: 1,
            b: 1,
            bdo: 1,
            big: 1,
            cite: 1,
            code: 1,
            del: 1,
            dfn: 1,
            em: 1,
            font: 1,
            i: 1,
            ins: 1,
            label: 1,
            kbd: 1,
            q: 1,
            s: 1,
            samp: 1,
            small: 1,
            span: 1,
            strike: 1,
            strong: 1,
            sub: 1,
            sup: 1,
            tt: 1,
            u: 1,
            var: 1
        }),

        $removeEmptyBlock: _({p: 1, div: 1}),

        //在table元素里的元素列表
        $tableContent: _({
            caption: 1,
            col: 1,
            colgroup: 1,
            tbody: 1,
            td: 1,
            tfoot: 1,
            th: 1,
            thead: 1,
            tr: 1,
            table: 1
        }),
        //不转换的标签
        $notTransContent: _({pre: 1, script: 1, style: 1, textarea: 1}),
        html: U,
        head: T,
        style: N,
        script: N,
        body: P,
        base: {},
        link: {},
        meta: {},
        title: N,
        col: {},
        tr: _({td: 1, th: 1}),
        img: {},
        embed: {},
        colgroup: _({thead: 1, col: 1, tbody: 1, tr: 1, tfoot: 1}),
        noscript: P,
        td: P,
        br: {},
        th: P,
        center: P,
        kbd: L,
        button: X(I, E),
        basefont: {},
        h5: L,
        h4: L,
        samp: L,
        h6: L,
        ol: Q,
        h1: L,
        h3: L,
        option: N,
        h2: L,
        form: X(A, D, E, I),
        select: _({optgroup: 1, option: 1}),
        font: L,
        ins: L,
        menu: Q,
        abbr: L,
        label: L,
        table: _({
            thead: 1,
            col: 1,
            tbody: 1,
            tr: 1,
            colgroup: 1,
            caption: 1,
            tfoot: 1
        }),
        code: L,
        tfoot: M,
        cite: L,
        li: P,
        input: {},
        iframe: P,
        strong: L,
        textarea: N,
        noframes: P,
        big: L,
        small: L,
        //trace:
        span: _({
            "#": 1,
            br: 1,
            b: 1,
            strong: 1,
            u: 1,
            i: 1,
            em: 1,
            sub: 1,
            sup: 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,
        mark: 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
     * <body>
     *
     *      <div id="test">
     *      </div>
     *
     *      <script type="text/javascript">
     *
     *          //output: DIV, BODY
     *          var filterNode = UE.dom.domUtils.findParent( document.getElementById( "test" ), function ( node ) {
     *
     *              console.log( node.tagName );
     *              return false;
     *
     *          }, true );
     *
     *      </script>
     * </body>
     * ```
     */
    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
     * <div id="test">
     *     <div id="child">你好</div>
     * </div>
     * <script>
     *     UE.dom.domUtils.remove( document.body, false );
     *     //output: false
     *     console.log( document.getElementById( "child" ) !== null );
     * </script>
     * ```
     */

    /**
     * 删除节点node,并根据keepChildren的值决定是否保留子节点
     * @method remove
     * @param { Node } node 需要删除的节点对象
     * @param { Boolean } keepChildren 是否需要保留子节点
     * @return { Node } 返回刚删除的节点对象
     * @example
     * ```html
     * <div id="test">
     *     <div id="child">你好</div>
     * </div>
     * <script>
     *     UE.dom.domUtils.remove( document.body, true );
     *     //output: true
     *     console.log( document.getElementById( "child" ) !== null );
     * </script>
     * ```
     */
    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
     *     <body>
     *      <div id="test">
     *          <span></span>
     *      </div>
     *      <i>xxx</i>
     * </body>
     * <script>
     *
     *     //output: i节点
     *     console.log( UE.dom.domUtils.getNextDomNode( document.getElementById( "test" ) ) );
     *
     * </script>
     * ```
     * @example
     * ```html
     * <body>
     *      <div>
     *          <span></span>
     *          <i id="test">xxx</i>
     *      </div>
     *      <b>xxx</b>
     * </body>
     * <script>
     *
     *     //由于id为test的i节点之后没有兄弟节点, 则查找其父节点(div)后面的兄弟节点
     *     //output: b节点
     *     console.log( UE.dom.domUtils.getNextDomNode( document.getElementById( "test" ) ) );
     *
     * </script>
     * ```
     */

    /**
     * 取得node节点的下一个兄弟节点, 如果startFromChild的值为ture,则先获取其子节点,
     * 如果有子节点则直接返回第一个子节点;如果没有子节点或者startFromChild的值为false,
     * 则执行<a href="#UE.dom.domUtils.getNextDomNode(Node)">getNextDomNode(Node node)</a>的查找过程。
     * @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
     * <span id="_baidu_bookmark_1"></span>
     * <script>
     *      var bookmarkNode = document.getElementById("_baidu_bookmark_1");
     *      //output: true
     *      console.log( UE.dom.domUtils.isBookmarkNode( bookmarkNode ) );
     * </script>
     * ```
     */
    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
     * <body>
     *     <div></div>
     *     <span id="test"></span>
     *     <i></i>
     *     <b></b>
     *     <em>xxx</em>
     *     <span></span>
     * </body>
     * <script>
     *
     *      UE.dom.domUtils.clearEmptySibling( document.getElementById( "test" ) );
     *
     *      //output: <div></div><span id="test"></span><em>xxx</em><span></span>
     *      console.log( document.body.innerHTML );
     *
     * </script>
     * ```
     */

    /**
     * 清除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
     * <div id="test">abcdef</div>
     * <script>
     *      var newNode = UE.dom.domUtils.split( document.getElementById( "test" ).firstChild, 3 );
     *      //output: def
     *      console.log( newNode.nodeValue );
     * </script>
     * ```
     */
    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
     * <div id="test">
     *
     * </div>
     * <script>
     *      //output: true
     *      console.log( UE.dom.domUtils.isWhitespace( document.getElementById("test").firstChild ) );
     * </script>
     * ```
     */
    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 {string} 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
     * <span style="font-size:12px">ssss</span>
     * <span style="font-size:12px">bbbbb</span>
     * <span style="font-size:13px">ssss</span>
     * <span style="font-size:14px">bbbbb</span>
     *
     * <script>
     *
     *     var nodes = document.getElementsByTagName( "span" );
     *
     *     //output: true
     *     console.log( UE.dom.domUtils.isSameElement( nodes[0], nodes[1] ) );
     *
     *     //output: false
     *     console.log( UE.dom.domUtils.isSameElement( nodes[2], nodes[3] ) );
     *
     * </script>
     * ```
     */
    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
     * <span style="font-size:12px">ssss</span>
     * <span style="font-size:12px">bbbbb</span>
     * <span style="font-size:13px">ssss</span>
     * <span style="font-size:14px">bbbbb</span>
     *
     * <script>
     *
     *     var nodes = document.getElementsByTagName( "span" );
     *
     *     //output: true
     *     console.log( UE.dom.domUtils.isSameStyle( nodes[0], nodes[1] ) );
     *
     *     //output: false
     *     console.log( UE.dom.domUtils.isSameStyle( nodes[2], nodes[3] ) );
     *
     * </script>
     * ```
     */
    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
     * <span id="test1" style="display: block"></span>
     * <span id="test2"></span>
     * <div id="test3" style="display: inline"></div>
     *
     * <script>
     *
     *     //output: true
     *     console.log( UE.dom.domUtils.isBlockElm( document.getElementById("test1") ) );
     *
     *     //output: false
     *     console.log( UE.dom.domUtils.isBlockElm( document.getElementById("test2") ) );
     *
     *     //output: true
     *     console.log( UE.dom.domUtils.isBlockElm( document.getElementById("test3") ) );
     *
     * </script>
     * ```
     */
    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: <p><span></span></p>
     *      console.log( wrapNode.innerHTML );
     *
     *
     *      UE.dom.domUtils.breakParent( node, parent );
     *      //拆分后
     *      //output: <p></p><span></span><p></p>
     *      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
     * <b><i></i></b> => 1
     * <b><i></i><u></u></b> => 1
     * <b></b> => 1
     * <b>xx<i></i></b> => 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
     * <p><span style="font-size:12px;">xx<span style="font-size:12px;">aa</span>xx</span></p>
     * ==> UE.dom.domUtils.mergeChild(node,'span')
     * <p><span style="font-size:12px;">xxaaxx</span></p>
     */
    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, tagName, filter) {
        if (filter && utils.isString(filter)) {
            var className = filter;
            filter = function (node) {
                return domUtils.hasClass(node, className);
            };
        }
        tagName = utils.trim(tagName).replace(/[ ]{2,}/g, " ").split(" ");
        var arr = [];
        for (var n = 0, ni; (ni = tagName[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
     * <div id="parent">
     *     <div id="sub">
     *         <span id="child"></span>
     *     </div>
     * </div>
     *
     * <script>
     *
     *     var child = document.getElementById( "child" );
     *
     *     //output: sub
     *     console.log( child.parentNode.id );
     *
     *     UE.dom.domUtils.mergeToParent( child );
     *
     *     //output: parent
     *     console.log( child.parentNode.id );
     *
     * </script>
     * ```
     */
    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需要特殊处理  不处理这样的情况 <span stlye="color:#fff">xxx<span style="color:#ccc">xxx</span>xxx</span>
                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
     * <b>xxxx</b><b id="test">ooo</b><b>xxxx</b>
     *
     * <script>
     *     var demoNode = document.getElementById("test");
     *     UE.dom.domUtils.mergeSibling( demoNode );
     *     //output: xxxxoooxxxx
     *     console.log( demoNode.innerHTML );
     * </script>
     * ```
     */

    /**
     * 合并节点node的左右兄弟节点, 可以根据给定的条件选择是否忽略合并左节点。
     * @method mergeSibling
     * @param { Element } node 需要合并的目标节点
     * @param { Boolean } ignorePre 是否忽略合并左节点
     * @example
     * ```html
     * <b>xxxx</b><b id="test">ooo</b><b>xxxx</b>
     *
     * <script>
     *     var demoNode = document.getElementById("test");
     *     UE.dom.domUtils.mergeSibling( demoNode, true );
     *     //output: oooxxxx
     *     console.log( demoNode.innerHTML );
     * </script>
     * ```
     */

    /**
     * 合并节点node的左右兄弟节点,可以根据给定的条件选择是否忽略合并左右节点。
     * @method mergeSibling
     * @param { Element } node 需要合并的目标节点
     * @param { Boolean } ignorePre 是否忽略合并左节点
     * @param { Boolean } ignoreNext 是否忽略合并右节点
     * @remind 如果同时忽略左右节点, 则该操作什么也不会做
     * @example
     * ```html
     * <b>xxxx</b><b id="test">ooo</b><b>xxxx</b>
     *
     * <script>
     *     var demoNode = document.getElementById("test");
     *     UE.dom.domUtils.mergeSibling( demoNode, false, true );
     *     //output: xxxxooo
     *     console.log( demoNode.innerHTML );
     * </script>
     * ```
     */
    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
     * <div id="wrap">
     *      <span style="font-size:14px;" id="test" name="followMe">xxxxx</span>
     * </div>
     *
     * <script>
     *
     *     UE.dom.domUtils.removeAttributes( document.getElementById( "test" ), "id name" );
     *
     *     //output: <span style="font-size:14px;">xxxxx</span>
     *     console.log( document.getElementById("wrap").innerHTML );
     *
     * </script>
     * ```
     */

    /**
     * 删除节点node上的指定属性名称的属性
     * @method  removeAttributes
     * @param { Node } node 需要删除属性的节点对象
     * @param { Array } attrNames 需要删除的属性名数组
     * @example
     * ```html
     * <div id="wrap">
     *      <span style="font-size:14px;" id="test" name="followMe">xxxxx</span>
     * </div>
     *
     * <script>
     *
     *     UE.dom.domUtils.removeAttributes( document.getElementById( "test" ), ["id", "name"] );
     *
     *     //output: <span style="font-size:14px;">xxxxx</span>
     *     console.log( document.getElementById("wrap").innerHTML );
     *
     * </script>
     * ```
     */
    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
     * <span id="test"></span>
     *
     * <script>
     *
     *     var testNode = UE.dom.domUtils.setAttributes( document.getElementById( "test" ), {
     *         id: 'demo'
     *     } );
     *
     *     //output: demo
     *     console.log( testNode.id );
     *
     * </script>
     *
     */
    setAttributes: function (node, attrs) {
        for (var attr in attrs) {
            if ('_propertyDelete' === attr) {
                for (var j = 0; j < attrs[attr].length; j++) {
                    if (node.hasAttribute(attrs[attr][j])) {
                        node.removeAttribute(attrs[attr][j]);
                    }
                }
                continue;
            }
            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
     * <style type="text/css">
     *      #test {
     *          font-size: 15px;
     *      }
     * </style>
     *
     * <span id="test"></span>
     *
     * <script>
     *     //output: 15px
     *     console.log( UE.dom.domUtils.getComputedStyle( document.getElementById( "test" ), 'font-size' ) );
     * </script>
     * ```
     */
    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
     * <span id="test" class="test1 test2 test3">xxx</span>
     *
     * <script>
     *
     *     var testNode = document.getElementById( "test" );
     *     UE.dom.domUtils.removeClasses( testNode, "test1 test2" );
     *
     *     //output: test3
     *     console.log( testNode.className );
     *
     * </script>
     * ```
     */

    /**
     * 删除元素element指定的className
     * @method removeClasses
     * @param { Element } ele 需要删除class的元素节点
     * @param { Array } classNames 需要删除的className数组
     * @example
     * ```html
     * <span id="test" class="test1 test2 test3">xxx</span>
     *
     * <script>
     *
     *     var testNode = document.getElementById( "test" );
     *     UE.dom.domUtils.removeClasses( testNode, ["test1", "test2"] );
     *
     *     //output: test3
     *     console.log( testNode.className );
     *
     * </script>
     * ```
     */
    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
     * <span id="test" class="cls1 cls2"></span>
     *
     * <script>
     *     var testNode = document.getElementById("test");
     *
     *     UE.dom.domUtils.addClass( testNode, "cls2 cls3 cls4" );
     *
     *     //output: cl1 cls2 cls3 cls4
     *     console.log( testNode.className );
     *
     * <script>
     * ```
     */

    /**
     * 给元素element添加className
     * @method addClass
     * @param { Node } ele 需要增加className的元素
     * @param { Array } classNames 需要添加的className的数组
     * @remind 相同的类名不会被重复添加
     * @example
     * ```html
     * <span id="test" class="cls1 cls2"></span>
     *
     * <script>
     *     var testNode = document.getElementById("test");
     *
     *     UE.dom.domUtils.addClass( testNode, ["cls2", "cls3", "cls4"] );
     *
     *     //output: cl1 cls2 cls3 cls4
     *     console.log( testNode.className );
     *
     * <script>
     * ```
     */
    addClass: function (elm, classNames) {
        if (!elm) return;
        classNames = utils.trim(classNames).replace(/[ ]{2,}/g, " ").split(" ");
        for (var i = 0, ci, cls = elm.className; (ci = classNames[i++]);) {
            if (!new RegExp("\\b" + ci + "\\b").test(cls)) {
                cls += " " + ci;
            }
        }
        elm.className = utils.trim(cls);
    },
    /**
     * 判断元素element是否包含给定的样式类名className
     * @method hasClass
     * @param { Node } ele 需要检测的元素
     * @param { String } classNames 需要检测的className, 多个className之间用空格分割
     * @return { Boolean } 元素是否包含所有给定的className
     * @example
     * ```html
     * <span id="test1" class="cls1 cls2"></span>
     *
     * <script>
     *     var test1 = document.getElementById("test1");
     *
     *     //output: false
     *     console.log( UE.dom.domUtils.hasClass( test1, "cls2 cls1 cls3" ) );
     *
     *     //output: true
     *     console.log( UE.dom.domUtils.hasClass( test1, "cls2 cls1" ) );
     * </script>
     * ```
     */

    /**
     * 判断元素element是否包含给定的样式类名className
     * @method hasClass
     * @param { Node } ele 需要检测的元素
     * @param { Array } classNames 需要检测的className数组
     * @return { Boolean } 元素是否包含所有给定的className
     * @example
     * ```html
     * <span id="test1" class="cls1 cls2"></span>
     *
     * <script>
     *     var test1 = document.getElementById("test1");
     *
     *     //output: false
     *     console.log( UE.dom.domUtils.hasClass( test1, [ "cls2", "cls1", "cls3" ] ) );
     *
     *     //output: true
     *     console.log( UE.dom.domUtils.hasClass( test1, [ "cls2", "cls1" ]) );
     * </script>
     * ```
     */
    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
     * <span id="test" style="color: red; background: blue;"></span>
     *
     * <script>
     *
     *     var testNode = document.getElementById("test");
     *
     *     UE.dom.domUtils.removeStyle( testNode, 'color' );
     *
     *     //output: background: blue;
     *     console.log( testNode.style.cssText );
     *
     * </script>
     * ```
     */
    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
     * <div id="test" style="color: red;"></div>
     *
     * <script>
     *
     *      var testNode = document.getElementById( "test" );
     *
     *      //output: red
     *      console.log( UE.dom.domUtils.getStyle( testNode, "color" ) );
     *
     *      //output: ""
     *      console.log( UE.dom.domUtils.getStyle( testNode, "background" ) );
     *
     * </script>
     * ```
     */
    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
     * <div id="test"></div>
     *
     * <script>
     *
     *      var testNode = document.getElementById( "test" );
     *
     *      //output: ""
     *      console.log( testNode.style.color );
     *
     *      UE.dom.domUtils.setStyle( testNode, 'color', 'red' );
     *      //output: "red"
     *      console.log( testNode.style.color );
     *
     * </script>
     * ```
     */
    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
     * <div id="test"></div>
     *
     * <script>
     *
     *      var testNode = document.getElementById( "test" );
     *
     *      //output: ""
     *      console.log( testNode.style.color );
     *
     *      UE.dom.domUtils.setStyles( testNode, {
     *          'color': 'red'
     *      } );
     *      //output: "red"
     *      console.log( testNode.style.color );
     *
     * </script>
     * ```
     */
    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
     * <div id="test">
     *      <span></span>
     * </div>
     *
     * <script>
     *
     *     //output: 3
     *     console.log( UE.dom.domUtils.getChildCount( document.getElementById("test") ) );
     *
     * </script>
     * ```
     */

    /**
     * 根据给定的过滤规则, 获取符合条件的子节点的数量
     * @method getChildCount
     * @param { Element } node 需要检测的元素
     * @param { Function } fn 过滤器, 要求对符合条件的子节点返回true, 反之则要求返回false
     * @return { Number } 符合过滤条件的node元素的子节点数量
     * @example
     * ```html
     * <div id="test">
     *      <span></span>
     * </div>
     *
     * <script>
     *
     *     //output: 1
     *     console.log( UE.dom.domUtils.getChildCount( document.getElementById("test"), function ( node ) {
     *
     *         return node.nodeType === 1;
     *
     *     } ) );
     *
     * </script>
     * ```
     */
    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) {
        offsetTop = offsetTop || 0
        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;
        // console.log({currentScroll,winHeight,offset,y:elementPosition.y});
        // offset += 50;
        if (offset > currentScroll || offset < currentScroll - winHeight) {
            win.scrollTo({
                top: offset + (offset < 0 ? -20 : 20),
                behavior: "smooth"
            });
        }
    },
    /**
     * 判断给定节点是否为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
     * <div id="test"></div>
     *
     * <script>
     *     //output: true
     *     console.log( UE.dom.domUtils.isEmptyBlock( document.getElementById("test") ) );
     * </script>
     * ```
     */

    /**
     * 根据指定的判断规则判断给定的元素是否是一个空元素
     * @method isEmptyBlock
     * @param { Element } node 需要判断的元素
     * @param { RegExp } reg 对内容执行判断的正则表达式对象
     * @return { Boolean } 是否是空元素
     */
    isEmptyBlock: function (node, reg) {
        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
     * <div id="test" style="top: 100px; left: 50px; position: absolute;"></div>
     *
     * <script>
     *
     *     var testNode = document.getElementById("test");
     *
     *     UE.dom.domUtils.setViewportOffset( testNode, {
     *         left: 200,
     *         top: 50
     *     } );
     *
     *     //output: top: 300px; left: 100px; position: absolute;
     *     console.log( testNode.style.cssText );
     *
     * </script>
     * ```
     */
    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
     * <div id="test"></div>
     *
     * <script>
     *     var testNode = document.getElementById("test");
     *
     *     //output: 0
     *     console.log( testNode.childNodes.length );
     *
     *     UE.dom.domUtils.fillNode( document, testNode );
     *
     *     //output: 1
     *     console.log( testNode.childNodes.length );
     *
     * </script>
     * ```
     */
    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
     * <div id="test1">
     *      <span></span>
     * </div>
     * <div id="test2">
     *     <div></div>
     * </div>
     *
     * <script>
     *
     *     var test1 = document.getElementById("test1"),
     *         test2 = document.getElementById("test2");
     *
     *     UE.dom.domUtils.moveChild( test1, test2 );
     *
     *     //output: ""(空字符串)
     *     console.log( test1.innerHTML );
     *
     *     //output: "<div></div><span></span>"
     *     console.log( test2.innerHTML );
     *
     * </script>
     * ```
     */

    /**
     * 把节点src的所有子节点移动到另一个节点tag上去, 可以通过dir参数控制附加的行为是“追加”还是“插入顶部”
     * @method moveChild
     * @param { Node } src 源节点, 该节点下的所有子节点将被移除
     * @param { Node } tag 目标节点, 从源节点移除的子节点将被附加到该节点下
     * @param { Boolean } dir 附加方式, 如果为true, 则附加进去的节点将被放到目标节点的顶部, 反之,则放到末尾
     * @example
     * ```html
     * <div id="test1">
     *      <span></span>
     * </div>
     * <div id="test2">
     *     <div></div>
     * </div>
     *
     * <script>
     *
     *     var test1 = document.getElementById("test1"),
     *         test2 = document.getElementById("test2");
     *
     *     UE.dom.domUtils.moveChild( test1, test2, true );
     *
     *     //output: ""(空字符串)
     *     console.log( test1.innerHTML );
     *
     *     //output: "<span></span><div></div>"
     *     console.log( test2.innerHTML );
     *
     * </script>
     * ```
     */
    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
     * <div id="test"><span>xxxx</span></div>
     *
     * <script>
     *
     *     //output: false
     *     console.log( UE.dom.domUtils.hasNoAttributes( document.getElementById("test") ) );
     *
     *     //output: true
     *     console.log( UE.dom.domUtils.hasNoAttributes( document.getElementById("test").firstChild ) );
     *
     * </script>
     * ```
     */
    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
     * <div id="test"></div>
     *
     * <script>
     *
     *     //output: true
     *     console.log( UE.dom.domUtils.isTagNode( document.getElementById("test"), "div" ) );
     *
     * </script>
     * ```
     */
    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 ? "&nbsp;" : "<br/>",
    loadScript: function (url, cb) {
        var script;
        script = document.createElement('script');
        script.src = url;
        script.onload = function () {
            cb && cb({isNew: true})
        };
        document.getElementsByTagName('head')[0].appendChild(script);
    }
});
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
         * <body>
         *      <!-- 中括号表示选区 -->
         *      <b>x<i>x[x</i>xx]x</b>
         *
         *      <script>
         *          //range是已选中的选区
         *          var fragment = range.cloneContents(),
         *              node = document.createElement("div");
         *
         *          node.appendChild( fragment );
         *
         *          //output: <i>x</i>xx
         *          console.log( node.innerHTML );
         *
         *      </script>
         * </body>
         * ```
         */
        cloneContents: function () {
            return this.collapsed ? null : execContentsAction(this, 0);
        },

        /**
         * 删除当前选区范围中的所有内容
         * @method deleteContents
         * @remind 执行完该操作后, 当前Range对象变成了闭合状态
         * @return { UE.dom.Range } 当前操作的Range对象
         * @example
         * ```html
         * <body>
         *      <!-- 中括号表示选区 -->
         *      <b>x<i>x[x</i>xx]x</b>
         *
         *      <script>
         *          //range是已选中的选区
         *          range.deleteContents();
         *
         *          //竖线表示闭合后的选区位置
         *          //output: <b>x<i>x</i>|x</b>
         *          console.log( document.body.innerHTML );
         *
         *          //此时, range的各项属性为
         *          //output: B
         *          console.log( range.startContainer.tagName );
         *          //output: 2
         *          console.log( range.startOffset );
         *          //output: B
         *          console.log( range.endContainer.tagName );
         *          //output: 2
         *          console.log( range.endOffset );
         *          //output: true
         *          console.log( range.collapsed );
         *
         *      </script>
         * </body>
         * ```
         */
        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
         * <body>
         *      <!-- 中括号表示选区 -->
         *      <b>x<i>x[x</i>xx]x</b>
         *
         *      <script>
         *          //range是已选中的选区
         *          var fragment = range.extractContents(),
         *              node = document.createElement( "div" );
         *
         *          node.appendChild( fragment );
         *
         *          //竖线表示闭合后的选区位置
         *
         *          //output: <b>x<i>x</i>|x</b>
         *          console.log( document.body.innerHTML );
         *          //output: <i>x</i>xx
         *          console.log( node.innerHTML );
         *
         *          //此时, range的各项属性为
         *          //output: B
         *          console.log( range.startContainer.tagName );
         *          //output: 2
         *          console.log( range.startOffset );
         *          //output: B
         *          console.log( range.endContainer.tagName );
         *          //output: 2
         *          console.log( range.endOffset );
         *          //output: true
         *          console.log( range.collapsed );
         *
         *      </script>
         * </body>
         */
        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
         * <!-- 选区 -->
         * <b>xxx<i>x<span>xx</span>xx<em>xx</em>xxx</i>[xxx]</b>
         *
         * <script>
         *
         *     //执行操作
         *     range.setStart( document.getElementsByTagName("i")[0], 1 );
         *
         *     //此时, 选区变成了
         *     //<b>xxx<i>x[<span>xx</span>xx<em>xx</em>xxx</i>xxx]</b>
         *
         * </script>
         * ```
         * @example
         * ```html
         * <!-- 选区 -->
         * <b>xxx<img>[xx]x</b>
         *
         * <script>
         *
         *     //执行操作
         *     range.setStart( document.getElementsByTagName("img")[0], 3 );
         *
         *     //此时, 选区变成了
         *     //<b>xxx[<img>xx]x</b>
         *
         * </script>
         * ```
         */
        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
         * <!-- 选区示例 -->
         * <b>xx<i>xxx</i><span>xx[x</span>xxx]</b>
         *
         * <script>
         *
         *     //执行操作
         *     range.setStartAfter( document.getElementsByTagName("i")[0] );
         *
         *     //结果选区
         *     //<b>xx<i>xxx</i>[<span>xxx</span>xxx]</b>
         *
         * </script>
         * ```
         */
        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
         * <!-- 选区示例 -->
         * <b>[xx<i>xxx</i><span>xx]x</span>xxx</b>
         *
         * <script>
         *
         *     //执行操作
         *     range.setStartAfter( document.getElementsByTagName("span")[0] );
         *
         *     //结果选区
         *     //<b>[xx<i>xxx</i><span>xxx</span>]xxx</b>
         *
         * </script>
         * ```
         */
        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
         * <!-- 选区示例 -->
         * <b>xx<i>xxx</i><span>[xx]x</span>xxx</b>
         *
         * <script>
         *
         *     //执行操作
         *     range.setStartAtFirst( document.getElementsByTagName("i")[0] );
         *
         *     //结果选区
         *     //<b>xx<i>[xxx</i><span>xx]x</span>xxx</b>
         *
         * </script>
         * ```
         */
        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
         * <!-- 选区示例 -->
         * <b>xx<i>xxx</i><span>[xx]x</span>xxx</b>
         *
         * <script>
         *
         *     //执行操作
         *     range.selectNode( document.getElementsByTagName("i")[0] );
         *
         *     //结果选区
         *     //<b>xx[<i>xxx</i>]<span>xxx</span>xxx</b>
         *
         * </script>
         * ```
         */
        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
         * <!-- 选区示例 -->
         * <b>xx<i>xxx</i><span>[xx]x</span>xxx</b>
         *
         * <script>
         *
         *     //执行操作
         *     range.selectNode( document.getElementsByTagName("b")[0] );
         *
         *     //结果选区
         *     //<b>[xx<i>xxx</i><span>xxx</span>xxx]</b>
         *
         * </script>
         * ```
         */
        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
         * <!-- 选区示例 -->
         * <b>xx<i>xxx</i><span>[xx]x</span>xxx</b>
         *
         * <script>
         *
         *     //执行操作
         *     range.collapse();
         *
         *     //结果选区
         *     //“|”表示选区已闭合
         *     //<b>xx<i>xxx</i><span>xx|x</span>xxx</b>
         *
         * </script>
         * ```
         */

        /**
         * 闭合当前选区,根据给定的toStart参数项决定是向当前选区开始处闭合还是向结束处闭合,
         * 如果toStart的值为true,则向开始位置闭合, 反之,向结束位置闭合。
         * @method  collapse
         * @param { Boolean } toStart 是否向选区开始处闭合
         * @return { UE.dom.Range } 当前range对象,此时range对象处于闭合状态
         * @see UE.dom.Range:collapse()
         * @example
         * ```html
         * <!-- 选区示例 -->
         * <b>xx<i>xxx</i><span>[xx]x</span>xxx</b>
         *
         * <script>
         *
         *     //执行操作
         *     range.collapse( true );
         *
         *     //结果选区
         *     //“|”表示选区已闭合
         *     //<b>xx<i>xxx</i><span>|xxx</span>xxx</b>
         *
         * </script>
         * ```
         */
        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
         * <span>xx<b>xx[</b>xxxxx]</span> => <span>xx<b>xx</b>[xxxxx]</span>
         * ```
         *
         * @example
         * ```html
         * <!-- 选区示例 -->
         * <b>x[xx</b><i>]xxx</i>
         *
         * <script>
         *
         *     //执行收缩
         *     range.shrinkBoundary();
         *
         *     //结果选区
         *     //<b>x[xx]</b><i>xxx</i>
         * </script>
         * ```
         *
         * @example
         * ```html
         * [<b><i>xxxx</i>xxxxxxx</b>] => <b><i>[xxxx</i>xxxxxxx]</b>
         * ```
         */

        /**
         * 调整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
         * //选区示例
         * <span>xxx<b>x[x<em>xx]x</em>xxx</b>xx</span>
         * <script>
         *
         *     var node = range.getCommonAncestor();
         *
         *     //公共祖先节点是: b节点
         *     //输出: B
         *     console.log(node.tagName);
         *
         * </script>
         * ```
         */

        /**
         * 获取当前选区所包含的所有节点的公共祖先节点, 可以根据给定的参数 includeSelf 决定获取到
         * 的公共祖先节点是否可以是当前选区的startContainer或endContainer节点, 如果 includeSelf
         * 的取值为true, 则返回的节点可以是自身的容器节点, 否则, 则不能是容器节点
         * @method  getCommonAncestor
         * @param { Boolean } includeSelf 是否允许获取到的公共祖先节点是当前range对象的容器节点
         * @return { Node } 当前range对象内所有节点的公共祖先节点
         * @see UE.dom.Range:getCommonAncestor()
         * @example
         * ```html
         * <body>
         *
         *     <!-- 选区示例 -->
         *     <b>xxx<i>xxxx<span>xx[x</span>xx]x</i>xxxxxxx</b>
         *
         *     <script>
         *
         *         var node = range.getCommonAncestor( false );
         *
         *         //这里的公共祖先节点是B而不是I, 是因为参数限制了获取到的节点不能是容器节点
         *         //output: B
         *         console.log( node.tagName );
         *
         *     </script>
         *
         * </body>
         * ```
         */

        /**
         * 获取当前选区所包含的所有节点的公共祖先节点, 可以根据给定的参数 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
         * <body>
         *
         *     <!-- 选区示例 -->
         *     <b>xxx<i>xxxx<span>x[x]x</span>xxx</i>xxxxxxx</b>
         *
         *     <script>
         *
         *         var node = range.getCommonAncestor( true, false );
         *
         *         //output: SPAN
         *         console.log( node.tagName );
         *
         *     </script>
         *
         * </body>
         * ```
         */
        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
         *
         * //选区示例
         * <b>xxx<i>[xxxxx]</i>xxx</b>
         *
         * <script>
         *     //未调整前, 选区的开始容器和结束都是文本节点
         *     //执行调整
         *     range.trimBoundary();
         *
         *     //调整之后, 容器节点变成了i节点
         *     //<b>xxx[<i>xxxxx</i>]xxx</b>
         * </script>
         * ```
         */

        /**
         * 调整当前Range的开始和结束边界容器,如果是容器节点是文本节点,就调整到包含该文本节点的父节点上,
         * 可以根据 ignoreEnd 参数的值决定是否调整对结束边界的调整
         * @method trimBoundary
         * @param { Boolean } ignoreEnd 是否忽略对结束边界的调整
         * @return { UE.dom.Range } 当前range对象
         * @example
         * ```html
         *
         * //选区示例
         * <b>xxx<i>[xxxxx]</i>xxx</b>
         *
         * <script>
         *     //未调整前, 选区的开始容器和结束都是文本节点
         *     //执行调整
         *     range.trimBoundary( true );
         *
         *     //调整之后, 开始容器节点变成了i节点
         *     //但是, 结束容器没有发生变化
         *     //<b>xxx[<i>xxxxx]</i>xxx</b>
         * </script>
         * ```
         */
        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
         * <p>xxxx[xxxx]x</p>  ==>  range.applyInlineStyle("strong")  ==>  <p>xxxx[<strong>xxxx</strong>]x</p>
         * ```
         */

        /**
         * 给range选区中的内容添加给定的inline标签, 并且为标签附加上一些初始化属性。
         * @method applyInlineStyle
         * @param { String } tagName 需要添加的标签名
         * @param { Object } attrs 跟随新添加的标签的属性
         * @return { UE.dom.Range } 当前选区
         * @example
         * ```html
         * <p>xxxx[xxxx]x</p>
         *
         * ==>
         *
         * <!-- 执行操作 -->
         * range.applyInlineStyle("strong",{"style":"font-size:12px"})
         *
         * ==>
         *
         * <p>xxxx[<strong style="font-size:12px">xxxx</strong>]x</p>
         * ```
         */
        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);
                    //针对嵌套span的全局样式指定,做容错处理
                    if (elm.tagName == "SPAN" && attrs && attrs.style) {
                        utils.each(elm.getElementsByTagName("span"), function (s) {
                            s.style.cssText = s.style.cssText + ";" + attrs.style;
                        });
                    }
                    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[x<span>xxx<em>yyy</em>zz]z</span>  => range.removeInlineStyle(["em"])  => xx[x<span>xxxyyyzz]z</span>
         * ```
         */

        /**
         * 移除当前选区内指定的一组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) {
                        //使用<span>|x<span>固定住光标
                        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);
                        //                    }
                        //
                        //处理光标落在文本节点的情况
                        //处理以下的情况
                        //<b>|xxxx</b>
                        //<b>xxxx</b>|xxxx
                        //xxxx<b>|</b>
                        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);
            offset = offset || (win.innerHeight - 100);
            // console.log('xxx',win, offset);
            var me = this,
                span = me.document.createElement("span");
            //trace:717
            span.innerHTML = "&nbsp;";
            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
         * <body>
         *     <p>
         *         aaaa
         *         <em>
         *             <!-- 选区开始 -->
         *             bbbb
         *             <!-- 选区结束 -->
         *         </em>
         *     </p>
         *
         *     <script>
         *         //output: {startAddress:[0,1,0,0],endAddress:[0,1,0,4]}
         *         console.log( range.createAddress() );
         *     </script>
         * </body>
         * ```
         */
        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
         * <body>
         *     <p>
         *         aaaa
         *         <em>
         *             <!-- 选区开始 -->
         *             bbbb
         *             <!-- 选区结束 -->
         *         </em>
         *     </p>
         *
         *     <script>
         *         var range = editor.selection.getRange();
         *         range.moveToAddress({startAddress:[0,1,0,0],endAddress:[0,1,0,4]});
         *         range.select();
         *         //output: 'bbbb'
         *         console.log(editor.selection.getText());
         *     </script>
         * </body>
         * ```
         */
        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 < l; i++) {
                    ci = address[i];
                    parentNode = tmpNode;
                    tmpNode = tmpNode.childNodes[ci];
                    if (!tmpNode) {
                        offset = ci;
                        break;
                    }
                }
                if (isStart) {
                    if (tmpNode) {
                        me.setStartBefore(tmpNode);
                    } else {
                        me.setStart(parentNode, offset);
                    }
                } else {
                    if (tmpNode) {
                        me.setEndBefore(tmpNode);
                    } else {
                        me.setEnd(parentNode, offset);
                    }
                }
            }

            getNode(addr.startAddress, true);
            !ignoreEnd && addr.endAddress && getNode(addr.endAddress);
            return me;
        },

        /**
         * 判断给定的Range对象是否和当前Range对象表示的是同一个选区
         * @method equals
         * @param { UE.dom.Range } 需要判断的Range对象
         * @return { Boolean } 如果给定的Range对象与当前Range对象表示的是同一个选区, 则返回true, 否则返回false
         */
        equals: function (rng) {
            for (var p in this) {
                if (this.hasOwnProperty(p)) {
                    if (this[p] !== rng[p]) return false;
                }
            }
            return true;
        },

        /**
         * 遍历range内的节点。每当遍历一个节点时, 都会执行参数项 doFn 指定的函数, 该函数的接受当前遍历的节点
         * 作为其参数。
         * @method traversal
         * @param { Function }  doFn 对每个遍历的节点要执行的方法, 该方法接受当前遍历的节点作为其参数
         * @return { UE.dom.Range } 当前range对象
         * @example
         * ```html
         *
         * <body>
         *
         *     <!-- 选区开始 -->
         *     <span></span>
         *     <a></a>
         *     <!-- 选区结束 -->
         * </body>
         *
         * <script>
         *
         *     //output: <span></span><a></a>
         *     console.log( range.cloneContents() );
         *
         *     range.traversal( function ( node ) {
         *
         *         if ( node.nodeType === 1 ) {
         *             node.className = "test";
         *         }
         *
         *     } );
         *
         *     //output: <span class="test"></span><a class="test"></a>
         *     console.log( range.cloneContents() );
         *
         * </script>
         * ```
         */

        /**
         * 遍历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
         *
         * <body>
         *
         *     <!-- 选区开始 -->
         *     <span></span>
         *     <a></a>
         *     <!-- 选区结束 -->
         * </body>
         *
         * <script>
         *
         *     //output: <span></span><a></a>
         *     console.log( range.cloneContents() );
         *
         *     range.traversal( function ( node ) {
         *
         *         node.className = "test";
         *
         *     }, function ( node ) {
         *          return node.nodeType === 1;
         *     } );
         *
         *     //output: <span class="test"></span><a class="test"></a>
         *     console.log( range.cloneContents() );
         *
         * </script>
         * ```
         */
        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下<b>x</b>[xx] 闭合后 <b>x|</b>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) {
        if (!editor.options.textarea) {
            return;
        }
        var textarea;
        textarea = editor.textarea;
        if (!textarea) {
            textarea = domUtils.getElementsByTagName(form, "textarea", function (node) {
                return node.id === 'ueditor_textarea_' + editor.options.textarea;
            })[0];
        }
        if (!textarea) {
            textarea = domUtils.getElementsByTagName(form, "textarea", function (node) {
                return node.name === editor.options.textarea;
            })[0];
        }
        if (!textarea) {
            form.appendChild(
                (textarea = domUtils.createElement(document, "textarea", {
                    name: editor.options.textarea,
                    id: "ueditor_textarea_" + editor.options.textarea,
                    style: "display:none"
                }))
            );
        }
        if (textarea && !editor.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?7a537435",
                    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 additionCssHtml = [];
                for (var i in options.iframeCssUrlsAddition) {
                    additionCssHtml.push("<link rel='stylesheet' type='text/css' href='" + utils.unhtml(options.iframeCssUrlsAddition[i]) + "'/>")
                }
                var html =
                    (ie && browser.version < 9 ? "" : "<!DOCTYPE html>") +
                    "<html xmlns='http://www.w3.org/1999/xhtml' class='view' >" +
                    "<head>" +
                    "<style type='text/css'>" +
                    //设置四周的留边
                    ".view{padding:0;word-wrap:break-word;cursor:text;height:90%;}\n" +
                    //设置默认字体和字号
                    //font-family不能呢随便改,在safari下fillchar会有解析问题
                    "body{margin:8px;font-family:sans-serif;font-size:16px;}" +
                    //设置段落间距
                    "p{margin:5px 0;}</style>" +
                    (options.iframeCssUrl
                        ? "<link rel='stylesheet' type='text/css' href='" +
                        utils.unhtml(options.iframeCssUrl) +
                        "'/>"
                        : "") +
                    (options.initialStyle
                        ? "<style>" + options.initialStyle + "</style>"
                        : "") +
                    additionCssHtml.join("") +
                    "</head>" +
                    "<body class='view' ></body>" +
                    "<script type='text/javascript' " +
                    (ie ? "defer='defer'" : "") +
                    " id='_initialScript'>" +
                    "setTimeout(function(){editor = window.parent.UE.instants['ueditorInstant" +
                    me.uid +
                    "'];editor._setup(document);},0);" +
                    "var _tmpScript = document.getElementById('_initialScript');_tmpScript.parentNode.removeChild(_tmpScript);" +
                    "</script>" +
                    (options.iframeJsUrl
                        ? "<script type='text/javascript' src='" +
                        utils.unhtml(options.iframeJsUrl) +
                        "'></script>"
                        : "") +
                    "</html>";

                container.appendChild(
                    domUtils.createElement(document, "iframe", {
                        id: "ueditor_" + me.uid,
                        width: "100%",
                        height: "100%",
                        frameborder: "0",
                        //先注释掉了,加的原因忘记了,但开启会直接导致全屏模式下内容多时不会出现滚动条
                        //                    scrolling :'no',
                        src:
                            "javascript:void(function(){document.open();" +
                            (options.customDomain && document.domain != location.hostname
                                ? 'document.domain="' + document.domain + '";'
                                : "") +
                            'document.write("' +
                            html +
                            '");document.close();}())'
                    })
                );
                container.style.overflow = "hidden";
                //解决如果是给定的百分比,会导致高度算不对的问题
                setTimeout(function () {
                    if (/%$/.test(options.initialFrameWidth)) {
                        options.minFrameWidth = options.initialFrameWidth =
                            container.offsetWidth;
                        //如果这里给定宽度,会导致ie在拖动窗口大小时,编辑区域不随着变化
                        //                        container.style.width = options.initialFrameWidth + 'px';
                    }
                    if (/%$/.test(options.initialFrameHeight)) {
                        options.minFrameHeight = options.initialFrameHeight =
                            container.offsetHeight;
                        container.style.height = options.initialFrameHeight + "px";
                    }
                });
            }
        },

        /**
         * 编辑器初始化
         * @method _setup
         * @private
         * @param { Element } doc 编辑器Iframe中的文档对象
         */
        _setup: function (doc) {
            var me = this,
                options = me.options;
            if (ie) {
                doc.body.disabled = true;
                doc.body.contentEditable = true;
                doc.body.disabled = false;
            } else {
                doc.body.contentEditable = true;
            }
            doc.body.spellcheck = false;
            me.document = doc;
            me.window = doc.defaultView || doc.parentWindow;
            me.iframe = me.window.frameElement;
            me.body = doc.body;
            me.selection = new dom.Selection(doc);
            //gecko初始化就能得到range,无法判断isFocus了
            var geckoSel;
            if (browser.gecko && (geckoSel = this.selection.getNative())) {
                geckoSel.removeAllRanges();
            }
            this._initEvents();
            //为form提交提供一个隐藏的textarea
            for (
                var form = this.iframe.parentNode;
                !domUtils.isBody(form);
                form = form.parentNode
            ) {
                if (form.tagName === "FORM") {
                    me.form = form;
                    if (me.options.autoSyncData) {
                        domUtils.on(me.window, "blur", function () {
                            setValue(form, me);
                        });
                        domUtils.on(form, "submit", function () {
                            me.fireEvent("beforesubmit");
                        });
                    } else {
                        domUtils.on(form, "submit", function () {
                            setValue(this, me);
                            me.fireEvent("beforesubmit");
                        });
                    }
                    break;
                }
            }
            if (options.initialContent) {
                if (options.autoClearinitialContent) {
                    var oldExecCommand = me.execCommand;
                    me.execCommand = function () {
                        me.fireEvent("firstBeforeExecCommand");
                        return oldExecCommand.apply(me, arguments);
                    };
                    this._setDefaultContent(options.initialContent);
                } else this.setContent(options.initialContent, false, true);
            }

            //编辑器不能为空内容

            if (domUtils.isEmptyNode(me.body)) {
                me.body.innerHTML = "<p>" + (browser.ie ? "" : "<br/>") + "</p>";
            }
            //如果要求focus, 就把光标定位到内容开始
            if (options.focus) {
                setTimeout(function () {
                    me.focus(me.options.focusInEnd);
                    //如果自动清除开着,就不需要做selectionchange;
                    !me.options.autoClearinitialContent && me._selectionChange();
                }, 0);
            }
            if (!me.container) {
                me.container = this.iframe.parentNode;
            }
            if (options.fullscreen && me.ui) {
                me.ui.setFullScreen(true);
            }

            try {
                me.document.execCommand("2D-position", false, false);
            } catch (e) {
            }
            try {
                me.document.execCommand("enableInlineTableEditing", false, false);
            } catch (e) {
            }
            try {
                me.document.execCommand("enableObjectResizing", false, false);
            } catch (e) {
            }

            //挂接快捷键
            me._bindshortcutKeys();
            me.isReady = 1;
            me.fireEvent("ready");
            options.onready && options.onready.call(me);
            if (!browser.ie9below) {
                domUtils.on(me.window, ["blur", "focus"], function (e) {
                    //chrome下会出现alt+tab切换时,导致选区位置不对
                    if (e.type == "blur") {
                        me._bakRange = me.selection.getRange();
                        try {
                            me._bakNativeRange = me.selection.getNative().getRangeAt(0);
                            me.selection.getNative().removeAllRanges();
                        } catch (e) {
                            me._bakNativeRange = null;
                        }
                    } else {
                        try {
                            me._bakRange && me._bakRange.select();
                        } catch (e) {
                        }
                    }
                });
            }
            //trace:1518 ff3.6body不够寛,会导致点击空白处无法获得焦点
            if (browser.gecko && browser.version <= 10902) {
                //修复ff3.6初始化进来,不能点击获得焦点
                me.body.contentEditable = false;
                setTimeout(function () {
                    me.body.contentEditable = true;
                }, 100);
                setInterval(function () {
                    me.body.style.height = me.iframe.offsetHeight - 20 + "px";
                }, 100);
            }

            !options.isShow && me.setHide();
            options.readonly && me.setDisabled();
        },

        /**
         * 同步数据到编辑器所在的form
         * 从编辑器的容器节点向上查找form元素,若找到,就同步编辑内容到找到的form里,为提交数据做准备,主要用于是手动提交的情况
         * 后台取得数据的键值,使用你容器上的name属性,如果没有就使用参数里的textarea项
         * @method sync
         * @example
         * ```javascript
         * editor.sync();
         * form.sumbit(); //form变量已经指向了form元素
         * ```
         */

        /**
         * 根据传入的formId,在页面上查找要同步数据的表单,若找到,就同步编辑内容到找到的form里,为提交数据做准备
         * 后台取得数据的键值,该键值默认使用给定的编辑器容器的name属性,如果没有name属性则使用参数项里给定的“textarea”项
         * @method sync
         * @param { String } formID 指定一个要同步数据的form的id,编辑器的数据会同步到你指定form下
         */
        sync: function (formId) {
            var me = this,
                form = formId
                    ? document.getElementById(formId)
                    : domUtils.findParent(
                        me.iframe.parentNode,
                        function (node) {
                            return node.tagName === "FORM";
                        },
                        true
                    );
            form && setValue(form, me);
        },

        /**
         * 手动触发更新按钮栏状态
         */
        syncCommandState: function () {
            this.fireEvent("selectionchange");
        },

        /**
         * 设置编辑器宽度
         * @param width
         */
        setWidth: function (width) {
            if (width !== parseInt(this.iframe.parentNode.parentNode.style.width)) {
                this.iframe.parentNode.parentNode.style.width = width + "px";
            }
        },

        /**
         * 设置编辑器高度
         * @method setHeight
         * @remind 当配置项autoHeightEnabled为真时,该方法无效
         * @param { Number } number 设置的高度值,纯数值,不带单位
         * @example
         * ```javascript
         * editor.setHeight(number);
         * ```
         */
        setHeight: function (height, notSetHeight) {
            if (height !== parseInt(this.iframe.parentNode.style.height)) {
                this.iframe.parentNode.style.height = height + "px";
            }
            !notSetHeight &&
            (this.options.minFrameHeight = this.options.initialFrameHeight = height);
            this.body.style.height = height + "px";
            !notSetHeight && this.trigger("setHeight");
        },

        /**
         * 为编辑器的编辑命令提供快捷键
         * 这个接口是为插件扩展提供的接口,主要是为新添加的插件,如果需要添加快捷键,所提供的接口
         * @method addshortcutkey
         * @param { Object } keyset 命令名和快捷键键值对对象,多个按钮的快捷键用“+”分隔
         * @example
         * ```javascript
         * editor.addshortcutkey({
         *     "Bold" : "ctrl+66",//^B
         *     "Italic" : "ctrl+73", //^I
         * });
         * ```
         */
        /**
         * 这个接口是为插件扩展提供的接口,主要是为新添加的插件,如果需要添加快捷键,所提供的接口
         * @method addshortcutkey
         * @param { String } cmd 触发快捷键时,响应的命令
         * @param { String } keys 快捷键的字符串,多个按钮用“+”分隔
         * @example
         * ```javascript
         * editor.addshortcutkey("Underline", "ctrl+85"); //^U
         * ```
         */
        addshortcutkey: function (cmd, keys) {
            var obj = {};
            if (keys) {
                obj[cmd] = keys;
            } else {
                obj = cmd;
            }
            utils.extend(this.shortcutkeys, obj);
        },

        /**
         * 对编辑器设置keydown事件监听,绑定快捷键和命令,当快捷键组合触发成功,会响应对应的命令
         * @method _bindshortcutKeys
         * @private
         */
        _bindshortcutKeys: function () {
            var me = this,
                shortcutkeys = this.shortcutkeys;
            me.addListener("keydown", function (type, e) {
                var keyCode = e.keyCode || e.which;
                for (var i in shortcutkeys) {
                    var tmp = shortcutkeys[i].split(",");
                    for (var t = 0, ti; (ti = tmp[t++]);) {
                        ti = ti.split(":");
                        var key = ti[0],
                            param = ti[1];
                        if (
                            /^(ctrl)(\+shift)?\+(\d+)$/.test(key.toLowerCase()) ||
                            /^(\d+)$/.test(key)
                        ) {
                            if (
                                ((RegExp.$1 == "ctrl" ? e.ctrlKey || e.metaKey : 0) &&
                                    (RegExp.$2 != "" ? e[RegExp.$2.slice(1) + "Key"] : 1) &&
                                    keyCode == RegExp.$3) ||
                                keyCode == RegExp.$1
                            ) {
                                if (me.queryCommandState(i, param) != -1)
                                    me.execCommand(i, param);
                                domUtils.preventDefault(e);
                            }
                        }
                    }
                }
            });
        },

        /**
         * 获取编辑器的内容
         * @method getContent
         * @warning 该方法获取到的是经过编辑器内置的过滤规则进行过滤后得到的内容
         * @return { String } 编辑器的内容字符串, 如果编辑器的内容为空,或者是空的标签内容(如:”&lt;p&gt;&lt;br/&gt;&lt;/p&gt;“), 则返回空字符串
         * @example
         * ```javascript
         * //编辑器html内容:<p>1<strong>2<em>34</em>5</strong>6</p>
         * var content = editor.getContent(); //返回值:<p>1<strong>2<em>34</em>5</strong>6</p>
         * ```
         */

        /**
         * 获取编辑器的内容。 可以通过参数定义编辑器内置的判空规则
         * @method getContent
         * @param { Function } fn 自定的判空规则, 要求该方法返回一个boolean类型的值,
         *                      代表当前编辑器的内容是否空,
         *                      如果返回true, 则该方法将直接返回空字符串;如果返回false,则编辑器将返回
         *                      经过内置过滤规则处理后的内容。
         * @remind 该方法在处理包含有初始化内容的时候能起到很好的作用。
         * @warning 该方法获取到的是经过编辑器内置的过滤规则进行过滤后得到的内容
         * @return { String } 编辑器的内容字符串
         * @example
         * ```javascript
         * // editor 是一个编辑器的实例
         * var content = editor.getContent( function ( editor ) {
         *      return editor.body.innerHTML === '欢迎使用UEditor'; //返回空字符串
         * } );
         * ```
         */
        getContent: function (cmd, fn, notSetCursor, ignoreBlank, formatter) {
            var me = this;
            if (cmd && utils.isFunction(cmd)) {
                fn = cmd;
                cmd = "";
            }
            if (fn ? !fn() : !this.hasContents()) {
                return "";
            }
            me.fireEvent("beforegetcontent");
            var root = UE.htmlparser(me.body.innerHTML, ignoreBlank);
            me.filterOutputRule(root);
            me.fireEvent("aftergetcontent", cmd, root);
            return root.toHtml(formatter);
        },

        /**
         * 取得完整的html代码,可以直接显示成完整的html文档
         * @method getAllHtml
         * @return { String } 编辑器的内容html文档字符串
         * @eaxmple
         * ```javascript
         * editor.getAllHtml(); //返回格式大致是: <html><head>...</head><body>...</body></html>
         * ```
         */
        getAllHtml: function () {
            var me = this,
                headHtml = [],
                html = "";
            me.fireEvent("getAllHtml", headHtml);
            if (browser.ie && browser.version > 8) {
                var headHtmlForIE9 = "";
                utils.each(me.document.styleSheets, function (si) {
                    headHtmlForIE9 += si.href
                        ? '<link rel="stylesheet" type="text/css" href="' + si.href + '" />'
                        : "<style>" + si.cssText + "</style>";
                });
                utils.each(me.document.getElementsByTagName("script"), function (si) {
                    headHtmlForIE9 += si.outerHTML;
                });
            }
            return (
                "<html><head>" +
                (me.options.charset
                    ? '<meta http-equiv="Content-Type" content="text/html; charset=' +
                    me.options.charset +
                    '"/>'
                    : "") +
                (headHtmlForIE9 ||
                    me.document.getElementsByTagName("head")[0].innerHTML) +
                headHtml.join("\n") +
                "</head>" +
                "<body " +
                (ie && browser.version < 9 ? 'class="view"' : "") +
                ">" +
                me.getContent(null, null, true) +
                "</body></html>"
            );
        },

        /**
         * 得到编辑器的纯文本内容,但会保留段落格式
         * @method getPlainTxt
         * @return { String } 编辑器带段落格式的纯文本内容字符串
         * @example
         * ```javascript
         * //编辑器html内容:<p><strong>1</strong></p><p><strong>2</strong></p>
         * console.log(editor.getPlainTxt()); //输出:"1\n2\n
         * ```
         */
        getPlainTxt: function () {
            var reg = new RegExp(domUtils.fillChar, "g"),
                html = this.body.innerHTML.replace(/[\n\r]/g, ""); //ie要先去了\n在处理
            html = html
                .replace(/<(p|div)[^>]*>(<br\/?>|&nbsp;)<\/\1>/gi, "\n")
                .replace(/<br\/?>/gi, "\n")
                .replace(/<[^>/]+>/g, "")
                .replace(/(\n)?<\/([^>]+)>/g, function (a, b, c) {
                    return dtd.$block[c] ? "\n" : b ? b : "";
                });
            //取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0
            return html
                .replace(reg, "")
                .replace(/\u00a0/g, " ")
                .replace(/&nbsp;/g, " ");
        },

        /**
         * 获取编辑器中的纯文本内容,没有段落格式
         * @method getContentTxt
         * @return { String } 编辑器不带段落格式的纯文本内容字符串
         * @example
         * ```javascript
         * //编辑器html内容:<p><strong>1</strong></p><p><strong>2</strong></p>
         * console.log(editor.getPlainTxt()); //输出:"12
         * ```
         */
        getContentTxt: function () {
            var reg = new RegExp(domUtils.fillChar, "g");
            //取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0
            return this.body[browser.ie ? "innerText" : "textContent"]
                .replace(reg, "")
                .replace(/\u00a0/g, " ");
        },

        /**
         * 设置编辑器的内容,可修改编辑器当前的html内容
         * @method setContent
         * @warning 通过该方法插入的内容,是经过编辑器内置的过滤规则进行过滤后得到的内容
         * @warning 该方法会触发selectionchange事件
         * @param { String } html 要插入的html内容
         * @example
         * ```javascript
         * editor.getContent('<p>test</p>');
         * ```
         */

        /**
         * 设置编辑器的内容,可修改编辑器当前的html内容
         * @method setContent
         * @warning 通过该方法插入的内容,是经过编辑器内置的过滤规则进行过滤后得到的内容
         * @warning 该方法会触发selectionchange事件
         * @param { String } html 要插入的html内容
         * @param { Boolean } isAppendTo 若传入true,不清空原来的内容,在最后插入内容,否则,清空内容再插入
         * @param { Boolean } notFireSelectionchange 是否阻止触发选区变化,true为阻止,false为不阻止
         * @example
         * ```javascript
         * //假设设置前的编辑器内容是 <p>old text</p>
         * editor.setContent('<p>new text</p>', true); //插入的结果是<p>old text</p><p>new text</p>
         * ```
         */
        setContent: function (html, isAppendTo, notFireSelectionchange) {
            var me = this;

            me.fireEvent("beforesetcontent", html);
            var root = UE.htmlparser(html);
            me.filterInputRule(root);
            html = root.toHtml();

            me.body.innerHTML = (isAppendTo ? me.body.innerHTML : "") + html;

            function isCdataDiv(node) {
                return node.tagName == "DIV" && node.getAttribute("cdata_tag");
            }

            //给文本或者inline节点套p标签
            if (me.options.enterTag == "p") {
                var child = this.body.firstChild,
                    tmpNode;
                if (
                    !child ||
                    (child.nodeType == 1 &&
                        (dtd.$cdata[child.tagName] ||
                            isCdataDiv(child) ||
                            domUtils.isCustomeNode(child)) &&
                        child === this.body.lastChild)
                ) {
                    this.body.innerHTML =
                        "<p>" +
                        (browser.ie ? "&nbsp;" : "<br/>") +
                        "</p>" +
                        this.body.innerHTML;
                } else {
                    var p = me.document.createElement("p");
                    while (child) {
                        while (
                            child &&
                            (child.nodeType == 3 ||
                                (child.nodeType == 1 &&
                                    dtd.p[child.tagName] &&
                                    !dtd.$cdata[child.tagName]))
                            ) {
                            tmpNode = child.nextSibling;
                            p.appendChild(child);
                            child = tmpNode;
                        }
                        if (p.firstChild) {
                            if (!child) {
                                me.body.appendChild(p);
                                break;
                            } else {
                                child.parentNode.insertBefore(p, child);
                                p = me.document.createElement("p");
                            }
                        }
                        child = child.nextSibling;
                    }
                }
            }
            me.fireEvent("aftersetcontent");
            me.fireEvent("contentchange");

            !notFireSelectionchange && me._selectionChange();
            //清除保存的选区
            me._bakRange = me._bakIERange = me._bakNativeRange = null;
            //trace:1742 setContent后gecko能得到焦点问题
            var geckoSel;
            if (browser.gecko && (geckoSel = this.selection.getNative())) {
                geckoSel.removeAllRanges();
            }
            if (me.options.autoSyncData) {
                me.form && setValue(me.form, me);
            }
        },

        /**
         * 让编辑器获得焦点,默认focus到编辑器头部
         * @method focus
         * @example
         * ```javascript
         * editor.focus()
         * ```
         */

        /**
         * 让编辑器获得焦点,toEnd确定focus位置
         * @method focus
         * @param { Boolean } toEnd 默认focus到编辑器头部,toEnd为true时focus到内容尾部
         * @example
         * ```javascript
         * editor.focus(true)
         * ```
         */
        focus: function (toEnd) {
            try {
                var me = this,
                    rng = me.selection.getRange();
                if (toEnd) {
                    var node = me.body.lastChild;
                    if (node && node.nodeType == 1 && !dtd.$empty[node.tagName]) {
                        if (domUtils.isEmptyBlock(node)) {
                            rng.setStartAtFirst(node);
                        } else {
                            rng.setStartAtLast(node);
                        }
                        rng.collapse(true);
                    }
                    rng.setCursor(true);
                } else {
                    if (
                        !rng.collapsed &&
                        domUtils.isBody(rng.startContainer) &&
                        rng.startOffset == 0
                    ) {
                        var node = me.body.firstChild;
                        if (node && node.nodeType == 1 && !dtd.$empty[node.tagName]) {
                            rng.setStartAtFirst(node).collapse(true);
                        }
                    }

                    rng.select(true);
                }
                this.fireEvent("focus selectionchange");
            } catch (e) {
            }
        },
        isFocus: function () {
            return this.selection.isFocus();
        },
        blur: function () {
            var sel = this.selection.getNative();
            if (sel.empty && browser.ie) {
                var nativeRng = document.body.createTextRange();
                nativeRng.moveToElementText(document.body);
                nativeRng.collapse(true);
                nativeRng.select();
                sel.empty();
            } else {
                sel.removeAllRanges();
            }

            //this.fireEvent('blur selectionchange');
        },
        /**
         * 初始化UE事件及部分事件代理
         * @method _initEvents
         * @private
         */
        _initEvents: function () {
            var me = this,
                doc = me.document,
                win = me.window;
            me._proxyDomEvent = utils.bind(me._proxyDomEvent, me);
            domUtils.on(
                doc,
                [
                    "click",
                    "contextmenu",
                    "mousedown",
                    "keydown",
                    "keyup",
                    "keypress",
                    "mouseup",
                    "mouseover",
                    "mouseout",
                    "selectstart"
                ],
                me._proxyDomEvent
            );
            domUtils.on(win, ["focus", "blur"], me._proxyDomEvent);
            domUtils.on(me.body, "drop", function (e) {
                //阻止ff下默认的弹出新页面打开图片
                if (browser.gecko && e.stopPropagation) {
                    e.stopPropagation();
                }
                me.fireEvent("contentchange");
            });
            // 当内容最末尾为非字符时,比较难以在最后插入字符,所以在点击时,自动添加一个空的p标签
            domUtils.on(me.body, "dblclick", function (e) {
                try {
                    var node = me.body.lastChild;
                    if (!node) {
                        return;
                    }
                    var rect = node.getBoundingClientRect();
                    if (e.clientY > rect.top + rect.height) {
                        var p = document.createElement('p');
                        p.innerHTML = '<br />';
                        me.body.appendChild(p);
                        setTimeout(function () {
                            me.focus(true);
                        }, 100);
                    }
                } catch (e) {
                    console.error('auto insert p at end', e);
                }
            });
            domUtils.on(doc, ["mouseup", "keydown"], function (evt) {
                //特殊键不触发selectionchange
                if (
                    evt.type === "keydown" &&
                    (evt.ctrlKey || evt.metaKey || evt.shiftKey || evt.altKey)
                ) {
                    return;
                }
                if (evt.button === 2) return;
                me._selectionChange(250, evt);
            });
        },
        /**
         * 触发事件代理
         * @method _proxyDomEvent
         * @private
         * @return { * } fireEvent的返回值
         * @see UE.EventBase:fireEvent(String)
         */
        _proxyDomEvent: function (evt) {
            if (
                this.fireEvent("before" + evt.type.replace(/^on/, "").toLowerCase()) ===
                false
            ) {
                return false;
            }
            if (this.fireEvent(evt.type.replace(/^on/, ""), evt) === false) {
                return false;
            }
            return this.fireEvent(
                "after" + evt.type.replace(/^on/, "").toLowerCase()
            );
        },
        /**
         * 变化选区
         * @method _selectionChange
         * @private
         */
        _selectionChange: function (delay, evt) {
            var me = this;
            //有光标才做selectionchange 为了解决未focus时点击source不能触发更改工具栏状态的问题(source命令notNeedUndo=1)
            //            if ( !me.selection.isFocus() ){
            //                return;
            //            }

            var hackForMouseUp = false;
            var mouseX, mouseY;
            if (browser.ie && browser.version < 9 && evt && evt.type == "mouseup") {
                var range = this.selection.getRange();
                if (!range.collapsed) {
                    hackForMouseUp = true;
                    mouseX = evt.clientX;
                    mouseY = evt.clientY;
                }
            }
            clearTimeout(_selectionChangeTimer);
            _selectionChangeTimer = setTimeout(function () {
                if (!me.selection || !me.selection.getNative()) {
                    return;
                }
                //修复一个IE下的bug: 鼠标点击一段已选择的文本中间时,可能在mouseup后的一段时间内取到的range是在selection的type为None下的错误值.
                //IE下如果用户是拖拽一段已选择文本,则不会触发mouseup事件,所以这里的特殊处理不会对其有影响
                var ieRange;
                if (hackForMouseUp && me.selection.getNative().type == "None") {
                    ieRange = me.document.body.createTextRange();
                    try {
                        ieRange.moveToPoint(mouseX, mouseY);
                    } catch (ex) {
                        ieRange = null;
                    }
                }
                var bakGetIERange;
                if (ieRange) {
                    bakGetIERange = me.selection.getIERange;
                    me.selection.getIERange = function () {
                        return ieRange;
                    };
                }
                me.selection.cache();
                if (bakGetIERange) {
                    me.selection.getIERange = bakGetIERange;
                }
                if (me.selection._cachedRange && me.selection._cachedStartElement) {
                    me.fireEvent("beforeselectionchange");
                    // 第二个参数causeByUi为true代表由用户交互造成的selectionchange.
                    me.fireEvent("selectionchange", !!evt);
                    me.fireEvent("afterselectionchange");
                    me.selection.clear();
                }
            }, delay || 50);
        },

        /**
         * 执行编辑命令
         * @method _callCmdFn
         * @private
         * @param { String } fnName 函数名称
         * @param { * } args 传给命令函数的参数
         * @return { * } 返回命令函数运行的返回值
         */
        _callCmdFn: function (fnName, args) {
            var cmdName = args[0].toLowerCase(),
                cmd,
                cmdFn;
            cmd = this.commands[cmdName] || UE.commands[cmdName];
            cmdFn = cmd && cmd[fnName];
            //没有querycommandstate或者没有command的都默认返回0
            if ((!cmd || !cmdFn) && fnName == "queryCommandState") {
                return 0;
            } else if (cmdFn) {
                return cmdFn.apply(this, args);
            }
        },

        /**
         * 执行编辑命令cmdName,完成富文本编辑效果
         * @method execCommand
         * @param { String } cmdName 需要执行的命令
         * @remind 具体命令的使用请参考<a href="#COMMAND.LIST">命令列表</a>
         * @return { * } 返回命令函数运行的返回值
         * @example
         * ```javascript
         * editor.execCommand(cmdName);
         * ```
         */
        execCommand: function (cmdName) {
            cmdName = cmdName.toLowerCase();
            var me = this,
                result,
                cmd = me.commands[cmdName] || UE.commands[cmdName];
            if (!cmd || !cmd.execCommand) {
                return null;
            }
            if (!cmd.notNeedUndo && !me.__hasEnterExecCommand) {
                me.__hasEnterExecCommand = true;
                if (me.queryCommandState.apply(me, arguments) != -1) {
                    me.fireEvent("saveScene");
                    me.fireEvent.apply(
                        me,
                        ["beforeexeccommand", cmdName].concat(arguments)
                    );
                    result = this._callCmdFn("execCommand", arguments);
                    //保存场景时,做了内容对比,再看是否进行contentchange触发,这里多触发了一次,去掉
                    //                    (!cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange');
                    me.fireEvent.apply(
                        me,
                        ["afterexeccommand", cmdName].concat(arguments)
                    );
                    me.fireEvent("saveScene");
                }
                me.__hasEnterExecCommand = false;
            } else {
                result = this._callCmdFn("execCommand", arguments);
                !me.__hasEnterExecCommand &&
                !cmd.ignoreContentChange &&
                !me._ignoreContentChange &&
                me.fireEvent("contentchange");
            }
            !me.__hasEnterExecCommand &&
            !cmd.ignoreContentChange &&
            !me._ignoreContentChange &&
            me._selectionChange();
            return result;
        },

        /**
         * 根据传入的command命令,查选编辑器当前的选区,返回命令的状态
         * @method  queryCommandState
         * @param { String } cmdName 需要查询的命令名称
         * @remind 具体命令的使用请参考<a href="#COMMAND.LIST">命令列表</a>
         * @return { Number } number 返回放前命令的状态,返回值三种情况:(-1|0|1)
         * @example
         * ```javascript
         * editor.queryCommandState(cmdName)  => (-1|0|1)
         * ```
         * @see COMMAND.LIST
         */
        queryCommandState: function (cmdName) {
            return this._callCmdFn("queryCommandState", arguments);
        },

        /**
         * 根据传入的command命令,查选编辑器当前的选区,根据命令返回相关的值
         * @method queryCommandValue
         * @param { String } cmdName 需要查询的命令名称
         * @remind 具体命令的使用请参考<a href="#COMMAND.LIST">命令列表</a>
         * @remind 只有部分插件有此方法
         * @return { * } 返回每个命令特定的当前状态值
         * @grammar editor.queryCommandValue(cmdName)  =>  {*}
         * @see COMMAND.LIST
         */
        queryCommandValue: function (cmdName) {
            return this._callCmdFn("queryCommandValue", arguments);
        },

        /**
         * 检查编辑区域中是否有内容
         * @method  hasContents
         * @remind 默认有文本内容,或者有以下节点都不认为是空
         * table,ul,ol,dl,iframe,area,base,col,hr,img,embed,input,link,meta,param
         * @return { Boolean } 检查有内容返回true,否则返回false
         * @example
         * ```javascript
         * editor.hasContents()
         * ```
         */

        /**
         * 检查编辑区域中是否有内容,若包含参数tags中的节点类型,直接返回true
         * @method  hasContents
         * @param { Array } tags 传入数组判断时用到的节点类型
         * @return { Boolean } 若文档中包含tags数组里对应的tag,返回true,否则返回false
         * @example
         * ```javascript
         * editor.hasContents(['span']);
         * ```
         */
        hasContents: function (tags) {
            if (tags) {
                for (var i = 0, ci; (ci = tags[i++]);) {
                    if (this.document.getElementsByTagName(ci).length > 0) {
                        return true;
                    }
                }
            }
            if (!domUtils.isEmptyBlock(this.body)) {
                return true;
            }
            // 随时添加,定义的特殊标签如果存在,不能认为是空
            tags = ["div"];
            for (i = 0; (ci = tags[i++]);) {
                var nodes = domUtils.getElementsByTagName(this.document, ci);
                for (var n = 0, cn; (cn = nodes[n++]);) {
                    if (domUtils.isCustomeNode(cn)) {
                        return true;
                    }
                }
            }
            // 部分如媒体标签,不能认为为空
            tags = ["video", "iframe"]
            for (i = 0; (ci = tags[i++]);) {
                var nodes = domUtils.getElementsByTagName(this.document, ci);
                for (var n = 0, cn; (cn = nodes[n++]);) {
                    return true;
                }
            }
            return false;
        },

        /**
         * 重置编辑器,可用来做多个tab 使用同一个编辑器实例
         * @method  reset
         * @remind 此方法会清空编辑器内容,清空回退列表,会触发reset事件
         * @example
         * ```javascript
         * editor.reset()
         * ```
         */
        reset: function () {
            this.clear();
            this.fireEvent("reset");
        },

        /**
         * 清空编辑器内容
         * @method clear
         * @remind 此方法会清空编辑器内容
         * @example
         * ```javascript
         * editor.clear()
         * ```
         */
        clear: function () {
            this.setContent("");
        },

        /**
         * 设置当前编辑区域可以编辑
         * @method setEnabled
         * @example
         * ```javascript
         * editor.setEnabled()
         * ```
         */
        setEnabled: function () {
            var me = this,
                range;
            if (me.body.contentEditable === "false") {
                me.body.contentEditable = true;
                range = me.selection.getRange();
                //有可能内容丢失了
                try {
                    range.moveToBookmark(me.lastBk);
                    delete me.lastBk;
                } catch (e) {
                    range.setStartAtFirst(me.body).collapse(true);
                }
                range.select(true);
                if (me.bkqueryCommandState) {
                    me.queryCommandState = me.bkqueryCommandState;
                    delete me.bkqueryCommandState;
                }
                if (me.bkqueryCommandValue) {
                    me.queryCommandValue = me.bkqueryCommandValue;
                    delete me.bkqueryCommandValue;
                }
                me.fireEvent("selectionchange");
            }
        },
        enable: function () {
            return this.setEnabled();
        },

        /** 设置当前编辑区域不可编辑
         * @method setDisabled
         */

        /** 设置当前编辑区域不可编辑,except中的命令除外
         * @method setDisabled
         * @param { String } except 例外命令的字符串
         * @remind 即使设置了disable,此处配置的例外命令仍然可以执行
         * @example
         * ```javascript
         * editor.setDisabled('bold'); //禁用工具栏中除加粗之外的所有功能
         * ```
         */

        /** 设置当前编辑区域不可编辑,except中的命令除外
         * @method setDisabled
         * @param { Array } except 例外命令的字符串数组,数组中的命令仍然可以执行
         * @remind 即使设置了disable,此处配置的例外命令仍然可以执行
         * @example
         * ```javascript
         * editor.setDisabled(['bold','insertimage']); //禁用工具栏中除加粗和插入图片之外的所有功能
         * ```
         */
        setDisabled: function (except) {
            var me = this;
            except = except ? (utils.isArray(except) ? except : [except]) : [];
            if (me.body.contentEditable == "true") {
                if (!me.lastBk) {
                    me.lastBk = me.selection.getRange().createBookmark(true);
                }
                me.body.contentEditable = false;
                me.bkqueryCommandState = me.queryCommandState;
                me.bkqueryCommandValue = me.queryCommandValue;
                me.queryCommandState = function (type) {
                    if (utils.indexOf(except, type) != -1) {
                        return me.bkqueryCommandState.apply(me, arguments);
                    }
                    return -1;
                };
                me.queryCommandValue = function (type) {
                    if (utils.indexOf(except, type) != -1) {
                        return me.bkqueryCommandValue.apply(me, arguments);
                    }
                    return null;
                };
                me.fireEvent("selectionchange");
            }
        },
        disable: function (except) {
            return this.setDisabled(except);
        },

        /**
         * 设置默认内容
         * @method _setDefaultContent
         * @private
         * @param  { String } cont 要存入的内容
         */
        _setDefaultContent: (function () {
            function clear() {
                var me = this;
                if (me.document.getElementById("initContent")) {
                    me.body.innerHTML = "<p>" + (ie ? "" : "<br/>") + "</p>";
                    me.removeListener("firstBeforeExecCommand focus", clear);
                    setTimeout(function () {
                        me.focus();
                        me._selectionChange();
                    }, 0);
                }
            }

            return function (cont) {
                var me = this;
                me.body.innerHTML = '<p id="initContent">' + cont + "</p>";

                me.addListener("firstBeforeExecCommand focus", clear);
            };
        })(),

        /**
         * 显示编辑器
         * @method setShow
         * @example
         * ```javascript
         * editor.setShow()
         * ```
         */
        setShow: function () {
            var me = this,
                range = me.selection.getRange();
            if (me.container.style.display == "none") {
                //有可能内容丢失了
                try {
                    range.moveToBookmark(me.lastBk);
                    delete me.lastBk;
                } catch (e) {
                    range.setStartAtFirst(me.body).collapse(true);
                }
                //ie下focus实效,所以做了个延迟
                setTimeout(function () {
                    range.select(true);
                }, 100);
                me.container.style.display = "";
            }
        },
        show: function () {
            return this.setShow();
        },
        /**
         * 隐藏编辑器
         * @method setHide
         * @example
         * ```javascript
         * editor.setHide()
         * ```
         */
        setHide: function () {
            var me = this;
            if (!me.lastBk) {
                me.lastBk = me.selection.getRange().createBookmark(true);
            }
            me.container.style.display = "none";
        },
        hide: function () {
            return this.setHide();
        },

        /**
         * 根据指定的路径,获取对应的语言资源
         * @method getLang
         * @param { String } path 路径根据的是lang目录下的语言文件的路径结构
         * @return { Object | String } 根据路径返回语言资源的Json格式对象或者语言字符串
         * @example
         * ```javascript
         * editor.getLang('contextMenu.delete'); //如果当前是中文,那返回是的是'删除'
         * ```
         */
        getLang: function (path) {
            var lang = UE.I18N[this.options.lang];
            if (!lang) {
                throw Error("not import language file");
            }
            path = (path || "").split(".");
            for (var i = 0, ci; (ci = path[i++]);) {
                lang = lang[ci];
                if (!lang) break;
            }
            return lang;
        },

        /**
         * 计算编辑器html内容字符串的长度
         * @method  getContentLength
         * @return { Number } 返回计算的长度
         * @example
         * ```javascript
         * //编辑器html内容<p><strong>132</strong></p>
         * editor.getContentLength() //返回27
         * ```
         */
        /**
         * 计算编辑器当前纯文本内容的长度
         * @method  getContentLength
         * @param { Boolean } ingoneHtml 传入true时,只按照纯文本来计算
         * @return { Number } 返回计算的长度,内容中有hr/img/iframe标签,长度加1
         * @example
         * ```javascript
         * //编辑器html内容<p><strong>132</strong></p>
         * editor.getContentLength() //返回3
         * ```
         */
        getContentLength: function (ingoneHtml, tagNames) {
            var count = this.getContent(false, false, true).length;
            if (ingoneHtml) {
                tagNames = (tagNames || []).concat(["hr", "img", "iframe"]);
                count = this.getContentTxt().replace(/[\t\r\n]+/g, "").length;
                for (var i = 0, ci; (ci = tagNames[i++]);) {
                    count += this.document.getElementsByTagName(ci).length;
                }
            }
            return count;
        },

        getScrollTop: function () {
            return Math.max(this.document.documentElement.scrollTop, this.document.body.scrollTop);
        },
        getScrollLeft: function () {
            return Math.max(this.document.documentElement.scrollLeft, this.document.body.scrollLeft);
        },

        /**
         * 注册输入过滤规则
         * @method  addInputRule
         * @param { Function } rule 要添加的过滤规则
         * @example
         * ```javascript
         * editor.addInputRule(function(root){
         *   $.each(root.getNodesByTagName('div'),function(i,node){
         *       node.tagName="p";
         *   });
         * });
         * ```
         */
        addInputRule: function (rule) {
            this.inputRules.push(rule);
        },

        /**
         * 执行注册的过滤规则
         * @method  filterInputRule
         * @param { UE.uNode } root 要过滤的uNode节点
         * @remind 执行editor.setContent方法和执行'inserthtml'命令后,会运行该过滤函数
         * @example
         * ```javascript
         * editor.filterInputRule(editor.body);
         * ```
         * @see UE.Editor:addInputRule
         */
        filterInputRule: function (root) {
            for (var i = 0, ci; (ci = this.inputRules[i++]);) {
                ci.call(this, root);
            }
        },

        /**
         * 注册输出过滤规则
         * @method  addOutputRule
         * @param { Function } rule 要添加的过滤规则
         * @example
         * ```javascript
         * editor.addOutputRule(function(root){
         *   $.each(root.getNodesByTagName('p'),function(i,node){
         *       node.tagName="div";
         *   });
         * });
         * ```
         */
        addOutputRule: function (rule) {
            this.outputRules.push(rule);
        },

        /**
         * 根据输出过滤规则,过滤编辑器内容
         * @method  filterOutputRule
         * @remind 执行editor.getContent方法的时候,会先运行该过滤函数
         * @param { UE.uNode } root 要过滤的uNode节点
         * @example
         * ```javascript
         * editor.filterOutputRule(editor.body);
         * ```
         * @see UE.Editor:addOutputRule
         */
        filterOutputRule: function (root) {
            for (var i = 0, ci; (ci = this.outputRules[i++]);) {
                ci.call(this, root);
            }
        },

        /**
         * 根据action名称获取请求的路径
         * @method  getActionUrl
         * @remind 假如没有设置serverUrl,会根据imageUrl设置默认的controller路径
         * @param { String } action action名称
         * @example
         * ```javascript
         * editor.getActionUrl('config'); //返回 "/ueditor/php/controller.php?action=config"
         * editor.getActionUrl('image'); //返回 "/ueditor/php/controller.php?action=uplaodimage"
         * editor.getActionUrl('scrawl'); //返回 "/ueditor/php/controller.php?action=uplaodscrawl"
         * editor.getActionUrl('imageManager'); //返回 "/ueditor/php/controller.php?action=listimage"
         * ```
         */
        getActionUrl: function (action) {
            var serverUrl = this.getOpt("serverUrl")
            if (!action) {
                return serverUrl;
            }
            var actionName = this.getOpt(action) || action,
                imageUrl = this.getOpt("imageUrl");

            if (!serverUrl && imageUrl) {
                serverUrl = imageUrl.replace(/^(.*[\/]).+([\.].+)$/, "$1controller$2");
            }
            if (serverUrl) {
                serverUrl =
                    serverUrl +
                    (serverUrl.indexOf("?") === -1 ? "?" : "&") +
                    "action=" +
                    (actionName || "");
                return utils.formatUrl(serverUrl);
            } else {
                return "";
            }
        }
    };
    utils.inherits(Editor, EventBase);
})();


// core/Editor.defaultoptions.js
//维护编辑器一下默认的不在插件中的配置项
UE.Editor.defaultOptions = function (editor) {
    var _url = editor.options.UEDITOR_HOME_URL;
    return {
        isShow: true,
        initialContent: "",
        initialStyle: "",
        autoClearinitialContent: false,
        iframeCssUrl: _url + "themes/iframe.css?c20ec247",
        iframeCssUrlsAddition: [],
        textarea: '',
        focus: false,
        focusInEnd: true,
        autoClearEmptyNode: true,
        fullscreen: false,
        readonly: false,
        zIndex: 999,
        imagePopup: true,
        enterTag: "p",
        customDomain: false,
        lang: "zh-cn",
        langPath: _url + "lang/",
        theme: "default",
        themePath: _url + "themes/",
        allHtmlEnabled: false,
        scaleEnabled: false,
        tableNativeEditInFF: false,
        autoSyncData: true,
        fileNameFormat: "{time}{rand:6}"
    };
};


// core/loadconfig.js
(function () {
    UE.Editor.prototype.loadServerConfig = function () {
        var me = this;
        setTimeout(function () {
            try {
                me.options.imageUrl &&
                me.setOpt(
                    "serverUrl",
                    me.options.imageUrl.replace(
                        /^(.*[\/]).+([\.].+)$/,
                        "$1controller$2"
                    )
                );

                var configUrl = me.getActionUrl("config"),
                    // isJsonp = utils.isCrossDomainUrl(configUrl);
                  isJsonp = false;

                /* 发出ajax请求 */
                me._serverConfigLoaded = false;

                configUrl &&
                UE.ajax.request(configUrl, {
                    method: "GET",
                    dataType: isJsonp ? "jsonp" : "",
                    headers: me.options.serverHeaders || {},
                    onsuccess: function (r) {
                        try {
                            var config = isJsonp ? r : eval("(" + r.responseText + ")");
                            config = me.options.serverResponsePrepare( config )
                            // console.log('me.options.before', me.options.audioConfig);
                            me.options = utils.merge(me.options, config);
                            // console.log('server.config', config.audioConfig);
                            // console.log('me.options.after', me.options.audioConfig);
                            me.fireEvent("serverConfigLoaded");
                            me._serverConfigLoaded = true;
                        } catch (e) {
                            showErrorMsg(me.getLang("loadconfigFormatError"));
                        }
                    },
                    onerror: function () {
                        showErrorMsg(me.getLang("loadconfigHttpError"));
                    }
                });
            } catch (e) {
                showErrorMsg(me.getLang("loadconfigError"));
            }
        });

        function showErrorMsg(msg) {
            console && console.error(msg);
            //me.fireEvent('showMessage', {
            //    'title': msg,
            //    'type': 'error'
            //});
        }
    };

    UE.Editor.prototype.isServerConfigLoaded = function () {
        var me = this;
        return me._serverConfigLoaded || false;
    };

    UE.Editor.prototype.afterConfigReady = function (handler) {
        if (!handler || !utils.isFunction(handler)) return;
        var me = this;
        var readyHandler = function () {
            handler.apply(me, arguments);
            me.removeListener("serverConfigLoaded", readyHandler);
        };

        if (me.isServerConfigLoaded()) {
            handler.call(me, "serverConfigLoaded");
        } else {
            me.addListener("serverConfigLoaded", readyHandler);
        }
    };
})();


// core/ajax.js
/**
 * @file
 * @module UE.ajax
 * @since 1.2.6.1
 */

/**
 * 提供对ajax请求的支持
 * @module UE.ajax
 */
UE.ajax = (function () {
    //创建一个ajaxRequest对象
    var fnStr = "XMLHttpRequest()";
    try {
        new ActiveXObject("Msxml2.XMLHTTP");
        fnStr = "ActiveXObject('Msxml2.XMLHTTP')";
    } catch (e) {
        try {
            new ActiveXObject("Microsoft.XMLHTTP");
            fnStr = "ActiveXObject('Microsoft.XMLHTTP')";
        } catch (e) {
        }
    }
    var creatAjaxRequest = new Function("return new " + fnStr);

    /**
     * 将json参数转化成适合ajax提交的参数列表
     * @param json
     */
    function json2str(json) {
        var strArr = [];
        for (var i in json) {
            //忽略默认的几个参数
            if (
                i == "method" ||
                i == "timeout" ||
                i == "async" ||
                i == "dataType" ||
                i == "callback"
            )
                continue;
            //忽略控制
            if (json[i] == undefined || json[i] == null) 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("&");
    }

    function doAjax(url, ajaxOptions) {
        var xhr = creatAjaxRequest(),
            //是否超时
            timeIsOut = false,
            //默认参数
            defaultAjaxOptions = {
                method: "POST",
                timeout: 5000,
                async: true,
                headers: {},
                data: {}, //需要传递对象的话只能覆盖
                onsuccess: function () {
                },
                onerror: function () {
                }
            };

        if (typeof url === "object") {
            ajaxOptions = url;
            url = ajaxOptions.url;
        }
        if (!xhr || !url) return;
        var ajaxOpts = ajaxOptions
            ? utils.extend(defaultAjaxOptions, ajaxOptions)
            : defaultAjaxOptions;

        // console.log('ajaxOpts',ajaxOpts);

        var submitStr = json2str(ajaxOpts); // { name:"Jim",city:"Beijing" } --> "name=Jim&city=Beijing"
        //如果用户直接通过data参数传递json对象过来,则也要将此json对象转化为字符串
        if (!utils.isEmptyObject(ajaxOpts.data)) {
            submitStr += (submitStr ? "&" : "") + json2str(ajaxOpts.data);
        }
        //超时检测
        var timerID = setTimeout(function () {
            if (xhr.readyState !== 4) {
                timeIsOut = true;
                xhr.abort();
                clearTimeout(timerID);
            }
        }, ajaxOpts.timeout);

        var method = ajaxOpts.method.toUpperCase();
        var str =
            url +
            (url.indexOf("?") === -1 ? "?" : "&") +
            (method === "POST" ? "" : submitStr + "&noCache=" + +new Date());
        xhr.open(method, str, ajaxOpts.async);
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (!timeIsOut && xhr.status === 200) {
                    ajaxOpts.onsuccess(xhr);
                } else {
                    ajaxOpts.onerror(xhr);
                }
            }
        };
        if (ajaxOpts.headers) {
            for (var key in ajaxOpts.headers) {
                xhr.setRequestHeader(key, ajaxOpts.headers[key]);
            }
        }
        if (method === "POST") {
            xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            xhr.send(submitStr);
        } else {
            xhr.send(null);
        }
    }

    function doJsonp(url, opts) {
        var successhandler = opts.onsuccess || function () {
            },
            scr = document.createElement("SCRIPT"),
            options = opts || {},
            charset = options["charset"],
            callbackField = options["jsonp"] || "callback",
            callbackFnName,
            timeOut = options["timeOut"] || 0,
            timer,
            reg = new RegExp("(\\?|&)" + callbackField + "=([^&]*)"),
            matches;

        if (utils.isFunction(successhandler)) {
            callbackFnName =
                "bd__editor__" + Math.floor(Math.random() * 2147483648).toString(36);
            window[callbackFnName] = getCallBack(0);
        } else if (utils.isString(successhandler)) {
            callbackFnName = successhandler;
        } else {
            if ((matches = reg.exec(url))) {
                callbackFnName = matches[2];
            }
        }

        url = url.replace(reg, "\x241" + callbackField + "=" + callbackFnName);

        if (url.search(reg) < 0) {
            url +=
                (url.indexOf("?") < 0 ? "?" : "&") +
                callbackField +
                "=" +
                callbackFnName;
        }

        var queryStr = json2str(opts); // { name:"Jim",city:"Beijing" } --> "name=Jim&city=Beijing"
        //如果用户直接通过data参数传递json对象过来,则也要将此json对象转化为字符串
        if (!utils.isEmptyObject(opts.data)) {
            queryStr += (queryStr ? "&" : "") + json2str(opts.data);
        }
        if (queryStr) {
            url = url.replace(/\?/, "?" + queryStr + "&");
        }

        scr.onerror = getCallBack(1);
        if (timeOut) {
            timer = setTimeout(getCallBack(1), timeOut);
        }
        createScriptTag(scr, url, charset);

        function createScriptTag(scr, url, charset) {
            scr.setAttribute("type", "text/javascript");
            scr.setAttribute("defer", "defer");
            charset && scr.setAttribute("charset", charset);
            scr.setAttribute("src", url);
            document.getElementsByTagName("head")[0].appendChild(scr);
        }

        function getCallBack(onTimeOut) {
            return function () {
                try {
                    if (onTimeOut) {
                        options.onerror && options.onerror();
                    } else {
                        try {
                            clearTimeout(timer);
                            successhandler.apply(window, arguments);
                        } catch (e) {
                        }
                    }
                } catch (exception) {
                    options.onerror && options.onerror.call(window, exception);
                } finally {
                    options.oncomplete && options.oncomplete.apply(window, arguments);
                    scr.parentNode && scr.parentNode.removeChild(scr);
                    window[callbackFnName] = null;
                    try {
                        delete window[callbackFnName];
                    } catch (e) {
                    }
                }
            };
        }
    }

    return {
        /**
         * 根据给定的参数项,向指定的url发起一个ajax请求。 ajax请求完成后,会根据请求结果调用相应回调: 如果请求
         * 成功, 则调用onsuccess回调, 失败则调用 onerror 回调
         * @method request
         * @param { URLString } url ajax请求的url地址
         * @param { Object } ajaxOptions ajax请求选项的键值对,支持的选项如下:
         * @example
         * ```javascript
         * //向sayhello.php发起一个异步的Ajax GET请求, 请求超时时间为10s, 请求完成后执行相应的回调。
         * UE.ajax.requeset( 'sayhello.php', {
         *
         *     //请求方法。可选值: 'GET', 'POST',默认值是'POST'
         *     method: 'GET',
         *
         *     //超时时间。 默认为5000, 单位是ms
         *     timeout: 10000,
         *
         *     //是否是异步请求。 true为异步请求, false为同步请求
         *     async: true,
         *
         *     //请求携带的数据。如果请求为GET请求, data会经过stringify后附加到请求url之后。
         *     data: {
         *         name: 'ueditor'
         *     },
         *
         *     //请求成功后的回调, 该回调接受当前的XMLHttpRequest对象作为参数。
         *     onsuccess: function ( xhr ) {
         *         console.log( xhr.responseText );
         *     },
         *
         *     //请求失败或者超时后的回调。
         *     onerror: function ( xhr ) {
         *          alert( 'Ajax请求失败' );
         *     }
         *
         * } );
         * ```
         */

        /**
         * 根据给定的参数项发起一个ajax请求, 参数项里必须包含一个url地址。 ajax请求完成后,会根据请求结果调用相应回调: 如果请求
         * 成功, 则调用onsuccess回调, 失败则调用 onerror 回调。
         * @method request
         * @warning 如果在参数项里未提供一个key为“url”的地址值,则该请求将直接退出。
         * @param { Object } ajaxOptions ajax请求选项的键值对,支持的选项如下:
         * @example
         * ```javascript
         *
         * //向sayhello.php发起一个异步的Ajax POST请求, 请求超时时间为5s, 请求完成后不执行任何回调。
         * UE.ajax.requeset( 'sayhello.php', {
         *
         *     //请求的地址, 该项是必须的。
         *     url: 'sayhello.php'
         *
         * } );
         * ```
         */
        request: function (url, opts) {
            if (opts && opts.dataType === "jsonp") {
                doJsonp(url, opts);
            } else {
                doAjax(url, opts);
            }
        },
        getJSONP: function (url, data, fn) {
            var opts = {
                data: data,
                oncomplete: fn
            };
            doJsonp(url, opts);
        }
    };
})();


// core/api.js
UE.api = (function () {
    // axios import
    var axios = null;
    !function (e, t) {
        axios = t()
    }(this, (function () {
        "use strict";

        function e(t) {
            return e = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (e) {
                return typeof e
            } : function (e) {
                return e && "function" == typeof Symbol && e.constructor === Symbol && e !== Symbol.prototype ? "symbol" : typeof e
            }, e(t)
        }

        function t(e, t) {
            if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function")
        }

        function n(e, t) {
            for (var n = 0; n < t.length; n++) {
                var r = t[n];
                r.enumerable = r.enumerable || !1, r.configurable = !0, "value" in r && (r.writable = !0), Object.defineProperty(e, r.key, r)
            }
        }

        function r(e, t, r) {
            return t && n(e.prototype, t), r && n(e, r), Object.defineProperty(e, "prototype", {writable: !1}), e
        }

        function o(e, t) {
            return function (e) {
                if (Array.isArray(e)) return e
            }(e) || function (e, t) {
                var n = null == e ? null : "undefined" != typeof Symbol && e[Symbol.iterator] || e["@@iterator"];
                if (null == n) return;
                var r, o, i = [], a = !0, s = !1;
                try {
                    for (n = n.call(e); !(a = (r = n.next()).done) && (i.push(r.value), !t || i.length !== t); a = !0) ;
                } catch (e) {
                    s = !0, o = e
                } finally {
                    try {
                        a || null == n.return || n.return()
                    } finally {
                        if (s) throw o
                    }
                }
                return i
            }(e, t) || function (e, t) {
                if (!e) return;
                if ("string" == typeof e) return i(e, t);
                var n = Object.prototype.toString.call(e).slice(8, -1);
                "Object" === n && e.constructor && (n = e.constructor.name);
                if ("Map" === n || "Set" === n) return Array.from(e);
                if ("Arguments" === n || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return i(e, t)
            }(e, t) || function () {
                throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")
            }()
        }

        function i(e, t) {
            (null == t || t > e.length) && (t = e.length);
            for (var n = 0, r = new Array(t); n < t; n++) r[n] = e[n];
            return r
        }

        function a(e, t) {
            return function () {
                return e.apply(t, arguments)
            }
        }

        var s, u = Object.prototype.toString, c = Object.getPrototypeOf, f = (s = Object.create(null), function (e) {
            var t = u.call(e);
            return s[t] || (s[t] = t.slice(8, -1).toLowerCase())
        }), l = function (e) {
            return e = e.toLowerCase(), function (t) {
                return f(t) === e
            }
        }, d = function (t) {
            return function (n) {
                return e(n) === t
            }
        }, p = Array.isArray, h = d("undefined");
        var m = l("ArrayBuffer");
        var y = d("string"), v = d("function"), b = d("number"), g = function (t) {
            return null !== t && "object" === e(t)
        }, w = function (e) {
            if ("object" !== f(e)) return !1;
            var t = c(e);
            return !(null !== t && t !== Object.prototype && null !== Object.getPrototypeOf(t) || Symbol.toStringTag in e || Symbol.iterator in e)
        }, E = l("Date"), O = l("File"), S = l("Blob"), R = l("FileList"), A = l("URLSearchParams");

        function T(t, n) {
            var r, o, i = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : {}, a = i.allOwnKeys,
                s = void 0 !== a && a;
            if (null != t) if ("object" !== e(t) && (t = [t]), p(t)) for (r = 0, o = t.length; r < o; r++) n.call(null, t[r], r, t); else {
                var u, c = s ? Object.getOwnPropertyNames(t) : Object.keys(t), f = c.length;
                for (r = 0; r < f; r++) u = c[r], n.call(null, t[u], u, t)
            }
        }

        function j(e, t) {
            t = t.toLowerCase();
            for (var n, r = Object.keys(e), o = r.length; o-- > 0;) if (t === (n = r[o]).toLowerCase()) return n;
            return null
        }

        var N = "undefined" != typeof globalThis ? globalThis : "undefined" != typeof self ? self : "undefined" != typeof window ? window : global,
            C = function (e) {
                return !h(e) && e !== N
            };
        var x, P = (x = "undefined" != typeof Uint8Array && c(Uint8Array), function (e) {
                return x && e instanceof x
            }), k = l("HTMLFormElement"), U = function (e) {
                var t = Object.prototype.hasOwnProperty;
                return function (e, n) {
                    return t.call(e, n)
                }
            }(), _ = l("RegExp"), F = function (e, t) {
                var n = Object.getOwnPropertyDescriptors(e), r = {};
                T(n, (function (n, o) {
                    !1 !== t(n, o, e) && (r[o] = n)
                })), Object.defineProperties(e, r)
            }, B = "abcdefghijklmnopqrstuvwxyz", L = "0123456789",
            D = {DIGIT: L, ALPHA: B, ALPHA_DIGIT: B + B.toUpperCase() + L};
        var I = l("AsyncFunction"), q = {
            isArray: p,
            isArrayBuffer: m,
            isBuffer: function (e) {
                return null !== e && !h(e) && null !== e.constructor && !h(e.constructor) && v(e.constructor.isBuffer) && e.constructor.isBuffer(e)
            },
            isFormData: function (e) {
                var t;
                return e && ("function" == typeof FormData && e instanceof FormData || v(e.append) && ("formdata" === (t = f(e)) || "object" === t && v(e.toString) && "[object FormData]" === e.toString()))
            },
            isArrayBufferView: function (e) {
                return "undefined" != typeof ArrayBuffer && ArrayBuffer.isView ? ArrayBuffer.isView(e) : e && e.buffer && m(e.buffer)
            },
            isString: y,
            isNumber: b,
            isBoolean: function (e) {
                return !0 === e || !1 === e
            },
            isObject: g,
            isPlainObject: w,
            isUndefined: h,
            isDate: E,
            isFile: O,
            isBlob: S,
            isRegExp: _,
            isFunction: v,
            isStream: function (e) {
                return g(e) && v(e.pipe)
            },
            isURLSearchParams: A,
            isTypedArray: P,
            isFileList: R,
            forEach: T,
            merge: function e() {
                for (var t = C(this) && this || {}, n = t.caseless, r = {}, o = function (t, o) {
                    var i = n && j(r, o) || o;
                    w(r[i]) && w(t) ? r[i] = e(r[i], t) : w(t) ? r[i] = e({}, t) : p(t) ? r[i] = t.slice() : r[i] = t
                }, i = 0, a = arguments.length; i < a; i++) arguments[i] && T(arguments[i], o);
                return r
            },
            extend: function (e, t, n) {
                var r = arguments.length > 3 && void 0 !== arguments[3] ? arguments[3] : {}, o = r.allOwnKeys;
                return T(t, (function (t, r) {
                    n && v(t) ? e[r] = a(t, n) : e[r] = t
                }), {allOwnKeys: o}), e
            },
            trim: function (e) {
                return e.trim ? e.trim() : e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "")
            },
            stripBOM: function (e) {
                return 65279 === e.charCodeAt(0) && (e = e.slice(1)), e
            },
            inherits: function (e, t, n, r) {
                e.prototype = Object.create(t.prototype, r), e.prototype.constructor = e, Object.defineProperty(e, "super", {value: t.prototype}), n && Object.assign(e.prototype, n)
            },
            toFlatObject: function (e, t, n, r) {
                var o, i, a, s = {};
                if (t = t || {}, null == e) return t;
                do {
                    for (i = (o = Object.getOwnPropertyNames(e)).length; i-- > 0;) a = o[i], r && !r(a, e, t) || s[a] || (t[a] = e[a], s[a] = !0);
                    e = !1 !== n && c(e)
                } while (e && (!n || n(e, t)) && e !== Object.prototype);
                return t
            },
            kindOf: f,
            kindOfTest: l,
            endsWith: function (e, t, n) {
                e = String(e), (void 0 === n || n > e.length) && (n = e.length), n -= t.length;
                var r = e.indexOf(t, n);
                return -1 !== r && r === n
            },
            toArray: function (e) {
                if (!e) return null;
                if (p(e)) return e;
                var t = e.length;
                if (!b(t)) return null;
                for (var n = new Array(t); t-- > 0;) n[t] = e[t];
                return n
            },
            forEachEntry: function (e, t) {
                for (var n, r = (e && e[Symbol.iterator]).call(e); (n = r.next()) && !n.done;) {
                    var o = n.value;
                    t.call(e, o[0], o[1])
                }
            },
            matchAll: function (e, t) {
                for (var n, r = []; null !== (n = e.exec(t));) r.push(n);
                return r
            },
            isHTMLForm: k,
            hasOwnProperty: U,
            hasOwnProp: U,
            reduceDescriptors: F,
            freezeMethods: function (e) {
                F(e, (function (t, n) {
                    if (v(e) && -1 !== ["arguments", "caller", "callee"].indexOf(n)) return !1;
                    var r = e[n];
                    v(r) && (t.enumerable = !1, "writable" in t ? t.writable = !1 : t.set || (t.set = function () {
                        throw Error("Can not rewrite read-only method '" + n + "'")
                    }))
                }))
            },
            toObjectSet: function (e, t) {
                var n = {}, r = function (e) {
                    e.forEach((function (e) {
                        n[e] = !0
                    }))
                };
                return p(e) ? r(e) : r(String(e).split(t)), n
            },
            toCamelCase: function (e) {
                return e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g, (function (e, t, n) {
                    return t.toUpperCase() + n
                }))
            },
            noop: function () {
            },
            toFiniteNumber: function (e, t) {
                return e = +e, Number.isFinite(e) ? e : t
            },
            findKey: j,
            global: N,
            isContextDefined: C,
            ALPHABET: D,
            generateString: function () {
                for (var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : 16, t = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : D.ALPHA_DIGIT, n = "", r = t.length; e--;) n += t[Math.random() * r | 0];
                return n
            },
            isSpecCompliantForm: function (e) {
                return !!(e && v(e.append) && "FormData" === e[Symbol.toStringTag] && e[Symbol.iterator])
            },
            toJSONObject: function (e) {
                var t = new Array(10);
                return function e(n, r) {
                    if (g(n)) {
                        if (t.indexOf(n) >= 0) return;
                        if (!("toJSON" in n)) {
                            t[r] = n;
                            var o = p(n) ? [] : {};
                            return T(n, (function (t, n) {
                                var i = e(t, r + 1);
                                !h(i) && (o[n] = i)
                            })), t[r] = void 0, o
                        }
                    }
                    return n
                }(e, 0)
            },
            isAsyncFn: I,
            isThenable: function (e) {
                return e && (g(e) || v(e)) && v(e.then) && v(e.catch)
            }
        };

        function M(e, t, n, r, o) {
            Error.call(this), Error.captureStackTrace ? Error.captureStackTrace(this, this.constructor) : this.stack = (new Error).stack, this.message = e, this.name = "AxiosError", t && (this.code = t), n && (this.config = n), r && (this.request = r), o && (this.response = o)
        }

        q.inherits(M, Error, {
            toJSON: function () {
                return {
                    message: this.message,
                    name: this.name,
                    description: this.description,
                    number: this.number,
                    fileName: this.fileName,
                    lineNumber: this.lineNumber,
                    columnNumber: this.columnNumber,
                    stack: this.stack,
                    config: q.toJSONObject(this.config),
                    code: this.code,
                    status: this.response && this.response.status ? this.response.status : null
                }
            }
        });
        var z = M.prototype, H = {};
        ["ERR_BAD_OPTION_VALUE", "ERR_BAD_OPTION", "ECONNABORTED", "ETIMEDOUT", "ERR_NETWORK", "ERR_FR_TOO_MANY_REDIRECTS", "ERR_DEPRECATED", "ERR_BAD_RESPONSE", "ERR_BAD_REQUEST", "ERR_CANCELED", "ERR_NOT_SUPPORT", "ERR_INVALID_URL"].forEach((function (e) {
            H[e] = {value: e}
        })), Object.defineProperties(M, H), Object.defineProperty(z, "isAxiosError", {value: !0}), M.from = function (e, t, n, r, o, i) {
            var a = Object.create(z);
            return q.toFlatObject(e, a, (function (e) {
                return e !== Error.prototype
            }), (function (e) {
                return "isAxiosError" !== e
            })), M.call(a, e.message, t, n, r, o), a.cause = e, a.name = e.name, i && Object.assign(a, i), a
        };

        function J(e) {
            return q.isPlainObject(e) || q.isArray(e)
        }

        function W(e) {
            return q.endsWith(e, "[]") ? e.slice(0, -2) : e
        }

        function K(e, t, n) {
            return e ? e.concat(t).map((function (e, t) {
                return e = W(e), !n && t ? "[" + e + "]" : e
            })).join(n ? "." : "") : t
        }

        var V = q.toFlatObject(q, {}, null, (function (e) {
            return /^is[A-Z]/.test(e)
        }));

        function G(t, n, r) {
            if (!q.isObject(t)) throw new TypeError("target must be an object");
            n = n || new FormData;
            var o = (r = q.toFlatObject(r, {metaTokens: !0, dots: !1, indexes: !1}, !1, (function (e, t) {
                    return !q.isUndefined(t[e])
                }))).metaTokens, i = r.visitor || f, a = r.dots, s = r.indexes,
                u = (r.Blob || "undefined" != typeof Blob && Blob) && q.isSpecCompliantForm(n);
            if (!q.isFunction(i)) throw new TypeError("visitor must be a function");

            function c(e) {
                if (null === e) return "";
                if (q.isDate(e)) return e.toISOString();
                if (!u && q.isBlob(e)) throw new M("Blob is not supported. Use a Buffer instead.");
                return q.isArrayBuffer(e) || q.isTypedArray(e) ? u && "function" == typeof Blob ? new Blob([e]) : Buffer.from(e) : e
            }

            function f(t, r, i) {
                var u = t;
                if (t && !i && "object" === e(t)) if (q.endsWith(r, "{}")) r = o ? r : r.slice(0, -2), t = JSON.stringify(t); else if (q.isArray(t) && function (e) {
                    return q.isArray(e) && !e.some(J)
                }(t) || (q.isFileList(t) || q.endsWith(r, "[]")) && (u = q.toArray(t))) return r = W(r), u.forEach((function (e, t) {
                    !q.isUndefined(e) && null !== e && n.append(!0 === s ? K([r], t, a) : null === s ? r : r + "[]", c(e))
                })), !1;
                return !!J(t) || (n.append(K(i, r, a), c(t)), !1)
            }

            var l = [], d = Object.assign(V, {defaultVisitor: f, convertValue: c, isVisitable: J});
            if (!q.isObject(t)) throw new TypeError("data must be an object");
            return function e(t, r) {
                if (!q.isUndefined(t)) {
                    if (-1 !== l.indexOf(t)) throw Error("Circular reference detected in " + r.join("."));
                    l.push(t), q.forEach(t, (function (t, o) {
                        !0 === (!(q.isUndefined(t) || null === t) && i.call(n, t, q.isString(o) ? o.trim() : o, r, d)) && e(t, r ? r.concat(o) : [o])
                    })), l.pop()
                }
            }(t), n
        }

        function $(e) {
            var t = {"!": "%21", "'": "%27", "(": "%28", ")": "%29", "~": "%7E", "%20": "+", "%00": "\0"};
            return encodeURIComponent(e).replace(/[!'()~]|%20|%00/g, (function (e) {
                return t[e]
            }))
        }

        function X(e, t) {
            this._pairs = [], e && G(e, this, t)
        }

        var Q = X.prototype;

        function Z(e) {
            return encodeURIComponent(e).replace(/%3A/gi, ":").replace(/%24/g, "$").replace(/%2C/gi, ",").replace(/%20/g, "+").replace(/%5B/gi, "[").replace(/%5D/gi, "]")
        }

        function Y(e, t, n) {
            if (!t) return e;
            var r, o = n && n.encode || Z, i = n && n.serialize;
            if (r = i ? i(t, n) : q.isURLSearchParams(t) ? t.toString() : new X(t, n).toString(o)) {
                var a = e.indexOf("#");
                -1 !== a && (e = e.slice(0, a)), e += (-1 === e.indexOf("?") ? "?" : "&") + r
            }
            return e
        }

        Q.append = function (e, t) {
            this._pairs.push([e, t])
        }, Q.toString = function (e) {
            var t = e ? function (t) {
                return e.call(this, t, $)
            } : $;
            return this._pairs.map((function (e) {
                return t(e[0]) + "=" + t(e[1])
            }), "").join("&")
        };
        var ee, te = function () {
            function e() {
                t(this, e), this.handlers = []
            }

            return r(e, [{
                key: "use", value: function (e, t, n) {
                    return this.handlers.push({
                        fulfilled: e,
                        rejected: t,
                        synchronous: !!n && n.synchronous,
                        runWhen: n ? n.runWhen : null
                    }), this.handlers.length - 1
                }
            }, {
                key: "eject", value: function (e) {
                    this.handlers[e] && (this.handlers[e] = null)
                }
            }, {
                key: "clear", value: function () {
                    this.handlers && (this.handlers = [])
                }
            }, {
                key: "forEach", value: function (e) {
                    q.forEach(this.handlers, (function (t) {
                        null !== t && e(t)
                    }))
                }
            }]), e
        }(), ne = {silentJSONParsing: !0, forcedJSONParsing: !0, clarifyTimeoutError: !1}, re = {
            isBrowser: !0,
            classes: {
                URLSearchParams: "undefined" != typeof URLSearchParams ? URLSearchParams : X,
                FormData: "undefined" != typeof FormData ? FormData : null,
                Blob: "undefined" != typeof Blob ? Blob : null
            },
            isStandardBrowserEnv: ("undefined" == typeof navigator || "ReactNative" !== (ee = navigator.product) && "NativeScript" !== ee && "NS" !== ee) && "undefined" != typeof window && "undefined" != typeof document,
            isStandardBrowserWebWorkerEnv: "undefined" != typeof WorkerGlobalScope && self instanceof WorkerGlobalScope && "function" == typeof self.importScripts,
            protocols: ["http", "https", "file", "blob", "url", "data"]
        };

        function oe(e) {
            function t(e, n, r, o) {
                var i = e[o++], a = Number.isFinite(+i), s = o >= e.length;
                return i = !i && q.isArray(r) ? r.length : i, s ? (q.hasOwnProp(r, i) ? r[i] = [r[i], n] : r[i] = n, !a) : (r[i] && q.isObject(r[i]) || (r[i] = []), t(e, n, r[i], o) && q.isArray(r[i]) && (r[i] = function (e) {
                    var t, n, r = {}, o = Object.keys(e), i = o.length;
                    for (t = 0; t < i; t++) r[n = o[t]] = e[n];
                    return r
                }(r[i])), !a)
            }

            if (q.isFormData(e) && q.isFunction(e.entries)) {
                var n = {};
                return q.forEachEntry(e, (function (e, r) {
                    t(function (e) {
                        return q.matchAll(/\w+|\[(\w*)]/g, e).map((function (e) {
                            return "[]" === e[0] ? "" : e[1] || e[0]
                        }))
                    }(e), r, n, 0)
                })), n
            }
            return null
        }

        var ie = {"Content-Type": void 0};
        var ae = {
            transitional: ne,
            adapter: ["xhr", "http"],
            transformRequest: [function (e, t) {
                var n, r = t.getContentType() || "", o = r.indexOf("application/json") > -1, i = q.isObject(e);
                if (i && q.isHTMLForm(e) && (e = new FormData(e)), q.isFormData(e)) return o && o ? JSON.stringify(oe(e)) : e;
                if (q.isArrayBuffer(e) || q.isBuffer(e) || q.isStream(e) || q.isFile(e) || q.isBlob(e)) return e;
                if (q.isArrayBufferView(e)) return e.buffer;
                if (q.isURLSearchParams(e)) return t.setContentType("application/x-www-form-urlencoded;charset=utf-8", !1), e.toString();
                if (i) {
                    if (r.indexOf("application/x-www-form-urlencoded") > -1) return function (e, t) {
                        return G(e, new re.classes.URLSearchParams, Object.assign({
                            visitor: function (e, t, n, r) {
                                return re.isNode && q.isBuffer(e) ? (this.append(t, e.toString("base64")), !1) : r.defaultVisitor.apply(this, arguments)
                            }
                        }, t))
                    }(e, this.formSerializer).toString();
                    if ((n = q.isFileList(e)) || r.indexOf("multipart/form-data") > -1) {
                        var a = this.env && this.env.FormData;
                        return G(n ? {"files[]": e} : e, a && new a, this.formSerializer)
                    }
                }
                return i || o ? (t.setContentType("application/json", !1), function (e, t, n) {
                    if (q.isString(e)) try {
                        return (t || JSON.parse)(e), q.trim(e)
                    } catch (e) {
                        if ("SyntaxError" !== e.name) throw e
                    }
                    return (n || JSON.stringify)(e)
                }(e)) : e
            }],
            transformResponse: [function (e) {
                var t = this.transitional || ae.transitional, n = t && t.forcedJSONParsing,
                    r = "json" === this.responseType;
                if (e && q.isString(e) && (n && !this.responseType || r)) {
                    var o = !(t && t.silentJSONParsing) && r;
                    try {
                        return JSON.parse(e)
                    } catch (e) {
                        if (o) {
                            if ("SyntaxError" === e.name) throw M.from(e, M.ERR_BAD_RESPONSE, this, null, this.response);
                            throw e
                        }
                    }
                }
                return e
            }],
            timeout: 0,
            xsrfCookieName: "XSRF-TOKEN",
            xsrfHeaderName: "X-XSRF-TOKEN",
            maxContentLength: -1,
            maxBodyLength: -1,
            env: {FormData: re.classes.FormData, Blob: re.classes.Blob},
            validateStatus: function (e) {
                return e >= 200 && e < 300
            },
            headers: {common: {Accept: "application/json, text/plain, */*"}}
        };
        q.forEach(["delete", "get", "head"], (function (e) {
            ae.headers[e] = {}
        })), q.forEach(["post", "put", "patch"], (function (e) {
            ae.headers[e] = q.merge(ie)
        }));
        var se = ae,
            ue = q.toObjectSet(["age", "authorization", "content-length", "content-type", "etag", "expires", "from", "host", "if-modified-since", "if-unmodified-since", "last-modified", "location", "max-forwards", "proxy-authorization", "referer", "retry-after", "user-agent"]),
            ce = Symbol("internals");

        function fe(e) {
            return e && String(e).trim().toLowerCase()
        }

        function le(e) {
            return !1 === e || null == e ? e : q.isArray(e) ? e.map(le) : String(e)
        }

        function de(e, t, n, r, o) {
            return q.isFunction(r) ? r.call(this, t, n) : (o && (t = n), q.isString(t) ? q.isString(r) ? -1 !== t.indexOf(r) : q.isRegExp(r) ? r.test(t) : void 0 : void 0)
        }

        var pe = function (e, n) {
            function i(e) {
                t(this, i), e && this.set(e)
            }

            return r(i, [{
                key: "set", value: function (e, t, n) {
                    var r = this;

                    function o(e, t, n) {
                        var o = fe(t);
                        if (!o) throw new Error("header name must be a non-empty string");
                        var i = q.findKey(r, o);
                        (!i || void 0 === r[i] || !0 === n || void 0 === n && !1 !== r[i]) && (r[i || t] = le(e))
                    }

                    var i, a, s, u, c, f = function (e, t) {
                        return q.forEach(e, (function (e, n) {
                            return o(e, n, t)
                        }))
                    };
                    return q.isPlainObject(e) || e instanceof this.constructor ? f(e, t) : q.isString(e) && (e = e.trim()) && !/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim()) ? f((c = {}, (i = e) && i.split("\n").forEach((function (e) {
                        u = e.indexOf(":"), a = e.substring(0, u).trim().toLowerCase(), s = e.substring(u + 1).trim(), !a || c[a] && ue[a] || ("set-cookie" === a ? c[a] ? c[a].push(s) : c[a] = [s] : c[a] = c[a] ? c[a] + ", " + s : s)
                    })), c), t) : null != e && o(t, e, n), this
                }
            }, {
                key: "get", value: function (e, t) {
                    if (e = fe(e)) {
                        var n = q.findKey(this, e);
                        if (n) {
                            var r = this[n];
                            if (!t) return r;
                            if (!0 === t) return function (e) {
                                for (var t, n = Object.create(null), r = /([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g; t = r.exec(e);) n[t[1]] = t[2];
                                return n
                            }(r);
                            if (q.isFunction(t)) return t.call(this, r, n);
                            if (q.isRegExp(t)) return t.exec(r);
                            throw new TypeError("parser must be boolean|regexp|function")
                        }
                    }
                }
            }, {
                key: "has", value: function (e, t) {
                    if (e = fe(e)) {
                        var n = q.findKey(this, e);
                        return !(!n || void 0 === this[n] || t && !de(0, this[n], n, t))
                    }
                    return !1
                }
            }, {
                key: "delete", value: function (e, t) {
                    var n = this, r = !1;

                    function o(e) {
                        if (e = fe(e)) {
                            var o = q.findKey(n, e);
                            !o || t && !de(0, n[o], o, t) || (delete n[o], r = !0)
                        }
                    }

                    return q.isArray(e) ? e.forEach(o) : o(e), r
                }
            }, {
                key: "clear", value: function (e) {
                    for (var t = Object.keys(this), n = t.length, r = !1; n--;) {
                        var o = t[n];
                        e && !de(0, this[o], o, e, !0) || (delete this[o], r = !0)
                    }
                    return r
                }
            }, {
                key: "normalize", value: function (e) {
                    var t = this, n = {};
                    return q.forEach(this, (function (r, o) {
                        var i = q.findKey(n, o);
                        if (i) return t[i] = le(r), void delete t[o];
                        var a = e ? function (e) {
                            return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g, (function (e, t, n) {
                                return t.toUpperCase() + n
                            }))
                        }(o) : String(o).trim();
                        a !== o && delete t[o], t[a] = le(r), n[a] = !0
                    })), this
                }
            }, {
                key: "concat", value: function () {
                    for (var e, t = arguments.length, n = new Array(t), r = 0; r < t; r++) n[r] = arguments[r];
                    return (e = this.constructor).concat.apply(e, [this].concat(n))
                }
            }, {
                key: "toJSON", value: function (e) {
                    var t = Object.create(null);
                    return q.forEach(this, (function (n, r) {
                        null != n && !1 !== n && (t[r] = e && q.isArray(n) ? n.join(", ") : n)
                    })), t
                }
            }, {
                key: Symbol.iterator, value: function () {
                    return Object.entries(this.toJSON())[Symbol.iterator]()
                }
            }, {
                key: "toString", value: function () {
                    return Object.entries(this.toJSON()).map((function (e) {
                        var t = o(e, 2);
                        return t[0] + ": " + t[1]
                    })).join("\n")
                }
            }, {
                key: Symbol.toStringTag, get: function () {
                    return "AxiosHeaders"
                }
            }], [{
                key: "from", value: function (e) {
                    return e instanceof this ? e : new this(e)
                }
            }, {
                key: "concat", value: function (e) {
                    for (var t = new this(e), n = arguments.length, r = new Array(n > 1 ? n - 1 : 0), o = 1; o < n; o++) r[o - 1] = arguments[o];
                    return r.forEach((function (e) {
                        return t.set(e)
                    })), t
                }
            }, {
                key: "accessor", value: function (e) {
                    var t = (this[ce] = this[ce] = {accessors: {}}).accessors, n = this.prototype;

                    function r(e) {
                        var r = fe(e);
                        t[r] || (!function (e, t) {
                            var n = q.toCamelCase(" " + t);
                            ["get", "set", "has"].forEach((function (r) {
                                Object.defineProperty(e, r + n, {
                                    value: function (e, n, o) {
                                        return this[r].call(this, t, e, n, o)
                                    }, configurable: !0
                                })
                            }))
                        }(n, e), t[r] = !0)
                    }

                    return q.isArray(e) ? e.forEach(r) : r(e), this
                }
            }]), i
        }();
        pe.accessor(["Content-Type", "Content-Length", "Accept", "Accept-Encoding", "User-Agent", "Authorization"]), q.freezeMethods(pe.prototype), q.freezeMethods(pe);
        var he = pe;

        function me(e, t) {
            var n = this || se, r = t || n, o = he.from(r.headers), i = r.data;
            return q.forEach(e, (function (e) {
                i = e.call(n, i, o.normalize(), t ? t.status : void 0)
            })), o.normalize(), i
        }

        function ye(e) {
            return !(!e || !e.__CANCEL__)
        }

        function ve(e, t, n) {
            M.call(this, null == e ? "canceled" : e, M.ERR_CANCELED, t, n), this.name = "CanceledError"
        }

        q.inherits(ve, M, {__CANCEL__: !0});
        var be = re.isStandardBrowserEnv ? {
            write: function (e, t, n, r, o, i) {
                var a = [];
                a.push(e + "=" + encodeURIComponent(t)), q.isNumber(n) && a.push("expires=" + new Date(n).toGMTString()), q.isString(r) && a.push("path=" + r), q.isString(o) && a.push("domain=" + o), !0 === i && a.push("secure"), document.cookie = a.join("; ")
            }, read: function (e) {
                var t = document.cookie.match(new RegExp("(^|;\\s*)(" + e + ")=([^;]*)"));
                return t ? decodeURIComponent(t[3]) : null
            }, remove: function (e) {
                this.write(e, "", Date.now() - 864e5)
            }
        } : {
            write: function () {
            }, read: function () {
                return null
            }, remove: function () {
            }
        };

        function ge(e, t) {
            return e && !/^([a-z][a-z\d+\-.]*:)?\/\//i.test(t) ? function (e, t) {
                return t ? e.replace(/\/+$/, "") + "/" + t.replace(/^\/+/, "") : e
            }(e, t) : t
        }

        var we = re.isStandardBrowserEnv ? function () {
            var e, t = /(msie|trident)/i.test(navigator.userAgent), n = document.createElement("a");

            function r(e) {
                var r = e;
                return t && (n.setAttribute("href", r), r = n.href), n.setAttribute("href", r), {
                    href: n.href,
                    protocol: n.protocol ? n.protocol.replace(/:$/, "") : "",
                    host: n.host,
                    search: n.search ? n.search.replace(/^\?/, "") : "",
                    hash: n.hash ? n.hash.replace(/^#/, "") : "",
                    hostname: n.hostname,
                    port: n.port,
                    pathname: "/" === n.pathname.charAt(0) ? n.pathname : "/" + n.pathname
                }
            }

            return e = r(window.location.href), function (t) {
                var n = q.isString(t) ? r(t) : t;
                return n.protocol === e.protocol && n.host === e.host
            }
        }() : function () {
            return !0
        };

        function Ee(e, t) {
            var n = 0, r = function (e, t) {
                e = e || 10;
                var n, r = new Array(e), o = new Array(e), i = 0, a = 0;
                return t = void 0 !== t ? t : 1e3, function (s) {
                    var u = Date.now(), c = o[a];
                    n || (n = u), r[i] = s, o[i] = u;
                    for (var f = a, l = 0; f !== i;) l += r[f++], f %= e;
                    if ((i = (i + 1) % e) === a && (a = (a + 1) % e), !(u - n < t)) {
                        var d = c && u - c;
                        return d ? Math.round(1e3 * l / d) : void 0
                    }
                }
            }(50, 250);
            return function (o) {
                var i = o.loaded, a = o.lengthComputable ? o.total : void 0, s = i - n, u = r(s);
                n = i;
                var c = {
                    loaded: i,
                    total: a,
                    progress: a ? i / a : void 0,
                    bytes: s,
                    rate: u || void 0,
                    estimated: u && a && i <= a ? (a - i) / u : void 0,
                    event: o
                };
                c[t ? "download" : "upload"] = !0, e(c)
            }
        }

        var Oe = {
            http: null, xhr: "undefined" != typeof XMLHttpRequest && function (e) {
                return new Promise((function (t, n) {
                    var r, o = e.data, i = he.from(e.headers).normalize(), a = e.responseType;

                    function s() {
                        e.cancelToken && e.cancelToken.unsubscribe(r), e.signal && e.signal.removeEventListener("abort", r)
                    }

                    q.isFormData(o) && (re.isStandardBrowserEnv || re.isStandardBrowserWebWorkerEnv ? i.setContentType(!1) : i.setContentType("multipart/form-data;", !1));
                    var u = new XMLHttpRequest;
                    if (e.auth) {
                        var c = e.auth.username || "",
                            f = e.auth.password ? unescape(encodeURIComponent(e.auth.password)) : "";
                        i.set("Authorization", "Basic " + btoa(c + ":" + f))
                    }
                    var l = ge(e.baseURL, e.url);

                    function d() {
                        if (u) {
                            var r = he.from("getAllResponseHeaders" in u && u.getAllResponseHeaders());
                            !function (e, t, n) {
                                var r = n.config.validateStatus;
                                n.status && r && !r(n.status) ? t(new M("Request failed with status code " + n.status, [M.ERR_BAD_REQUEST, M.ERR_BAD_RESPONSE][Math.floor(n.status / 100) - 4], n.config, n.request, n)) : e(n)
                            }((function (e) {
                                t(e), s()
                            }), (function (e) {
                                n(e), s()
                            }), {
                                data: a && "text" !== a && "json" !== a ? u.response : u.responseText,
                                status: u.status,
                                statusText: u.statusText,
                                headers: r,
                                config: e,
                                request: u
                            }), u = null
                        }
                    }

                    if (u.open(e.method.toUpperCase(), Y(l, e.params, e.paramsSerializer), !0), u.timeout = e.timeout, "onloadend" in u ? u.onloadend = d : u.onreadystatechange = function () {
                        u && 4 === u.readyState && (0 !== u.status || u.responseURL && 0 === u.responseURL.indexOf("file:")) && setTimeout(d)
                    }, u.onabort = function () {
                        u && (n(new M("Request aborted", M.ECONNABORTED, e, u)), u = null)
                    }, u.onerror = function () {
                        n(new M("Network Error", M.ERR_NETWORK, e, u)), u = null
                    }, u.ontimeout = function () {
                        var t = e.timeout ? "timeout of " + e.timeout + "ms exceeded" : "timeout exceeded",
                            r = e.transitional || ne;
                        e.timeoutErrorMessage && (t = e.timeoutErrorMessage), n(new M(t, r.clarifyTimeoutError ? M.ETIMEDOUT : M.ECONNABORTED, e, u)), u = null
                    }, re.isStandardBrowserEnv) {
                        var p = (e.withCredentials || we(l)) && e.xsrfCookieName && be.read(e.xsrfCookieName);
                        p && i.set(e.xsrfHeaderName, p)
                    }
                    void 0 === o && i.setContentType(null), "setRequestHeader" in u && q.forEach(i.toJSON(), (function (e, t) {
                        u.setRequestHeader(t, e)
                    })), q.isUndefined(e.withCredentials) || (u.withCredentials = !!e.withCredentials), a && "json" !== a && (u.responseType = e.responseType), "function" == typeof e.onDownloadProgress && u.addEventListener("progress", Ee(e.onDownloadProgress, !0)), "function" == typeof e.onUploadProgress && u.upload && u.upload.addEventListener("progress", Ee(e.onUploadProgress)), (e.cancelToken || e.signal) && (r = function (t) {
                        u && (n(!t || t.type ? new ve(null, e, u) : t), u.abort(), u = null)
                    }, e.cancelToken && e.cancelToken.subscribe(r), e.signal && (e.signal.aborted ? r() : e.signal.addEventListener("abort", r)));
                    var h, m = (h = /^([-+\w]{1,25})(:?\/\/|:)/.exec(l)) && h[1] || "";
                    m && -1 === re.protocols.indexOf(m) ? n(new M("Unsupported protocol " + m + ":", M.ERR_BAD_REQUEST, e)) : u.send(o || null)
                }))
            }
        };
        q.forEach(Oe, (function (e, t) {
            if (e) {
                try {
                    Object.defineProperty(e, "name", {value: t})
                } catch (e) {
                }
                Object.defineProperty(e, "adapterName", {value: t})
            }
        }));
        var Se = function (e) {
            for (var t, n, r = (e = q.isArray(e) ? e : [e]).length, o = 0; o < r && (t = e[o], !(n = q.isString(t) ? Oe[t.toLowerCase()] : t)); o++) ;
            if (!n) {
                if (!1 === n) throw new M("Adapter ".concat(t, " is not supported by the environment"), "ERR_NOT_SUPPORT");
                throw new Error(q.hasOwnProp(Oe, t) ? "Adapter '".concat(t, "' is not available in the build") : "Unknown adapter '".concat(t, "'"))
            }
            if (!q.isFunction(n)) throw new TypeError("adapter is not a function");
            return n
        };

        function Re(e) {
            if (e.cancelToken && e.cancelToken.throwIfRequested(), e.signal && e.signal.aborted) throw new ve(null, e)
        }

        function Ae(e) {
            return Re(e), e.headers = he.from(e.headers), e.data = me.call(e, e.transformRequest), -1 !== ["post", "put", "patch"].indexOf(e.method) && e.headers.setContentType("application/x-www-form-urlencoded", !1), Se(e.adapter || se.adapter)(e).then((function (t) {
                return Re(e), t.data = me.call(e, e.transformResponse, t), t.headers = he.from(t.headers), t
            }), (function (t) {
                return ye(t) || (Re(e), t && t.response && (t.response.data = me.call(e, e.transformResponse, t.response), t.response.headers = he.from(t.response.headers))), Promise.reject(t)
            }))
        }

        var Te = function (e) {
            return e instanceof he ? e.toJSON() : e
        };

        function je(e, t) {
            t = t || {};
            var n = {};

            function r(e, t, n) {
                return q.isPlainObject(e) && q.isPlainObject(t) ? q.merge.call({caseless: n}, e, t) : q.isPlainObject(t) ? q.merge({}, t) : q.isArray(t) ? t.slice() : t
            }

            function o(e, t, n) {
                return q.isUndefined(t) ? q.isUndefined(e) ? void 0 : r(void 0, e, n) : r(e, t, n)
            }

            function i(e, t) {
                if (!q.isUndefined(t)) return r(void 0, t)
            }

            function a(e, t) {
                return q.isUndefined(t) ? q.isUndefined(e) ? void 0 : r(void 0, e) : r(void 0, t)
            }

            function s(n, o, i) {
                return i in t ? r(n, o) : i in e ? r(void 0, n) : void 0
            }

            var u = {
                url: i,
                method: i,
                data: i,
                baseURL: a,
                transformRequest: a,
                transformResponse: a,
                paramsSerializer: a,
                timeout: a,
                timeoutMessage: a,
                withCredentials: a,
                adapter: a,
                responseType: a,
                xsrfCookieName: a,
                xsrfHeaderName: a,
                onUploadProgress: a,
                onDownloadProgress: a,
                decompress: a,
                maxContentLength: a,
                maxBodyLength: a,
                beforeRedirect: a,
                transport: a,
                httpAgent: a,
                httpsAgent: a,
                cancelToken: a,
                socketPath: a,
                responseEncoding: a,
                validateStatus: s,
                headers: function (e, t) {
                    return o(Te(e), Te(t), !0)
                }
            };
            return q.forEach(Object.keys(Object.assign({}, e, t)), (function (r) {
                var i = u[r] || o, a = i(e[r], t[r], r);
                q.isUndefined(a) && i !== s || (n[r] = a)
            })), n
        }

        var Ne = "1.4.0", Ce = {};
        ["object", "boolean", "number", "function", "string", "symbol"].forEach((function (t, n) {
            Ce[t] = function (r) {
                return e(r) === t || "a" + (n < 1 ? "n " : " ") + t
            }
        }));
        var xe = {};
        Ce.transitional = function (e, t, n) {
            function r(e, t) {
                return "[Axios v1.4.0] Transitional option '" + e + "'" + t + (n ? ". " + n : "")
            }

            return function (n, o, i) {
                if (!1 === e) throw new M(r(o, " has been removed" + (t ? " in " + t : "")), M.ERR_DEPRECATED);
                return t && !xe[o] && (xe[o] = !0, console.warn(r(o, " has been deprecated since v" + t + " and will be removed in the near future"))), !e || e(n, o, i)
            }
        };
        var Pe = {
            assertOptions: function (t, n, r) {
                if ("object" !== e(t)) throw new M("options must be an object", M.ERR_BAD_OPTION_VALUE);
                for (var o = Object.keys(t), i = o.length; i-- > 0;) {
                    var a = o[i], s = n[a];
                    if (s) {
                        var u = t[a], c = void 0 === u || s(u, a, t);
                        if (!0 !== c) throw new M("option " + a + " must be " + c, M.ERR_BAD_OPTION_VALUE)
                    } else if (!0 !== r) throw new M("Unknown option " + a, M.ERR_BAD_OPTION)
                }
            }, validators: Ce
        }, ke = Pe.validators, Ue = function () {
            function e(n) {
                t(this, e), this.defaults = n, this.interceptors = {request: new te, response: new te}
            }

            return r(e, [{
                key: "request", value: function (e, t) {
                    "string" == typeof e ? (t = t || {}).url = e : t = e || {};
                    var n, r = t = je(this.defaults, t), o = r.transitional, i = r.paramsSerializer, a = r.headers;
                    void 0 !== o && Pe.assertOptions(o, {
                        silentJSONParsing: ke.transitional(ke.boolean),
                        forcedJSONParsing: ke.transitional(ke.boolean),
                        clarifyTimeoutError: ke.transitional(ke.boolean)
                    }, !1), null != i && (q.isFunction(i) ? t.paramsSerializer = {serialize: i} : Pe.assertOptions(i, {
                        encode: ke.function,
                        serialize: ke.function
                    }, !0)), t.method = (t.method || this.defaults.method || "get").toLowerCase(), (n = a && q.merge(a.common, a[t.method])) && q.forEach(["delete", "get", "head", "post", "put", "patch", "common"], (function (e) {
                        delete a[e]
                    })), t.headers = he.concat(n, a);
                    var s = [], u = !0;
                    this.interceptors.request.forEach((function (e) {
                        "function" == typeof e.runWhen && !1 === e.runWhen(t) || (u = u && e.synchronous, s.unshift(e.fulfilled, e.rejected))
                    }));
                    var c, f = [];
                    this.interceptors.response.forEach((function (e) {
                        f.push(e.fulfilled, e.rejected)
                    }));
                    var l, d = 0;
                    if (!u) {
                        var p = [Ae.bind(this), void 0];
                        for (p.unshift.apply(p, s), p.push.apply(p, f), l = p.length, c = Promise.resolve(t); d < l;) c = c.then(p[d++], p[d++]);
                        return c
                    }
                    l = s.length;
                    var h = t;
                    for (d = 0; d < l;) {
                        var m = s[d++], y = s[d++];
                        try {
                            h = m(h)
                        } catch (e) {
                            y.call(this, e);
                            break
                        }
                    }
                    try {
                        c = Ae.call(this, h)
                    } catch (e) {
                        return Promise.reject(e)
                    }
                    for (d = 0, l = f.length; d < l;) c = c.then(f[d++], f[d++]);
                    return c
                }
            }, {
                key: "getUri", value: function (e) {
                    return Y(ge((e = je(this.defaults, e)).baseURL, e.url), e.params, e.paramsSerializer)
                }
            }]), e
        }();
        q.forEach(["delete", "get", "head", "options"], (function (e) {
            Ue.prototype[e] = function (t, n) {
                return this.request(je(n || {}, {method: e, url: t, data: (n || {}).data}))
            }
        })), q.forEach(["post", "put", "patch"], (function (e) {
            function t(t) {
                return function (n, r, o) {
                    return this.request(je(o || {}, {
                        method: e,
                        headers: t ? {"Content-Type": "multipart/form-data"} : {},
                        url: n,
                        data: r
                    }))
                }
            }

            Ue.prototype[e] = t(), Ue.prototype[e + "Form"] = t(!0)
        }));
        var _e = Ue, Fe = function () {
            function e(n) {
                if (t(this, e), "function" != typeof n) throw new TypeError("executor must be a function.");
                var r;
                this.promise = new Promise((function (e) {
                    r = e
                }));
                var o = this;
                this.promise.then((function (e) {
                    if (o._listeners) {
                        for (var t = o._listeners.length; t-- > 0;) o._listeners[t](e);
                        o._listeners = null
                    }
                })), this.promise.then = function (e) {
                    var t, n = new Promise((function (e) {
                        o.subscribe(e), t = e
                    })).then(e);
                    return n.cancel = function () {
                        o.unsubscribe(t)
                    }, n
                }, n((function (e, t, n) {
                    o.reason || (o.reason = new ve(e, t, n), r(o.reason))
                }))
            }

            return r(e, [{
                key: "throwIfRequested", value: function () {
                    if (this.reason) throw this.reason
                }
            }, {
                key: "subscribe", value: function (e) {
                    this.reason ? e(this.reason) : this._listeners ? this._listeners.push(e) : this._listeners = [e]
                }
            }, {
                key: "unsubscribe", value: function (e) {
                    if (this._listeners) {
                        var t = this._listeners.indexOf(e);
                        -1 !== t && this._listeners.splice(t, 1)
                    }
                }
            }], [{
                key: "source", value: function () {
                    var t;
                    return {
                        token: new e((function (e) {
                            t = e
                        })), cancel: t
                    }
                }
            }]), e
        }();
        var Be = {
            Continue: 100,
            SwitchingProtocols: 101,
            Processing: 102,
            EarlyHints: 103,
            Ok: 200,
            Created: 201,
            Accepted: 202,
            NonAuthoritativeInformation: 203,
            NoContent: 204,
            ResetContent: 205,
            PartialContent: 206,
            MultiStatus: 207,
            AlreadyReported: 208,
            ImUsed: 226,
            MultipleChoices: 300,
            MovedPermanently: 301,
            Found: 302,
            SeeOther: 303,
            NotModified: 304,
            UseProxy: 305,
            Unused: 306,
            TemporaryRedirect: 307,
            PermanentRedirect: 308,
            BadRequest: 400,
            Unauthorized: 401,
            PaymentRequired: 402,
            Forbidden: 403,
            NotFound: 404,
            MethodNotAllowed: 405,
            NotAcceptable: 406,
            ProxyAuthenticationRequired: 407,
            RequestTimeout: 408,
            Conflict: 409,
            Gone: 410,
            LengthRequired: 411,
            PreconditionFailed: 412,
            PayloadTooLarge: 413,
            UriTooLong: 414,
            UnsupportedMediaType: 415,
            RangeNotSatisfiable: 416,
            ExpectationFailed: 417,
            ImATeapot: 418,
            MisdirectedRequest: 421,
            UnprocessableEntity: 422,
            Locked: 423,
            FailedDependency: 424,
            TooEarly: 425,
            UpgradeRequired: 426,
            PreconditionRequired: 428,
            TooManyRequests: 429,
            RequestHeaderFieldsTooLarge: 431,
            UnavailableForLegalReasons: 451,
            InternalServerError: 500,
            NotImplemented: 501,
            BadGateway: 502,
            ServiceUnavailable: 503,
            GatewayTimeout: 504,
            HttpVersionNotSupported: 505,
            VariantAlsoNegotiates: 506,
            InsufficientStorage: 507,
            LoopDetected: 508,
            NotExtended: 510,
            NetworkAuthenticationRequired: 511
        };
        Object.entries(Be).forEach((function (e) {
            var t = o(e, 2), n = t[0], r = t[1];
            Be[r] = n
        }));
        var Le = Be;
        var De = function e(t) {
            var n = new _e(t), r = a(_e.prototype.request, n);
            return q.extend(r, _e.prototype, n, {allOwnKeys: !0}), q.extend(r, n, null, {allOwnKeys: !0}), r.create = function (n) {
                return e(je(t, n))
            }, r
        }(se);
        return De.Axios = _e, De.CanceledError = ve, De.CancelToken = Fe, De.isCancel = ye, De.VERSION = Ne, De.toFormData = G, De.AxiosError = M, De.Cancel = De.CanceledError, De.all = function (e) {
            return Promise.all(e)
        }, De.spread = function (e) {
            return function (t) {
                return e.apply(null, t)
            }
        }, De.isAxiosError = function (e) {
            return q.isObject(e) && !0 === e.isAxiosError
        }, De.mergeConfig = je, De.AxiosHeaders = he, De.formToJSON = function (e) {
            return oe(q.isHTMLForm(e) ? new FormData(e) : e)
        }, De.HttpStatusCode = Le, De.default = De, De
    }));
    return {
        requestAction: function (me, action, config) {
            // config.url = me.getOpt('serverUrl');
            config.url = me.getActionUrl();
            config.method = 'post';
            config.params = config.params || {};
            config.params = Object.assign(config.params, me.getOpt('serverparam'));
            config.params.action = action;
            return this.request(me, config);
        },
        request: function (me, config) {
            config.headers = config.headers || {};
            var customHeaders = me.getOpt('serverHeaders');
            if (customHeaders) {
                for (var key in customHeaders) {
                    config.headers[key] = customHeaders[key];
                }
            }
            return axios(config);
        }
    }
})();


// core/image.js
UE.image = (function () {
    // import browser-image-compression
    // https://www.npmjs.com/package/browser-image-compression
    var imageCompression = null;
    !function (e, t) {
        imageCompression = t();
    }(this, (function () {
        "use strict";

        function _mergeNamespaces(e, t) {
            return t.forEach((function (t) {
                t && "string" != typeof t && !Array.isArray(t) && Object.keys(t).forEach((function (r) {
                    if ("default" !== r && !(r in e)) {
                        var i = Object.getOwnPropertyDescriptor(t, r);
                        Object.defineProperty(e, r, i.get ? i : {
                            enumerable: !0, get: function () {
                                return t[r]
                            }
                        })
                    }
                }))
            })), Object.freeze(e)
        }

        function copyExifWithoutOrientation(e, t) {
            return new Promise((function (r, i) {
                let o;
                return getApp1Segment(e).then((function (e) {
                    try {
                        return o = e, r(new Blob([t.slice(0, 2), o, t.slice(2)], {type: "image/jpeg"}))
                    } catch (e) {
                        return i(e)
                    }
                }), i)
            }))
        }

        const getApp1Segment = e => new Promise(((t, r) => {
            const i = new FileReader;
            i.addEventListener("load", (({target: {result: e}}) => {
                const i = new DataView(e);
                let o = 0;
                if (65496 !== i.getUint16(o)) return r("not a valid JPEG");
                for (o += 2; ;) {
                    const a = i.getUint16(o);
                    if (65498 === a) break;
                    const s = i.getUint16(o + 2);
                    if (65505 === a && 1165519206 === i.getUint32(o + 4)) {
                        const a = o + 10;
                        let f;
                        switch (i.getUint16(a)) {
                            case 18761:
                                f = !0;
                                break;
                            case 19789:
                                f = !1;
                                break;
                            default:
                                return r("TIFF header contains invalid endian")
                        }
                        if (42 !== i.getUint16(a + 2, f)) return r("TIFF header contains invalid version");
                        const l = i.getUint32(a + 4, f), c = a + l + 2 + 12 * i.getUint16(a + l, f);
                        for (let e = a + l + 2; e < c; e += 12) {
                            if (274 == i.getUint16(e, f)) {
                                if (3 !== i.getUint16(e + 2, f)) return r("Orientation data type is invalid");
                                if (1 !== i.getUint32(e + 4, f)) return r("Orientation data count is invalid");
                                i.setUint16(e + 8, 1, f);
                                break
                            }
                        }
                        return t(e.slice(o, o + 2 + s))
                    }
                    o += 2 + s
                }
                return t(new Blob)
            })), i.readAsArrayBuffer(e)
        }));
        var e = {};
        !function (e) {
            var t, r, UZIP = {};
            e.exports = UZIP, UZIP.parse = function (e, t) {
                for (var r = UZIP.bin.readUshort, i = UZIP.bin.readUint, o = 0, a = {}, s = new Uint8Array(e), f = s.length - 4; 101010256 != i(s, f);) f--;
                o = f;
                o += 4;
                var l = r(s, o += 4);
                r(s, o += 2);
                var c = i(s, o += 2), u = i(s, o += 4);
                o += 4, o = u;
                for (var h = 0; h < l; h++) {
                    i(s, o), o += 4, o += 4, o += 4, i(s, o += 4);
                    c = i(s, o += 4);
                    var d = i(s, o += 4), A = r(s, o += 4), g = r(s, o + 2), p = r(s, o + 4);
                    o += 6;
                    var m = i(s, o += 8);
                    o += 4, o += A + g + p, UZIP._readLocal(s, m, a, c, d, t)
                }
                return a
            }, UZIP._readLocal = function (e, t, r, i, o, a) {
                var s = UZIP.bin.readUshort, f = UZIP.bin.readUint;
                f(e, t), s(e, t += 4), s(e, t += 2);
                var l = s(e, t += 2);
                f(e, t += 2), f(e, t += 4), t += 4;
                var c = s(e, t += 8), u = s(e, t += 2);
                t += 2;
                var h = UZIP.bin.readUTF8(e, t, c);
                if (t += c, t += u, a) r[h] = {size: o, csize: i}; else {
                    var d = new Uint8Array(e.buffer, t);
                    if (0 == l) r[h] = new Uint8Array(d.buffer.slice(t, t + i)); else {
                        if (8 != l) throw"unknown compression method: " + l;
                        var A = new Uint8Array(o);
                        UZIP.inflateRaw(d, A), r[h] = A
                    }
                }
            }, UZIP.inflateRaw = function (e, t) {
                return UZIP.F.inflate(e, t)
            }, UZIP.inflate = function (e, t) {
                return e[0], e[1], UZIP.inflateRaw(new Uint8Array(e.buffer, e.byteOffset + 2, e.length - 6), t)
            }, UZIP.deflate = function (e, t) {
                null == t && (t = {level: 6});
                var r = 0, i = new Uint8Array(50 + Math.floor(1.1 * e.length));
                i[r] = 120, i[r + 1] = 156, r += 2, r = UZIP.F.deflateRaw(e, i, r, t.level);
                var o = UZIP.adler(e, 0, e.length);
                return i[r + 0] = o >>> 24 & 255, i[r + 1] = o >>> 16 & 255, i[r + 2] = o >>> 8 & 255, i[r + 3] = o >>> 0 & 255, new Uint8Array(i.buffer, 0, r + 4)
            }, UZIP.deflateRaw = function (e, t) {
                null == t && (t = {level: 6});
                var r = new Uint8Array(50 + Math.floor(1.1 * e.length)), i = UZIP.F.deflateRaw(e, r, i, t.level);
                return new Uint8Array(r.buffer, 0, i)
            }, UZIP.encode = function (e, t) {
                null == t && (t = !1);
                var r = 0, i = UZIP.bin.writeUint, o = UZIP.bin.writeUshort, a = {};
                for (var s in e) {
                    var f = !UZIP._noNeed(s) && !t, l = e[s], c = UZIP.crc.crc(l, 0, l.length);
                    a[s] = {cpr: f, usize: l.length, crc: c, file: f ? UZIP.deflateRaw(l) : l}
                }
                for (var s in a) r += a[s].file.length + 30 + 46 + 2 * UZIP.bin.sizeUTF8(s);
                r += 22;
                var u = new Uint8Array(r), h = 0, d = [];
                for (var s in a) {
                    var A = a[s];
                    d.push(h), h = UZIP._writeHeader(u, h, s, A, 0)
                }
                var g = 0, p = h;
                for (var s in a) {
                    A = a[s];
                    d.push(h), h = UZIP._writeHeader(u, h, s, A, 1, d[g++])
                }
                var m = h - p;
                return i(u, h, 101010256), h += 4, o(u, h += 4, g), o(u, h += 2, g), i(u, h += 2, m), i(u, h += 4, p), h += 4, h += 2, u.buffer
            }, UZIP._noNeed = function (e) {
                var t = e.split(".").pop().toLowerCase();
                return -1 != "png,jpg,jpeg,zip".indexOf(t)
            }, UZIP._writeHeader = function (e, t, r, i, o, a) {
                var s = UZIP.bin.writeUint, f = UZIP.bin.writeUshort, l = i.file;
                return s(e, t, 0 == o ? 67324752 : 33639248), t += 4, 1 == o && (t += 2), f(e, t, 20), f(e, t += 2, 0), f(e, t += 2, i.cpr ? 8 : 0), s(e, t += 2, 0), s(e, t += 4, i.crc), s(e, t += 4, l.length), s(e, t += 4, i.usize), f(e, t += 4, UZIP.bin.sizeUTF8(r)), f(e, t += 2, 0), t += 2, 1 == o && (t += 2, t += 2, s(e, t += 6, a), t += 4), t += UZIP.bin.writeUTF8(e, t, r), 0 == o && (e.set(l, t), t += l.length), t
            }, UZIP.crc = {
                table: function () {
                    for (var e = new Uint32Array(256), t = 0; t < 256; t++) {
                        for (var r = t, i = 0; i < 8; i++) 1 & r ? r = 3988292384 ^ r >>> 1 : r >>>= 1;
                        e[t] = r
                    }
                    return e
                }(), update: function (e, t, r, i) {
                    for (var o = 0; o < i; o++) e = UZIP.crc.table[255 & (e ^ t[r + o])] ^ e >>> 8;
                    return e
                }, crc: function (e, t, r) {
                    return 4294967295 ^ UZIP.crc.update(4294967295, e, t, r)
                }
            }, UZIP.adler = function (e, t, r) {
                for (var i = 1, o = 0, a = t, s = t + r; a < s;) {
                    for (var f = Math.min(a + 5552, s); a < f;) o += i += e[a++];
                    i %= 65521, o %= 65521
                }
                return o << 16 | i
            }, UZIP.bin = {
                readUshort: function (e, t) {
                    return e[t] | e[t + 1] << 8
                }, writeUshort: function (e, t, r) {
                    e[t] = 255 & r, e[t + 1] = r >> 8 & 255
                }, readUint: function (e, t) {
                    return 16777216 * e[t + 3] + (e[t + 2] << 16 | e[t + 1] << 8 | e[t])
                }, writeUint: function (e, t, r) {
                    e[t] = 255 & r, e[t + 1] = r >> 8 & 255, e[t + 2] = r >> 16 & 255, e[t + 3] = r >> 24 & 255
                }, readASCII: function (e, t, r) {
                    for (var i = "", o = 0; o < r; o++) i += String.fromCharCode(e[t + o]);
                    return i
                }, writeASCII: function (e, t, r) {
                    for (var i = 0; i < r.length; i++) e[t + i] = r.charCodeAt(i)
                }, pad: function (e) {
                    return e.length < 2 ? "0" + e : e
                }, readUTF8: function (e, t, r) {
                    for (var i, o = "", a = 0; a < r; a++) o += "%" + UZIP.bin.pad(e[t + a].toString(16));
                    try {
                        i = decodeURIComponent(o)
                    } catch (i) {
                        return UZIP.bin.readASCII(e, t, r)
                    }
                    return i
                }, writeUTF8: function (e, t, r) {
                    for (var i = r.length, o = 0, a = 0; a < i; a++) {
                        var s = r.charCodeAt(a);
                        if (0 == (4294967168 & s)) e[t + o] = s, o++; else if (0 == (4294965248 & s)) e[t + o] = 192 | s >> 6, e[t + o + 1] = 128 | s >> 0 & 63, o += 2; else if (0 == (4294901760 & s)) e[t + o] = 224 | s >> 12, e[t + o + 1] = 128 | s >> 6 & 63, e[t + o + 2] = 128 | s >> 0 & 63, o += 3; else {
                            if (0 != (4292870144 & s)) throw"e";
                            e[t + o] = 240 | s >> 18, e[t + o + 1] = 128 | s >> 12 & 63, e[t + o + 2] = 128 | s >> 6 & 63, e[t + o + 3] = 128 | s >> 0 & 63, o += 4
                        }
                    }
                    return o
                }, sizeUTF8: function (e) {
                    for (var t = e.length, r = 0, i = 0; i < t; i++) {
                        var o = e.charCodeAt(i);
                        if (0 == (4294967168 & o)) r++; else if (0 == (4294965248 & o)) r += 2; else if (0 == (4294901760 & o)) r += 3; else {
                            if (0 != (4292870144 & o)) throw"e";
                            r += 4
                        }
                    }
                    return r
                }
            }, UZIP.F = {}, UZIP.F.deflateRaw = function (e, t, r, i) {
                var o = [[0, 0, 0, 0, 0], [4, 4, 8, 4, 0], [4, 5, 16, 8, 0], [4, 6, 16, 16, 0], [4, 10, 16, 32, 0], [8, 16, 32, 32, 0], [8, 16, 128, 128, 0], [8, 32, 128, 256, 0], [32, 128, 258, 1024, 1], [32, 258, 258, 4096, 1]][i],
                    a = UZIP.F.U, s = UZIP.F._goodIndex;
                UZIP.F._hash;
                var f = UZIP.F._putsE, l = 0, c = r << 3, u = 0, h = e.length;
                if (0 == i) {
                    for (; l < h;) {
                        f(t, c, l + (_ = Math.min(65535, h - l)) == h ? 1 : 0), c = UZIP.F._copyExact(e, l, _, t, c + 8), l += _
                    }
                    return c >>> 3
                }
                var d = a.lits, A = a.strt, g = a.prev, p = 0, m = 0, w = 0, v = 0, b = 0, y = 0;
                for (h > 2 && (A[y = UZIP.F._hash(e, 0)] = 0), l = 0; l < h; l++) {
                    if (b = y, l + 1 < h - 2) {
                        y = UZIP.F._hash(e, l + 1);
                        var E = l + 1 & 32767;
                        g[E] = A[y], A[y] = E
                    }
                    if (u <= l) {
                        (p > 14e3 || m > 26697) && h - l > 100 && (u < l && (d[p] = l - u, p += 2, u = l), c = UZIP.F._writeBlock(l == h - 1 || u == h ? 1 : 0, d, p, v, e, w, l - w, t, c), p = m = v = 0, w = l);
                        var F = 0;
                        l < h - 2 && (F = UZIP.F._bestMatch(e, l, g, b, Math.min(o[2], h - l), o[3]));
                        var _ = F >>> 16, B = 65535 & F;
                        if (0 != F) {
                            B = 65535 & F;
                            var U = s(_ = F >>> 16, a.of0);
                            a.lhst[257 + U]++;
                            var C = s(B, a.df0);
                            a.dhst[C]++, v += a.exb[U] + a.dxb[C], d[p] = _ << 23 | l - u, d[p + 1] = B << 16 | U << 8 | C, p += 2, u = l + _
                        } else a.lhst[e[l]]++;
                        m++
                    }
                }
                for (w == l && 0 != e.length || (u < l && (d[p] = l - u, p += 2, u = l), c = UZIP.F._writeBlock(1, d, p, v, e, w, l - w, t, c), p = 0, m = 0, p = m = v = 0, w = l); 0 != (7 & c);) c++;
                return c >>> 3
            }, UZIP.F._bestMatch = function (e, t, r, i, o, a) {
                var s = 32767 & t, f = r[s], l = s - f + 32768 & 32767;
                if (f == s || i != UZIP.F._hash(e, t - l)) return 0;
                for (var c = 0, u = 0, h = Math.min(32767, t); l <= h && 0 != --a && f != s;) {
                    if (0 == c || e[t + c] == e[t + c - l]) {
                        var d = UZIP.F._howLong(e, t, l);
                        if (d > c) {
                            if (u = l, (c = d) >= o) break;
                            l + 2 < d && (d = l + 2);
                            for (var A = 0, g = 0; g < d - 2; g++) {
                                var p = t - l + g + 32768 & 32767, m = p - r[p] + 32768 & 32767;
                                m > A && (A = m, f = p)
                            }
                        }
                    }
                    l += (s = f) - (f = r[s]) + 32768 & 32767
                }
                return c << 16 | u
            }, UZIP.F._howLong = function (e, t, r) {
                if (e[t] != e[t - r] || e[t + 1] != e[t + 1 - r] || e[t + 2] != e[t + 2 - r]) return 0;
                var i = t, o = Math.min(e.length, t + 258);
                for (t += 3; t < o && e[t] == e[t - r];) t++;
                return t - i
            }, UZIP.F._hash = function (e, t) {
                return (e[t] << 8 | e[t + 1]) + (e[t + 2] << 4) & 65535
            }, UZIP.saved = 0, UZIP.F._writeBlock = function (e, t, r, i, o, a, s, f, l) {
                var c, u, h, d, A, g, p, m, w, v = UZIP.F.U, b = UZIP.F._putsF, y = UZIP.F._putsE;
                v.lhst[256]++, u = (c = UZIP.F.getTrees())[0], h = c[1], d = c[2], A = c[3], g = c[4], p = c[5], m = c[6], w = c[7];
                var E = 32 + (0 == (l + 3 & 7) ? 0 : 8 - (l + 3 & 7)) + (s << 3),
                    F = i + UZIP.F.contSize(v.fltree, v.lhst) + UZIP.F.contSize(v.fdtree, v.dhst),
                    _ = i + UZIP.F.contSize(v.ltree, v.lhst) + UZIP.F.contSize(v.dtree, v.dhst);
                _ += 14 + 3 * p + UZIP.F.contSize(v.itree, v.ihst) + (2 * v.ihst[16] + 3 * v.ihst[17] + 7 * v.ihst[18]);
                for (var B = 0; B < 286; B++) v.lhst[B] = 0;
                for (B = 0; B < 30; B++) v.dhst[B] = 0;
                for (B = 0; B < 19; B++) v.ihst[B] = 0;
                var U = E < F && E < _ ? 0 : F < _ ? 1 : 2;
                if (b(f, l, e), b(f, l + 1, U), l += 3, 0 == U) {
                    for (; 0 != (7 & l);) l++;
                    l = UZIP.F._copyExact(o, a, s, f, l)
                } else {
                    var C, I;
                    if (1 == U && (C = v.fltree, I = v.fdtree), 2 == U) {
                        UZIP.F.makeCodes(v.ltree, u), UZIP.F.revCodes(v.ltree, u), UZIP.F.makeCodes(v.dtree, h), UZIP.F.revCodes(v.dtree, h), UZIP.F.makeCodes(v.itree, d), UZIP.F.revCodes(v.itree, d), C = v.ltree, I = v.dtree, y(f, l, A - 257), y(f, l += 5, g - 1), y(f, l += 5, p - 4), l += 4;
                        for (var Q = 0; Q < p; Q++) y(f, l + 3 * Q, v.itree[1 + (v.ordr[Q] << 1)]);
                        l += 3 * p, l = UZIP.F._codeTiny(m, v.itree, f, l), l = UZIP.F._codeTiny(w, v.itree, f, l)
                    }
                    for (var M = a, x = 0; x < r; x += 2) {
                        for (var T = t[x], S = T >>> 23, R = M + (8388607 & T); M < R;) l = UZIP.F._writeLit(o[M++], C, f, l);
                        if (0 != S) {
                            var O = t[x + 1], P = O >> 16, H = O >> 8 & 255, L = 255 & O;
                            y(f, l = UZIP.F._writeLit(257 + H, C, f, l), S - v.of0[H]), l += v.exb[H], b(f, l = UZIP.F._writeLit(L, I, f, l), P - v.df0[L]), l += v.dxb[L], M += S
                        }
                    }
                    l = UZIP.F._writeLit(256, C, f, l)
                }
                return l
            }, UZIP.F._copyExact = function (e, t, r, i, o) {
                var a = o >>> 3;
                return i[a] = r, i[a + 1] = r >>> 8, i[a + 2] = 255 - i[a], i[a + 3] = 255 - i[a + 1], a += 4, i.set(new Uint8Array(e.buffer, t, r), a), o + (r + 4 << 3)
            }, UZIP.F.getTrees = function () {
                for (var e = UZIP.F.U, t = UZIP.F._hufTree(e.lhst, e.ltree, 15), r = UZIP.F._hufTree(e.dhst, e.dtree, 15), i = [], o = UZIP.F._lenCodes(e.ltree, i), a = [], s = UZIP.F._lenCodes(e.dtree, a), f = 0; f < i.length; f += 2) e.ihst[i[f]]++;
                for (f = 0; f < a.length; f += 2) e.ihst[a[f]]++;
                for (var l = UZIP.F._hufTree(e.ihst, e.itree, 7), c = 19; c > 4 && 0 == e.itree[1 + (e.ordr[c - 1] << 1)];) c--;
                return [t, r, l, o, s, c, i, a]
            }, UZIP.F.getSecond = function (e) {
                for (var t = [], r = 0; r < e.length; r += 2) t.push(e[r + 1]);
                return t
            }, UZIP.F.nonZero = function (e) {
                for (var t = "", r = 0; r < e.length; r += 2) 0 != e[r + 1] && (t += (r >> 1) + ",");
                return t
            }, UZIP.F.contSize = function (e, t) {
                for (var r = 0, i = 0; i < t.length; i++) r += t[i] * e[1 + (i << 1)];
                return r
            }, UZIP.F._codeTiny = function (e, t, r, i) {
                for (var o = 0; o < e.length; o += 2) {
                    var a = e[o], s = e[o + 1];
                    i = UZIP.F._writeLit(a, t, r, i);
                    var f = 16 == a ? 2 : 17 == a ? 3 : 7;
                    a > 15 && (UZIP.F._putsE(r, i, s, f), i += f)
                }
                return i
            }, UZIP.F._lenCodes = function (e, t) {
                for (var r = e.length; 2 != r && 0 == e[r - 1];) r -= 2;
                for (var i = 0; i < r; i += 2) {
                    var o = e[i + 1], a = i + 3 < r ? e[i + 3] : -1, s = i + 5 < r ? e[i + 5] : -1,
                        f = 0 == i ? -1 : e[i - 1];
                    if (0 == o && a == o && s == o) {
                        for (var l = i + 5; l + 2 < r && e[l + 2] == o;) l += 2;
                        (c = Math.min(l + 1 - i >>> 1, 138)) < 11 ? t.push(17, c - 3) : t.push(18, c - 11), i += 2 * c - 2
                    } else if (o == f && a == o && s == o) {
                        for (l = i + 5; l + 2 < r && e[l + 2] == o;) l += 2;
                        var c = Math.min(l + 1 - i >>> 1, 6);
                        t.push(16, c - 3), i += 2 * c - 2
                    } else t.push(o, 0)
                }
                return r >>> 1
            }, UZIP.F._hufTree = function (e, t, r) {
                var i = [], o = e.length, a = t.length, s = 0;
                for (s = 0; s < a; s += 2) t[s] = 0, t[s + 1] = 0;
                for (s = 0; s < o; s++) 0 != e[s] && i.push({lit: s, f: e[s]});
                var f = i.length, l = i.slice(0);
                if (0 == f) return 0;
                if (1 == f) {
                    var c = i[0].lit;
                    l = 0 == c ? 1 : 0;
                    return t[1 + (c << 1)] = 1, t[1 + (l << 1)] = 1, 1
                }
                i.sort((function (e, t) {
                    return e.f - t.f
                }));
                var u = i[0], h = i[1], d = 0, A = 1, g = 2;
                for (i[0] = {
                    lit: -1,
                    f: u.f + h.f,
                    l: u,
                    r: h,
                    d: 0
                }; A != f - 1;) u = d != A && (g == f || i[d].f < i[g].f) ? i[d++] : i[g++], h = d != A && (g == f || i[d].f < i[g].f) ? i[d++] : i[g++], i[A++] = {
                    lit: -1,
                    f: u.f + h.f,
                    l: u,
                    r: h
                };
                var p = UZIP.F.setDepth(i[A - 1], 0);
                for (p > r && (UZIP.F.restrictDepth(l, r, p), p = r), s = 0; s < f; s++) t[1 + (l[s].lit << 1)] = l[s].d;
                return p
            }, UZIP.F.setDepth = function (e, t) {
                return -1 != e.lit ? (e.d = t, t) : Math.max(UZIP.F.setDepth(e.l, t + 1), UZIP.F.setDepth(e.r, t + 1))
            }, UZIP.F.restrictDepth = function (e, t, r) {
                var i = 0, o = 1 << r - t, a = 0;
                for (e.sort((function (e, t) {
                    return t.d == e.d ? e.f - t.f : t.d - e.d
                })), i = 0; i < e.length && e[i].d > t; i++) {
                    var s = e[i].d;
                    e[i].d = t, a += o - (1 << r - s)
                }
                for (a >>>= r - t; a > 0;) {
                    (s = e[i].d) < t ? (e[i].d++, a -= 1 << t - s - 1) : i++
                }
                for (; i >= 0; i--) e[i].d == t && a < 0 && (e[i].d--, a++);
                0 != a && console.log("debt left")
            }, UZIP.F._goodIndex = function (e, t) {
                var r = 0;
                return t[16 | r] <= e && (r |= 16), t[8 | r] <= e && (r |= 8), t[4 | r] <= e && (r |= 4), t[2 | r] <= e && (r |= 2), t[1 | r] <= e && (r |= 1), r
            }, UZIP.F._writeLit = function (e, t, r, i) {
                return UZIP.F._putsF(r, i, t[e << 1]), i + t[1 + (e << 1)]
            }, UZIP.F.inflate = function (e, t) {
                var r = Uint8Array;
                if (3 == e[0] && 0 == e[1]) return t || new r(0);
                var i = UZIP.F, o = i._bitsF, a = i._bitsE, s = i._decodeTiny, f = i.makeCodes, l = i.codes2map,
                    c = i._get17, u = i.U, h = null == t;
                h && (t = new r(e.length >>> 2 << 3));
                for (var d, A, g = 0, p = 0, m = 0, w = 0, v = 0, b = 0, y = 0, E = 0, F = 0; 0 == g;) if (g = o(e, F, 1), p = o(e, F + 1, 2), F += 3, 0 != p) {
                    if (h && (t = UZIP.F._check(t, E + (1 << 17))), 1 == p && (d = u.flmap, A = u.fdmap, b = 511, y = 31), 2 == p) {
                        m = a(e, F, 5) + 257, w = a(e, F + 5, 5) + 1, v = a(e, F + 10, 4) + 4, F += 14;
                        for (var _ = 0; _ < 38; _ += 2) u.itree[_] = 0, u.itree[_ + 1] = 0;
                        var B = 1;
                        for (_ = 0; _ < v; _++) {
                            var U = a(e, F + 3 * _, 3);
                            u.itree[1 + (u.ordr[_] << 1)] = U, U > B && (B = U)
                        }
                        F += 3 * v, f(u.itree, B), l(u.itree, B, u.imap), d = u.lmap, A = u.dmap, F = s(u.imap, (1 << B) - 1, m + w, e, F, u.ttree);
                        var C = i._copyOut(u.ttree, 0, m, u.ltree);
                        b = (1 << C) - 1;
                        var I = i._copyOut(u.ttree, m, w, u.dtree);
                        y = (1 << I) - 1, f(u.ltree, C), l(u.ltree, C, d), f(u.dtree, I), l(u.dtree, I, A)
                    }
                    for (; ;) {
                        var Q = d[c(e, F) & b];
                        F += 15 & Q;
                        var M = Q >>> 4;
                        if (M >>> 8 == 0) t[E++] = M; else {
                            if (256 == M) break;
                            var x = E + M - 254;
                            if (M > 264) {
                                var T = u.ldef[M - 257];
                                x = E + (T >>> 3) + a(e, F, 7 & T), F += 7 & T
                            }
                            var S = A[c(e, F) & y];
                            F += 15 & S;
                            var R = S >>> 4, O = u.ddef[R], P = (O >>> 4) + o(e, F, 15 & O);
                            for (F += 15 & O, h && (t = UZIP.F._check(t, E + (1 << 17))); E < x;) t[E] = t[E++ - P], t[E] = t[E++ - P], t[E] = t[E++ - P], t[E] = t[E++ - P];
                            E = x
                        }
                    }
                } else {
                    0 != (7 & F) && (F += 8 - (7 & F));
                    var H = 4 + (F >>> 3), L = e[H - 4] | e[H - 3] << 8;
                    h && (t = UZIP.F._check(t, E + L)), t.set(new r(e.buffer, e.byteOffset + H, L), E), F = H + L << 3, E += L
                }
                return t.length == E ? t : t.slice(0, E)
            }, UZIP.F._check = function (e, t) {
                var r = e.length;
                if (t <= r) return e;
                var i = new Uint8Array(Math.max(r << 1, t));
                return i.set(e, 0), i
            }, UZIP.F._decodeTiny = function (e, t, r, i, o, a) {
                for (var s = UZIP.F._bitsE, f = UZIP.F._get17, l = 0; l < r;) {
                    var c = e[f(i, o) & t];
                    o += 15 & c;
                    var u = c >>> 4;
                    if (u <= 15) a[l] = u, l++; else {
                        var h = 0, d = 0;
                        16 == u ? (d = 3 + s(i, o, 2), o += 2, h = a[l - 1]) : 17 == u ? (d = 3 + s(i, o, 3), o += 3) : 18 == u && (d = 11 + s(i, o, 7), o += 7);
                        for (var A = l + d; l < A;) a[l] = h, l++
                    }
                }
                return o
            }, UZIP.F._copyOut = function (e, t, r, i) {
                for (var o = 0, a = 0, s = i.length >>> 1; a < r;) {
                    var f = e[a + t];
                    i[a << 1] = 0, i[1 + (a << 1)] = f, f > o && (o = f), a++
                }
                for (; a < s;) i[a << 1] = 0, i[1 + (a << 1)] = 0, a++;
                return o
            }, UZIP.F.makeCodes = function (e, t) {
                for (var r, i, o, a, s = UZIP.F.U, f = e.length, l = s.bl_count, c = 0; c <= t; c++) l[c] = 0;
                for (c = 1; c < f; c += 2) l[e[c]]++;
                var u = s.next_code;
                for (r = 0, l[0] = 0, i = 1; i <= t; i++) r = r + l[i - 1] << 1, u[i] = r;
                for (o = 0; o < f; o += 2) 0 != (a = e[o + 1]) && (e[o] = u[a], u[a]++)
            }, UZIP.F.codes2map = function (e, t, r) {
                for (var i = e.length, o = UZIP.F.U.rev15, a = 0; a < i; a += 2) if (0 != e[a + 1]) for (var s = a >> 1, f = e[a + 1], l = s << 4 | f, c = t - f, u = e[a] << c, h = u + (1 << c); u != h;) {
                    r[o[u] >>> 15 - t] = l, u++
                }
            }, UZIP.F.revCodes = function (e, t) {
                for (var r = UZIP.F.U.rev15, i = 15 - t, o = 0; o < e.length; o += 2) {
                    var a = e[o] << t - e[o + 1];
                    e[o] = r[a] >>> i
                }
            }, UZIP.F._putsE = function (e, t, r) {
                r <<= 7 & t;
                var i = t >>> 3;
                e[i] |= r, e[i + 1] |= r >>> 8
            }, UZIP.F._putsF = function (e, t, r) {
                r <<= 7 & t;
                var i = t >>> 3;
                e[i] |= r, e[i + 1] |= r >>> 8, e[i + 2] |= r >>> 16
            }, UZIP.F._bitsE = function (e, t, r) {
                return (e[t >>> 3] | e[1 + (t >>> 3)] << 8) >>> (7 & t) & (1 << r) - 1
            }, UZIP.F._bitsF = function (e, t, r) {
                return (e[t >>> 3] | e[1 + (t >>> 3)] << 8 | e[2 + (t >>> 3)] << 16) >>> (7 & t) & (1 << r) - 1
            }, UZIP.F._get17 = function (e, t) {
                return (e[t >>> 3] | e[1 + (t >>> 3)] << 8 | e[2 + (t >>> 3)] << 16) >>> (7 & t)
            }, UZIP.F._get25 = function (e, t) {
                return (e[t >>> 3] | e[1 + (t >>> 3)] << 8 | e[2 + (t >>> 3)] << 16 | e[3 + (t >>> 3)] << 24) >>> (7 & t)
            }, UZIP.F.U = (t = Uint16Array, r = Uint32Array, {
                next_code: new t(16),
                bl_count: new t(16),
                ordr: [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15],
                of0: [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 999, 999, 999],
                exb: [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0, 0],
                ldef: new t(32),
                df0: [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 65535, 65535],
                dxb: [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 0, 0],
                ddef: new r(32),
                flmap: new t(512),
                fltree: [],
                fdmap: new t(32),
                fdtree: [],
                lmap: new t(32768),
                ltree: [],
                ttree: [],
                dmap: new t(32768),
                dtree: [],
                imap: new t(512),
                itree: [],
                rev15: new t(32768),
                lhst: new r(286),
                dhst: new r(30),
                ihst: new r(19),
                lits: new r(15e3),
                strt: new t(65536),
                prev: new t(32768)
            }), function () {
                for (var e = UZIP.F.U, t = 0; t < 32768; t++) {
                    var r = t;
                    r = (4278255360 & (r = (4042322160 & (r = (3435973836 & (r = (2863311530 & r) >>> 1 | (1431655765 & r) << 1)) >>> 2 | (858993459 & r) << 2)) >>> 4 | (252645135 & r) << 4)) >>> 8 | (16711935 & r) << 8, e.rev15[t] = (r >>> 16 | r << 16) >>> 17
                }

                function pushV(e, t, r) {
                    for (; 0 != t--;) e.push(0, r)
                }

                for (t = 0; t < 32; t++) e.ldef[t] = e.of0[t] << 3 | e.exb[t], e.ddef[t] = e.df0[t] << 4 | e.dxb[t];
                pushV(e.fltree, 144, 8), pushV(e.fltree, 112, 9), pushV(e.fltree, 24, 7), pushV(e.fltree, 8, 8), UZIP.F.makeCodes(e.fltree, 9), UZIP.F.codes2map(e.fltree, 9, e.flmap), UZIP.F.revCodes(e.fltree, 9), pushV(e.fdtree, 32, 5), UZIP.F.makeCodes(e.fdtree, 5), UZIP.F.codes2map(e.fdtree, 5, e.fdmap), UZIP.F.revCodes(e.fdtree, 5), pushV(e.itree, 19, 0), pushV(e.ltree, 286, 0), pushV(e.dtree, 30, 0), pushV(e.ttree, 320, 0)
            }()
        }({
            get exports() {
                return e
            }, set exports(t) {
                e = t
            }
        });
        var UZIP = _mergeNamespaces({__proto__: null, default: e}, [e]);
        const UPNG = function () {
            var e = {
                nextZero(e, t) {
                    for (; 0 != e[t];) t++;
                    return t
                },
                readUshort: (e, t) => e[t] << 8 | e[t + 1],
                writeUshort(e, t, r) {
                    e[t] = r >> 8 & 255, e[t + 1] = 255 & r
                },
                readUint: (e, t) => 16777216 * e[t] + (e[t + 1] << 16 | e[t + 2] << 8 | e[t + 3]),
                writeUint(e, t, r) {
                    e[t] = r >> 24 & 255, e[t + 1] = r >> 16 & 255, e[t + 2] = r >> 8 & 255, e[t + 3] = 255 & r
                },
                readASCII(e, t, r) {
                    let i = "";
                    for (let o = 0; o < r; o++) i += String.fromCharCode(e[t + o]);
                    return i
                },
                writeASCII(e, t, r) {
                    for (let i = 0; i < r.length; i++) e[t + i] = r.charCodeAt(i)
                },
                readBytes(e, t, r) {
                    const i = [];
                    for (let o = 0; o < r; o++) i.push(e[t + o]);
                    return i
                },
                pad: e => e.length < 2 ? `0${e}` : e,
                readUTF8(t, r, i) {
                    let o, a = "";
                    for (let o = 0; o < i; o++) a += `%${e.pad(t[r + o].toString(16))}`;
                    try {
                        o = decodeURIComponent(a)
                    } catch (o) {
                        return e.readASCII(t, r, i)
                    }
                    return o
                }
            };

            function decodeImage(t, r, i, o) {
                const a = r * i, s = _getBPP(o), f = Math.ceil(r * s / 8), l = new Uint8Array(4 * a),
                    c = new Uint32Array(l.buffer), {ctype: u} = o, {depth: h} = o, d = e.readUshort;
                if (6 == u) {
                    const e = a << 2;
                    if (8 == h) for (var A = 0; A < e; A += 4) l[A] = t[A], l[A + 1] = t[A + 1], l[A + 2] = t[A + 2], l[A + 3] = t[A + 3];
                    if (16 == h) for (A = 0; A < e; A++) l[A] = t[A << 1]
                } else if (2 == u) {
                    const e = o.tabs.tRNS;
                    if (null == e) {
                        if (8 == h) for (A = 0; A < a; A++) {
                            var g = 3 * A;
                            c[A] = 255 << 24 | t[g + 2] << 16 | t[g + 1] << 8 | t[g]
                        }
                        if (16 == h) for (A = 0; A < a; A++) {
                            g = 6 * A;
                            c[A] = 255 << 24 | t[g + 4] << 16 | t[g + 2] << 8 | t[g]
                        }
                    } else {
                        var p = e[0];
                        const r = e[1], i = e[2];
                        if (8 == h) for (A = 0; A < a; A++) {
                            var m = A << 2;
                            g = 3 * A;
                            c[A] = 255 << 24 | t[g + 2] << 16 | t[g + 1] << 8 | t[g], t[g] == p && t[g + 1] == r && t[g + 2] == i && (l[m + 3] = 0)
                        }
                        if (16 == h) for (A = 0; A < a; A++) {
                            m = A << 2, g = 6 * A;
                            c[A] = 255 << 24 | t[g + 4] << 16 | t[g + 2] << 8 | t[g], d(t, g) == p && d(t, g + 2) == r && d(t, g + 4) == i && (l[m + 3] = 0)
                        }
                    }
                } else if (3 == u) {
                    const e = o.tabs.PLTE, s = o.tabs.tRNS, c = s ? s.length : 0;
                    if (1 == h) for (var w = 0; w < i; w++) {
                        var v = w * f, b = w * r;
                        for (A = 0; A < r; A++) {
                            m = b + A << 2;
                            var y = 3 * (E = t[v + (A >> 3)] >> 7 - ((7 & A) << 0) & 1);
                            l[m] = e[y], l[m + 1] = e[y + 1], l[m + 2] = e[y + 2], l[m + 3] = E < c ? s[E] : 255
                        }
                    }
                    if (2 == h) for (w = 0; w < i; w++) for (v = w * f, b = w * r, A = 0; A < r; A++) {
                        m = b + A << 2, y = 3 * (E = t[v + (A >> 2)] >> 6 - ((3 & A) << 1) & 3);
                        l[m] = e[y], l[m + 1] = e[y + 1], l[m + 2] = e[y + 2], l[m + 3] = E < c ? s[E] : 255
                    }
                    if (4 == h) for (w = 0; w < i; w++) for (v = w * f, b = w * r, A = 0; A < r; A++) {
                        m = b + A << 2, y = 3 * (E = t[v + (A >> 1)] >> 4 - ((1 & A) << 2) & 15);
                        l[m] = e[y], l[m + 1] = e[y + 1], l[m + 2] = e[y + 2], l[m + 3] = E < c ? s[E] : 255
                    }
                    if (8 == h) for (A = 0; A < a; A++) {
                        var E;
                        m = A << 2, y = 3 * (E = t[A]);
                        l[m] = e[y], l[m + 1] = e[y + 1], l[m + 2] = e[y + 2], l[m + 3] = E < c ? s[E] : 255
                    }
                } else if (4 == u) {
                    if (8 == h) for (A = 0; A < a; A++) {
                        m = A << 2;
                        var F = t[_ = A << 1];
                        l[m] = F, l[m + 1] = F, l[m + 2] = F, l[m + 3] = t[_ + 1]
                    }
                    if (16 == h) for (A = 0; A < a; A++) {
                        var _;
                        m = A << 2, F = t[_ = A << 2];
                        l[m] = F, l[m + 1] = F, l[m + 2] = F, l[m + 3] = t[_ + 2]
                    }
                } else if (0 == u) for (p = o.tabs.tRNS ? o.tabs.tRNS : -1, w = 0; w < i; w++) {
                    const e = w * f, i = w * r;
                    if (1 == h) for (var B = 0; B < r; B++) {
                        var U = (F = 255 * (t[e + (B >>> 3)] >>> 7 - (7 & B) & 1)) == 255 * p ? 0 : 255;
                        c[i + B] = U << 24 | F << 16 | F << 8 | F
                    } else if (2 == h) for (B = 0; B < r; B++) {
                        U = (F = 85 * (t[e + (B >>> 2)] >>> 6 - ((3 & B) << 1) & 3)) == 85 * p ? 0 : 255;
                        c[i + B] = U << 24 | F << 16 | F << 8 | F
                    } else if (4 == h) for (B = 0; B < r; B++) {
                        U = (F = 17 * (t[e + (B >>> 1)] >>> 4 - ((1 & B) << 2) & 15)) == 17 * p ? 0 : 255;
                        c[i + B] = U << 24 | F << 16 | F << 8 | F
                    } else if (8 == h) for (B = 0; B < r; B++) {
                        U = (F = t[e + B]) == p ? 0 : 255;
                        c[i + B] = U << 24 | F << 16 | F << 8 | F
                    } else if (16 == h) for (B = 0; B < r; B++) {
                        F = t[e + (B << 1)], U = d(t, e + (B << 1)) == p ? 0 : 255;
                        c[i + B] = U << 24 | F << 16 | F << 8 | F
                    }
                }
                return l
            }

            function _decompress(e, r, i, o) {
                const a = _getBPP(e), s = Math.ceil(i * a / 8), f = new Uint8Array((s + 1 + e.interlace) * o);
                return r = e.tabs.CgBI ? t(r, f) : _inflate(r, f), 0 == e.interlace ? r = _filterZero(r, e, 0, i, o) : 1 == e.interlace && (r = function _readInterlace(e, t) {
                    const r = t.width, i = t.height, o = _getBPP(t), a = o >> 3, s = Math.ceil(r * o / 8),
                        f = new Uint8Array(i * s);
                    let l = 0;
                    const c = [0, 0, 4, 0, 2, 0, 1], u = [0, 4, 0, 2, 0, 1, 0], h = [8, 8, 8, 4, 4, 2, 2],
                        d = [8, 8, 4, 4, 2, 2, 1];
                    let A = 0;
                    for (; A < 7;) {
                        const p = h[A], m = d[A];
                        let w = 0, v = 0, b = c[A];
                        for (; b < i;) b += p, v++;
                        let y = u[A];
                        for (; y < r;) y += m, w++;
                        const E = Math.ceil(w * o / 8);
                        _filterZero(e, t, l, w, v);
                        let F = 0, _ = c[A];
                        for (; _ < i;) {
                            let t = u[A], i = l + F * E << 3;
                            for (; t < r;) {
                                var g;
                                if (1 == o) g = (g = e[i >> 3]) >> 7 - (7 & i) & 1, f[_ * s + (t >> 3)] |= g << 7 - ((7 & t) << 0);
                                if (2 == o) g = (g = e[i >> 3]) >> 6 - (7 & i) & 3, f[_ * s + (t >> 2)] |= g << 6 - ((3 & t) << 1);
                                if (4 == o) g = (g = e[i >> 3]) >> 4 - (7 & i) & 15, f[_ * s + (t >> 1)] |= g << 4 - ((1 & t) << 2);
                                if (o >= 8) {
                                    const r = _ * s + t * a;
                                    for (let t = 0; t < a; t++) f[r + t] = e[(i >> 3) + t]
                                }
                                i += o, t += m
                            }
                            F++, _ += p
                        }
                        w * v != 0 && (l += v * (1 + E)), A += 1
                    }
                    return f
                }(r, e)), r
            }

            function _inflate(e, r) {
                return t(new Uint8Array(e.buffer, 2, e.length - 6), r)
            }

            var t = function () {
                const e = {H: {}};
                return e.H.N = function (t, r) {
                    const i = Uint8Array;
                    let o, a, s = 0, f = 0, l = 0, c = 0, u = 0, h = 0, d = 0, A = 0, g = 0;
                    if (3 == t[0] && 0 == t[1]) return r || new i(0);
                    const p = e.H, m = p.b, w = p.e, v = p.R, b = p.n, y = p.A, E = p.Z, F = p.m, _ = null == r;
                    for (_ && (r = new i(t.length >>> 2 << 5)); 0 == s;) if (s = m(t, g, 1), f = m(t, g + 1, 2), g += 3, 0 != f) {
                        if (_ && (r = e.H.W(r, A + (1 << 17))), 1 == f && (o = F.J, a = F.h, h = 511, d = 31), 2 == f) {
                            l = w(t, g, 5) + 257, c = w(t, g + 5, 5) + 1, u = w(t, g + 10, 4) + 4, g += 14;
                            let e = 1;
                            for (var B = 0; B < 38; B += 2) F.Q[B] = 0, F.Q[B + 1] = 0;
                            for (B = 0; B < u; B++) {
                                const r = w(t, g + 3 * B, 3);
                                F.Q[1 + (F.X[B] << 1)] = r, r > e && (e = r)
                            }
                            g += 3 * u, b(F.Q, e), y(F.Q, e, F.u), o = F.w, a = F.d, g = v(F.u, (1 << e) - 1, l + c, t, g, F.v);
                            const r = p.V(F.v, 0, l, F.C);
                            h = (1 << r) - 1;
                            const i = p.V(F.v, l, c, F.D);
                            d = (1 << i) - 1, b(F.C, r), y(F.C, r, o), b(F.D, i), y(F.D, i, a)
                        }
                        for (; ;) {
                            const e = o[E(t, g) & h];
                            g += 15 & e;
                            const i = e >>> 4;
                            if (i >>> 8 == 0) r[A++] = i; else {
                                if (256 == i) break;
                                {
                                    let e = A + i - 254;
                                    if (i > 264) {
                                        const r = F.q[i - 257];
                                        e = A + (r >>> 3) + w(t, g, 7 & r), g += 7 & r
                                    }
                                    const o = a[E(t, g) & d];
                                    g += 15 & o;
                                    const s = o >>> 4, f = F.c[s], l = (f >>> 4) + m(t, g, 15 & f);
                                    for (g += 15 & f; A < e;) r[A] = r[A++ - l], r[A] = r[A++ - l], r[A] = r[A++ - l], r[A] = r[A++ - l];
                                    A = e
                                }
                            }
                        }
                    } else {
                        0 != (7 & g) && (g += 8 - (7 & g));
                        const o = 4 + (g >>> 3), a = t[o - 4] | t[o - 3] << 8;
                        _ && (r = e.H.W(r, A + a)), r.set(new i(t.buffer, t.byteOffset + o, a), A), g = o + a << 3, A += a
                    }
                    return r.length == A ? r : r.slice(0, A)
                }, e.H.W = function (e, t) {
                    const r = e.length;
                    if (t <= r) return e;
                    const i = new Uint8Array(r << 1);
                    return i.set(e, 0), i
                }, e.H.R = function (t, r, i, o, a, s) {
                    const f = e.H.e, l = e.H.Z;
                    let c = 0;
                    for (; c < i;) {
                        const e = t[l(o, a) & r];
                        a += 15 & e;
                        const i = e >>> 4;
                        if (i <= 15) s[c] = i, c++; else {
                            let e = 0, t = 0;
                            16 == i ? (t = 3 + f(o, a, 2), a += 2, e = s[c - 1]) : 17 == i ? (t = 3 + f(o, a, 3), a += 3) : 18 == i && (t = 11 + f(o, a, 7), a += 7);
                            const r = c + t;
                            for (; c < r;) s[c] = e, c++
                        }
                    }
                    return a
                }, e.H.V = function (e, t, r, i) {
                    let o = 0, a = 0;
                    const s = i.length >>> 1;
                    for (; a < r;) {
                        const r = e[a + t];
                        i[a << 1] = 0, i[1 + (a << 1)] = r, r > o && (o = r), a++
                    }
                    for (; a < s;) i[a << 1] = 0, i[1 + (a << 1)] = 0, a++;
                    return o
                }, e.H.n = function (t, r) {
                    const i = e.H.m, o = t.length;
                    let a, s, f;
                    let l;
                    const c = i.j;
                    for (var u = 0; u <= r; u++) c[u] = 0;
                    for (u = 1; u < o; u += 2) c[t[u]]++;
                    const h = i.K;
                    for (a = 0, c[0] = 0, s = 1; s <= r; s++) a = a + c[s - 1] << 1, h[s] = a;
                    for (f = 0; f < o; f += 2) l = t[f + 1], 0 != l && (t[f] = h[l], h[l]++)
                }, e.H.A = function (t, r, i) {
                    const o = t.length, a = e.H.m.r;
                    for (let e = 0; e < o; e += 2) if (0 != t[e + 1]) {
                        const o = e >> 1, s = t[e + 1], f = o << 4 | s, l = r - s;
                        let c = t[e] << l;
                        const u = c + (1 << l);
                        for (; c != u;) {
                            i[a[c] >>> 15 - r] = f, c++
                        }
                    }
                }, e.H.l = function (t, r) {
                    const i = e.H.m.r, o = 15 - r;
                    for (let e = 0; e < t.length; e += 2) {
                        const a = t[e] << r - t[e + 1];
                        t[e] = i[a] >>> o
                    }
                }, e.H.M = function (e, t, r) {
                    r <<= 7 & t;
                    const i = t >>> 3;
                    e[i] |= r, e[i + 1] |= r >>> 8
                }, e.H.I = function (e, t, r) {
                    r <<= 7 & t;
                    const i = t >>> 3;
                    e[i] |= r, e[i + 1] |= r >>> 8, e[i + 2] |= r >>> 16
                }, e.H.e = function (e, t, r) {
                    return (e[t >>> 3] | e[1 + (t >>> 3)] << 8) >>> (7 & t) & (1 << r) - 1
                }, e.H.b = function (e, t, r) {
                    return (e[t >>> 3] | e[1 + (t >>> 3)] << 8 | e[2 + (t >>> 3)] << 16) >>> (7 & t) & (1 << r) - 1
                }, e.H.Z = function (e, t) {
                    return (e[t >>> 3] | e[1 + (t >>> 3)] << 8 | e[2 + (t >>> 3)] << 16) >>> (7 & t)
                }, e.H.i = function (e, t) {
                    return (e[t >>> 3] | e[1 + (t >>> 3)] << 8 | e[2 + (t >>> 3)] << 16 | e[3 + (t >>> 3)] << 24) >>> (7 & t)
                }, e.H.m = function () {
                    const e = Uint16Array, t = Uint32Array;
                    return {
                        K: new e(16),
                        j: new e(16),
                        X: [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15],
                        S: [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 999, 999, 999],
                        T: [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0, 0],
                        q: new e(32),
                        p: [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 65535, 65535],
                        z: [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 0, 0],
                        c: new t(32),
                        J: new e(512),
                        _: [],
                        h: new e(32),
                        $: [],
                        w: new e(32768),
                        C: [],
                        v: [],
                        d: new e(32768),
                        D: [],
                        u: new e(512),
                        Q: [],
                        r: new e(32768),
                        s: new t(286),
                        Y: new t(30),
                        a: new t(19),
                        t: new t(15e3),
                        k: new e(65536),
                        g: new e(32768)
                    }
                }(), function () {
                    const t = e.H.m;
                    for (var r = 0; r < 32768; r++) {
                        let e = r;
                        e = (2863311530 & e) >>> 1 | (1431655765 & e) << 1, e = (3435973836 & e) >>> 2 | (858993459 & e) << 2, e = (4042322160 & e) >>> 4 | (252645135 & e) << 4, e = (4278255360 & e) >>> 8 | (16711935 & e) << 8, t.r[r] = (e >>> 16 | e << 16) >>> 17
                    }

                    function n(e, t, r) {
                        for (; 0 != t--;) e.push(0, r)
                    }

                    for (r = 0; r < 32; r++) t.q[r] = t.S[r] << 3 | t.T[r], t.c[r] = t.p[r] << 4 | t.z[r];
                    n(t._, 144, 8), n(t._, 112, 9), n(t._, 24, 7), n(t._, 8, 8), e.H.n(t._, 9), e.H.A(t._, 9, t.J), e.H.l(t._, 9), n(t.$, 32, 5), e.H.n(t.$, 5), e.H.A(t.$, 5, t.h), e.H.l(t.$, 5), n(t.Q, 19, 0), n(t.C, 286, 0), n(t.D, 30, 0), n(t.v, 320, 0)
                }(), e.H.N
            }();

            function _getBPP(e) {
                return [1, null, 3, 1, 2, null, 4][e.ctype] * e.depth
            }

            function _filterZero(e, t, r, i, o) {
                let a = _getBPP(t);
                const s = Math.ceil(i * a / 8);
                let f, l;
                a = Math.ceil(a / 8);
                let c = e[r], u = 0;
                if (c > 1 && (e[r] = [0, 0, 1][c - 2]), 3 == c) for (u = a; u < s; u++) e[u + 1] = e[u + 1] + (e[u + 1 - a] >>> 1) & 255;
                for (let t = 0; t < o; t++) if (f = r + t * s, l = f + t + 1, c = e[l - 1], u = 0, 0 == c) for (; u < s; u++) e[f + u] = e[l + u]; else if (1 == c) {
                    for (; u < a; u++) e[f + u] = e[l + u];
                    for (; u < s; u++) e[f + u] = e[l + u] + e[f + u - a]
                } else if (2 == c) for (; u < s; u++) e[f + u] = e[l + u] + e[f + u - s]; else if (3 == c) {
                    for (; u < a; u++) e[f + u] = e[l + u] + (e[f + u - s] >>> 1);
                    for (; u < s; u++) e[f + u] = e[l + u] + (e[f + u - s] + e[f + u - a] >>> 1)
                } else {
                    for (; u < a; u++) e[f + u] = e[l + u] + _paeth(0, e[f + u - s], 0);
                    for (; u < s; u++) e[f + u] = e[l + u] + _paeth(e[f + u - a], e[f + u - s], e[f + u - a - s])
                }
                return e
            }

            function _paeth(e, t, r) {
                const i = e + t - r, o = i - e, a = i - t, s = i - r;
                return o * o <= a * a && o * o <= s * s ? e : a * a <= s * s ? t : r
            }

            function _IHDR(t, r, i) {
                i.width = e.readUint(t, r), r += 4, i.height = e.readUint(t, r), r += 4, i.depth = t[r], r++, i.ctype = t[r], r++, i.compress = t[r], r++, i.filter = t[r], r++, i.interlace = t[r], r++
            }

            function _copyTile(e, t, r, i, o, a, s, f, l) {
                const c = Math.min(t, o), u = Math.min(r, a);
                let h = 0, d = 0;
                for (let r = 0; r < u; r++) for (let a = 0; a < c; a++) if (s >= 0 && f >= 0 ? (h = r * t + a << 2, d = (f + r) * o + s + a << 2) : (h = (-f + r) * t - s + a << 2, d = r * o + a << 2), 0 == l) i[d] = e[h], i[d + 1] = e[h + 1], i[d + 2] = e[h + 2], i[d + 3] = e[h + 3]; else if (1 == l) {
                    var A = e[h + 3] * (1 / 255), g = e[h] * A, p = e[h + 1] * A, m = e[h + 2] * A,
                        w = i[d + 3] * (1 / 255), v = i[d] * w, b = i[d + 1] * w, y = i[d + 2] * w;
                    const t = 1 - A, r = A + w * t, o = 0 == r ? 0 : 1 / r;
                    i[d + 3] = 255 * r, i[d + 0] = (g + v * t) * o, i[d + 1] = (p + b * t) * o, i[d + 2] = (m + y * t) * o
                } else if (2 == l) {
                    A = e[h + 3], g = e[h], p = e[h + 1], m = e[h + 2], w = i[d + 3], v = i[d], b = i[d + 1], y = i[d + 2];
                    A == w && g == v && p == b && m == y ? (i[d] = 0, i[d + 1] = 0, i[d + 2] = 0, i[d + 3] = 0) : (i[d] = g, i[d + 1] = p, i[d + 2] = m, i[d + 3] = A)
                } else if (3 == l) {
                    A = e[h + 3], g = e[h], p = e[h + 1], m = e[h + 2], w = i[d + 3], v = i[d], b = i[d + 1], y = i[d + 2];
                    if (A == w && g == v && p == b && m == y) continue;
                    if (A < 220 && w > 20) return !1
                }
                return !0
            }

            return {
                decode: function decode(r) {
                    const i = new Uint8Array(r);
                    let o = 8;
                    const a = e, s = a.readUshort, f = a.readUint, l = {tabs: {}, frames: []},
                        c = new Uint8Array(i.length);
                    let u, h = 0, d = 0;
                    const A = [137, 80, 78, 71, 13, 10, 26, 10];
                    for (var g = 0; g < 8; g++) if (i[g] != A[g]) throw"The input is not a PNG file!";
                    for (; o < i.length;) {
                        const e = a.readUint(i, o);
                        o += 4;
                        const r = a.readASCII(i, o, 4);
                        if (o += 4, "IHDR" == r) _IHDR(i, o, l); else if ("iCCP" == r) {
                            for (var p = o; 0 != i[p];) p++;
                            a.readASCII(i, o, p - o), i[p + 1];
                            const s = i.slice(p + 2, o + e);
                            let f = null;
                            try {
                                f = _inflate(s)
                            } catch (e) {
                                f = t(s)
                            }
                            l.tabs[r] = f
                        } else if ("CgBI" == r) l.tabs[r] = i.slice(o, o + 4); else if ("IDAT" == r) {
                            for (g = 0; g < e; g++) c[h + g] = i[o + g];
                            h += e
                        } else if ("acTL" == r) l.tabs[r] = {
                            num_frames: f(i, o),
                            num_plays: f(i, o + 4)
                        }, u = new Uint8Array(i.length); else if ("fcTL" == r) {
                            if (0 != d) (E = l.frames[l.frames.length - 1]).data = _decompress(l, u.slice(0, d), E.rect.width, E.rect.height), d = 0;
                            const e = {x: f(i, o + 12), y: f(i, o + 16), width: f(i, o + 4), height: f(i, o + 8)};
                            let t = s(i, o + 22);
                            t = s(i, o + 20) / (0 == t ? 100 : t);
                            const r = {rect: e, delay: Math.round(1e3 * t), dispose: i[o + 24], blend: i[o + 25]};
                            l.frames.push(r)
                        } else if ("fdAT" == r) {
                            for (g = 0; g < e - 4; g++) u[d + g] = i[o + g + 4];
                            d += e - 4
                        } else if ("pHYs" == r) l.tabs[r] = [a.readUint(i, o), a.readUint(i, o + 4), i[o + 8]]; else if ("cHRM" == r) {
                            l.tabs[r] = [];
                            for (g = 0; g < 8; g++) l.tabs[r].push(a.readUint(i, o + 4 * g))
                        } else if ("tEXt" == r || "zTXt" == r) {
                            null == l.tabs[r] && (l.tabs[r] = {});
                            var m = a.nextZero(i, o), w = a.readASCII(i, o, m - o), v = o + e - m - 1;
                            if ("tEXt" == r) y = a.readASCII(i, m + 1, v); else {
                                var b = _inflate(i.slice(m + 2, m + 2 + v));
                                y = a.readUTF8(b, 0, b.length)
                            }
                            l.tabs[r][w] = y
                        } else if ("iTXt" == r) {
                            null == l.tabs[r] && (l.tabs[r] = {});
                            m = 0, p = o;
                            m = a.nextZero(i, p);
                            w = a.readASCII(i, p, m - p);
                            const t = i[p = m + 1];
                            var y;
                            i[p + 1], p += 2, m = a.nextZero(i, p), a.readASCII(i, p, m - p), p = m + 1, m = a.nextZero(i, p), a.readUTF8(i, p, m - p);
                            v = e - ((p = m + 1) - o);
                            if (0 == t) y = a.readUTF8(i, p, v); else {
                                b = _inflate(i.slice(p, p + v));
                                y = a.readUTF8(b, 0, b.length)
                            }
                            l.tabs[r][w] = y
                        } else if ("PLTE" == r) l.tabs[r] = a.readBytes(i, o, e); else if ("hIST" == r) {
                            const e = l.tabs.PLTE.length / 3;
                            l.tabs[r] = [];
                            for (g = 0; g < e; g++) l.tabs[r].push(s(i, o + 2 * g))
                        } else if ("tRNS" == r) 3 == l.ctype ? l.tabs[r] = a.readBytes(i, o, e) : 0 == l.ctype ? l.tabs[r] = s(i, o) : 2 == l.ctype && (l.tabs[r] = [s(i, o), s(i, o + 2), s(i, o + 4)]); else if ("gAMA" == r) l.tabs[r] = a.readUint(i, o) / 1e5; else if ("sRGB" == r) l.tabs[r] = i[o]; else if ("bKGD" == r) 0 == l.ctype || 4 == l.ctype ? l.tabs[r] = [s(i, o)] : 2 == l.ctype || 6 == l.ctype ? l.tabs[r] = [s(i, o), s(i, o + 2), s(i, o + 4)] : 3 == l.ctype && (l.tabs[r] = i[o]); else if ("IEND" == r) break;
                        o += e, a.readUint(i, o), o += 4
                    }
                    var E;
                    return 0 != d && ((E = l.frames[l.frames.length - 1]).data = _decompress(l, u.slice(0, d), E.rect.width, E.rect.height)), l.data = _decompress(l, c, l.width, l.height), delete l.compress, delete l.interlace, delete l.filter, l
                }, toRGBA8: function toRGBA8(e) {
                    const t = e.width, r = e.height;
                    if (null == e.tabs.acTL) return [decodeImage(e.data, t, r, e).buffer];
                    const i = [];
                    null == e.frames[0].data && (e.frames[0].data = e.data);
                    const o = t * r * 4, a = new Uint8Array(o), s = new Uint8Array(o), f = new Uint8Array(o);
                    for (let c = 0; c < e.frames.length; c++) {
                        const u = e.frames[c], h = u.rect.x, d = u.rect.y, A = u.rect.width, g = u.rect.height,
                            p = decodeImage(u.data, A, g, e);
                        if (0 != c) for (var l = 0; l < o; l++) f[l] = a[l];
                        if (0 == u.blend ? _copyTile(p, A, g, a, t, r, h, d, 0) : 1 == u.blend && _copyTile(p, A, g, a, t, r, h, d, 1), i.push(a.buffer.slice(0)), 0 == u.dispose) ; else if (1 == u.dispose) _copyTile(s, A, g, a, t, r, h, d, 0); else if (2 == u.dispose) for (l = 0; l < o; l++) a[l] = f[l]
                    }
                    return i
                }, _paeth: _paeth, _copyTile: _copyTile, _bin: e
            }
        }();
        !function () {
            const {_copyTile: e} = UPNG, {_bin: t} = UPNG, r = UPNG._paeth;
            var i = {
                table: function () {
                    const e = new Uint32Array(256);
                    for (let t = 0; t < 256; t++) {
                        let r = t;
                        for (let e = 0; e < 8; e++) 1 & r ? r = 3988292384 ^ r >>> 1 : r >>>= 1;
                        e[t] = r
                    }
                    return e
                }(), update(e, t, r, o) {
                    for (let a = 0; a < o; a++) e = i.table[255 & (e ^ t[r + a])] ^ e >>> 8;
                    return e
                }, crc: (e, t, r) => 4294967295 ^ i.update(4294967295, e, t, r)
            };

            function addErr(e, t, r, i) {
                t[r] += e[0] * i >> 4, t[r + 1] += e[1] * i >> 4, t[r + 2] += e[2] * i >> 4, t[r + 3] += e[3] * i >> 4
            }

            function N(e) {
                return Math.max(0, Math.min(255, e))
            }

            function D(e, t) {
                const r = e[0] - t[0], i = e[1] - t[1], o = e[2] - t[2], a = e[3] - t[3];
                return r * r + i * i + o * o + a * a
            }

            function dither(e, t, r, i, o, a, s) {
                null == s && (s = 1);
                const f = i.length, l = [];
                for (var c = 0; c < f; c++) {
                    const e = i[c];
                    l.push([e >>> 0 & 255, e >>> 8 & 255, e >>> 16 & 255, e >>> 24 & 255])
                }
                for (c = 0; c < f; c++) {
                    let e = 4294967295;
                    for (var u = 0, h = 0; h < f; h++) {
                        var d = D(l[c], l[h]);
                        h != c && d < e && (e = d, u = h)
                    }
                }
                const A = new Uint32Array(o.buffer), g = new Int16Array(t * r * 4),
                    p = [0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5];
                for (c = 0; c < p.length; c++) p[c] = 255 * ((p[c] + .5) / 16 - .5);
                for (let o = 0; o < r; o++) for (let w = 0; w < t; w++) {
                    var m;
                    c = 4 * (o * t + w);
                    if (2 != s) m = [N(e[c] + g[c]), N(e[c + 1] + g[c + 1]), N(e[c + 2] + g[c + 2]), N(e[c + 3] + g[c + 3])]; else {
                        d = p[4 * (3 & o) + (3 & w)];
                        m = [N(e[c] + d), N(e[c + 1] + d), N(e[c + 2] + d), N(e[c + 3] + d)]
                    }
                    u = 0;
                    let v = 16777215;
                    for (h = 0; h < f; h++) {
                        const e = D(m, l[h]);
                        e < v && (v = e, u = h)
                    }
                    const b = l[u], y = [m[0] - b[0], m[1] - b[1], m[2] - b[2], m[3] - b[3]];
                    1 == s && (w != t - 1 && addErr(y, g, c + 4, 7), o != r - 1 && (0 != w && addErr(y, g, c + 4 * t - 4, 3), addErr(y, g, c + 4 * t, 5), w != t - 1 && addErr(y, g, c + 4 * t + 4, 1))), a[c >> 2] = u, A[c >> 2] = i[u]
                }
            }

            function _main(e, r, o, a, s) {
                null == s && (s = {});
                const {crc: f} = i, l = t.writeUint, c = t.writeUshort, u = t.writeASCII;
                let h = 8;
                const d = e.frames.length > 1;
                let A, g = !1, p = 33 + (d ? 20 : 0);
                if (null != s.sRGB && (p += 13), null != s.pHYs && (p += 21), null != s.iCCP && (A = pako.deflate(s.iCCP), p += 21 + A.length + 4), 3 == e.ctype) {
                    for (var m = e.plte.length, w = 0; w < m; w++) e.plte[w] >>> 24 != 255 && (g = !0);
                    p += 8 + 3 * m + 4 + (g ? 8 + 1 * m + 4 : 0)
                }
                for (var v = 0; v < e.frames.length; v++) {
                    d && (p += 38), p += (F = e.frames[v]).cimg.length + 12, 0 != v && (p += 4)
                }
                p += 12;
                const b = new Uint8Array(p), y = [137, 80, 78, 71, 13, 10, 26, 10];
                for (w = 0; w < 8; w++) b[w] = y[w];
                if (l(b, h, 13), h += 4, u(b, h, "IHDR"), h += 4, l(b, h, r), h += 4, l(b, h, o), h += 4, b[h] = e.depth, h++, b[h] = e.ctype, h++, b[h] = 0, h++, b[h] = 0, h++, b[h] = 0, h++, l(b, h, f(b, h - 17, 17)), h += 4, null != s.sRGB && (l(b, h, 1), h += 4, u(b, h, "sRGB"), h += 4, b[h] = s.sRGB, h++, l(b, h, f(b, h - 5, 5)), h += 4), null != s.iCCP) {
                    const e = 13 + A.length;
                    l(b, h, e), h += 4, u(b, h, "iCCP"), h += 4, u(b, h, "ICC profile"), h += 11, h += 2, b.set(A, h), h += A.length, l(b, h, f(b, h - (e + 4), e + 4)), h += 4
                }
                if (null != s.pHYs && (l(b, h, 9), h += 4, u(b, h, "pHYs"), h += 4, l(b, h, s.pHYs[0]), h += 4, l(b, h, s.pHYs[1]), h += 4, b[h] = s.pHYs[2], h++, l(b, h, f(b, h - 13, 13)), h += 4), d && (l(b, h, 8), h += 4, u(b, h, "acTL"), h += 4, l(b, h, e.frames.length), h += 4, l(b, h, null != s.loop ? s.loop : 0), h += 4, l(b, h, f(b, h - 12, 12)), h += 4), 3 == e.ctype) {
                    l(b, h, 3 * (m = e.plte.length)), h += 4, u(b, h, "PLTE"), h += 4;
                    for (w = 0; w < m; w++) {
                        const t = 3 * w, r = e.plte[w], i = 255 & r, o = r >>> 8 & 255, a = r >>> 16 & 255;
                        b[h + t + 0] = i, b[h + t + 1] = o, b[h + t + 2] = a
                    }
                    if (h += 3 * m, l(b, h, f(b, h - 3 * m - 4, 3 * m + 4)), h += 4, g) {
                        l(b, h, m), h += 4, u(b, h, "tRNS"), h += 4;
                        for (w = 0; w < m; w++) b[h + w] = e.plte[w] >>> 24 & 255;
                        h += m, l(b, h, f(b, h - m - 4, m + 4)), h += 4
                    }
                }
                let E = 0;
                for (v = 0; v < e.frames.length; v++) {
                    var F = e.frames[v];
                    d && (l(b, h, 26), h += 4, u(b, h, "fcTL"), h += 4, l(b, h, E++), h += 4, l(b, h, F.rect.width), h += 4, l(b, h, F.rect.height), h += 4, l(b, h, F.rect.x), h += 4, l(b, h, F.rect.y), h += 4, c(b, h, a[v]), h += 2, c(b, h, 1e3), h += 2, b[h] = F.dispose, h++, b[h] = F.blend, h++, l(b, h, f(b, h - 30, 30)), h += 4);
                    const t = F.cimg;
                    l(b, h, (m = t.length) + (0 == v ? 0 : 4)), h += 4;
                    const r = h;
                    u(b, h, 0 == v ? "IDAT" : "fdAT"), h += 4, 0 != v && (l(b, h, E++), h += 4), b.set(t, h), h += m, l(b, h, f(b, r, h - r)), h += 4
                }
                return l(b, h, 0), h += 4, u(b, h, "IEND"), h += 4, l(b, h, f(b, h - 4, 4)), h += 4, b.buffer
            }

            function compressPNG(e, t, r) {
                for (let i = 0; i < e.frames.length; i++) {
                    const o = e.frames[i];
                    o.rect.width;
                    const a = o.rect.height, s = new Uint8Array(a * o.bpl + a);
                    o.cimg = _filterZero(o.img, a, o.bpp, o.bpl, s, t, r)
                }
            }

            function compress(t, r, i, o, a) {
                const s = a[0], f = a[1], l = a[2], c = a[3], u = a[4], h = a[5];
                let d = 6, A = 8, g = 255;
                for (var p = 0; p < t.length; p++) {
                    const e = new Uint8Array(t[p]);
                    for (var m = e.length, w = 0; w < m; w += 4) g &= e[w + 3]
                }
                const v = 255 != g, b = function framize(t, r, i, o, a, s) {
                    const f = [];
                    for (var l = 0; l < t.length; l++) {
                        const h = new Uint8Array(t[l]), A = new Uint32Array(h.buffer);
                        var c;
                        let g = 0, p = 0, m = r, w = i, v = o ? 1 : 0;
                        if (0 != l) {
                            const b = s || o || 1 == l || 0 != f[l - 2].dispose ? 1 : 2;
                            let y = 0, E = 1e9;
                            for (let e = 0; e < b; e++) {
                                var u = new Uint8Array(t[l - 1 - e]);
                                const o = new Uint32Array(t[l - 1 - e]);
                                let s = r, f = i, c = -1, h = -1;
                                for (let e = 0; e < i; e++) for (let t = 0; t < r; t++) {
                                    A[d = e * r + t] != o[d] && (t < s && (s = t), t > c && (c = t), e < f && (f = e), e > h && (h = e))
                                }
                                -1 == c && (s = f = c = h = 0), a && (1 == (1 & s) && s--, 1 == (1 & f) && f--);
                                const v = (c - s + 1) * (h - f + 1);
                                v < E && (E = v, y = e, g = s, p = f, m = c - s + 1, w = h - f + 1)
                            }
                            u = new Uint8Array(t[l - 1 - y]);
                            1 == y && (f[l - 1].dispose = 2), c = new Uint8Array(m * w * 4), e(u, r, i, c, m, w, -g, -p, 0), v = e(h, r, i, c, m, w, -g, -p, 3) ? 1 : 0, 1 == v ? _prepareDiff(h, r, i, c, {
                                x: g,
                                y: p,
                                width: m,
                                height: w
                            }) : e(h, r, i, c, m, w, -g, -p, 0)
                        } else c = h.slice(0);
                        f.push({rect: {x: g, y: p, width: m, height: w}, img: c, blend: v, dispose: 0})
                    }
                    if (o) for (l = 0; l < f.length; l++) {
                        if (1 == (A = f[l]).blend) continue;
                        const e = A.rect, o = f[l - 1].rect, s = Math.min(e.x, o.x), c = Math.min(e.y, o.y), u = {
                            x: s,
                            y: c,
                            width: Math.max(e.x + e.width, o.x + o.width) - s,
                            height: Math.max(e.y + e.height, o.y + o.height) - c
                        };
                        f[l - 1].dispose = 1, l - 1 != 0 && _updateFrame(t, r, i, f, l - 1, u, a), _updateFrame(t, r, i, f, l, u, a)
                    }
                    let h = 0;
                    if (1 != t.length) for (var d = 0; d < f.length; d++) {
                        var A;
                        h += (A = f[d]).rect.width * A.rect.height
                    }
                    return f
                }(t, r, i, s, f, l), y = {}, E = [], F = [];
                if (0 != o) {
                    const e = [];
                    for (w = 0; w < b.length; w++) e.push(b[w].img.buffer);
                    const t = function concatRGBA(e) {
                        let t = 0;
                        for (var r = 0; r < e.length; r++) t += e[r].byteLength;
                        const i = new Uint8Array(t);
                        let o = 0;
                        for (r = 0; r < e.length; r++) {
                            const t = new Uint8Array(e[r]), a = t.length;
                            for (let e = 0; e < a; e += 4) {
                                let r = t[e], a = t[e + 1], s = t[e + 2];
                                const f = t[e + 3];
                                0 == f && (r = a = s = 0), i[o + e] = r, i[o + e + 1] = a, i[o + e + 2] = s, i[o + e + 3] = f
                            }
                            o += a
                        }
                        return i.buffer
                    }(e), r = quantize(t, o);
                    for (w = 0; w < r.plte.length; w++) E.push(r.plte[w].est.rgba);
                    let i = 0;
                    for (w = 0; w < b.length; w++) {
                        const e = (B = b[w]).img.length;
                        var _ = new Uint8Array(r.inds.buffer, i >> 2, e >> 2);
                        F.push(_);
                        const t = new Uint8Array(r.abuf, i, e);
                        h && dither(B.img, B.rect.width, B.rect.height, E, t, _), B.img.set(t), i += e
                    }
                } else for (p = 0; p < b.length; p++) {
                    var B = b[p];
                    const e = new Uint32Array(B.img.buffer);
                    var U = B.rect.width;
                    m = e.length, _ = new Uint8Array(m);
                    F.push(_);
                    for (w = 0; w < m; w++) {
                        const t = e[w];
                        if (0 != w && t == e[w - 1]) _[w] = _[w - 1]; else if (w > U && t == e[w - U]) _[w] = _[w - U]; else {
                            let e = y[t];
                            if (null == e && (y[t] = e = E.length, E.push(t), E.length >= 300)) break;
                            _[w] = e
                        }
                    }
                }
                const C = E.length;
                C <= 256 && 0 == u && (A = C <= 2 ? 1 : C <= 4 ? 2 : C <= 16 ? 4 : 8, A = Math.max(A, c));
                for (p = 0; p < b.length; p++) {
                    (B = b[p]).rect.x, B.rect.y;
                    U = B.rect.width;
                    const e = B.rect.height;
                    let t = B.img;
                    new Uint32Array(t.buffer);
                    let r = 4 * U, i = 4;
                    if (C <= 256 && 0 == u) {
                        r = Math.ceil(A * U / 8);
                        var I = new Uint8Array(r * e);
                        const o = F[p];
                        for (let t = 0; t < e; t++) {
                            w = t * r;
                            const e = t * U;
                            if (8 == A) for (var Q = 0; Q < U; Q++) I[w + Q] = o[e + Q]; else if (4 == A) for (Q = 0; Q < U; Q++) I[w + (Q >> 1)] |= o[e + Q] << 4 - 4 * (1 & Q); else if (2 == A) for (Q = 0; Q < U; Q++) I[w + (Q >> 2)] |= o[e + Q] << 6 - 2 * (3 & Q); else if (1 == A) for (Q = 0; Q < U; Q++) I[w + (Q >> 3)] |= o[e + Q] << 7 - 1 * (7 & Q)
                        }
                        t = I, d = 3, i = 1
                    } else if (0 == v && 1 == b.length) {
                        I = new Uint8Array(U * e * 3);
                        const o = U * e;
                        for (w = 0; w < o; w++) {
                            const e = 3 * w, r = 4 * w;
                            I[e] = t[r], I[e + 1] = t[r + 1], I[e + 2] = t[r + 2]
                        }
                        t = I, d = 2, i = 3, r = 3 * U
                    }
                    B.img = t, B.bpl = r, B.bpp = i
                }
                return {ctype: d, depth: A, plte: E, frames: b}
            }

            function _updateFrame(t, r, i, o, a, s, f) {
                const l = Uint8Array, c = Uint32Array, u = new l(t[a - 1]), h = new c(t[a - 1]),
                    d = a + 1 < t.length ? new l(t[a + 1]) : null, A = new l(t[a]), g = new c(A.buffer);
                let p = r, m = i, w = -1, v = -1;
                for (let e = 0; e < s.height; e++) for (let t = 0; t < s.width; t++) {
                    const i = s.x + t, f = s.y + e, l = f * r + i, c = g[l];
                    0 == c || 0 == o[a - 1].dispose && h[l] == c && (null == d || 0 != d[4 * l + 3]) || (i < p && (p = i), i > w && (w = i), f < m && (m = f), f > v && (v = f))
                }
                -1 == w && (p = m = w = v = 0), f && (1 == (1 & p) && p--, 1 == (1 & m) && m--), s = {
                    x: p,
                    y: m,
                    width: w - p + 1,
                    height: v - m + 1
                };
                const b = o[a];
                b.rect = s, b.blend = 1, b.img = new Uint8Array(s.width * s.height * 4), 0 == o[a - 1].dispose ? (e(u, r, i, b.img, s.width, s.height, -s.x, -s.y, 0), _prepareDiff(A, r, i, b.img, s)) : e(A, r, i, b.img, s.width, s.height, -s.x, -s.y, 0)
            }

            function _prepareDiff(t, r, i, o, a) {
                e(t, r, i, o, a.width, a.height, -a.x, -a.y, 2)
            }

            function _filterZero(e, t, r, i, o, a, s) {
                const f = [];
                let l, c = [0, 1, 2, 3, 4];
                -1 != a ? c = [a] : (t * i > 5e5 || 1 == r) && (c = [0]), s && (l = {level: 0});
                const u = UZIP;
                for (var h = 0; h < c.length; h++) {
                    for (let a = 0; a < t; a++) _filterLine(o, e, a, i, r, c[h]);
                    f.push(u.deflate(o, l))
                }
                let d, A = 1e9;
                for (h = 0; h < f.length; h++) f[h].length < A && (d = h, A = f[h].length);
                return f[d]
            }

            function _filterLine(e, t, i, o, a, s) {
                const f = i * o;
                let l = f + i;
                if (e[l] = s, l++, 0 == s) if (o < 500) for (var c = 0; c < o; c++) e[l + c] = t[f + c]; else e.set(new Uint8Array(t.buffer, f, o), l); else if (1 == s) {
                    for (c = 0; c < a; c++) e[l + c] = t[f + c];
                    for (c = a; c < o; c++) e[l + c] = t[f + c] - t[f + c - a] + 256 & 255
                } else if (0 == i) {
                    for (c = 0; c < a; c++) e[l + c] = t[f + c];
                    if (2 == s) for (c = a; c < o; c++) e[l + c] = t[f + c];
                    if (3 == s) for (c = a; c < o; c++) e[l + c] = t[f + c] - (t[f + c - a] >> 1) + 256 & 255;
                    if (4 == s) for (c = a; c < o; c++) e[l + c] = t[f + c] - r(t[f + c - a], 0, 0) + 256 & 255
                } else {
                    if (2 == s) for (c = 0; c < o; c++) e[l + c] = t[f + c] + 256 - t[f + c - o] & 255;
                    if (3 == s) {
                        for (c = 0; c < a; c++) e[l + c] = t[f + c] + 256 - (t[f + c - o] >> 1) & 255;
                        for (c = a; c < o; c++) e[l + c] = t[f + c] + 256 - (t[f + c - o] + t[f + c - a] >> 1) & 255
                    }
                    if (4 == s) {
                        for (c = 0; c < a; c++) e[l + c] = t[f + c] + 256 - r(0, t[f + c - o], 0) & 255;
                        for (c = a; c < o; c++) e[l + c] = t[f + c] + 256 - r(t[f + c - a], t[f + c - o], t[f + c - a - o]) & 255
                    }
                }
            }

            function quantize(e, t) {
                const r = new Uint8Array(e), i = r.slice(0), o = new Uint32Array(i.buffer), a = getKDtree(i, t),
                    s = a[0], f = a[1], l = r.length, c = new Uint8Array(l >> 2);
                let u;
                if (r.length < 2e7) for (var h = 0; h < l; h += 4) {
                    u = getNearest(s, d = r[h] * (1 / 255), A = r[h + 1] * (1 / 255), g = r[h + 2] * (1 / 255), p = r[h + 3] * (1 / 255)), c[h >> 2] = u.ind, o[h >> 2] = u.est.rgba
                } else for (h = 0; h < l; h += 4) {
                    var d = r[h] * (1 / 255), A = r[h + 1] * (1 / 255), g = r[h + 2] * (1 / 255),
                        p = r[h + 3] * (1 / 255);
                    for (u = s; u.left;) u = planeDst(u.est, d, A, g, p) <= 0 ? u.left : u.right;
                    c[h >> 2] = u.ind, o[h >> 2] = u.est.rgba
                }
                return {abuf: i.buffer, inds: c, plte: f}
            }

            function getKDtree(e, t, r) {
                null == r && (r = 1e-4);
                const i = new Uint32Array(e.buffer),
                    o = {i0: 0, i1: e.length, bst: null, est: null, tdst: 0, left: null, right: null};
                o.bst = stats(e, o.i0, o.i1), o.est = estats(o.bst);
                const a = [o];
                for (; a.length < t;) {
                    let t = 0, o = 0;
                    for (var s = 0; s < a.length; s++) a[s].est.L > t && (t = a[s].est.L, o = s);
                    if (t < r) break;
                    const f = a[o], l = splitPixels(e, i, f.i0, f.i1, f.est.e, f.est.eMq255);
                    if (f.i0 >= l || f.i1 <= l) {
                        f.est.L = 0;
                        continue
                    }
                    const c = {i0: f.i0, i1: l, bst: null, est: null, tdst: 0, left: null, right: null};
                    c.bst = stats(e, c.i0, c.i1), c.est = estats(c.bst);
                    const u = {i0: l, i1: f.i1, bst: null, est: null, tdst: 0, left: null, right: null};
                    u.bst = {R: [], m: [], N: f.bst.N - c.bst.N};
                    for (s = 0; s < 16; s++) u.bst.R[s] = f.bst.R[s] - c.bst.R[s];
                    for (s = 0; s < 4; s++) u.bst.m[s] = f.bst.m[s] - c.bst.m[s];
                    u.est = estats(u.bst), f.left = c, f.right = u, a[o] = c, a.push(u)
                }
                a.sort(((e, t) => t.bst.N - e.bst.N));
                for (s = 0; s < a.length; s++) a[s].ind = s;
                return [o, a]
            }

            function getNearest(e, t, r, i, o) {
                if (null == e.left) return e.tdst = function dist(e, t, r, i, o) {
                    const a = t - e[0], s = r - e[1], f = i - e[2], l = o - e[3];
                    return a * a + s * s + f * f + l * l
                }(e.est.q, t, r, i, o), e;
                const a = planeDst(e.est, t, r, i, o);
                let s = e.left, f = e.right;
                a > 0 && (s = e.right, f = e.left);
                const l = getNearest(s, t, r, i, o);
                if (l.tdst <= a * a) return l;
                const c = getNearest(f, t, r, i, o);
                return c.tdst < l.tdst ? c : l
            }

            function planeDst(e, t, r, i, o) {
                const {e: a} = e;
                return a[0] * t + a[1] * r + a[2] * i + a[3] * o - e.eMq
            }

            function splitPixels(e, t, r, i, o, a) {
                for (i -= 4; r < i;) {
                    for (; vecDot(e, r, o) <= a;) r += 4;
                    for (; vecDot(e, i, o) > a;) i -= 4;
                    if (r >= i) break;
                    const s = t[r >> 2];
                    t[r >> 2] = t[i >> 2], t[i >> 2] = s, r += 4, i -= 4
                }
                for (; vecDot(e, r, o) > a;) r -= 4;
                return r + 4
            }

            function vecDot(e, t, r) {
                return e[t] * r[0] + e[t + 1] * r[1] + e[t + 2] * r[2] + e[t + 3] * r[3]
            }

            function stats(e, t, r) {
                const i = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], o = [0, 0, 0, 0], a = r - t >> 2;
                for (let a = t; a < r; a += 4) {
                    const t = e[a] * (1 / 255), r = e[a + 1] * (1 / 255), s = e[a + 2] * (1 / 255),
                        f = e[a + 3] * (1 / 255);
                    o[0] += t, o[1] += r, o[2] += s, o[3] += f, i[0] += t * t, i[1] += t * r, i[2] += t * s, i[3] += t * f, i[5] += r * r, i[6] += r * s, i[7] += r * f, i[10] += s * s, i[11] += s * f, i[15] += f * f
                }
                return i[4] = i[1], i[8] = i[2], i[9] = i[6], i[12] = i[3], i[13] = i[7], i[14] = i[11], {
                    R: i,
                    m: o,
                    N: a
                }
            }

            function estats(e) {
                const {R: t} = e, {m: r} = e, {N: i} = e, a = r[0], s = r[1], f = r[2], l = r[3],
                    c = 0 == i ? 0 : 1 / i,
                    u = [t[0] - a * a * c, t[1] - a * s * c, t[2] - a * f * c, t[3] - a * l * c, t[4] - s * a * c, t[5] - s * s * c, t[6] - s * f * c, t[7] - s * l * c, t[8] - f * a * c, t[9] - f * s * c, t[10] - f * f * c, t[11] - f * l * c, t[12] - l * a * c, t[13] - l * s * c, t[14] - l * f * c, t[15] - l * l * c],
                    h = u, d = o;
                let A = [Math.random(), Math.random(), Math.random(), Math.random()], g = 0, p = 0;
                if (0 != i) for (let e = 0; e < 16 && (A = d.multVec(h, A), p = Math.sqrt(d.dot(A, A)), A = d.sml(1 / p, A), !(0 != e && Math.abs(p - g) < 1e-9)); e++) g = p;
                const m = [a * c, s * c, f * c, l * c];
                return {
                    Cov: u,
                    q: m,
                    e: A,
                    L: g,
                    eMq255: d.dot(d.sml(255, m), A),
                    eMq: d.dot(A, m),
                    rgba: (Math.round(255 * m[3]) << 24 | Math.round(255 * m[2]) << 16 | Math.round(255 * m[1]) << 8 | Math.round(255 * m[0]) << 0) >>> 0
                }
            }

            var o = {
                multVec: (e, t) => [e[0] * t[0] + e[1] * t[1] + e[2] * t[2] + e[3] * t[3], e[4] * t[0] + e[5] * t[1] + e[6] * t[2] + e[7] * t[3], e[8] * t[0] + e[9] * t[1] + e[10] * t[2] + e[11] * t[3], e[12] * t[0] + e[13] * t[1] + e[14] * t[2] + e[15] * t[3]],
                dot: (e, t) => e[0] * t[0] + e[1] * t[1] + e[2] * t[2] + e[3] * t[3],
                sml: (e, t) => [e * t[0], e * t[1], e * t[2], e * t[3]]
            };
            UPNG.encode = function encode(e, t, r, i, o, a, s) {
                null == i && (i = 0), null == s && (s = !1);
                const f = compress(e, t, r, i, [!1, !1, !1, 0, s, !1]);
                return compressPNG(f, -1), _main(f, t, r, o, a)
            }, UPNG.encodeLL = function encodeLL(e, t, r, i, o, a, s, f) {
                const l = {ctype: 0 + (1 == i ? 0 : 2) + (0 == o ? 0 : 4), depth: a, frames: []}, c = (i + o) * a,
                    u = c * t;
                for (let i = 0; i < e.length; i++) l.frames.push({
                    rect: {x: 0, y: 0, width: t, height: r},
                    img: new Uint8Array(e[i]),
                    blend: 0,
                    dispose: 1,
                    bpp: Math.ceil(c / 8),
                    bpl: Math.ceil(u / 8)
                });
                return compressPNG(l, 0, !0), _main(l, t, r, s, f)
            }, UPNG.encode.compress = compress, UPNG.encode.dither = dither, UPNG.quantize = quantize, UPNG.quantize.getKDtree = getKDtree, UPNG.quantize.getNearest = getNearest
        }();
        const t = {
            toArrayBuffer(e, r) {
                const i = e.width, o = e.height, a = i << 2, s = e.getContext("2d").getImageData(0, 0, i, o),
                    f = new Uint32Array(s.data.buffer), l = (32 * i + 31) / 32 << 2, c = l * o, u = 122 + c,
                    h = new ArrayBuffer(u), d = new DataView(h), A = 1 << 20;
                let g, p, m, w, v = A, b = 0, y = 0, E = 0;

                function set16(e) {
                    d.setUint16(y, e, !0), y += 2
                }

                function set32(e) {
                    d.setUint32(y, e, !0), y += 4
                }

                function seek(e) {
                    y += e
                }

                set16(19778), set32(u), seek(4), set32(122), set32(108), set32(i), set32(-o >>> 0), set16(1), set16(32), set32(3), set32(c), set32(2835), set32(2835), seek(8), set32(16711680), set32(65280), set32(255), set32(4278190080), set32(1466527264), function convert() {
                    for (; b < o && v > 0;) {
                        for (w = 122 + b * l, g = 0; g < a;) v--, p = f[E++], m = p >>> 24, d.setUint32(w + g, p << 8 | m), g += 4;
                        b++
                    }
                    E < f.length ? (v = A, setTimeout(convert, t._dly)) : r(h)
                }()
            }, toBlob(e, t) {
                this.toArrayBuffer(e, (e => {
                    t(new Blob([e], {type: "image/bmp"}))
                }))
            }, _dly: 9
        };
        var r = {
            CHROME: "CHROME",
            FIREFOX: "FIREFOX",
            DESKTOP_SAFARI: "DESKTOP_SAFARI",
            IE: "IE",
            IOS: "IOS",
            ETC: "ETC"
        }, i = {
            [r.CHROME]: 16384,
            [r.FIREFOX]: 11180,
            [r.DESKTOP_SAFARI]: 16384,
            [r.IE]: 8192,
            [r.IOS]: 4096,
            [r.ETC]: 8192
        };
        const o = "undefined" != typeof window,
            a = "undefined" != typeof WorkerGlobalScope && self instanceof WorkerGlobalScope,
            s = o && window.cordova && window.cordova.require && window.cordova.require("cordova/modulemapper"),
            CustomFile = (o || a) && (s && s.getOriginalSymbol(window, "File") || "undefined" != typeof File && File),
            CustomFileReader = (o || a) && (s && s.getOriginalSymbol(window, "FileReader") || "undefined" != typeof FileReader && FileReader);

        function getFilefromDataUrl(e, t, r = Date.now()) {
            return new Promise((i => {
                const o = e.split(","), a = o[0].match(/:(.*?);/)[1], s = globalThis.atob(o[1]);
                let f = s.length;
                const l = new Uint8Array(f);
                for (; f--;) l[f] = s.charCodeAt(f);
                const c = new Blob([l], {type: a});
                c.name = t, c.lastModified = r, i(c)
            }))
        }

        function getDataUrlFromFile(e) {
            return new Promise(((t, r) => {
                const i = new CustomFileReader;
                i.onload = () => t(i.result), i.onerror = e => r(e), i.readAsDataURL(e)
            }))
        }

        function loadImage(e) {
            return new Promise(((t, r) => {
                const i = new Image;
                i.onload = () => t(i), i.onerror = e => r(e), i.src = e
            }))
        }

        function getBrowserName() {
            if (void 0 !== getBrowserName.cachedResult) return getBrowserName.cachedResult;
            let e = r.ETC;
            const {userAgent: t} = navigator;
            return /Chrom(e|ium)/i.test(t) ? e = r.CHROME : /iP(ad|od|hone)/i.test(t) && /WebKit/i.test(t) ? e = r.IOS : /Safari/i.test(t) ? e = r.DESKTOP_SAFARI : /Firefox/i.test(t) ? e = r.FIREFOX : (/MSIE/i.test(t) || !0 == !!document.documentMode) && (e = r.IE), getBrowserName.cachedResult = e, getBrowserName.cachedResult
        }

        function approximateBelowMaximumCanvasSizeOfBrowser(e, t) {
            const r = getBrowserName(), o = i[r];
            let a = e, s = t, f = a * s;
            const l = a > s ? s / a : a / s;
            for (; f > o * o;) {
                const e = (o + a) / 2, t = (o + s) / 2;
                e < t ? (s = t, a = t * l) : (s = e * l, a = e), f = a * s
            }
            return {width: a, height: s}
        }

        function getNewCanvasAndCtx(e, t) {
            let r, i;
            try {
                if (r = new OffscreenCanvas(e, t), i = r.getContext("2d"), null === i) throw new Error("getContext of OffscreenCanvas returns null")
            } catch (e) {
                r = document.createElement("canvas"), i = r.getContext("2d")
            }
            return r.width = e, r.height = t, [r, i]
        }

        function drawImageInCanvas(e, t) {
            const {
                width: r,
                height: i
            } = approximateBelowMaximumCanvasSizeOfBrowser(e.width, e.height), [o, a] = getNewCanvasAndCtx(r, i);
            return t && /jpe?g/.test(t) && (a.fillStyle = "white", a.fillRect(0, 0, o.width, o.height)), a.drawImage(e, 0, 0, o.width, o.height), o
        }

        function isIOS() {
            return void 0 !== isIOS.cachedResult || (isIOS.cachedResult = ["iPad Simulator", "iPhone Simulator", "iPod Simulator", "iPad", "iPhone", "iPod"].includes(navigator.platform) || navigator.userAgent.includes("Mac") && "undefined" != typeof document && "ontouchend" in document), isIOS.cachedResult
        }

        function drawFileInCanvas(e, t = {}) {
            return new Promise((function (i, o) {
                let a, s;
                var $Try_2_Post = function () {
                    try {
                        return s = drawImageInCanvas(a, t.fileType || e.type), i([a, s])
                    } catch (e) {
                        return o(e)
                    }
                }, $Try_2_Catch = function (t) {
                    try {
                        0;
                        var $Try_3_Catch = function (e) {
                            try {
                                throw e
                            } catch (e) {
                                return o(e)
                            }
                        };
                        try {
                            let t;
                            return getDataUrlFromFile(e).then((function (e) {
                                try {
                                    return t = e, loadImage(t).then((function (e) {
                                        try {
                                            return a = e, function () {
                                                try {
                                                    return $Try_2_Post()
                                                } catch (e) {
                                                    return o(e)
                                                }
                                            }()
                                        } catch (e) {
                                            return $Try_3_Catch(e)
                                        }
                                    }), $Try_3_Catch)
                                } catch (e) {
                                    return $Try_3_Catch(e)
                                }
                            }), $Try_3_Catch)
                        } catch (e) {
                            $Try_3_Catch(e)
                        }
                    } catch (e) {
                        return o(e)
                    }
                };
                try {
                    if (isIOS() || [r.DESKTOP_SAFARI, r.MOBILE_SAFARI].includes(getBrowserName())) throw new Error("Skip createImageBitmap on IOS and Safari");
                    return createImageBitmap(e).then((function (e) {
                        try {
                            return a = e, $Try_2_Post()
                        } catch (e) {
                            return $Try_2_Catch()
                        }
                    }), $Try_2_Catch)
                } catch (e) {
                    $Try_2_Catch()
                }
            }))
        }

        function canvasToFile(e, r, i, o, a = 1) {
            return new Promise((function (s, f) {
                let l;
                if ("image/png" === r) {
                    let c, u, h;
                    return c = e.getContext("2d"), ({data: u} = c.getImageData(0, 0, e.width, e.height)), h = UPNG.encode([u.buffer], e.width, e.height, 4096 * a), l = new Blob([h], {type: r}), l.name = i, l.lastModified = o, $If_4.call(this)
                }
                {
                    if ("image/bmp" === r) return new Promise((r => t.toBlob(e, r))).then(function (e) {
                        try {
                            return l = e, l.name = i, l.lastModified = o, $If_5.call(this)
                        } catch (e) {
                            return f(e)
                        }
                    }.bind(this), f);
                    {
                        if ("function" == typeof OffscreenCanvas && e instanceof OffscreenCanvas) return e.convertToBlob({
                            type: r,
                            quality: a
                        }).then(function (e) {
                            try {
                                return l = e, l.name = i, l.lastModified = o, $If_6.call(this)
                            } catch (e) {
                                return f(e)
                            }
                        }.bind(this), f);
                        {
                            let d;
                            return d = e.toDataURL(r, a), getFilefromDataUrl(d, i, o).then(function (e) {
                                try {
                                    return l = e, $If_6.call(this)
                                } catch (e) {
                                    return f(e)
                                }
                            }.bind(this), f)
                        }

                        function $If_6() {
                            return $If_5.call(this)
                        }
                    }

                    function $If_5() {
                        return $If_4.call(this)
                    }
                }

                function $If_4() {
                    return s(l)
                }
            }))
        }

        function cleanupCanvasMemory(e) {
            e.width = 0, e.height = 0
        }

        function isAutoOrientationInBrowser() {
            return new Promise((function (e, t) {
                let r, i, o, a, s;
                return void 0 !== isAutoOrientationInBrowser.cachedResult ? e(isAutoOrientationInBrowser.cachedResult) : (r = "data:image/jpeg;base64,/9j/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAYAAAAAAAD/2wCEAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/AABEIAAEAAgMBEQACEQEDEQH/xABKAAEAAAAAAAAAAAAAAAAAAAALEAEAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAAAAAAAEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwA/8H//2Q==", getFilefromDataUrl("data:image/jpeg;base64,/9j/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAYAAAAAAAD/2wCEAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/AABEIAAEAAgMBEQACEQEDEQH/xABKAAEAAAAAAAAAAAAAAAAAAAALEAEAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAAAAAAAEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwA/8H//2Q==", "test.jpg", Date.now()).then((function (r) {
                    try {
                        return i = r, drawFileInCanvas(i).then((function (r) {
                            try {
                                return o = r[1], canvasToFile(o, i.type, i.name, i.lastModified).then((function (r) {
                                    try {
                                        return a = r, cleanupCanvasMemory(o), drawFileInCanvas(a).then((function (r) {
                                            try {
                                                return s = r[0], isAutoOrientationInBrowser.cachedResult = 1 === s.width && 2 === s.height, e(isAutoOrientationInBrowser.cachedResult)
                                            } catch (e) {
                                                return t(e)
                                            }
                                        }), t)
                                    } catch (e) {
                                        return t(e)
                                    }
                                }), t)
                            } catch (e) {
                                return t(e)
                            }
                        }), t)
                    } catch (e) {
                        return t(e)
                    }
                }), t))
            }))
        }

        function getExifOrientation(e) {
            return new Promise(((t, r) => {
                const i = new CustomFileReader;
                i.onload = e => {
                    const r = new DataView(e.target.result);
                    if (65496 != r.getUint16(0, !1)) return t(-2);
                    const i = r.byteLength;
                    let o = 2;
                    for (; o < i;) {
                        if (r.getUint16(o + 2, !1) <= 8) return t(-1);
                        const e = r.getUint16(o, !1);
                        if (o += 2, 65505 == e) {
                            if (1165519206 != r.getUint32(o += 2, !1)) return t(-1);
                            const e = 18761 == r.getUint16(o += 6, !1);
                            o += r.getUint32(o + 4, e);
                            const i = r.getUint16(o, e);
                            o += 2;
                            for (let a = 0; a < i; a++) if (274 == r.getUint16(o + 12 * a, e)) return t(r.getUint16(o + 12 * a + 8, e))
                        } else {
                            if (65280 != (65280 & e)) break;
                            o += r.getUint16(o, !1)
                        }
                    }
                    return t(-1)
                }, i.onerror = e => r(e), i.readAsArrayBuffer(e)
            }))
        }

        function handleMaxWidthOrHeight(e, t) {
            const {width: r} = e, {height: i} = e, {maxWidthOrHeight: o} = t;
            let a, s = e;
            return isFinite(o) && (r > o || i > o) && ([s, a] = getNewCanvasAndCtx(r, i), r > i ? (s.width = o, s.height = i / r * o) : (s.width = r / i * o, s.height = o), a.drawImage(e, 0, 0, s.width, s.height), cleanupCanvasMemory(e)), s
        }

        function followExifOrientation(e, t) {
            const {width: r} = e, {height: i} = e, [o, a] = getNewCanvasAndCtx(r, i);
            switch (t > 4 && t < 9 ? (o.width = i, o.height = r) : (o.width = r, o.height = i), t) {
                case 2:
                    a.transform(-1, 0, 0, 1, r, 0);
                    break;
                case 3:
                    a.transform(-1, 0, 0, -1, r, i);
                    break;
                case 4:
                    a.transform(1, 0, 0, -1, 0, i);
                    break;
                case 5:
                    a.transform(0, 1, 1, 0, 0, 0);
                    break;
                case 6:
                    a.transform(0, 1, -1, 0, i, 0);
                    break;
                case 7:
                    a.transform(0, -1, -1, 0, i, r);
                    break;
                case 8:
                    a.transform(0, -1, 1, 0, 0, r)
            }
            return a.drawImage(e, 0, 0, r, i), cleanupCanvasMemory(e), o
        }

        function compress(e, t, r = 0) {
            return new Promise((function (i, o) {
                let a, s, f, l, c, u, h, d, A, g, p, m, w, v, b, y, E, F, _, B;

                function incProgress(e = 5) {
                    if (t.signal && t.signal.aborted) throw t.signal.reason;
                    a += e, t.onProgress(Math.min(a, 100))
                }

                function setProgress(e) {
                    if (t.signal && t.signal.aborted) throw t.signal.reason;
                    a = Math.min(Math.max(e, a), 100), t.onProgress(a)
                }

                return a = r, s = t.maxIteration || 10, f = 1024 * t.maxSizeMB * 1024, incProgress(), drawFileInCanvas(e, t).then(function (r) {
                    try {
                        return [, l] = r, incProgress(), c = handleMaxWidthOrHeight(l, t), incProgress(), new Promise((function (r, i) {
                            var o;
                            if (!(o = t.exifOrientation)) return getExifOrientation(e).then(function (e) {
                                try {
                                    return o = e, $If_2.call(this)
                                } catch (e) {
                                    return i(e)
                                }
                            }.bind(this), i);

                            function $If_2() {
                                return r(o)
                            }

                            return $If_2.call(this)
                        })).then(function (r) {
                            try {
                                return u = r, incProgress(), isAutoOrientationInBrowser().then(function (r) {
                                    try {
                                        return h = r ? c : followExifOrientation(c, u), incProgress(), d = t.initialQuality || 1, A = t.fileType || e.type, canvasToFile(h, A, e.name, e.lastModified, d).then(function (r) {
                                            try {
                                                {
                                                    if (g = r, incProgress(), p = g.size > f, m = g.size > e.size, !p && !m) return setProgress(100), i(g);
                                                    var a;

                                                    function $Loop_3() {
                                                        if (s-- && (b > f || b > w)) {
                                                            let t, r;
                                                            return t = B ? .95 * _.width : _.width, r = B ? .95 * _.height : _.height, [E, F] = getNewCanvasAndCtx(t, r), F.drawImage(_, 0, 0, t, r), d *= "image/png" === A ? .85 : .95, canvasToFile(E, A, e.name, e.lastModified, d).then((function (e) {
                                                                try {
                                                                    return y = e, cleanupCanvasMemory(_), _ = E, b = y.size, setProgress(Math.min(99, Math.floor((v - b) / (v - f) * 100))), $Loop_3
                                                                } catch (e) {
                                                                    return o(e)
                                                                }
                                                            }), o)
                                                        }
                                                        return [1]
                                                    }

                                                    return w = e.size, v = g.size, b = v, _ = h, B = !t.alwaysKeepResolution && p, (a = function (e) {
                                                        for (; e;) {
                                                            if (e.then) return void e.then(a, o);
                                                            try {
                                                                if (e.pop) {
                                                                    if (e.length) return e.pop() ? $Loop_3_exit.call(this) : e;
                                                                    e = $Loop_3
                                                                } else e = e.call(this)
                                                            } catch (e) {
                                                                return o(e)
                                                            }
                                                        }
                                                    }.bind(this))($Loop_3);

                                                    function $Loop_3_exit() {
                                                        return cleanupCanvasMemory(_), cleanupCanvasMemory(E), cleanupCanvasMemory(c), cleanupCanvasMemory(h), cleanupCanvasMemory(l), setProgress(100), i(y)
                                                    }
                                                }
                                            } catch (u) {
                                                return o(u)
                                            }
                                        }.bind(this), o)
                                    } catch (e) {
                                        return o(e)
                                    }
                                }.bind(this), o)
                            } catch (e) {
                                return o(e)
                            }
                        }.bind(this), o)
                    } catch (e) {
                        return o(e)
                    }
                }.bind(this), o)
            }))
        }

        const f = "\nlet scriptImported = false\nself.addEventListener('message', async (e) => {\n  const { file, id, imageCompressionLibUrl, options } = e.data\n  options.onProgress = (progress) => self.postMessage({ progress, id })\n  try {\n    if (!scriptImported) {\n      // console.log('[worker] importScripts', imageCompressionLibUrl)\n      self.importScripts(imageCompressionLibUrl)\n      scriptImported = true\n    }\n    // console.log('[worker] self', self)\n    const compressedFile = await imageCompression(file, options)\n    self.postMessage({ file: compressedFile, id })\n  } catch (e) {\n    // console.error('[worker] error', e)\n    self.postMessage({ error: e.message + '\\n' + e.stack, id })\n  }\n})\n";
        let l;

        function compressOnWebWorker(e, t) {
            return new Promise(((r, i) => {
                l || (l = function createWorkerScriptURL(e) {
                    const t = [];
                    return "function" == typeof e ? t.push(`(${e})()`) : t.push(e), URL.createObjectURL(new Blob(t))
                }(f));
                const o = new Worker(l);
                o.addEventListener("message", (function handler(e) {
                    if (t.signal && t.signal.aborted) o.terminate(); else if (void 0 === e.data.progress) {
                        if (e.data.error) return i(new Error(e.data.error)), void o.terminate();
                        r(e.data.file), o.terminate()
                    } else t.onProgress(e.data.progress)
                })), o.addEventListener("error", i), t.signal && t.signal.addEventListener("abort", (() => {
                    i(t.signal.reason), o.terminate()
                })), o.postMessage({
                    file: e,
                    imageCompressionLibUrl: t.libURL,
                    options: {...t, onProgress: void 0, signal: void 0}
                })
            }))
        }

        function imageCompression(e, t) {
            return new Promise((function (r, i) {
                let o, a, s, f, l, c;
                if (o = {...t}, s = 0, ({onProgress: f} = o), o.maxSizeMB = o.maxSizeMB || Number.POSITIVE_INFINITY, l = "boolean" != typeof o.useWebWorker || o.useWebWorker, delete o.useWebWorker, o.onProgress = e => {
                    s = e, "function" == typeof f && f(s)
                }, !(1 || e instanceof Blob || e instanceof CustomFile)) return i(new Error("The file given is not an instance of Blob or File"));
                if (!/^image/.test(e.type)) return i(new Error("The file given is not an image"));
                if (c = "undefined" != typeof WorkerGlobalScope && self instanceof WorkerGlobalScope, !l || "function" != typeof Worker || c) return compress(e, o).then(function (e) {
                    try {
                        return a = e, $If_4.call(this)
                    } catch (e) {
                        return i(e)
                    }
                }.bind(this), i);
                var u = function () {
                    try {
                        return $If_4.call(this)
                    } catch (e) {
                        return i(e)
                    }
                }.bind(this), $Try_1_Catch = function (t) {
                    try {
                        return compress(e, o).then((function (e) {
                            try {
                                return a = e, u()
                            } catch (e) {
                                return i(e)
                            }
                        }), i)
                    } catch (e) {
                        return i(e)
                    }
                };
                try {
                    return o.libURL = o.libURL || "https://cdn.bootcdn.net/ajax/libs/browser-image-compression/2.0.2/browser-image-compression.js", compressOnWebWorker(e, o).then((function (e) {
                        try {
                            return a = e, u()
                        } catch (e) {
                            return $Try_1_Catch()
                        }
                    }), $Try_1_Catch)
                } catch (e) {
                    $Try_1_Catch()
                }

                function $If_4() {
                    try {
                        a.name = e.name, a.lastModified = e.lastModified
                    } catch (e) {
                    }
                    try {
                        o.preserveExif && "image/jpeg" === e.type && (!o.fileType || o.fileType && o.fileType === e.type) && (a = copyExifWithoutOrientation(e, a))
                    } catch (e) {
                    }
                    return r(a)
                }
            }))
        }

        return imageCompression.getDataUrlFromFile = getDataUrlFromFile, imageCompression.getFilefromDataUrl = getFilefromDataUrl, imageCompression.loadImage = loadImage, imageCompression.drawImageInCanvas = drawImageInCanvas, imageCompression.drawFileInCanvas = drawFileInCanvas, imageCompression.canvasToFile = canvasToFile, imageCompression.getExifOrientation = getExifOrientation, imageCompression.handleMaxWidthOrHeight = handleMaxWidthOrHeight, imageCompression.followExifOrientation = followExifOrientation, imageCompression.cleanupCanvasMemory = cleanupCanvasMemory, imageCompression.isAutoOrientationInBrowser = isAutoOrientationInBrowser, imageCompression.approximateBelowMaximumCanvasSizeOfBrowser = approximateBelowMaximumCanvasSizeOfBrowser, imageCompression.copyExifWithoutOrientation = copyExifWithoutOrientation, imageCompression.getBrowserName = getBrowserName, imageCompression.version = "2.0.2", imageCompression
    }));
    return {
        // {
        //   maxSizeMB: number,            // (default: Number.POSITIVE_INFINITY)
        //     maxWidthOrHeight: number,     // compressedFile will scale down by ratio to a point that width or height is smaller than maxWidthOrHeight (default: undefined)
        //   // but, automatically reduce the size to smaller than the maximum Canvas size supported by each browser.
        //   // Please check the Caveat part for details.
        //   onProgress: Function,         // optional, a function takes one progress argument (percentage from 0 to 100)
        //   useWebWorker: boolean,        // optional, use multi-thread web worker, fallback to run in main-thread (default: true)
        //   libURL: string,               // optional, the libURL of this library for importing script in Web Worker (default: https://cdn.jsdelivr.net/npm/browser-image-compression/dist/browser-image-compression.js)
        //   preserveExif: boolean,        // optional, use preserve Exif metadata for JPEG image e.g., Camera model, Focal length, etc (default: false)
        //
        //   signal: AbortSignal,          // optional, to abort / cancel the compression
        //
        //   // following options are for advanced users
        //   maxIteration: number,         // optional, max number of iteration to compress the image (default: 10)
        //   exifOrientation: number,      // optional, see https://stackoverflow.com/a/32490603/10395024
        //   fileType: string,             // optional, fileType override e.g., 'image/jpeg', 'image/png' (default: file.type)
        //   initialQuality: number,       // optional, initial quality value between 0 and 1 (default: 1)
        //   alwaysKeepResolution: boolean // optional, only reduce quality, always keep width and height (default: false)
        // }
        compress: function (file, option) {
            return imageCompression(file, option);
        }
    };
})();


// core/dialog.js
UE.dialog = (function () {
    return {
        loadingPlaceholder: function (me) {
            var loadingId = "loading_" + (+new Date()).toString(36);
            me.focus();
            me.execCommand(
                "inserthtml",
                '<img class="uep-loading" id="' +
                loadingId +
                '" src="' +
                me.options.themePath +
                me.options.theme +
                '/images/spacer.gif">'
            );
            return loadingId;
        },
        removeLoadingPlaceholder: function (me, loadingId) {
            var loader = me.document.getElementById(loadingId);
            if (loader) {
                domUtils.remove(loader, false);
            }
        },
        tipError: function (me, title) {
            me.fireEvent("showmessage", {
                content: title,
                type: "error",
                timeout: 4000
            });
        }
    }
})();


// core/filterword.js
/**
 * UE过滤word的静态方法
 * @file
 */

/**
 * UEditor公用空间,UEditor所有的功能都挂载在该空间下
 * @module UE
 */

/**
 * 根据传入html字符串过滤word
 * @module UE
 * @since 1.2.6.1
 * @method filterWord
 * @param { String } html html字符串
 * @return { String } 已过滤后的结果字符串
 * @example
 * ```javascript
 * UE.filterWord(html);
 * ```
 */
var filterWord = (UE.filterWord = (function () {
    //是否是word过来的内容
    function isWordDocument(str) {
        return /(class="?Mso|style="[^"]*\bmso\-|w:WordDocument|<(v|o):|lang=)/gi.test(
            str
        );
    }

    //去掉小数
    function transUnit(v) {
        v = v.replace(/[\d.]+\w+/g, function (m) {
            return utils.transUnitToPx(m);
        });
        return v;
    }

    function filterPasteWord(str) {
        return (
            str
                .replace(/[\t\r\n]+/g, " ")
                .replace(/<!--[\s\S]*?-->/gi, "")
                //转换图片
                .replace(/<v:shape [^>]*>[\s\S]*?.<\/v:shape>/gi, function (str) {
                    //opera能自己解析出image所这里直接返回空
                    if (browser.opera) {
                        return "";
                    }
                    try {
                        //有可能是bitmap占为图,无用,直接过滤掉,主要体现在粘贴excel表格中
                        if (/Bitmap/i.test(str)) {
                            return "";
                        }
                        var width = str.match(/width:([ \d.]*p[tx])/i)[1],
                            height = str.match(/height:([ \d.]*p[tx])/i)[1],
                            src = str.match(/src=\s*"([^"]*)"/i)[1];
                        return (
                            '<img width="' +
                            transUnit(width) +
                            '" height="' +
                            transUnit(height) +
                            '" src="' +
                            src +
                            '" />'
                        );
                    } catch (e) {
                        return "";
                    }
                })
                //针对wps添加的多余标签处理
                .replace(/<\/?div[^>]*>/g, "")
                //去掉多余的属性
                .replace(/v:\w+=(["']?)[^'"]+\1/g, "")
                .replace(
                    /<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|xml|meta|link|style|\w+:\w+)(?=[\s\/>]))[^>]*>/gi,
                    ""
                )
                .replace(
                    /<p [^>]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi,
                    "<p><strong>$1</strong></p>"
                )
                //去掉多余的属性
                .replace(/\s+(class|lang|align)\s*=\s*(['"]?)([\w-]+)\2/gi, function (
                    str,
                    name,
                    marks,
                    val
                ) {
                    //保留list的标示
                    return name == "class" && val == "MsoListParagraph" ? str : "";
                })
                //清除多余的font/span不能匹配&nbsp;有可能是空格
                .replace(/<(font|span)[^>]*>(\s*)<\/\1>/gi, function (a, b, c) {
                    return c.replace(/[\t\r\n ]+/g, " ");
                })
                //处理style的问题
                .replace(/(<[a-z][^>]*)\sstyle=(["'])([^\2]*?)\2/gi, function (
                    str,
                    tag,
                    tmp,
                    style
                ) {
                    var n = [],
                        s = style
                            .replace(/^\s+|\s+$/, "")
                            .replace(/&#39;/g, "'")
                            .replace(/&quot;/gi, "'")
                            .replace(/[\d.]+(cm|pt)/g, function (str) {
                                return utils.transUnitToPx(str);
                            })
                            .split(/;\s*/g);

                    for (var i = 0, v; (v = s[i]); i++) {
                        var name,
                            value,
                            parts = v.split(":");

                        if (parts.length == 2) {
                            name = parts[0].toLowerCase();
                            value = parts[1].toLowerCase();
                            if (
                                (/^(background)\w*/.test(name) &&
                                    value.replace(/(initial|\s)/g, "").length == 0) ||
                                (/^(margin)\w*/.test(name) && /^0\w+$/.test(value))
                            ) {
                                continue;
                            }

                            switch (name) {
                                case "mso-padding-alt":
                                case "mso-padding-top-alt":
                                case "mso-padding-right-alt":
                                case "mso-padding-bottom-alt":
                                case "mso-padding-left-alt":
                                case "mso-margin-alt":
                                case "mso-margin-top-alt":
                                case "mso-margin-right-alt":
                                case "mso-margin-bottom-alt":
                                case "mso-margin-left-alt":
                                //ie下会出现挤到一起的情况
                                //case "mso-table-layout-alt":
                                case "mso-height":
                                case "mso-width":
                                case "mso-vertical-align-alt":
                                    //trace:1819 ff下会解析出padding在table上
                                    if (!/<table/.test(tag))
                                        n[i] =
                                            name.replace(/^mso-|-alt$/g, "") + ":" + transUnit(value);
                                    continue;
                                case "horiz-align":
                                    n[i] = "text-align:" + value;
                                    continue;

                                case "vert-align":
                                    n[i] = "vertical-align:" + value;
                                    continue;

                                case "font-color":
                                case "mso-foreground":
                                    n[i] = "color:" + value;
                                    continue;

                                case "mso-background":
                                case "mso-highlight":
                                    n[i] = "background:" + value;
                                    continue;

                                case "mso-default-height":
                                    n[i] = "min-height:" + transUnit(value);
                                    continue;

                                case "mso-default-width":
                                    n[i] = "min-width:" + transUnit(value);
                                    continue;

                                case "mso-padding-between-alt":
                                    n[i] =
                                        "border-collapse:separate;border-spacing:" +
                                        transUnit(value);
                                    continue;

                                case "text-line-through":
                                    if (value == "single" || value == "double") {
                                        n[i] = "text-decoration:line-through";
                                    }
                                    continue;
                                case "mso-zero-height":
                                    if (value == "yes") {
                                        n[i] = "display:none";
                                    }
                                    continue;
                                //                                case 'background':
                                //                                    break;
                                case "margin":
                                    if (!/[1-9]/.test(value)) {
                                        continue;
                                    }
                            }

                            if (
                                /^(mso|column|font-emph|lang|layout|line-break|list-image|nav|panose|punct|row|ruby|sep|size|src|tab-|table-border|text-(?:decor|trans)|top-bar|version|vnd|word-break)/.test(
                                    name
                                ) ||
                                (/text\-indent|padding|margin/.test(name) &&
                                    /\-[\d.]+/.test(value))
                            ) {
                                continue;
                            }

                            n[i] = name + ":" + parts[1];
                        }
                    }
                    return (
                        tag +
                        (n.length
                            ? ' style="' + n.join(";").replace(/;{2,}/g, ";") + '"'
                            : "")
                    );
                })
        );
    }

    return function (html) {
        return isWordDocument(html) ? filterPasteWord(html) : html;
    };
})());


// core/node.js
/**
 * 编辑器模拟的节点类
 * @file
 * @module UE
 * @class uNode
 * @since 1.2.6.1
 */

/**
 * UEditor公用空间,UEditor所有的功能都挂载在该空间下
 * @unfile
 * @module UE
 */

(function () {
    /**
     * 编辑器模拟的节点类
     * @unfile
     * @module UE
     * @class uNode
     */

    /**
     * 通过一个键值对,创建一个uNode对象
     * @constructor
     * @param { Object } attr 传入要创建的uNode的初始属性
     * @example
     * ```javascript
     * var node = new uNode({
     *     type:'element',
     *     tagName:'span',
     *     attrs:{style:'font-size:14px;'}
     * })
     * ```
     */
    var uNode = (UE.uNode = function (obj) {
        this.type = obj.type;
        this.data = obj.data;
        this.tagName = obj.tagName;
        this.parentNode = obj.parentNode;
        this.attrs = obj.attrs || {};
        this.children = obj.children;
    });

    var notTransAttrs = {
        href: 1,
        src: 1,
        _src: 1,
        _href: 1,
        cdata_data: 1
    };

    var notTransTagName = {
        style: 1,
        script: 1
    };

    var indentChar = "    ",
        breakChar = "\n";

    function insertLine(arr, current, begin) {
        arr.push(breakChar);
        return current + (begin ? 1 : -1);
    }

    function insertIndent(arr, current) {
        //插入缩进
        for (var i = 0; i < current; i++) {
            arr.push(indentChar);
        }
    }

    //创建uNode的静态方法
    //支持标签和html
    uNode.createElement = function (html) {
        if (/[<>]/.test(html)) {
            return UE.htmlparser(html).children[0];
        } else {
            return new uNode({
                type: "element",
                children: [],
                tagName: html
            });
        }
    };
    uNode.createText = function (data, noTrans) {
        return new UE.uNode({
            type: "text",
            data: noTrans ? data : utils.unhtml(data || "")
        });
    };

    function nodeToHtml(node, arr, formatter, current) {
        switch (node.type) {
            case "root":
                for (var i = 0, ci; (ci = node.children[i++]);) {
                    //插入新行
                    if (
                        formatter &&
                        ci.type == "element" &&
                        !dtd.$inlineWithA[ci.tagName] &&
                        i > 1
                    ) {
                        insertLine(arr, current, true);
                        insertIndent(arr, current);
                    }
                    nodeToHtml(ci, arr, formatter, current);
                }
                break;
            case "text":
                isText(node, arr);
                break;
            case "element":
                isElement(node, arr, formatter, current);
                break;
            case "comment":
                isComment(node, arr, formatter);
        }
        return arr;
    }

    function isText(node, arr) {
        if (node.parentNode.tagName == "pre") {
            //源码模式下输入html标签,不能做转换处理,直接输出
            arr.push(node.data);
        } else {
            arr.push(
                notTransTagName[node.parentNode.tagName]
                    ? utils.html(node.data)
                    : node.data.replace(/[ ]{2}/g, " &nbsp;")
            );
        }
    }

    function isElement(node, arr, formatter, current) {
        var attrhtml = "";
        if (node.attrs) {
            attrhtml = [];
            var attrs = node.attrs;
            for (var a in attrs) {
                //这里就针对
                //<p>'<img src='http://nsclick.baidu.com/u.gif?&asdf=\"sdf&asdfasdfs;asdf'></p>
                //这里边的\"做转换,要不用innerHTML直接被截断了,属性src
                //有可能做的不够
                attrhtml.push(
                    a +
                    (attrs[a] !== undefined
                        ? '="' +
                        (notTransAttrs[a]
                            ? utils.html(attrs[a]).replace(/["]/g, function (a) {
                                return "&quot;";
                            })
                            : utils.unhtml(attrs[a])) +
                        '"'
                        : "")
                );
            }
            attrhtml = attrhtml.join(" ");
        }
        arr.push(
            "<" +
            node.tagName +
            (attrhtml ? " " + attrhtml : "") +
            (dtd.$empty[node.tagName] ? "/" : "") +
            ">"
        );
        //插入新行
        if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != "pre") {
            if (node.children && node.children.length) {
                current = insertLine(arr, current, true);
                insertIndent(arr, current);
            }
        }
        if (node.children && node.children.length) {
            for (var i = 0, ci; (ci = node.children[i++]);) {
                if (
                    formatter &&
                    ci.type == "element" &&
                    !dtd.$inlineWithA[ci.tagName] &&
                    i > 1
                ) {
                    insertLine(arr, current);
                    insertIndent(arr, current);
                }
                nodeToHtml(ci, arr, formatter, current);
            }
        }
        if (!dtd.$empty[node.tagName]) {
            if (
                formatter &&
                !dtd.$inlineWithA[node.tagName] &&
                node.tagName != "pre"
            ) {
                if (node.children && node.children.length) {
                    current = insertLine(arr, current);
                    insertIndent(arr, current);
                }
            }
            arr.push("</" + node.tagName + ">");
        }
    }

    function isComment(node, arr) {
        arr.push("<!--" + node.data + "-->");
    }

    function getNodeById(root, id) {
        var node;
        if (root.type == "element" && root.getAttr("id") == id) {
            return root;
        }
        if (root.children && root.children.length) {
            for (var i = 0, ci; (ci = root.children[i++]);) {
                if ((node = getNodeById(ci, id))) {
                    return node;
                }
            }
        }
    }

    function getNodesByTagName(node, tagName, arr) {
        if (node.type == "element" && node.tagName == tagName) {
            arr.push(node);
        }
        if (node.children && node.children.length) {
            for (var i = 0, ci; (ci = node.children[i++]);) {
                getNodesByTagName(ci, tagName, arr);
            }
        }
    }

    function nodeTraversal(root, fn) {
        if (root.children && root.children.length) {
            for (var i = 0, ci; (ci = root.children[i]);) {
                nodeTraversal(ci, fn);
                //ci被替换的情况,这里就不再走 fn了
                if (ci.parentNode) {
                    if (ci.children && ci.children.length) {
                        fn(ci);
                    }
                    if (ci.parentNode) i++;
                }
            }
        } else {
            fn(root);
        }
    }

    uNode.prototype = {
        /**
         * 当前节点对象,转换成html文本
         * @method toHtml
         * @return { String } 返回转换后的html字符串
         * @example
         * ```javascript
         * node.toHtml();
         * ```
         */

        /**
         * 当前节点对象,转换成html文本
         * @method toHtml
         * @param { Boolean } formatter 是否格式化返回值
         * @return { String } 返回转换后的html字符串
         * @example
         * ```javascript
         * node.toHtml( true );
         * ```
         */
        toHtml: function (formatter) {
            var arr = [];
            nodeToHtml(this, arr, formatter, 0);
            return arr.join("");
        },

        /**
         * 获取节点的html内容
         * @method innerHTML
         * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
         * @return { String } 返回节点的html内容
         * @example
         * ```javascript
         * var htmlstr = node.innerHTML();
         * ```
         */

        /**
         * 设置节点的html内容
         * @method innerHTML
         * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
         * @param { String } htmlstr 传入要设置的html内容
         * @return { UE.uNode } 返回节点本身
         * @example
         * ```javascript
         * node.innerHTML('<span>text</span>');
         * ```
         */
        innerHTML: function (htmlstr) {
            if (this.type != "element" || dtd.$empty[this.tagName]) {
                return this;
            }
            if (utils.isString(htmlstr)) {
                if (this.children) {
                    for (var i = 0, ci; (ci = this.children[i++]);) {
                        ci.parentNode = null;
                    }
                }
                this.children = [];
                var tmpRoot = UE.htmlparser(htmlstr);
                for (var i = 0, ci; (ci = tmpRoot.children[i++]);) {
                    this.children.push(ci);
                    ci.parentNode = this;
                }
                return this;
            } else {
                var tmpRoot = new UE.uNode({
                    type: "root",
                    children: this.children
                });
                return tmpRoot.toHtml();
            }
        },

        /**
         * 获取节点的纯文本内容
         * @method innerText
         * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
         * @return { String } 返回节点的存文本内容
         * @example
         * ```javascript
         * var textStr = node.innerText();
         * ```
         */

        /**
         * 设置节点的纯文本内容
         * @method innerText
         * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
         * @param { String } textStr 传入要设置的文本内容
         * @return { UE.uNode } 返回节点本身
         * @example
         * ```javascript
         * node.innerText('<span>text</span>');
         * ```
         */
        innerText: function (textStr, noTrans) {
            if (this.type != "element" || dtd.$empty[this.tagName]) {
                return this;
            }
            if (textStr) {
                if (this.children) {
                    for (var i = 0, ci; (ci = this.children[i++]);) {
                        ci.parentNode = null;
                    }
                }
                this.children = [];
                this.appendChild(uNode.createText(textStr, noTrans));
                return this;
            } else {
                return this.toHtml().replace(/<[^>]+>/g, "");
            }
        },

        /**
         * 获取当前对象的data属性
         * @method getData
         * @return { Object } 若节点的type值是elemenet,返回空字符串,否则返回节点的data属性
         * @example
         * ```javascript
         * node.getData();
         * ```
         */
        getData: function () {
            if (this.type == "element") return "";
            return this.data;
        },

        /**
         * 获取当前节点下的第一个子节点
         * @method firstChild
         * @return { UE.uNode } 返回第一个子节点
         * @example
         * ```javascript
         * node.firstChild(); //返回第一个子节点
         * ```
         */
        firstChild: function () {
            //            if (this.type != 'element' || dtd.$empty[this.tagName]) {
            //                return this;
            //            }
            return this.children ? this.children[0] : null;
        },

        /**
         * 获取当前节点下的最后一个子节点
         * @method lastChild
         * @return { UE.uNode } 返回最后一个子节点
         * @example
         * ```javascript
         * node.lastChild(); //返回最后一个子节点
         * ```
         */
        lastChild: function () {
            //            if (this.type != 'element' || dtd.$empty[this.tagName] ) {
            //                return this;
            //            }
            return this.children ? this.children[this.children.length - 1] : null;
        },

        /**
         * 获取和当前节点有相同父亲节点的前一个节点
         * @method previousSibling
         * @return { UE.uNode } 返回前一个节点
         * @example
         * ```javascript
         * node.children[2].previousSibling(); //返回子节点node.children[1]
         * ```
         */
        previousSibling: function () {
            var parent = this.parentNode;
            for (var i = 0, ci; (ci = parent.children[i]); i++) {
                if (ci === this) {
                    return i == 0 ? null : parent.children[i - 1];
                }
            }
        },

        /**
         * 获取和当前节点有相同父亲节点的后一个节点
         * @method nextSibling
         * @return { UE.uNode } 返回后一个节点,找不到返回null
         * @example
         * ```javascript
         * node.children[2].nextSibling(); //如果有,返回子节点node.children[3]
         * ```
         */
        nextSibling: function () {
            var parent = this.parentNode;
            for (var i = 0, ci; (ci = parent.children[i++]);) {
                if (ci === this) {
                    return parent.children[i];
                }
            }
        },

        /**
         * 用新的节点替换当前节点
         * @method replaceChild
         * @param { UE.uNode } target 要替换成该节点参数
         * @param { UE.uNode } source 要被替换掉的节点
         * @return { UE.uNode } 返回替换之后的节点对象
         * @example
         * ```javascript
         * node.replaceChild(newNode, childNode); //用newNode替换childNode,childNode是node的子节点
         * ```
         */
        replaceChild: function (target, source) {
            if (this.children) {
                if (target.parentNode) {
                    target.parentNode.removeChild(target);
                }
                for (var i = 0, ci; (ci = this.children[i]); i++) {
                    if (ci === source) {
                        this.children.splice(i, 1, target);
                        source.parentNode = null;
                        target.parentNode = this;
                        return target;
                    }
                }
            }
        },

        /**
         * 在节点的子节点列表最后位置插入一个节点
         * @method appendChild
         * @param { UE.uNode } node 要插入的节点
         * @return { UE.uNode } 返回刚插入的子节点
         * @example
         * ```javascript
         * node.appendChild( newNode ); //在node内插入子节点newNode
         * ```
         */
        appendChild: function (node) {
            if (
                this.type == "root" ||
                (this.type == "element" && !dtd.$empty[this.tagName])
            ) {
                if (!this.children) {
                    this.children = [];
                }
                if (node.parentNode) {
                    node.parentNode.removeChild(node);
                }
                for (var i = 0, ci; (ci = this.children[i]); i++) {
                    if (ci === node) {
                        this.children.splice(i, 1);
                        break;
                    }
                }
                this.children.push(node);
                node.parentNode = this;
                return node;
            }
        },

        /**
         * 在传入节点的前面插入一个节点
         * @method insertBefore
         * @param { UE.uNode } target 要插入的节点
         * @param { UE.uNode } source 在该参数节点前面插入
         * @return { UE.uNode } 返回刚插入的子节点
         * @example
         * ```javascript
         * node.parentNode.insertBefore(newNode, node); //在node节点后面插入newNode
         * ```
         */
        insertBefore: function (target, source) {
            if (this.children) {
                if (target.parentNode) {
                    target.parentNode.removeChild(target);
                }
                for (var i = 0, ci; (ci = this.children[i]); i++) {
                    if (ci === source) {
                        this.children.splice(i, 0, target);
                        target.parentNode = this;
                        return target;
                    }
                }
            }
        },

        /**
         * 在传入节点的后面插入一个节点
         * @method insertAfter
         * @param { UE.uNode } target 要插入的节点
         * @param { UE.uNode } source 在该参数节点后面插入
         * @return { UE.uNode } 返回刚插入的子节点
         * @example
         * ```javascript
         * node.parentNode.insertAfter(newNode, node); //在node节点后面插入newNode
         * ```
         */
        insertAfter: function (target, source) {
            if (this.children) {
                if (target.parentNode) {
                    target.parentNode.removeChild(target);
                }
                for (var i = 0, ci; (ci = this.children[i]); i++) {
                    if (ci === source) {
                        this.children.splice(i + 1, 0, target);
                        target.parentNode = this;
                        return target;
                    }
                }
            }
        },

        /**
         * 从当前节点的子节点列表中,移除节点
         * @method removeChild
         * @param { UE.uNode } node 要移除的节点引用
         * @param { Boolean } keepChildren 是否保留移除节点的子节点,若传入true,自动把移除节点的子节点插入到移除的位置
         * @return { * } 返回刚移除的子节点
         * @example
         * ```javascript
         * node.removeChild(childNode,true); //在node的子节点列表中移除child节点,并且吧child的子节点插入到移除的位置
         * ```
         */
        removeChild: function (node, keepChildren) {
            if (this.children) {
                for (var i = 0, ci; (ci = this.children[i]); i++) {
                    if (ci === node) {
                        this.children.splice(i, 1);
                        ci.parentNode = null;
                        if (keepChildren && ci.children && ci.children.length) {
                            for (var j = 0, cj; (cj = ci.children[j]); j++) {
                                this.children.splice(i + j, 0, cj);
                                cj.parentNode = this;
                            }
                        }
                        return ci;
                    }
                }
            }
        },

        /**
         * 获取当前节点所代表的元素属性,即获取attrs对象下的属性值
         * @method getAttr
         * @param { String } attrName 要获取的属性名称
         * @return { * } 返回attrs对象下的属性值
         * @example
         * ```javascript
         * node.getAttr('title');
         * ```
         */
        getAttr: function (attrName) {
            return this.attrs && this.attrs[attrName.toLowerCase()];
        },

        /**
         * 设置当前节点所代表的元素属性,即设置attrs对象下的属性值
         * @method setAttr
         * @param { String } attrName 要设置的属性名称
         * @param { * } attrVal 要设置的属性值,类型视设置的属性而定
         * @return { * } 返回attrs对象下的属性值
         * @example
         * ```javascript
         * node.setAttr('title','标题');
         * ```
         */
        setAttr: function (attrName, attrVal) {
            if (!attrName) {
                delete this.attrs;
                return;
            }
            if (!this.attrs) {
                this.attrs = {};
            }
            if (utils.isObject(attrName)) {
                for (var a in attrName) {
                    if (!attrName[a]) {
                        delete this.attrs[a];
                    } else {
                        this.attrs[a.toLowerCase()] = attrName[a];
                    }
                }
            } else {
                if (!attrVal) {
                    delete this.attrs[attrName];
                } else {
                    this.attrs[attrName.toLowerCase()] = attrVal;
                }
            }
        },

        /**
         * 获取当前节点在父节点下的位置索引
         * @method getIndex
         * @return { Number } 返回索引数值,如果没有父节点,返回-1
         * @example
         * ```javascript
         * node.getIndex();
         * ```
         */
        getIndex: function () {
            var parent = this.parentNode;
            for (var i = 0, ci; (ci = parent.children[i]); i++) {
                if (ci === this) {
                    return i;
                }
            }
            return -1;
        },

        /**
         * 在当前节点下,根据id查找节点
         * @method getNodeById
         * @param { String } id 要查找的id
         * @return { UE.uNode } 返回找到的节点
         * @example
         * ```javascript
         * node.getNodeById('textId');
         * ```
         */
        getNodeById: function (id) {
            var node;
            if (this.children && this.children.length) {
                for (var i = 0, ci; (ci = this.children[i++]);) {
                    if ((node = getNodeById(ci, id))) {
                        return node;
                    }
                }
            }
        },

        /**
         * 在当前节点下,根据元素名称查找节点列表
         * @method getNodesByTagName
         * @param { String } tagNames 要查找的元素名称
         * @return { Array } 返回找到的节点列表
         * @example
         * ```javascript
         * node.getNodesByTagName('span');
         * ```
         */
        getNodesByTagName: function (tagNames) {
            tagNames = utils.trim(tagNames).replace(/[ ]{2,}/g, " ").split(" ");
            var arr = [],
                me = this;
            utils.each(tagNames, function (tagName) {
                if (me.children && me.children.length) {
                    for (var i = 0, ci; (ci = me.children[i++]);) {
                        getNodesByTagName(ci, tagName, arr);
                    }
                }
            });
            return arr;
        },

        /**
         * 根据样式名称,获取节点的样式值
         * @method getStyle
         * @param { String } name 要获取的样式名称
         * @return { String } 返回样式值
         * @example
         * ```javascript
         * node.getStyle('font-size');
         * ```
         */
        getStyle: function (name) {
            var cssStyle = this.getAttr("style");
            if (!cssStyle) {
                return "";
            }
            var reg = new RegExp("(^|;)\\s*" + name + ":([^;]+)", "i");
            var match = cssStyle.match(reg);
            if (match && match[0]) {
                return match[2];
            }
            return "";
        },

        /**
         * 给节点设置样式
         * @method setStyle
         * @param { String } name 要设置的的样式名称
         * @param { String } val 要设置的的样值
         * @example
         * ```javascript
         * node.setStyle('font-size', '12px');
         * ```
         */
        setStyle: function (name, val) {
            function exec(name, val) {
                var reg = new RegExp("(^|;)\\s*" + name + ":([^;]+;?)", "gi");
                cssStyle = cssStyle.replace(reg, "$1");
                if (val) {
                    cssStyle = name + ":" + utils.unhtml(val) + ";" + cssStyle;
                }
            }

            var cssStyle = this.getAttr("style");
            if (!cssStyle) {
                cssStyle = "";
            }
            if (utils.isObject(name)) {
                for (var a in name) {
                    exec(a, name[a]);
                }
            } else {
                exec(name, val);
            }
            this.setAttr("style", utils.trim(cssStyle));
        },

        /**
         * 传入一个函数,递归遍历当前节点下的所有节点
         * @method traversal
         * @param { Function } fn 遍历到节点的时,传入节点作为参数,运行此函数
         * @example
         * ```javascript
         * traversal(node, function(){
         *     console.log(node.type);
         * });
         * ```
         */
        traversal: function (fn) {
            if (this.children && this.children.length) {
                nodeTraversal(this, fn);
            }
            return this;
        }
    };
})();


// core/htmlparser.js
/**
 * html字符串转换成uNode节点
 * @file
 * @module UE
 * @since 1.2.6.1
 */

/**
 * UEditor公用空间,UEditor所有的功能都挂载在该空间下
 * @unfile
 * @module UE
 */

/**
 * html字符串转换成uNode节点的静态方法
 * @method htmlparser
 * @param { String } htmlstr 要转换的html代码
 * @param { Boolean } ignoreBlank 若设置为true,转换的时候忽略\n\r\t等空白字符
 * @return { uNode } 给定的html片段转换形成的uNode对象
 * @example
 * ```javascript
 * var root = UE.htmlparser('<p><b>htmlparser</b></p>', true);
 * ```
 */

var htmlparser = (UE.htmlparser = function (htmlstr, ignoreBlank) {
    //todo 原来的方式  [^"'<>\/] 有\/就不能配对上 <TD vAlign=top background=../AAA.JPG> 这样的标签了
    //先去掉了,加上的原因忘了,这里先记录
    //var re_tag = /<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\s\/<>]+)\s*((?:(?:"[^"]*")|(?:'[^']*')|[^"'<>])*)\/?>))/g,
    //以上的正则表达式无法匹配:<div style="text-align:center;font-family:" font-size:14px;"=""><img src="http://hs-album.oss.aliyuncs.com/static/27/78/35/image/20161206/20161206174331_41105.gif" alt="" /><br /></div>
    //修改为如下正则表达式:
    var re_tag = /<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\/\s>]+)((?:\s+[\w\-:.]+(?:\s*=\s*?(?:(?:"[^"]*")|(?:'[^']*')|[^\s"'\/>]+))?)*)[\S\s]*?(\/?)>))/g,
        re_attr = /([\w\-:.]+)(?:(?:\s*=\s*(?:(?:"([^"]*)")|(?:'([^']*)')|([^\s>]+)))|(?=\s|$))/g;

    //ie下取得的html可能会有\n存在,要去掉,在处理replace(/[\t\r\n]*/g,'');代码高量的\n不能去除
    var allowEmptyTags = {
        b: 1,
        code: 1,
        i: 1,
        u: 1,
        strike: 1,
        s: 1,
        tt: 1,
        strong: 1,
        q: 1,
        samp: 1,
        em: 1,
        span: 1,
        sub: 1,
        img: 1,
        sup: 1,
        font: 1,
        big: 1,
        small: 1,
        iframe: 1,
        a: 1,
        br: 1,
        pre: 1
    };
    htmlstr = htmlstr.replace(new RegExp(domUtils.fillChar, "g"), "");
    if (!ignoreBlank) {
        htmlstr = htmlstr.replace(
            new RegExp(
                "[\\r\\t\\n" +
                (ignoreBlank ? "" : " ") +
                "]*</?(\\w+)\\s*(?:[^>]*)>[\\r\\t\\n" +
                (ignoreBlank ? "" : " ") +
                "]*",
                "g"
            ),
            function (a, b) {
                //br暂时单独处理
                if (b && allowEmptyTags[b.toLowerCase()]) {
                    return a.replace(/(^[\n\r]+)|([\n\r]+$)/g, "");
                }
                return a
                    .replace(new RegExp("^[\\r\\n" + (ignoreBlank ? "" : " ") + "]+"), "")
                    .replace(
                        new RegExp("[\\r\\n" + (ignoreBlank ? "" : " ") + "]+$"),
                        ""
                    );
            }
        );
    }

    var notTransAttrs = {
        href: 1,
        src: 1
    };

    var uNode = UE.uNode,
        needParentNode = {
            td: "tr",
            tr: ["tbody", "thead", "tfoot"],
            tbody: "table",
            th: "tr",
            thead: "table",
            tfoot: "table",
            caption: "table",
            li: ["ul", "ol"],
            dt: "dl",
            dd: "dl",
            option: "select"
        },
        needChild = {
            ol: "li",
            ul: "li"
        };

    function text(parent, data) {
        if (needChild[parent.tagName]) {
            var tmpNode = uNode.createElement(needChild[parent.tagName]);
            parent.appendChild(tmpNode);
            tmpNode.appendChild(uNode.createText(data));
            parent = tmpNode;
        } else {
            parent.appendChild(uNode.createText(data));
        }
    }

    function element(parent, tagName, htmlattr) {
        var needParentTag;
        if ((needParentTag = needParentNode[tagName])) {
            var tmpParent = parent,
                hasParent;
            while (tmpParent.type != "root") {
                if (
                    utils.isArray(needParentTag)
                        ? utils.indexOf(needParentTag, tmpParent.tagName) != -1
                        : needParentTag == tmpParent.tagName
                ) {
                    parent = tmpParent;
                    hasParent = true;
                    break;
                }
                tmpParent = tmpParent.parentNode;
            }
            if (!hasParent) {
                parent = element(
                    parent,
                    utils.isArray(needParentTag) ? needParentTag[0] : needParentTag
                );
            }
        }
        //按dtd处理嵌套
        //        if(parent.type != 'root' && !dtd[parent.tagName][tagName])
        //            parent = parent.parentNode;
        var elm = new uNode({
            parentNode: parent,
            type: "element",
            tagName: tagName.toLowerCase(),
            //是自闭合的处理一下
            children: dtd.$empty[tagName] ? null : []
        });
        //如果属性存在,处理属性
        if (htmlattr) {
            var attrs = {},
                match;
            while ((match = re_attr.exec(htmlattr))) {
                attrs[match[1].toLowerCase()] = notTransAttrs[match[1].toLowerCase()]
                    ? match[2] || match[3] || match[4]
                    : utils.unhtml(match[2] || match[3] || match[4]);
            }
            elm.attrs = attrs;
        }
        //trace:3970
        //        //如果parent下不能放elm
        //        if(dtd.$inline[parent.tagName] && dtd.$block[elm.tagName] && !dtd[parent.tagName][elm.tagName]){
        //            parent = parent.parentNode;
        //            elm.parentNode = parent;
        //        }
        parent.children.push(elm);
        //如果是自闭合节点返回父亲节点
        return dtd.$empty[tagName] ? parent : elm;
    }

    function comment(parent, data) {
        parent.children.push(
            new uNode({
                type: "comment",
                data: data,
                parentNode: parent
            })
        );
    }

    var match,
        currentIndex = 0,
        nextIndex = 0;
    //设置根节点
    var root = new uNode({
        type: "root",
        children: []
    });
    var currentParent = root;

    while ((match = re_tag.exec(htmlstr))) {
        currentIndex = match.index;
        try {
            if (currentIndex > nextIndex) {
                //text node
                text(currentParent, htmlstr.slice(nextIndex, currentIndex));
            }
            if (match[3]) {
                if (dtd.$cdata[currentParent.tagName]) {
                    text(currentParent, match[0]);
                } else {
                    //start tag
                    currentParent = element(
                        currentParent,
                        match[3].toLowerCase(),
                        match[4]
                    );
                }
            } else if (match[1]) {
                if (currentParent.type != "root") {
                    if (dtd.$cdata[currentParent.tagName] && !dtd.$cdata[match[1]]) {
                        text(currentParent, match[0]);
                    } else {
                        var tmpParent = currentParent;
                        while (
                            currentParent.type == "element" &&
                            currentParent.tagName != match[1].toLowerCase()
                            ) {
                            currentParent = currentParent.parentNode;
                            if (currentParent.type == "root") {
                                currentParent = tmpParent;
                                throw "break";
                            }
                        }
                        //end tag
                        currentParent = currentParent.parentNode;
                    }
                }
            } else if (match[2]) {
                //comment
                comment(currentParent, match[2]);
            }
        } catch (e) {
        }

        nextIndex = re_tag.lastIndex;
    }
    //如果结束是文本,就有可能丢掉,所以这里手动判断一下
    //例如 <li>sdfsdfsdf<li>sdfsdfsdfsdf
    if (nextIndex < htmlstr.length) {
        text(currentParent, htmlstr.slice(nextIndex));
    }
    return root;
});


// core/filternode.js
/**
 * UE过滤节点的静态方法
 * @file
 */

/**
 * UEditor公用空间,UEditor所有的功能都挂载在该空间下
 * @module UE
 */

/**
 * 根据传入节点和过滤规则过滤相应节点
 * @module UE
 * @since 1.2.6.1
 * @method filterNode
 * @param { Object } root 指定root节点
 * @param { Object } rules 过滤规则json对象
 * @example
 * ```javascript
 * UE.filterNode(root,editor.options.filterRules);
 * ```
 */
var filterNode = (UE.filterNode = (function () {
    function filterNode(node, rules) {
        switch (node.type) {
            case "text":
                break;
            case "element":
                var val;
                if ((val = rules[node.tagName])) {
                    if (val === "-") {
                        node.parentNode.removeChild(node);
                    } else if (utils.isFunction(val)) {
                        var parentNode = node.parentNode,
                            index = node.getIndex();
                        val(node);
                        if (node.parentNode) {
                            if (node.children) {
                                for (var i = 0, ci; (ci = node.children[i]);) {
                                    filterNode(ci, rules);
                                    if (ci.parentNode) {
                                        i++;
                                    }
                                }
                            }
                        } else {
                            for (var i = index, ci; (ci = parentNode.children[i]);) {
                                filterNode(ci, rules);
                                if (ci.parentNode) {
                                    i++;
                                }
                            }
                        }
                    } else {
                        var attrs = val["$"];
                        if (attrs && node.attrs) {
                            var tmpAttrs = {},
                                tmpVal;
                            for (var a in attrs) {
                                tmpVal = node.getAttr(a);
                                //todo 只先对style单独处理
                                if (a == "style" && utils.isArray(attrs[a])) {
                                    var tmpCssStyle = [];
                                    utils.each(attrs[a], function (v) {
                                        var tmp;
                                        if ((tmp = node.getStyle(v))) {
                                            tmpCssStyle.push(v + ":" + tmp);
                                        }
                                    });
                                    tmpVal = tmpCssStyle.join(";");
                                }
                                if (tmpVal) {
                                    tmpAttrs[a] = tmpVal;
                                }
                            }
                            node.attrs = tmpAttrs;
                        }
                        if (node.children) {
                            for (var i = 0, ci; (ci = node.children[i]);) {
                                filterNode(ci, rules);
                                if (ci.parentNode) {
                                    i++;
                                }
                            }
                        }
                    }
                } else {
                    //如果不在名单里扣出子节点并删除该节点,cdata除外
                    if (dtd.$cdata[node.tagName]) {
                        node.parentNode.removeChild(node);
                    } else {
                        var parentNode = node.parentNode,
                            index = node.getIndex();
                        node.parentNode.removeChild(node, true);
                        for (var i = index, ci; (ci = parentNode.children[i]);) {
                            filterNode(ci, rules);
                            if (ci.parentNode) {
                                i++;
                            }
                        }
                    }
                }
                break;
            case "comment":
                node.parentNode.removeChild(node);
        }
    }

    return function (root, rules) {
        if (utils.isEmptyObject(rules)) {
            return root;
        }
        var val;
        if ((val = rules["-"])) {
            utils.each(val.split(" "), function (k) {
                rules[k] = "-";
            });
        }
        for (var i = 0, ci; (ci = root.children[i]);) {
            filterNode(ci, rules);
            if (ci.parentNode) {
                i++;
            }
        }
        return root;
    };
})());


// core/plugin.js
/**
 * Created with JetBrains PhpStorm.
 * User: campaign
 * Date: 10/8/13
 * Time: 6:15 PM
 * To change this template use File | Settings | File Templates.
 */
UE.plugin = (function () {
    var _plugins = {};
    return {
        register: function (pluginName, fn, oldOptionName, afterDisabled) {
            if (oldOptionName && utils.isFunction(oldOptionName)) {
                afterDisabled = oldOptionName;
                oldOptionName = null;
            }
            _plugins[pluginName] = {
                optionName: oldOptionName || pluginName,
                execFn: fn,
                //当插件被禁用时执行
                afterDisabled: afterDisabled
            };
        },
        load: function (editor) {
            utils.each(_plugins, function (plugin) {
                var _export = plugin.execFn.call(editor);
                if (editor.options[plugin.optionName] !== false) {
                    if (_export) {
                        //后边需要再做扩展
                        utils.each(_export, function (v, k) {
                            switch (k.toLowerCase()) {
                                case "shortcutkey":
                                    editor.addshortcutkey(v);
                                    break;
                                case "bindevents":
                                    utils.each(v, function (fn, eventName) {
                                        editor.addListener(eventName, fn);
                                    });
                                    break;
                                case "bindmultievents":
                                    utils.each(utils.isArray(v) ? v : [v], function (event) {
                                        var types = utils.trim(event.type).split(/\s+/);
                                        utils.each(types, function (eventName) {
                                            editor.addListener(eventName, event.handler);
                                        });
                                    });
                                    break;
                                case "commands":
                                    utils.each(v, function (execFn, execName) {
                                        editor.commands[execName] = execFn;
                                    });
                                    break;
                                case "outputrule":
                                    editor.addOutputRule(v);
                                    break;
                                case "inputrule":
                                    editor.addInputRule(v);
                                    break;
                                case "defaultoptions":
                                    editor.setOpt(v);
                            }
                        });
                    }
                } else if (plugin.afterDisabled) {
                    plugin.afterDisabled.call(editor);
                }
            });
            //向下兼容
            utils.each(UE.plugins, function (plugin) {
                plugin.call(editor);
            });
        },
        run: function (pluginName, editor) {
            var plugin = _plugins[pluginName];
            if (plugin) {
                plugin.exeFn.call(editor);
            }
        }
    };
})();


// core/keymap.js
var keymap = (UE.keymap = {
    Backspace: 8,
    Tab: 9,
    Enter: 13,

    Shift: 16,
    Control: 17,
    Alt: 18,
    CapsLock: 20,

    Esc: 27,

    Spacebar: 32,

    PageUp: 33,
    PageDown: 34,
    End: 35,
    Home: 36,

    Left: 37,
    Up: 38,
    Right: 39,
    Down: 40,

    Insert: 45,

    Del: 46,

    NumLock: 144,

    Cmd: 91,

    "=": 187,
    "-": 189,

    b: 66,
    i: 73,
    //回退
    z: 90,
    y: 89,
    //粘贴
    v: 86,
    x: 88,

    s: 83,

    n: 78
});


// core/localstorage.js
var LocalStorage = (UE.LocalStorage = (function () {

    var storage = window.localStorage

    return {
        saveLocalData: function (key, data) {
            // console.log('saveLocalData', key, data);
            if (!storage) {
                return false;
            }
            storage.setItem(key, data);
            return true;
        },
        getLocalData: function (key) {
            // console.log('getLocalData', key);
            if (!storage) {
                return null;
            }
            return storage.getItem(key) || null;
        },
        removeItem: function (key) {
            // console.log('removeItem', key);
            storage && storage.removeItem(key);
        }
    };

})());

(function () {

    var ROOT_KEY = "UEditorPlusPref";

    UE.Editor.prototype.setPreferences = function (key, value) {
        // console.log('setPreferences', key, value);
        var obj = {};
        if (utils.isString(key)) {
            obj[key] = value;
        } else {
            obj = key;
        }
        var data = LocalStorage.getLocalData(ROOT_KEY);
        if (data && (data = utils.str2json(data))) {
            utils.extend(data, obj);
        } else {
            data = obj;
        }
        data && LocalStorage.saveLocalData(ROOT_KEY, utils.json2str(data));
    };

    UE.Editor.prototype.getPreferences = function (key) {
        // console.log('getPreferences', key);
        var data = LocalStorage.getLocalData(ROOT_KEY);
        if (data && (data = utils.str2json(data))) {
            return key ? data[key] : data;
        }
        return null;
    };

    UE.Editor.prototype.removePreferences = function (key) {
        // console.log('removePreferences', key);
        var data = LocalStorage.getLocalData(ROOT_KEY);
        if (data && (data = utils.str2json(data))) {
            data[key] = undefined;
            delete data[key];
        }
        data && LocalStorage.saveLocalData(ROOT_KEY, utils.json2str(data));
    };
})();


// plugins/defaultfilter.js
///import core
///plugin 编辑器默认的过滤转换机制

UE.plugins["defaultfilter"] = function () {
    var me = this;
    me.setOpt({
        allowDivTransToP: true,
        disabledTableInTable: true,
        rgb2Hex: true
    });
    //默认的过滤处理
    //进入编辑器的内容处理
    me.addInputRule(function (root) {
        var allowDivTransToP = this.options.allowDivTransToP;
        var val;

        function tdParent(node) {
            while (node && node.type == "element") {
                if (node.tagName == "td") {
                    return true;
                }
                node = node.parentNode;
            }
            return false;
        }

        //进行默认的处理
        root.traversal(function (node) {
            if (node.type == "element") {
                if (
                    !dtd.$cdata[node.tagName] &&
                    me.options.autoClearEmptyNode &&
                    dtd.$inline[node.tagName] &&
                    !dtd.$empty[node.tagName] &&
                    (!node.attrs || utils.isEmptyObject(node.attrs))
                ) {
                    if (!node.firstChild()) node.parentNode.removeChild(node);
                    else if (
                        node.tagName == "span" &&
                        (!node.attrs || utils.isEmptyObject(node.attrs))
                    ) {
                        node.parentNode.removeChild(node, true);
                    }
                    return;
                }
                switch (node.tagName) {
                    case "style":
                    case "script":
                        node.setAttr({
                            cdata_tag: node.tagName,
                            cdata_data: node.innerHTML() || "",
                            _ue_custom_node_: "true"
                        });
                        node.tagName = "div";
                        node.innerHTML("");
                        break;
                    case "a":
                        if ((val = node.getAttr("href"))) {
                            node.setAttr("_href", val);
                        }
                        break;
                    case "img":
                        //todo base64暂时去掉,后边做远程图片上传后,干掉这个
                        if ((val = node.getAttr("src"))) {
                            if (/^data:/.test(val)) {
                                node.parentNode.removeChild(node);
                                break;
                            }
                        }
                        node.setAttr("_src", node.getAttr("src"));
                        break;
                    case "span":
                        if (browser.webkit && (val = node.getStyle("white-space"))) {
                            if (/nowrap|normal/.test(val)) {
                                node.setStyle("white-space", "");
                                if (
                                    me.options.autoClearEmptyNode &&
                                    utils.isEmptyObject(node.attrs)
                                ) {
                                    node.parentNode.removeChild(node, true);
                                }
                            }
                        }
                        val = node.getAttr("id");
                        if (val && /^_baidu_bookmark_/i.test(val)) {
                            node.parentNode.removeChild(node);
                        }
                        break;
                    case "p":
                        if ((val = node.getAttr("align"))) {
                            node.setAttr("align");
                            node.setStyle("text-align", val);
                        }
                        //trace:3431
                        //                        var cssStyle = node.getAttr('style');
                        //                        if (cssStyle) {
                        //                            cssStyle = cssStyle.replace(/(margin|padding)[^;]+/g, '');
                        //                            node.setAttr('style', cssStyle)
                        //
                        //                        }
                        //p标签不允许嵌套
                        utils.each(node.children, function (n) {
                            if (n.type == "element" && n.tagName == "p") {
                                var next = n.nextSibling();
                                node.parentNode.insertAfter(n, node);
                                var last = n;
                                while (next) {
                                    var tmp = next.nextSibling();
                                    node.parentNode.insertAfter(next, last);
                                    last = next;
                                    next = tmp;
                                }
                                return false;
                            }
                        });
                        if (!node.firstChild()) {
                            node.innerHTML(browser.ie ? "&nbsp;" : "<br/>");
                        }
                        break;
                    case "div":
                        if (node.getAttr("cdata_tag")) {
                            break;
                        }
                        //针对代码这里不处理插入代码的div
                        val = node.getAttr("class");
                        if (val && /^line number\d+/.test(val)) {
                            break;
                        }
                        if (!allowDivTransToP) {
                            break;
                        }
                        var tmpNode,
                            p = UE.uNode.createElement("p");
                        while ((tmpNode = node.firstChild())) {
                            if (
                                tmpNode.type == "text" ||
                                !UE.dom.dtd.$block[tmpNode.tagName]
                            ) {
                                p.appendChild(tmpNode);
                            } else {
                                if (p.firstChild()) {
                                    node.parentNode.insertBefore(p, node);
                                    p = UE.uNode.createElement("p");
                                } else {
                                    node.parentNode.insertBefore(tmpNode, node);
                                }
                            }
                        }
                        if (p.firstChild()) {
                            node.parentNode.insertBefore(p, node);
                        }
                        node.parentNode.removeChild(node);
                        break;
                    case "dl":
                        node.tagName = "ul";
                        break;
                    case "dt":
                    case "dd":
                        node.tagName = "li";
                        break;
                    case "li":
                        var className = node.getAttr("class");
                        if (!className || !/list\-/.test(className)) {
                            node.setAttr();
                        }
                        var tmpNodes = node.getNodesByTagName("ol ul");
                        UE.utils.each(tmpNodes, function (n) {
                            node.parentNode.insertAfter(n, node);
                        });
                        break;
                    case "td":
                    case "th":
                    case "caption":
                        if (!node.children || !node.children.length) {
                            node.appendChild(
                                browser.ie11below
                                    ? UE.uNode.createText(" ")
                                    : UE.uNode.createElement("br")
                            );
                        }
                        break;
                    case "table":
                        if (me.options.disabledTableInTable && tdParent(node)) {
                            node.parentNode.insertBefore(
                                UE.uNode.createText(node.innerText()),
                                node
                            );
                            node.parentNode.removeChild(node);
                        }
                }
            }
            //            if(node.type == 'comment'){
            //                node.parentNode.removeChild(node);
            //            }
        });
    });

    //从编辑器出去的内容处理
    me.addOutputRule(function (root) {
        var val;
        root.traversal(function (node) {
            if (node.type == "element") {
                if (
                    me.options.autoClearEmptyNode &&
                    dtd.$inline[node.tagName] &&
                    !dtd.$empty[node.tagName] &&
                    (!node.attrs || utils.isEmptyObject(node.attrs))
                ) {
                    if (!node.firstChild()) node.parentNode.removeChild(node);
                    else if (
                        node.tagName == "span" &&
                        (!node.attrs || utils.isEmptyObject(node.attrs))
                    ) {
                        node.parentNode.removeChild(node, true);
                    }
                    return;
                }
                switch (node.tagName) {
                    case "div":
                        if ((val = node.getAttr("cdata_tag"))) {
                            node.tagName = val;
                            node.appendChild(UE.uNode.createText(node.getAttr("cdata_data")));
                            node.setAttr({
                                cdata_tag: "",
                                cdata_data: "",
                                _ue_custom_node_: ""
                            });
                        }
                        break;
                    case "a":
                        if ((val = node.getAttr("_href"))) {
                            node.setAttr({
                                href: utils.html(val),
                                _href: ""
                            });
                        }
                        break;
                        break;
                    case "span":
                        val = node.getAttr("id");
                        if (val && /^_baidu_bookmark_/i.test(val)) {
                            node.parentNode.removeChild(node);
                        }
                        //将color的rgb格式转换为#16进制格式
                        if (me.getOpt("rgb2Hex")) {
                            var cssStyle = node.getAttr("style");
                            if (cssStyle) {
                                node.setAttr(
                                    "style",
                                    cssStyle.replace(/rgba?\(([\d,\s]+)\)/g, function (a, 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;
                                        }
                                        return value.toUpperCase();
                                    })
                                );
                            }
                        }
                        break;
                    case "img":
                        if ((val = node.getAttr("_src"))) {
                            node.setAttr({
                                src: node.getAttr("_src"),
                                _src: ""
                            });
                        }
                }
            }
        });
    });
};


// plugins/inserthtml.js
/**
 * 插入html字符串插件
 * @file
 * @since 1.2.6.1
 */

/**
 * 插入html代码
 * @command inserthtml
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @param { String } html 插入的html字符串
 * @remaind 插入的标签内容是在当前的选区位置上插入,如果当前是闭合状态,那直接插入内容, 如果当前是选中状态,将先清除当前选中内容后,再做插入
 * @warning 注意:该命令会对当前选区的位置,对插入的内容进行过滤转换处理。 过滤的规则遵循html语意化的原则。
 * @example
 * ```javascript
 * //xxx[BB]xxx 当前选区为非闭合选区,选中BB这两个文本
 * //执行命令,插入<b>CC</b>
 * //插入后的效果 xxx<b>CC</b>xxx
 * //<p>xx|xxx</p> 当前选区为闭合状态
 * //插入<p>CC</p>
 * //结果 <p>xx</p><p>CC</p><p>xxx</p>
 * //<p>xxxx</p>|</p>xxx</p> 当前选区在两个p标签之间
 * //插入 xxxx
 * //结果 <p>xxxx</p><p>xxxx</p></p>xxx</p>
 * ```
 */

UE.commands["inserthtml"] = {
    execCommand: function (command, html, notNeedFilter) {
        var me = this,
            range,
            div;
        if (!html) {
            return;
        }
        if (me.fireEvent("beforeinserthtml", html) === true) {
            return;
        }
        range = me.selection.getRange();
        div = range.document.createElement("div");
        div.style.display = "inline";

        if (!notNeedFilter) {
            var root = UE.htmlparser(html);
            //如果给了过滤规则就先进行过滤
            if (me.options.filterRules) {
                UE.filterNode(root, me.options.filterRules);
            }
            //执行默认的处理
            me.filterInputRule(root);
            html = root.toHtml();
        }
        div.innerHTML = utils.trim(html);

        if (!range.collapsed) {
            var tmpNode = range.startContainer;
            if (domUtils.isFillChar(tmpNode)) {
                range.setStartBefore(tmpNode);
            }
            tmpNode = range.endContainer;
            if (domUtils.isFillChar(tmpNode)) {
                range.setEndAfter(tmpNode);
            }
            range.txtToElmBoundary();
            //结束边界可能放到了br的前边,要把br包含进来
            // x[xxx]<br/>
            if (range.endContainer && range.endContainer.nodeType == 1) {
                tmpNode = range.endContainer.childNodes[range.endOffset];
                if (tmpNode && domUtils.isBr(tmpNode)) {
                    range.setEndAfter(tmpNode);
                }
            }
            if (range.startOffset == 0) {
                tmpNode = range.startContainer;
                if (domUtils.isBoundaryNode(tmpNode, "firstChild")) {
                    tmpNode = range.endContainer;
                    if (
                        range.endOffset ==
                        (tmpNode.nodeType == 3
                            ? tmpNode.nodeValue.length
                            : tmpNode.childNodes.length) &&
                        domUtils.isBoundaryNode(tmpNode, "lastChild")
                    ) {
                        me.body.innerHTML = "<p>" + (browser.ie ? "" : "<br/>") + "</p>";
                        range.setStart(me.body.firstChild, 0).collapse(true);
                    }
                }
            }
            !range.collapsed && range.deleteContents();
            if (range.startContainer.nodeType == 1) {
                var child = range.startContainer.childNodes[range.startOffset],
                    pre;
                if (
                    child &&
                    domUtils.isBlockElm(child) &&
                    (pre = child.previousSibling) &&
                    domUtils.isBlockElm(pre)
                ) {
                    range.setEnd(pre, pre.childNodes.length).collapse();
                    while (child.firstChild) {
                        pre.appendChild(child.firstChild);
                    }
                    domUtils.remove(child);
                }
            }
        }

        var child,
            parent,
            pre,
            tmp,
            hadBreak = 0,
            nextNode;
        //如果当前位置选中了fillchar要干掉,要不会产生空行
        if (range.inFillChar()) {
            child = range.startContainer;
            if (domUtils.isFillChar(child)) {
                range.setStartBefore(child).collapse(true);
                domUtils.remove(child);
            } else if (domUtils.isFillChar(child, true)) {
                child.nodeValue = child.nodeValue.replace(fillCharReg, "");
                range.startOffset--;
                range.collapsed && range.collapse(true);
            }
        }
        //列表单独处理
        var li = domUtils.findParentByTagName(range.startContainer, "li", true);
        if (li) {
            var next, last;
            while ((child = div.firstChild)) {
                //针对hr单独处理一下先
                while (
                    child &&
                    (child.nodeType == 3 ||
                        !domUtils.isBlockElm(child) ||
                        child.tagName == "HR")
                    ) {
                    next = child.nextSibling;
                    range.insertNode(child).collapse();
                    last = child;
                    child = next;
                }
                if (child) {
                    if (/^(ol|ul)$/i.test(child.tagName)) {
                        while (child.firstChild) {
                            last = child.firstChild;
                            domUtils.insertAfter(li, child.firstChild);
                            li = li.nextSibling;
                        }
                        domUtils.remove(child);
                    } else {
                        var tmpLi;
                        next = child.nextSibling;
                        tmpLi = me.document.createElement("li");
                        domUtils.insertAfter(li, tmpLi);
                        tmpLi.appendChild(child);
                        last = child;
                        child = next;
                        li = tmpLi;
                    }
                }
            }
            li = domUtils.findParentByTagName(range.startContainer, "li", true);
            if (domUtils.isEmptyBlock(li)) {
                domUtils.remove(li);
            }
            if (last) {
                range.setStartAfter(last).collapse(true).select(true);
            }
        } else {
            while ((child = div.firstChild)) {
                if (hadBreak) {
                    var p = me.document.createElement("p");
                    while (child && (child.nodeType == 3 || !dtd.$block[child.tagName])) {
                        nextNode = child.nextSibling;
                        p.appendChild(child);
                        child = nextNode;
                    }
                    if (p.firstChild) {
                        child = p;
                    }
                }
                range.insertNode(child);
                nextNode = child.nextSibling;
                if (
                    !hadBreak &&
                    child.nodeType == domUtils.NODE_ELEMENT &&
                    domUtils.isBlockElm(child)
                ) {
                    parent = domUtils.findParent(child, function (node) {
                        return domUtils.isBlockElm(node);
                    });
                    if (
                        parent &&
                        parent.tagName.toLowerCase() != "body" &&
                        !(
                            dtd[parent.tagName][child.nodeName] && child.parentNode === parent
                        )
                    ) {
                        if (!dtd[parent.tagName][child.nodeName]) {
                            pre = parent;
                        } else {
                            tmp = child.parentNode;
                            while (tmp !== parent) {
                                pre = tmp;
                                tmp = tmp.parentNode;
                            }
                        }

                        domUtils.breakParent(child, pre || tmp);
                        //去掉break后前一个多余的节点  <p>|<[p> ==> <p></p><div></div><p>|</p>
                        var pre = child.previousSibling;
                        domUtils.trimWhiteTextNode(pre);
                        if (!pre.childNodes.length) {
                            domUtils.remove(pre);
                        }
                        //trace:2012,在非ie的情况,切开后剩下的节点有可能不能点入光标添加br占位

                        if (
                            !browser.ie &&
                            (next = child.nextSibling) &&
                            domUtils.isBlockElm(next) &&
                            next.lastChild &&
                            !domUtils.isBr(next.lastChild)
                        ) {
                            next.appendChild(me.document.createElement("br"));
                        }
                        hadBreak = 1;
                    }
                }
                var next = child.nextSibling;
                if (!div.firstChild && next && domUtils.isBlockElm(next)) {
                    range.setStart(next, 0).collapse(true);
                    break;
                }
                range.setEndAfter(child).collapse();
            }

            child = range.startContainer;

            if (nextNode && domUtils.isBr(nextNode)) {
                domUtils.remove(nextNode);
            }
            //用chrome可能有空白展位符
            if (domUtils.isBlockElm(child) && domUtils.isEmptyNode(child)) {
                if ((nextNode = child.nextSibling)) {
                    domUtils.remove(child);
                    if (nextNode.nodeType == 1 && dtd.$block[nextNode.tagName]) {
                        range.setStart(nextNode, 0).collapse(true).shrinkBoundary();
                    }
                } else {
                    try {
                        child.innerHTML = browser.ie ? domUtils.fillChar : "<br/>";
                    } catch (e) {
                        range.setStartBefore(child);
                        domUtils.remove(child);
                    }
                }
            }
            //加上true因为在删除表情等时会删两次,第一次是删的fillData
            try {
                range.select(true);
            } catch (e) {
            }
        }

        setTimeout(function () {
            range = me.selection.getRange();
            range.scrollToView(
                me.autoHeightEnabled,
                me.autoHeightEnabled ? domUtils.getXY(me.iframe).y : 0
            );
            me.fireEvent("afterinserthtml", html);
        }, 200);
    }
};


// plugins/autotypeset.js
/**
 * 自动排版
 * @file
 * @since 1.2.6.1
 */

/**
 * 对当前编辑器的内容执行自动排版, 排版的行为根据config配置文件里的“autotypeset”选项进行控制。
 * @command autotypeset
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @example
 * ```javascript
 * editor.execCommand( 'autotypeset' );
 * ```
 */

UE.plugins["autotypeset"] = function () {
    this.setOpt({
        // 自动排版参数
        autotypeset: {
            // 合并空行
            mergeEmptyline: true,
            // 去掉冗余的class
            removeClass: true,
            // 去掉空行
            removeEmptyline: false,
            // 段落的排版方式,可以是 left,right,center,justify 去掉这个属性表示不执行排版
            textAlign: "left",
            // 图片的浮动方式,独占一行剧中,左右浮动,默认: center,left,right,none 去掉这个属性表示不执行排版
            imageBlockLine: "center",
            // 根据规则过滤没事粘贴进来的内容
            pasteFilter: false,
            // 去掉所有的内嵌字号,使用编辑器默认的字号
            clearFontSize: false,
            // 去掉所有的内嵌字体,使用编辑器默认的字体
            clearFontFamily: false,
            // 去掉空节点
            removeEmptyNode: false,
            // 可以去掉的标签
            removeTagNames: utils.extend({div: 1}, dtd.$removeEmpty),
            // 行首缩进
            indent: false,
            // 行首缩进的大小
            indentValue: "2em",
            // 全角转半角
            bdc2sb: false,
            // 半角转全角
            tobdc: false
        }
    });

    var me = this,
        opt = me.options.autotypeset,
        remainClass = {
            selectTdClass: 1,
            pagebreak: 1,
            anchorclass: 1
        },
        remainTag = {
            li: 1
        },
        tags = {
            div: 1,
            p: 1,
            //trace:2183 这些也认为是行
            blockquote: 1,
            center: 1,
            h1: 1,
            h2: 1,
            h3: 1,
            h4: 1,
            h5: 1,
            h6: 1,
            span: 1
        },
        highlightCont;
    //升级了版本,但配置项目里没有autotypeset
    if (!opt) {
        return;
    }

    readLocalOpts();

    function isLine(node, notEmpty) {
        if (!node || node.nodeType == 3) return 0;
        if (domUtils.isBr(node)) return 1;
        if (node && node.parentNode && tags[node.tagName.toLowerCase()]) {
            if (
                (highlightCont && highlightCont.contains(node)) ||
                node.getAttribute("pagebreak")
            ) {
                return 0;
            }

            return notEmpty
                ? !domUtils.isEmptyBlock(node)
                : domUtils.isEmptyBlock(
                    node,
                    new RegExp("[\\s" + domUtils.fillChar + "]", "g")
                );
        }
    }

    function removeNotAttributeSpan(node) {
        if (!node.style.cssText) {
            domUtils.removeAttributes(node, ["style"]);
            if (
                node.tagName.toLowerCase() == "span" &&
                domUtils.hasNoAttributes(node)
            ) {
                domUtils.remove(node, true);
            }
        }
    }

    function autotype(type, html) {
        var me = this,
            cont;
        if (html) {
            if (!opt.pasteFilter) {
                return;
            }
            cont = me.document.createElement("div");
            cont.innerHTML = html.html;
        } else {
            cont = me.document.body;
        }
        var nodes = domUtils.getElementsByTagName(cont, "*");

        // 行首缩进,段落方向,段间距,段内间距
        for (var i = 0, ci; (ci = nodes[i++]);) {
            if (me.fireEvent("excludeNodeinautotype", ci) === true) {
                continue;
            }
            //font-size
            if (opt.clearFontSize && ci.style.fontSize) {
                domUtils.removeStyle(ci, "font-size");

                removeNotAttributeSpan(ci);
            }
            //font-family
            if (opt.clearFontFamily && ci.style.fontFamily) {
                domUtils.removeStyle(ci, "font-family");
                removeNotAttributeSpan(ci);
            }

            if (isLine(ci)) {
                //合并空行
                if (opt.mergeEmptyline) {
                    var next = ci.nextSibling,
                        tmpNode,
                        isBr = domUtils.isBr(ci);
                    while (isLine(next)) {
                        tmpNode = next;
                        next = tmpNode.nextSibling;
                        if (isBr && (!next || (next && !domUtils.isBr(next)))) {
                            break;
                        }
                        domUtils.remove(tmpNode);
                    }
                }
                //去掉空行,保留占位的空行
                if (
                    opt.removeEmptyline &&
                    domUtils.inDoc(ci, cont) &&
                    !remainTag[ci.parentNode.tagName.toLowerCase()]
                ) {
                    if (domUtils.isBr(ci)) {
                        next = ci.nextSibling;
                        if (next && !domUtils.isBr(next)) {
                            continue;
                        }
                    }
                    domUtils.remove(ci);
                    continue;
                }
            }
            if (isLine(ci, true) && ci.tagName != "SPAN") {
                if (opt.indent) {
                    ci.style.textIndent = opt.indentValue;
                }
                if (opt.textAlign) {
                    ci.style.textAlign = opt.textAlign;
                }
                // if(opt.lineHeight)
                //     ci.style.lineHeight = opt.lineHeight + 'cm';
            }

            //去掉class,保留的class不去掉
            if (
                opt.removeClass &&
                ci.className &&
                !remainClass[ci.className.toLowerCase()]
            ) {
                if (highlightCont && highlightCont.contains(ci)) {
                    continue;
                }
                domUtils.removeAttributes(ci, ["class"]);
            }

            //表情不处理
            if (
                opt.imageBlockLine &&
                ci.tagName.toLowerCase() == "img" &&
                !ci.getAttribute("emotion")
            ) {
                if (html) {
                    var img = ci;
                    switch (opt.imageBlockLine) {
                        case "left":
                        case "right":
                        case "none":
                            var pN = img.parentNode,
                                tmpNode,
                                pre,
                                next;
                            while (dtd.$inline[pN.tagName] || pN.tagName == "A") {
                                pN = pN.parentNode;
                            }
                            tmpNode = pN;
                            if (
                                tmpNode.tagName == "P" &&
                                domUtils.getStyle(tmpNode, "text-align") == "center"
                            ) {
                                if (
                                    !domUtils.isBody(tmpNode) &&
                                    domUtils.getChildCount(tmpNode, function (node) {
                                        return !domUtils.isBr(node) && !domUtils.isWhitespace(node);
                                    }) == 1
                                ) {
                                    pre = tmpNode.previousSibling;
                                    next = tmpNode.nextSibling;
                                    if (
                                        pre &&
                                        next &&
                                        pre.nodeType == 1 &&
                                        next.nodeType == 1 &&
                                        pre.tagName == next.tagName &&
                                        domUtils.isBlockElm(pre)
                                    ) {
                                        pre.appendChild(tmpNode.firstChild);
                                        while (next.firstChild) {
                                            pre.appendChild(next.firstChild);
                                        }
                                        domUtils.remove(tmpNode);
                                        domUtils.remove(next);
                                    } else {
                                        domUtils.setStyle(tmpNode, "text-align", "");
                                    }
                                }
                            }
                            domUtils.setStyle(img, "float", opt.imageBlockLine);
                            break;
                        case "center":
                            if (me.queryCommandValue("imagefloat") != "center") {
                                pN = img.parentNode;
                                domUtils.setStyle(img, "float", "none");
                                tmpNode = img;
                                while (
                                    pN &&
                                    domUtils.getChildCount(pN, function (node) {
                                        return !domUtils.isBr(node) && !domUtils.isWhitespace(node);
                                    }) == 1 &&
                                    (dtd.$inline[pN.tagName] || pN.tagName == "A")
                                    ) {
                                    tmpNode = pN;
                                    pN = pN.parentNode;
                                }
                                var pNode = me.document.createElement("p");
                                domUtils.setAttributes(pNode, {
                                    style: "text-align:center"
                                });
                                tmpNode.parentNode.insertBefore(pNode, tmpNode);
                                pNode.appendChild(tmpNode);
                                domUtils.setStyle(tmpNode, "float", "");
                            }
                    }
                } else {
                    var range = me.selection.getRange();
                    range.selectNode(ci).select();
                    me.execCommand("imagefloat", opt.imageBlockLine);
                }
            }

            //去掉冗余的标签
            if (opt.removeEmptyNode) {
                if (
                    opt.removeTagNames[ci.tagName.toLowerCase()] &&
                    domUtils.hasNoAttributes(ci) &&
                    domUtils.isEmptyBlock(ci)
                ) {
                    domUtils.remove(ci);
                }
            }
        }
        if (opt.tobdc) {
            var root = UE.htmlparser(cont.innerHTML);
            root.traversal(function (node) {
                if (node.type == "text") {
                    node.data = ToDBC(node.data);
                }
            });
            cont.innerHTML = root.toHtml();
        }
        if (opt.bdc2sb) {
            var root = UE.htmlparser(cont.innerHTML);
            root.traversal(function (node) {
                if (node.type == "text") {
                    node.data = DBC2SB(node.data);
                }
            });
            cont.innerHTML = root.toHtml();
        }
        if (html) {
            html.html = cont.innerHTML;
        }
    }

    if (opt.pasteFilter) {
        me.addListener("beforepaste", autotype);
    }

    function DBC2SB(str) {
        var result = "";
        for (var i = 0; i < str.length; i++) {
            var code = str.charCodeAt(i); //获取当前字符的unicode编码
            if (code >= 65281 && code <= 65373) {
                //在这个unicode编码范围中的是所有的英文字母已经各种字符
                result += String.fromCharCode(str.charCodeAt(i) - 65248); //把全角字符的unicode编码转换为对应半角字符的unicode码
            } else if (code == 12288) {
                //空格
                result += String.fromCharCode(str.charCodeAt(i) - 12288 + 32);
            } else {
                result += str.charAt(i);
            }
        }
        return result;
    }

    function ToDBC(txtstring) {
        txtstring = utils.html(txtstring);
        var tmp = "";
        var mark = ""; /*用于判断,如果是html尖括里的标记,则不进行全角的转换*/
        for (var i = 0; i < txtstring.length; i++) {
            if (txtstring.charCodeAt(i) == 32) {
                tmp = tmp + String.fromCharCode(12288);
            } else if (txtstring.charCodeAt(i) < 127) {
                tmp = tmp + String.fromCharCode(txtstring.charCodeAt(i) + 65248);
            } else {
                tmp += txtstring.charAt(i);
            }
        }
        return tmp;
    }

    function readLocalOpts() {
        var cookieOpt = me.getPreferences("autotypeset");
        utils.extend(me.options.autotypeset, cookieOpt);
    }

    me.commands["autotypeset"] = {
        execCommand: function () {
            me.removeListener("beforepaste", autotype);
            if (opt.pasteFilter) {
                me.addListener("beforepaste", autotype);
            }
            autotype.call(me);
        }
    };
};


// plugins/autosubmit.js
/**
 * 快捷键提交
 * @file
 * @since 1.2.6.1
 */

/**
 * 提交表单
 * @command autosubmit
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @example
 * ```javascript
 * editor.execCommand( 'autosubmit' );
 * ```
 */

UE.plugin.register("autosubmit", function () {
    return {
        shortcutkey: {
            autosubmit: "ctrl+13" //手动提交
        },
        commands: {
            autosubmit: {
                execCommand: function () {
                    var me = this,
                        form = domUtils.findParentByTagName(me.iframe, "form", false);
                    if (form) {
                        if (me.fireEvent("beforesubmit") === false) {
                            return;
                        }
                        me.sync();
                        form.submit();
                    }
                }
            }
        }
    };
});


// plugins/background.js
/**
 * 背景插件,为UEditor提供设置背景功能
 * @file
 * @since 1.2.6.1
 */
UE.plugin.register("background", function () {
    var me = this,
        cssRuleId = "editor_background",
        isSetColored,
        reg = new RegExp("body[\\s]*\\{(.+)\\}", "i");

    function stringToObj(str) {
        var obj = {},
            styles = str.split(";");
        utils.each(styles, function (v) {
            var index = v.indexOf(":"),
                key = utils.trim(v.substr(0, index)).toLowerCase();
            key && (obj[key] = utils.trim(v.substr(index + 1) || ""));
        });
        return obj;
    }

    function setBackground(obj) {
        if (obj) {
            var styles = [];
            for (var name in obj) {
                if (obj.hasOwnProperty(name)) {
                    styles.push(name + ":" + obj[name] + "; ");
                }
            }
            utils.cssRule(
                cssRuleId,
                styles.length ? "body{" + styles.join("") + "}" : "",
                me.document
            );
        } else {
            utils.cssRule(cssRuleId, "", me.document);
        }
    }

    //重写editor.hasContent方法

    var orgFn = me.hasContents;
    me.hasContents = function () {
        if (me.queryCommandValue("background")) {
            return true;
        }
        return orgFn.apply(me, arguments);
    };
    return {
        bindEvents: {
            getAllHtml: function (type, headHtml) {
                var body = this.body,
                    su = domUtils.getComputedStyle(body, "background-image"),
                    url = "";
                if (su.indexOf(me.options.imagePath) > 0) {
                    url = su
                        .substring(su.indexOf(me.options.imagePath), su.length - 1)
                        .replace(/"|\(|\)/gi, "");
                } else {
                    url = su != "none" ? su.replace(/url\("?|"?\)/gi, "") : "";
                }
                var html = '<style type="text/css">body{';
                var bgObj = {
                    "background-color":
                        domUtils.getComputedStyle(body, "background-color") || "#ffffff",
                    "background-image": url ? "url(" + url + ")" : "",
                    "background-repeat":
                        domUtils.getComputedStyle(body, "background-repeat") || "",
                    "background-position": browser.ie
                        ? domUtils.getComputedStyle(body, "background-position-x") +
                        " " +
                        domUtils.getComputedStyle(body, "background-position-y")
                        : domUtils.getComputedStyle(body, "background-position"),
                    height: domUtils.getComputedStyle(body, "height")
                };
                for (var name in bgObj) {
                    if (bgObj.hasOwnProperty(name)) {
                        html += name + ":" + bgObj[name] + "; ";
                    }
                }
                html += "}</style> ";
                headHtml.push(html);
            },
            aftersetcontent: function () {
                if (isSetColored == false) setBackground();
            }
        },
        inputRule: function (root) {
            isSetColored = false;
            utils.each(root.getNodesByTagName("p"), function (p) {
                var styles = p.getAttr("data-background");
                if (styles) {
                    isSetColored = true;
                    setBackground(stringToObj(styles));
                    p.parentNode.removeChild(p);
                }
            });
        },
        outputRule: function (root) {
            var me = this,
                styles = (utils.cssRule(cssRuleId, me.document) || "")
                    .replace(/[\n\r]+/g, "")
                    .match(reg);
            if (styles) {
                root.appendChild(
                    UE.uNode.createElement(
                        '<p style="display:none;" data-background="' +
                        utils.trim(styles[1].replace(/"/g, "").replace(/[\s]+/g, " ")) +
                        '"><br/></p>'
                    )
                );
            }
        },
        commands: {
            background: {
                execCommand: function (cmd, obj) {
                    setBackground(obj);
                },
                queryCommandValue: function () {
                    var me = this,
                        styles = (utils.cssRule(cssRuleId, me.document) || "")
                            .replace(/[\n\r]+/g, "")
                            .match(reg);
                    return styles ? stringToObj(styles[1]) : null;
                },
                notNeedUndo: true
            }
        }
    };
});


// plugins/image.js
/**
 * 图片插入、排版插件
 * @file
 * @since 1.2.6.1
 */

/**
 * 图片对齐方式
 * @command imagefloat
 * @method execCommand
 * @remind 值center为独占一行居中
 * @param { String } cmd 命令字符串
 * @param { String } align 对齐方式,可传left、right、none、center
 * @remaind center表示图片独占一行
 * @example
 * ```javascript
 * editor.execCommand( 'imagefloat', 'center' );
 * ```
 */

/**
 * 如果选区所在位置是图片区域
 * @command imagefloat
 * @method queryCommandValue
 * @param { String } cmd 命令字符串
 * @return { String } 返回图片对齐方式
 * @example
 * ```javascript
 * editor.queryCommandValue( 'imagefloat' );
 * ```
 */

UE.commands["imagefloat"] = {
    execCommand: function (cmd, align) {
        var me = this,
            range = me.selection.getRange();
        if (!range.collapsed) {
            var img = range.getClosedNode();
            if (img && img.tagName === "IMG") {
                switch (align) {
                    case "left":
                    case "right":
                    case "none":
                        var pN = img.parentNode,
                            tmpNode,
                            pre,
                            next;
                        while (dtd.$inline[pN.tagName] || pN.tagName == "A") {
                            pN = pN.parentNode;
                        }
                        tmpNode = pN;
                        if (
                            tmpNode.tagName == "P" &&
                            domUtils.getStyle(tmpNode, "text-align") == "center"
                        ) {
                            if (
                                !domUtils.isBody(tmpNode) &&
                                domUtils.getChildCount(tmpNode, function (node) {
                                    return !domUtils.isBr(node) && !domUtils.isWhitespace(node);
                                }) == 1
                            ) {
                                pre = tmpNode.previousSibling;
                                next = tmpNode.nextSibling;
                                if (
                                    pre &&
                                    next &&
                                    pre.nodeType == 1 &&
                                    next.nodeType == 1 &&
                                    pre.tagName == next.tagName &&
                                    domUtils.isBlockElm(pre)
                                ) {
                                    pre.appendChild(tmpNode.firstChild);
                                    while (next.firstChild) {
                                        pre.appendChild(next.firstChild);
                                    }
                                    domUtils.remove(tmpNode);
                                    domUtils.remove(next);
                                } else {
                                    domUtils.setStyle(tmpNode, "text-align", "");
                                }
                            }

                            range.selectNode(img).select();
                        }
                        domUtils.setStyle(img, "float", align == "none" ? "" : align);
                        if (align == "none") {
                            domUtils.removeAttributes(img, "align");
                        }

                        break;
                    case "center":
                        if (me.queryCommandValue("imagefloat") != "center") {
                            pN = img.parentNode;
                            domUtils.setStyle(img, "float", "");
                            domUtils.removeAttributes(img, "align");
                            tmpNode = img;
                            while (
                                pN &&
                                domUtils.getChildCount(pN, function (node) {
                                    return !domUtils.isBr(node) && !domUtils.isWhitespace(node);
                                }) == 1 &&
                                (dtd.$inline[pN.tagName] || pN.tagName == "A")
                                ) {
                                tmpNode = pN;
                                pN = pN.parentNode;
                            }
                            range.setStartBefore(tmpNode).setCursor(false);
                            pN = me.document.createElement("div");
                            pN.appendChild(tmpNode);
                            domUtils.setStyle(tmpNode, "float", "");

                            me.execCommand(
                                "insertHtml",
                                '<p id="_img_parent_tmp" style="text-align:center">' +
                                pN.innerHTML +
                                "</p>"
                            );

                            tmpNode = me.document.getElementById("_img_parent_tmp");
                            tmpNode.removeAttribute("id");
                            tmpNode = tmpNode.firstChild;
                            range.selectNode(tmpNode).select();
                            //去掉后边多余的元素
                            next = tmpNode.parentNode.nextSibling;
                            if (next && domUtils.isEmptyNode(next)) {
                                domUtils.remove(next);
                            }
                        }

                        break;
                }
            }
        }
    },
    queryCommandValue: function () {
        var range = this.selection.getRange(),
            startNode,
            floatStyle;
        if (range.collapsed) {
            return "none";
        }
        startNode = range.getClosedNode();
        if (startNode && startNode.nodeType == 1 && startNode.tagName == "IMG") {
            floatStyle =
                domUtils.getComputedStyle(startNode, "float") ||
                startNode.getAttribute("align");

            if (floatStyle == "none") {
                floatStyle = domUtils.getComputedStyle(
                    startNode.parentNode,
                    "text-align"
                ) == "center"
                    ? "center"
                    : floatStyle;
            }
            return {
                left: 1,
                right: 1,
                center: 1
            }[floatStyle]
                ? floatStyle
                : "none";
        }
        return "none";
    },
    queryCommandState: function () {
        var range = this.selection.getRange(),
            startNode;

        if (range.collapsed) return -1;

        startNode = range.getClosedNode();
        if (startNode && startNode.nodeType === 1 && startNode.tagName === "IMG") {
            return 0;
        }
        return -1;
    }
};

/**
 * 插入图片
 * @command insertimage
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @param { Object } opt 属性键值对,这些属性都将被复制到当前插入图片
 * @remind 该命令第二个参数可接受一个图片配置项对象的数组,可以插入多张图片,
 * 此时数组的每一个元素都是一个Object类型的图片属性集合。
 * @example
 * ```javascript
 * editor.execCommand( 'insertimage', {
 *     src:'a/b/c.jpg',
 *     width:'100',
 *     height:'100'
 * } );
 * ```
 * @example
 * ```javascript
 * editor.execCommand( 'insertimage', [{
 *     src:'a/b/c.jpg',
 *     width:'100',
 *     height:'100'
 * },{
 *     src:'a/b/d.jpg',
 *     width:'100',
 *     height:'100'
 * }] );
 * ```
 */

UE.commands["insertimage"] = {
    execCommand: function (cmd, opt) {
        opt = utils.isArray(opt) ? opt : [opt];
        if (!opt.length) {
            return;
        }
        var me = this,
            range = me.selection.getRange(),
            img = range.getClosedNode();

        if (me.fireEvent("beforeinsertimage", opt) === true) {
            return;
        }

        if (
            img &&
            /img/i.test(img.tagName) &&
            (img.className != "edui-faked-video" ||
                img.className.indexOf("edui-upload-video") != -1) &&
            !img.getAttribute("data-word-image")
        ) {
            var first = opt.shift();
            var floatStyle = first["floatStyle"];
            delete first["floatStyle"];
            ////                img.style.border = (first.border||0) +"px solid #000";
            ////                img.style.margin = (first.margin||0) +"px";
            //                img.style.cssText += ';margin:' + (first.margin||0) +"px;" + 'border:' + (first.border||0) +"px solid #000";
            domUtils.setAttributes(img, first);
            me.execCommand("imagefloat", floatStyle);
            if (opt.length > 0) {
                range.setStartAfter(img).setCursor(false, true);
                me.execCommand("insertimage", opt);
            }
        } else {
            var html = [],
                str = "",
                ci;
            ci = opt[0];
            if (opt.length == 1) {
                str =
                    '<img src="' +
                    ci.src +
                    '" ' +
                    (ci._src ? ' _src="' + ci._src + '" ' : "") +
                    (ci.width ? 'width="' + ci.width + '" ' : "") +
                    (ci.height ? ' height="' + ci.height + '" ' : "") +
                    (ci["floatStyle"] == "left" || ci["floatStyle"] == "right"
                        ? ' style="float:' + ci["floatStyle"] + ';"'
                        : "") +
                    (ci.title && ci.title != "" ? ' title="' + ci.title + '"' : "") +
                    (ci.border && ci.border != "0" ? ' border="' + ci.border + '"' : "") +
                    (ci.alt && ci.alt != "" ? ' alt="' + ci.alt + '"' : "") +
                    (ci.hspace && ci.hspace != "0"
                        ? ' hspace = "' + ci.hspace + '"'
                        : "") +
                    (ci.vspace && ci.vspace != "0"
                        ? ' vspace = "' + ci.vspace + '"'
                        : "") +
                    "/>";
                if (ci["floatStyle"] == "center") {
                    str = '<p style="text-align: center">' + str + "</p>";
                }
                html.push(str);
            } else {
                for (var i = 0; (ci = opt[i++]);) {
                    str =
                        "<p " +
                        (ci["floatStyle"] == "center"
                            ? 'style="text-align: center" '
                            : "") +
                        '><img src="' +
                        ci.src +
                        '" ' +
                        (ci.width ? 'width="' + ci.width + '" ' : "") +
                        (ci._src ? ' _src="' + ci._src + '" ' : "") +
                        (ci.height ? ' height="' + ci.height + '" ' : "") +
                        ' style="' +
                        (ci["floatStyle"] && ci["floatStyle"] != "center"
                            ? "float:" + ci["floatStyle"] + ";"
                            : "") +
                        (ci.border || "") +
                        '" ' +
                        (ci.title ? ' title="' + ci.title + '"' : "") +
                        " /></p>";
                    html.push(str);
                }
            }

            me.execCommand("insertHtml", html.join(""));
        }

        me.fireEvent("afterinsertimage", opt);
    }
};


// plugins/justify.js
/**
 * 段落格式
 * @file
 * @since 1.2.6.1
 */

/**
 * 段落对齐方式
 * @command justify
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @param { String } align 对齐方式:left => 居左,right => 居右,center => 居中,justify => 两端对齐
 * @example
 * ```javascript
 * editor.execCommand( 'justify', 'center' );
 * ```
 */
/**
 * 如果选区所在位置是段落区域,返回当前段落对齐方式
 * @command justify
 * @method queryCommandValue
 * @param { String } cmd 命令字符串
 * @return { String } 返回段落对齐方式
 * @example
 * ```javascript
 * editor.queryCommandValue( 'justify' );
 * ```
 */

UE.plugins["justify"] = function () {
    var me = this,
        block = domUtils.isBlockElm,
        defaultValue = {
            left: 1,
            right: 1,
            center: 1,
            justify: 1
        },
        doJustify = function (range, style) {
            var bookmark = range.createBookmark(),
                filterFn = function (node) {
                    return node.nodeType == 1
                        ? node.tagName.toLowerCase() != "br" &&
                        !domUtils.isBookmarkNode(node)
                        : !domUtils.isWhitespace(node);
                };

            range.enlarge(true);
            var bookmark2 = range.createBookmark(),
                current = domUtils.getNextDomNode(bookmark2.start, false, filterFn),
                tmpRange = range.cloneRange(),
                tmpNode;
            while (
                current &&
                !(
                    domUtils.getPosition(current, bookmark2.end) &
                    domUtils.POSITION_FOLLOWING
                )
                ) {
                if (current.nodeType == 3 || !block(current)) {
                    tmpRange.setStartBefore(current);
                    while (current && current !== bookmark2.end && !block(current)) {
                        tmpNode = current;
                        current = domUtils.getNextDomNode(current, false, null, function (
                            node
                        ) {
                            return !block(node);
                        });
                    }
                    tmpRange.setEndAfter(tmpNode);
                    var common = tmpRange.getCommonAncestor();
                    if (!domUtils.isBody(common) && block(common)) {
                        domUtils.setStyles(
                            common,
                            utils.isString(style) ? {"text-align": style} : style
                        );
                        current = common;
                    } else {
                        var p = range.document.createElement("p");
                        domUtils.setStyles(
                            p,
                            utils.isString(style) ? {"text-align": style} : style
                        );
                        var frag = tmpRange.extractContents();
                        p.appendChild(frag);
                        tmpRange.insertNode(p);
                        current = p;
                    }
                    current = domUtils.getNextDomNode(current, false, filterFn);
                } else {
                    current = domUtils.getNextDomNode(current, true, filterFn);
                }
            }
            return range.moveToBookmark(bookmark2).moveToBookmark(bookmark);
        };

    UE.commands["justify"] = {
        execCommand: function (cmdName, align) {
            var range = this.selection.getRange(),
                txt;

            //闭合时单独处理
            if (range.collapsed) {
                txt = this.document.createTextNode("p");
                range.insertNode(txt);
            }
            doJustify(range, align);
            if (txt) {
                range.setStartBefore(txt).collapse(true);
                domUtils.remove(txt);
            }

            range.select();

            return true;
        },
        queryCommandValue: function () {
            var startNode = this.selection.getStart(),
                value = domUtils.getComputedStyle(startNode, "text-align");
            return defaultValue[value] ? value : "left";
        },
        queryCommandState: function () {
            var start = this.selection.getStart(),
                cell =
                    start &&
                    domUtils.findParentByTagName(start, ["td", "th", "caption"], true);

            return cell ? -1 : 0;
        }
    };
};


// plugins/font.js
/**
 * 字体颜色,背景色,字号,字体,下划线,删除线
 * @file
 * @since 1.2.6.1
 */

/**
 * 字体颜色
 * @command forecolor
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @param { String } value 色值(必须十六进制)
 * @example
 * ```javascript
 * editor.execCommand( 'forecolor', '#000' );
 * ```
 */
/**
 * 返回选区字体颜色
 * @command forecolor
 * @method queryCommandValue
 * @param { String } cmd 命令字符串
 * @return { String } 返回字体颜色
 * @example
 * ```javascript
 * editor.queryCommandValue( 'forecolor' );
 * ```
 */

/**
 * 字体背景颜色
 * @command backcolor
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @param { String } value 色值(必须十六进制)
 * @example
 * ```javascript
 * editor.execCommand( 'backcolor', '#000' );
 * ```
 */
/**
 * 返回选区字体颜色
 * @command backcolor
 * @method queryCommandValue
 * @param { String } cmd 命令字符串
 * @return { String } 返回字体背景颜色
 * @example
 * ```javascript
 * editor.queryCommandValue( 'backcolor' );
 * ```
 */

/**
 * 字体大小
 * @command fontsize
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @param { String } value 字体大小
 * @example
 * ```javascript
 * editor.execCommand( 'fontsize', '14px' );
 * ```
 */
/**
 * 返回选区字体大小
 * @command fontsize
 * @method queryCommandValue
 * @param { String } cmd 命令字符串
 * @return { String } 返回字体大小
 * @example
 * ```javascript
 * editor.queryCommandValue( 'fontsize' );
 * ```
 */

/**
 * 字体样式
 * @command fontfamily
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @param { String } value 字体样式
 * @example
 * ```javascript
 * editor.execCommand( 'fontfamily', '微软雅黑' );
 * ```
 */
/**
 * 返回选区字体样式
 * @command fontfamily
 * @method queryCommandValue
 * @param { String } cmd 命令字符串
 * @return { String } 返回字体样式
 * @example
 * ```javascript
 * editor.queryCommandValue( 'fontfamily' );
 * ```
 */

/**
 * 字体下划线,与删除线互斥
 * @command underline
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @example
 * ```javascript
 * editor.execCommand( 'underline' );
 * ```
 */

/**
 * 字体删除线,与下划线互斥
 * @command strikethrough
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @example
 * ```javascript
 * editor.execCommand( 'strikethrough' );
 * ```
 */

/**
 * 字体边框
 * @command fontborder
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @example
 * ```javascript
 * editor.execCommand( 'fontborder' );
 * ```
 */

UE.plugins["font"] = function () {
    var me = this,
        fonts = {
            forecolor: "color",
            backcolor: "background-color",
            fontsize: "font-size",
            fontfamily: "font-family",
            underline: "text-decoration",
            strikethrough: "text-decoration",
            fontborder: "border"
        },
        lang = me.getLang(),
        needCmd = {underline: 1, strikethrough: 1, fontborder: 1},
        needSetChild = {
            forecolor: "color",
            backcolor: "background-color",
            fontsize: "font-size",
            fontfamily: "font-family"
        };
    me.setOpt({
        fontfamily: [
            {name: "default", val: "default"},
            {name: "songti", val: "宋体,SimSun"},
            {name: "yahei", val: "微软雅黑,Microsoft YaHei"},
            {name: "kaiti", val: "楷体,楷体_GB2312,SimKai"},
            {name: "heiti", val: "黑体,SimHei"},
            {name: "lishu", val: "隶书,SimLi"},
            // { name: "andaleMono", val: "andale mono" },
            {name: "arial", val: "arial,helvetica,sans-serif"},
            // { name: "arialBlack", val: "arial black,avant garde" },
            // { name: "comicSansMs", val: "comic sans ms" },
            // { name: "impact", val: "impact,chicago" },
            {name: "timesNewRoman", val: "times new roman"}
        ],
        fontsize: [10, 11, 12, 14, 16, 18, 20, 24, 36]
    });

    function mergeWithParent(node) {
        var parent;
        while ((parent = node.parentNode)) {
            if (
                parent.tagName == "SPAN" &&
                domUtils.getChildCount(parent, function (child) {
                    return !domUtils.isBookmarkNode(child) && !domUtils.isBr(child);
                }) == 1
            ) {
                parent.style.cssText += node.style.cssText;
                domUtils.remove(node, true);
                node = parent;
            } else {
                break;
            }
        }
    }

    function mergeChild(rng, cmdName, value) {
        if (needSetChild[cmdName]) {
            rng.adjustmentBoundary();
            if (!rng.collapsed && rng.startContainer.nodeType == 1) {
                var start = rng.startContainer.childNodes[rng.startOffset];
                if (start && domUtils.isTagNode(start, "span")) {
                    var bk = rng.createBookmark();
                    utils.each(domUtils.getElementsByTagName(start, "span"), function (
                        span
                    ) {
                        if (!span.parentNode || domUtils.isBookmarkNode(span)) return;
                        if (
                            cmdName == "backcolor" &&
                            domUtils
                                .getComputedStyle(span, "background-color")
                                .toLowerCase() === value
                        ) {
                            return;
                        }
                        domUtils.removeStyle(span, needSetChild[cmdName]);
                        if (span.style.cssText.replace(/^\s+$/, "").length == 0) {
                            domUtils.remove(span, true);
                        }
                    });
                    rng.moveToBookmark(bk);
                }
            }
        }
    }

    function mergesibling(rng, cmdName, value) {
        var collapsed = rng.collapsed,
            bk = rng.createBookmark(),
            common;
        if (collapsed) {
            common = bk.start.parentNode;
            while (dtd.$inline[common.tagName]) {
                common = common.parentNode;
            }
        } else {
            common = domUtils.getCommonAncestor(bk.start, bk.end);
        }
        utils.each(domUtils.getElementsByTagName(common, "span"), function (span) {
            if (!span.parentNode || domUtils.isBookmarkNode(span)) return;
            if (/\s*border\s*:\s*none;?\s*/i.test(span.style.cssText)) {
                if (/^\s*border\s*:\s*none;?\s*$/.test(span.style.cssText)) {
                    domUtils.remove(span, true);
                } else {
                    domUtils.removeStyle(span, "border");
                }
                return;
            }
            if (
                /border/i.test(span.style.cssText) &&
                span.parentNode.tagName == "SPAN" &&
                /border/i.test(span.parentNode.style.cssText)
            ) {
                span.style.cssText = span.style.cssText.replace(
                    /border[^:]*:[^;]+;?/gi,
                    ""
                );
            }
            if (!(cmdName == "fontborder" && value == "none")) {
                var next = span.nextSibling;
                while (next && next.nodeType == 1 && next.tagName == "SPAN") {
                    if (domUtils.isBookmarkNode(next) && cmdName == "fontborder") {
                        span.appendChild(next);
                        next = span.nextSibling;
                        continue;
                    }
                    if (next.style.cssText == span.style.cssText) {
                        domUtils.moveChild(next, span);
                        domUtils.remove(next);
                    }
                    if (span.nextSibling === next) break;
                    next = span.nextSibling;
                }
            }

            mergeWithParent(span);
            if (browser.ie && browser.version > 8) {
                //拷贝父亲们的特别的属性,这里只做背景颜色的处理
                var parent = domUtils.findParent(span, function (n) {
                    return (
                        n.tagName == "SPAN" && /background-color/.test(n.style.cssText)
                    );
                });
                if (parent && !/background-color/.test(span.style.cssText)) {
                    span.style.backgroundColor = parent.style.backgroundColor;
                }
            }
        });
        rng.moveToBookmark(bk);
        mergeChild(rng, cmdName, value);
    }

    me.addInputRule(function (root) {
        utils.each(root.getNodesByTagName("u s del font strike"), function (node) {
            if (node.tagName == "font") {
                var cssStyle = [];
                for (var p in node.attrs) {
                    switch (p) {
                        case "size":
                            cssStyle.push(
                                "font-size:" +
                                ({
                                    "1": "10",
                                    "2": "12",
                                    "3": "16",
                                    "4": "18",
                                    "5": "24",
                                    "6": "32",
                                    "7": "48"
                                }[node.attrs[p]] || node.attrs[p]) +
                                "px"
                            );
                            break;
                        case "color":
                            cssStyle.push("color:" + node.attrs[p]);
                            break;
                        case "face":
                            cssStyle.push("font-family:" + node.attrs[p]);
                            break;
                        case "style":
                            cssStyle.push(node.attrs[p]);
                    }
                }
                node.attrs = {
                    style: cssStyle.join(";")
                };
            } else {
                var val = node.tagName == "u" ? "underline" : "line-through";
                node.attrs = {
                    style: (node.getAttr("style") || "") + "text-decoration:" + val + ";"
                };
            }
            node.tagName = "span";
        });
        //        utils.each(root.getNodesByTagName('span'), function (node) {
        //            var val;
        //            if(val = node.getAttr('class')){
        //                if(/fontstrikethrough/.test(val)){
        //                    node.setStyle('text-decoration','line-through');
        //                    if(node.attrs['class']){
        //                        node.attrs['class'] = node.attrs['class'].replace(/fontstrikethrough/,'');
        //                    }else{
        //                        node.setAttr('class')
        //                    }
        //                }
        //                if(/fontborder/.test(val)){
        //                    node.setStyle('border','1px solid #000');
        //                    if(node.attrs['class']){
        //                        node.attrs['class'] = node.attrs['class'].replace(/fontborder/,'');
        //                    }else{
        //                        node.setAttr('class')
        //                    }
        //                }
        //            }
        //        });
    });
    //    me.addOutputRule(function(root){
    //        utils.each(root.getNodesByTagName('span'), function (node) {
    //            var val;
    //            if(val = node.getStyle('text-decoration')){
    //                if(/line-through/.test(val)){
    //                    if(node.attrs['class']){
    //                        node.attrs['class'] += ' fontstrikethrough';
    //                    }else{
    //                        node.setAttr('class','fontstrikethrough')
    //                    }
    //                }
    //
    //                node.setStyle('text-decoration')
    //            }
    //            if(val = node.getStyle('border')){
    //                if(/1px/.test(val) && /solid/.test(val)){
    //                    if(node.attrs['class']){
    //                        node.attrs['class'] += ' fontborder';
    //
    //                    }else{
    //                        node.setAttr('class','fontborder')
    //                    }
    //                }
    //                node.setStyle('border')
    //
    //            }
    //        });
    //    });
    for (var p in fonts) {
        (function (cmd, style) {
            UE.commands[cmd] = {
                execCommand: function (cmdName, value) {
                    // console.log("execCommand", cmdName, value);
                    value =
                        value ||
                        (this.queryCommandState(cmdName)
                            ? "none"
                            : cmdName === "underline"
                                ? "underline"
                                : cmdName === "fontborder" ? "1px solid #000" : "line-through");
                    var me = this,
                        range = this.selection.getRange(),
                        text;

                    if (value === "default") {
                        if (range.collapsed) {
                            text = me.document.createTextNode("font");
                            range.insertNode(text).select();
                        }
                        me.execCommand("removeFormat", "span,a", style);
                        if (text) {
                            range.setStartBefore(text).collapse(true);
                            domUtils.remove(text);
                        }
                        mergesibling(range, cmdName, value);
                        range.select();
                    } else {
                        if (!range.collapsed) {
                            if (needCmd[cmd] && me.queryCommandValue(cmd)) {
                                me.execCommand("removeFormat", "span,a", style);
                            }
                            range = me.selection.getRange();

                            range.applyInlineStyle("span", {style: style + ":" + value});
                            mergesibling(range, cmdName, value);
                            range.select();
                        } else {
                            var span = domUtils.findParentByTagName(
                                range.startContainer,
                                "span",
                                true
                            );
                            text = me.document.createTextNode("font");
                            if (
                                span &&
                                !span.children.length &&
                                !span[browser.ie ? "innerText" : "textContent"].replace(
                                    fillCharReg,
                                    ""
                                ).length
                            ) {
                                //for ie hack when enter
                                range.insertNode(text);
                                if (needCmd[cmd]) {
                                    range.selectNode(text).select();
                                    me.execCommand("removeFormat", "span,a", style, null);

                                    span = domUtils.findParentByTagName(text, "span", true);
                                    range.setStartBefore(text);
                                }
                                span && (span.style.cssText += ";" + style + ":" + value);
                                range.collapse(true).select();
                            } else {
                                range.insertNode(text);
                                range.selectNode(text).select();
                                span = range.document.createElement("span");

                                if (needCmd[cmd]) {
                                    //a标签内的不处理跳过
                                    if (domUtils.findParentByTagName(text, "a", true)) {
                                        range.setStartBefore(text).setCursor();
                                        domUtils.remove(text);
                                        return;
                                    }
                                    me.execCommand("removeFormat", "span,a", style);
                                }

                                span.style.cssText = style + ":" + value;

                                text.parentNode.insertBefore(span, text);
                                //修复,span套span 但样式不继承的问题
                                if (!browser.ie || (browser.ie && browser.version === 9)) {
                                    var spanParent = span.parentNode;
                                    while (!domUtils.isBlockElm(spanParent)) {
                                        if (spanParent.tagName === "SPAN") {
                                            //opera合并style不会加入";"
                                            span.style.cssText =
                                                spanParent.style.cssText + ";" + span.style.cssText;
                                        }
                                        spanParent = spanParent.parentNode;
                                    }
                                }

                                if (opera) {
                                    setTimeout(function () {
                                        range.setStart(span, 0).collapse(true);
                                        mergesibling(range, cmdName, value);
                                        range.select();
                                    });
                                } else {
                                    range.setStart(span, 0).collapse(true);
                                    mergesibling(range, cmdName, value);
                                    range.select();
                                }

                                //trace:981
                                //domUtils.mergeToParent(span)
                            }
                            domUtils.remove(text);
                        }
                    }
                    return true;
                },
                queryCommandValue: function (cmdName) {
                    var startNode = this.selection.getStart();
                    var styleVal;

                    //trace:946
                    if (cmdName === "underline" || cmdName === "strikethrough") {
                        var tmpNode = startNode,
                            value;
                        while (
                            tmpNode &&
                            !domUtils.isBlockElm(tmpNode) &&
                            !domUtils.isBody(tmpNode)
                            ) {
                            if (tmpNode.nodeType === 1) {
                                value = domUtils.getComputedStyle(tmpNode, style);
                                if (value !== "none") {
                                    return value;
                                }
                            }

                            tmpNode = tmpNode.parentNode;
                        }
                        return "none";
                    } else if (cmdName === "fontborder") {
                        var tmp = startNode,
                            val;
                        while (tmp && dtd.$inline[tmp.tagName]) {
                            if ((val = domUtils.getComputedStyle(tmp, "border"))) {
                                if (/1px/.test(val) && /solid/.test(val)) {
                                    return val;
                                }
                            }
                            tmp = tmp.parentNode;
                        }
                        return "";
                    } else if (cmdName === "FontSize") {
                        styleVal = domUtils.getComputedStyle(startNode, style);
                        tmp = /^([\d\.]+)(\w+)$/.exec(styleVal);

                        if (tmp) {
                            return Math.floor(tmp[1]) + tmp[2];
                        }

                        return styleVal;
                    } else if (cmdName === 'FontFamily') {
                        styleVal = domUtils.getComputedStyle(startNode, style)
                        // 移除左右引号
                        styleVal = styleVal.replace(/['"]/g, '');
                        // 移除字体 宋体, SimSun 转为 宋体,SimSun,否则以下的判断会出错
                        styleVal = styleVal.replace(/\s*,\s*/g, ',');
                        var fontFamily = lang.fontfamily.default;
                        var fontList = me.options["fontfamily"] || [];
                        for (var i = 0; i < fontList.length; i++) {
                            var v = fontList[i];
                            // console.log('FontFamily', styleVal, v.val);
                            if (v.val === styleVal) {
                                fontFamily = styleVal;
                                break;
                            }
                        }
                        // console.log('fontList', fontList);
                        // console.log('FontFamily', styleVal, fontFamily);
                        return fontFamily;
                    }

                    value = domUtils.getComputedStyle(startNode, style);
                    return value;
                },
                queryCommandState: function (cmdName) {
                    if (!needCmd[cmdName]) return 0;
                    var val = this.queryCommandValue(cmdName);
                    if (cmdName === "fontborder") {
                        return /1px/.test(val) && /solid/.test(val);
                    } else {
                        return cmdName === "underline"
                            ? /underline/.test(val)
                            : /line\-through/.test(val);
                    }
                }
            };
        })(p, fonts[p]);
    }
};


// plugins/link.js
/**
 * 超链接
 * @file
 * @since 1.2.6.1
 */

/**
 * 插入超链接
 * @command link
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @param { Object } options   设置自定义属性,例如:url、title、target
 * @example
 * ```javascript
 * editor.execCommand( 'link', '{
 *     url:'ueditor.baidu.com',
 *     title:'ueditor',
 *     target:'_blank'
 * }' );
 * ```
 */
/**
 * 返回当前选中的第一个超链接节点
 * @command link
 * @method queryCommandValue
 * @param { String } cmd 命令字符串
 * @return { Element } 超链接节点
 * @example
 * ```javascript
 * editor.queryCommandValue( 'link' );
 * ```
 */

/**
 * 取消超链接
 * @command unlink
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @example
 * ```javascript
 * editor.execCommand( 'unlink');
 * ```
 */

UE.plugins["link"] = function () {
    function optimize(range) {
        var start = range.startContainer,
            end = range.endContainer;

        if ((start = domUtils.findParentByTagName(start, "a", true))) {
            range.setStartBefore(start);
        }
        if ((end = domUtils.findParentByTagName(end, "a", true))) {
            range.setEndAfter(end);
        }
    }

    UE.commands["unlink"] = {
        execCommand: function () {
            var range = this.selection.getRange(),
                bookmark;
            if (
                range.collapsed &&
                !domUtils.findParentByTagName(range.startContainer, "a", true)
            ) {
                return;
            }
            bookmark = range.createBookmark();
            optimize(range);
            range.removeInlineStyle("a").moveToBookmark(bookmark).select();
        },
        queryCommandState: function () {
            return !this.highlight && this.queryCommandValue("link") ? 0 : -1;
        }
    };

    function doLink(range, opt, me) {
        var rngClone = range.cloneRange(),
            link = me.queryCommandValue("link");
        optimize((range = range.adjustmentBoundary()));
        var start = range.startContainer;
        if (start.nodeType == 1 && link) {
            start = start.childNodes[range.startOffset];
            if (
                start &&
                start.nodeType == 1 &&
                start.tagName == "A" &&
                /^(?:https?|ftp|file)\s*:\s*\/\//.test(
                    start[browser.ie ? "innerText" : "textContent"]
                )
            ) {
                start[browser.ie ? "innerText" : "textContent"] = utils.html(
                    opt.textValue || opt.href
                );
            }
        }
        if (!rngClone.collapsed || link) {
            range.removeInlineStyle("a");
            rngClone = range.cloneRange();
        }

        if (rngClone.collapsed) {
            var a = range.document.createElement("a"),
                text = "";
            if (opt.textValue) {
                text = utils.html(opt.textValue);
                delete opt.textValue;
            } else {
                text = utils.html(opt.href);
            }
            domUtils.setAttributes(a, opt);
            start = domUtils.findParentByTagName(rngClone.startContainer, "a", true);
            if (start && domUtils.isInNodeEndBoundary(rngClone, start)) {
                range.setStartAfter(start).collapse(true);
            }
            a[browser.ie ? "innerText" : "textContent"] = text;
            range.insertNode(a).selectNode(a);
        } else {
            range.applyInlineStyle("a", opt);
        }
    }

    UE.commands["link"] = {
        execCommand: function (cmdName, opt) {
            var range;
            opt._href && (opt._href = utils.unhtml(opt._href, /[<">]/g));
            opt.href && (opt.href = utils.unhtml(opt.href, /[<">]/g));
            opt.textValue && (opt.textValue = utils.unhtml(opt.textValue, /[<">]/g));
            doLink((range = this.selection.getRange()), opt, this);
            //闭合都不加占位符,如果加了会在a后边多个占位符节点,导致a是图片背景组成的列表,出现空白问题
            range.collapse().select(true);
        },
        queryCommandValue: function () {
            var range = this.selection.getRange(),
                node;
            if (range.collapsed) {
                //                    node = this.selection.getStart();
                //在ie下getstart()取值偏上了
                node = range.startContainer;
                node = node.nodeType == 1 ? node : node.parentNode;

                if (
                    node &&
                    (node = domUtils.findParentByTagName(node, "a", true)) &&
                    !domUtils.isInNodeEndBoundary(range, node)
                ) {
                    return node;
                }
            } else {
                //trace:1111  如果是<p><a>xx</a></p> startContainer是p就会找不到a
                range.shrinkBoundary();
                var start = range.startContainer.nodeType == 3 ||
                    !range.startContainer.childNodes[range.startOffset]
                    ? range.startContainer
                    : range.startContainer.childNodes[range.startOffset],
                    end = range.endContainer.nodeType == 3 || range.endOffset == 0
                        ? range.endContainer
                        : range.endContainer.childNodes[range.endOffset - 1],
                    common = range.getCommonAncestor();
                node = domUtils.findParentByTagName(common, "a", true);
                if (!node && common.nodeType == 1) {
                    var as = common.getElementsByTagName("a"),
                        ps,
                        pe;

                    for (var i = 0, ci; (ci = as[i++]);) {
                        (ps = domUtils.getPosition(ci, start)), (pe = domUtils.getPosition(
                            ci,
                            end
                        ));
                        if (
                            (ps & domUtils.POSITION_FOLLOWING ||
                                ps & domUtils.POSITION_CONTAINS) &&
                            (pe & domUtils.POSITION_PRECEDING ||
                                pe & domUtils.POSITION_CONTAINS)
                        ) {
                            node = ci;
                            break;
                        }
                    }
                }
                return node;
            }
        },
        queryCommandState: function () {
            //判断如果是视频的话连接不可用
            //fix 853
            var img = this.selection.getRange().getClosedNode(),
                flag =
                    img &&
                    (img.className == "edui-faked-video" ||
                        img.className.indexOf("edui-upload-video") != -1);
            return flag ? -1 : 0;
        }
    };
};


// plugins/iframe.js
///import core
///import plugins\inserthtml.js
///commands 插入框架
///commandsName  InsertFrame
///commandsTitle  插入Iframe
///commandsDialog  dialogs\insertframe

UE.plugins["insertframe"] = function () {
    var me = this;

    function deleteIframe() {
        me._iframe && delete me._iframe;
    }

    me.addListener("selectionchange", function () {
        deleteIframe();
    });
};


// plugins/scrawl.js
///import core
///commands 涂鸦
///commandsName  Scrawl
///commandsTitle  涂鸦
///commandsDialog  dialogs\scrawl
UE.commands["scrawl"] = {
    queryCommandState: function () {
        return browser.ie && browser.version <= 8 ? -1 : 0;
    }
};


// plugins/removeformat.js
/**
 * 清除格式
 * @file
 * @since 1.2.6.1
 */

/**
 * 清除文字样式
 * @command removeformat
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @param   {String}   tags     以逗号隔开的标签。如:strong
 * @param   {String}   style    样式如:color
 * @param   {String}   attrs    属性如:width
 * @example
 * ```javascript
 * editor.execCommand( 'removeformat', 'strong','color','width' );
 * ```
 */

UE.plugins["removeformat"] = function () {
    var me = this;
    me.setOpt({
        removeFormatTags:
            "b,big,code,del,dfn,em,font,i,ins,kbd,q,samp,small,span,strike,strong,sub,sup,tt,u,var",
        removeFormatAttributes: "class,style,lang,width,height,align,hspace,valign"
    });
    me.commands["removeformat"] = {
        execCommand: function (cmdName, tags, style, attrs, notIncludeA) {
            var tagReg = new RegExp(
                "^(?:" +
                (tags || this.options.removeFormatTags).replace(/,/g, "|") +
                ")$",
                "i"
                ),
                removeFormatAttributes = style
                    ? []
                    : (attrs || this.options.removeFormatAttributes).split(","),
                range = new dom.Range(this.document),
                bookmark,
                node,
                parent,
                filter = function (node) {
                    return node.nodeType == 1;
                };

            function isRedundantSpan(node) {
                if (node.nodeType == 3 || node.tagName.toLowerCase() != "span") {
                    return 0;
                }
                if (browser.ie) {
                    //ie 下判断实效,所以只能简单用style来判断
                    //return node.style.cssText == '' ? 1 : 0;
                    var attrs = node.attributes;
                    if (attrs.length) {
                        for (var i = 0, l = attrs.length; i < l; i++) {
                            if (attrs[i].specified) {
                                return 0;
                            }
                        }
                        return 1;
                    }
                }
                return !node.attributes.length;
            }

            function doRemove(range) {
                var bookmark1 = range.createBookmark();
                if (range.collapsed) {
                    range.enlarge(true);
                }

                //不能把a标签切了
                if (!notIncludeA) {
                    var aNode = domUtils.findParentByTagName(
                        range.startContainer,
                        "a",
                        true
                    );
                    if (aNode) {
                        range.setStartBefore(aNode);
                    }

                    aNode = domUtils.findParentByTagName(range.endContainer, "a", true);
                    if (aNode) {
                        range.setEndAfter(aNode);
                    }
                }

                bookmark = range.createBookmark();

                node = bookmark.start;

                //切开始
                while ((parent = node.parentNode) && !domUtils.isBlockElm(parent)) {
                    domUtils.breakParent(node, parent);

                    domUtils.clearEmptySibling(node);
                }
                if (bookmark.end) {
                    //切结束
                    node = bookmark.end;
                    while ((parent = node.parentNode) && !domUtils.isBlockElm(parent)) {
                        domUtils.breakParent(node, parent);
                        domUtils.clearEmptySibling(node);
                    }

                    //开始去除样式
                    var current = domUtils.getNextDomNode(bookmark.start, false, filter),
                        next;
                    while (current) {
                        if (current == bookmark.end) {
                            break;
                        }

                        next = domUtils.getNextDomNode(current, true, filter);

                        if (
                            !dtd.$empty[current.tagName.toLowerCase()] &&
                            !domUtils.isBookmarkNode(current)
                        ) {
                            if (tagReg.test(current.tagName)) {
                                if (style) {
                                    domUtils.removeStyle(current, style);
                                    if (isRedundantSpan(current) && style != "text-decoration") {
                                        domUtils.remove(current, true);
                                    }
                                } else {
                                    domUtils.remove(current, true);
                                }
                            } else {
                                //trace:939  不能把list上的样式去掉
                                // 清除格式时,默认移除Table、List上的样式
                                if (
                                    true
                                    // !dtd.$tableContent[current.tagName] && !dtd.$list[current.tagName]
                                ) {
                                    domUtils.removeAttributes(current, removeFormatAttributes);
                                    if (isRedundantSpan(current)) {
                                        domUtils.remove(current, true);
                                    }
                                } else {
                                    // console.log('current.ignore',current);
                                }
                            }
                        }
                        current = next;
                    }
                }
                //trace:1035
                //trace:1096 不能把td上的样式去掉,比如边框
                var pN = bookmark.start.parentNode;
                if (
                    domUtils.isBlockElm(pN) &&
                    !dtd.$tableContent[pN.tagName] &&
                    !dtd.$list[pN.tagName]
                ) {
                    domUtils.removeAttributes(pN, removeFormatAttributes);
                }
                pN = bookmark.end.parentNode;
                if (
                    bookmark.end &&
                    domUtils.isBlockElm(pN) &&
                    !dtd.$tableContent[pN.tagName] &&
                    !dtd.$list[pN.tagName]
                ) {
                    domUtils.removeAttributes(pN, removeFormatAttributes);
                }
                range.moveToBookmark(bookmark).moveToBookmark(bookmark1);
                //清除冗余的代码 <b><bookmark></b>
                var node = range.startContainer,
                    tmp,
                    collapsed = range.collapsed;
                while (
                    node.nodeType == 1 &&
                    domUtils.isEmptyNode(node) &&
                    dtd.$removeEmpty[node.tagName]
                    ) {
                    tmp = node.parentNode;
                    range.setStartBefore(node);
                    //trace:937
                    //更新结束边界
                    if (range.startContainer === range.endContainer) {
                        range.endOffset--;
                    }
                    domUtils.remove(node);
                    node = tmp;
                }

                if (!collapsed) {
                    node = range.endContainer;
                    while (
                        node.nodeType == 1 &&
                        domUtils.isEmptyNode(node) &&
                        dtd.$removeEmpty[node.tagName]
                        ) {
                        tmp = node.parentNode;
                        range.setEndBefore(node);
                        domUtils.remove(node);

                        node = tmp;
                    }
                }
            }

            range = this.selection.getRange();
            doRemove(range);
            range.select();
        }
    };
};


// plugins/blockquote.js
/**
 * 添加引用
 * @file
 * @since 1.2.6.1
 */

/**
 * 添加引用
 * @command blockquote
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @example
 * ```javascript
 * editor.execCommand( 'blockquote' );
 * ```
 */

/**
 * 添加引用
 * @command blockquote
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @param { Object } attrs 节点属性
 * @example
 * ```javascript
 * editor.execCommand( 'blockquote',{
 *     style: "color: red;"
 * } );
 * ```
 */

UE.plugins["blockquote"] = function () {
    var me = this;

    function getObj(editor) {
        return domUtils.filterNodeList(
            editor.selection.getStartElementPath(),
            "blockquote"
        );
    }

    me.commands["blockquote"] = {
        execCommand: function (cmdName, attrs) {
            var range = this.selection.getRange(),
                obj = getObj(this),
                blockquote = dtd.blockquote,
                bookmark = range.createBookmark();

            if (obj) {
                var start = range.startContainer,
                    startBlock = domUtils.isBlockElm(start)
                        ? start
                        : domUtils.findParent(start, function (node) {
                            return domUtils.isBlockElm(node);
                        }),
                    end = range.endContainer,
                    endBlock = domUtils.isBlockElm(end)
                        ? end
                        : domUtils.findParent(end, function (node) {
                            return domUtils.isBlockElm(node);
                        });

                //处理一下li
                startBlock =
                    domUtils.findParentByTagName(startBlock, "li", true) || startBlock;
                endBlock =
                    domUtils.findParentByTagName(endBlock, "li", true) || endBlock;

                if (
                    startBlock.tagName == "LI" ||
                    startBlock.tagName == "TD" ||
                    startBlock === obj ||
                    domUtils.isBody(startBlock)
                ) {
                    domUtils.remove(obj, true);
                } else {
                    domUtils.breakParent(startBlock, obj);
                }

                if (startBlock !== endBlock) {
                    obj = domUtils.findParentByTagName(endBlock, "blockquote");
                    if (obj) {
                        if (
                            endBlock.tagName == "LI" ||
                            endBlock.tagName == "TD" ||
                            domUtils.isBody(endBlock)
                        ) {
                            obj.parentNode && domUtils.remove(obj, true);
                        } else {
                            domUtils.breakParent(endBlock, obj);
                        }
                    }
                }

                var blockquotes = domUtils.getElementsByTagName(
                    this.document,
                    "blockquote"
                );
                for (var i = 0, bi; (bi = blockquotes[i++]);) {
                    if (!bi.childNodes.length) {
                        domUtils.remove(bi);
                    } else if (
                        domUtils.getPosition(bi, startBlock) &
                        domUtils.POSITION_FOLLOWING &&
                        domUtils.getPosition(bi, endBlock) & domUtils.POSITION_PRECEDING
                    ) {
                        domUtils.remove(bi, true);
                    }
                }
            } else {
                var tmpRange = range.cloneRange(),
                    node = tmpRange.startContainer.nodeType == 1
                        ? tmpRange.startContainer
                        : tmpRange.startContainer.parentNode,
                    preNode = node,
                    doEnd = 1;

                //调整开始
                while (1) {
                    if (domUtils.isBody(node)) {
                        if (preNode !== node) {
                            if (range.collapsed) {
                                tmpRange.selectNode(preNode);
                                doEnd = 0;
                            } else {
                                tmpRange.setStartBefore(preNode);
                            }
                        } else {
                            tmpRange.setStart(node, 0);
                        }

                        break;
                    }
                    if (!blockquote[node.tagName]) {
                        if (range.collapsed) {
                            tmpRange.selectNode(preNode);
                        } else {
                            tmpRange.setStartBefore(preNode);
                        }
                        break;
                    }

                    preNode = node;
                    node = node.parentNode;
                }

                //调整结束
                if (doEnd) {
                    preNode = node = node = tmpRange.endContainer.nodeType == 1
                        ? tmpRange.endContainer
                        : tmpRange.endContainer.parentNode;
                    while (1) {
                        if (domUtils.isBody(node)) {
                            if (preNode !== node) {
                                tmpRange.setEndAfter(preNode);
                            } else {
                                tmpRange.setEnd(node, node.childNodes.length);
                            }

                            break;
                        }
                        if (!blockquote[node.tagName]) {
                            tmpRange.setEndAfter(preNode);
                            break;
                        }

                        preNode = node;
                        node = node.parentNode;
                    }
                }

                node = range.document.createElement("blockquote");
                domUtils.setAttributes(node, attrs);
                node.appendChild(tmpRange.extractContents());
                tmpRange.insertNode(node);
                //去除重复的
                var childs = domUtils.getElementsByTagName(node, "blockquote");
                for (var i = 0, ci; (ci = childs[i++]);) {
                    if (ci.parentNode) {
                        domUtils.remove(ci, true);
                    }
                }
            }
            range.moveToBookmark(bookmark).select();
        },
        queryCommandState: function () {
            return getObj(this) ? 1 : 0;
        }
    };
};


// plugins/convertcase.js
/**
 * 大小写转换
 * @file
 * @since 1.2.6.1
 */

/**
 * 把选区内文本变大写,与“tolowercase”命令互斥
 * @command touppercase
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @example
 * ```javascript
 * editor.execCommand( 'touppercase' );
 * ```
 */

/**
 * 把选区内文本变小写,与“touppercase”命令互斥
 * @command tolowercase
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @example
 * ```javascript
 * editor.execCommand( 'tolowercase' );
 * ```
 */
UE.commands["touppercase"] = UE.commands["tolowercase"] = {
    execCommand: function (cmd) {
        var me = this;
        var rng = me.selection.getRange();
        if (rng.collapsed) {
            return rng;
        }
        var bk = rng.createBookmark(),
            bkEnd = bk.end,
            filterFn = function (node) {
                return !domUtils.isBr(node) && !domUtils.isWhitespace(node);
            },
            curNode = domUtils.getNextDomNode(bk.start, false, filterFn);
        while (
            curNode &&
            domUtils.getPosition(curNode, bkEnd) & domUtils.POSITION_PRECEDING
            ) {
            if (curNode.nodeType == 3) {
                curNode.nodeValue = curNode.nodeValue[
                    cmd == "touppercase" ? "toUpperCase" : "toLowerCase"
                    ]();
            }
            curNode = domUtils.getNextDomNode(curNode, true, filterFn);
            if (curNode === bkEnd) {
                break;
            }
        }
        rng.moveToBookmark(bk).select();
    }
};


// plugins/indent.js
/**
 * 首行缩进
 * @file
 * @since 1.2.6.1
 */

/**
 * 缩进
 * @command indent
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @example
 * ```javascript
 * editor.execCommand( 'indent' );
 * ```
 */
UE.commands["indent"] = {
    execCommand: function () {
        var me = this,
            value = me.queryCommandState("indent")
                ? "0em"
                : me.options.indentValue || "2em";
        me.execCommand("Paragraph", "p", {style: "text-indent:" + value});
    },
    queryCommandState: function () {
        var pN = domUtils.filterNodeList(
            this.selection.getStartElementPath(),
            "p h1 h2 h3 h4 h5 h6"
        );
        return pN && pN.style.textIndent && parseInt(pN.style.textIndent) ? 1 : 0;
    }
};


// plugins/print.js
/**
 * 打印
 * @file
 * @since 1.2.6.1
 */

/**
 * 打印
 * @command print
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @example
 * ```javascript
 * editor.execCommand( 'print' );
 * ```
 */
UE.commands["print"] = {
    execCommand: function () {
        this.window.print();
    },
    notNeedUndo: 1
};


// plugins/preview.js
/**
 * 预览
 * @file
 * @since 1.2.6.1
 */

/**
 * 预览
 * @command preview
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @example
 * ```javascript
 * editor.execCommand( 'preview' );
 * ```
 */
UE.commands["preview"] = {
    execCommand: function () {
        var w = window.open("", "_blank", ""),
            d = w.document;
        d.open();
        d.write(
            '<!DOCTYPE html><html><head><meta charset="utf-8"/><script src=' +
            this.options.UEDITOR_HOME_URL +
            '"ueditor.parse.js"></script><script>' +
            "setTimeout(function(){uParse('div',{rootPath: '" +
            this.options.UEDITOR_HOME_URL +
            "'})},300)" +
            "</script></head><body><div>" +
            this.getContent(null, null, true) +
            "</div></body></html>"
        );
        d.close();
    },
    notNeedUndo: 1
};


// plugins/selectall.js
/**
 * 全选
 * @file
 * @since 1.2.6.1
 */

/**
 * 选中所有内容
 * @command selectall
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @example
 * ```javascript
 * editor.execCommand( 'selectall' );
 * ```
 */
UE.plugins["selectall"] = function () {
    var me = this;
    me.commands["selectall"] = {
        execCommand: function () {
            //去掉了原生的selectAll,因为会出现报错和当内容为空时,不能出现闭合状态的光标
            var me = this,
                body = me.body,
                range = me.selection.getRange();
            range.selectNodeContents(body);
            if (domUtils.isEmptyBlock(body)) {
                //opera不能自动合并到元素的里边,要手动处理一下
                if (browser.opera && body.firstChild && body.firstChild.nodeType == 1) {
                    range.setStartAtFirst(body.firstChild);
                }
                range.collapse(true);
            }
            range.select(true);
        },
        notNeedUndo: 1
    };

    //快捷键
    me.addshortcutkey({
        selectAll: "ctrl+65"
    });
};


// plugins/paragraph.js
/**
 * 段落样式
 * @file
 * @since 1.2.6.1
 */

/**
 * 段落格式
 * @command paragraph
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @param {String}   style               标签值为:'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
 * @param {Object}   attrs               标签的属性
 * @example
 * ```javascript
 * editor.execCommand( 'Paragraph','h1','{
 *     class:'test'
 * }' );
 * ```
 */

/**
 * 返回选区内节点标签名
 * @command paragraph
 * @method queryCommandValue
 * @param { String } cmd 命令字符串
 * @return { String } 节点标签名
 * @example
 * ```javascript
 * editor.queryCommandValue( 'Paragraph' );
 * ```
 */

UE.plugins["paragraph"] = function () {
    var me = this,
        block = domUtils.isBlockElm,
        notExchange = ["TD", "LI", "PRE"],
        doParagraph = function (range, style, attrs, sourceCmdName) {
            var bookmark = range.createBookmark(),
                filterFn = function (node) {
                    return node.nodeType == 1
                        ? node.tagName.toLowerCase() != "br" &&
                        !domUtils.isBookmarkNode(node)
                        : !domUtils.isWhitespace(node);
                },
                para;

            range.enlarge(true);
            var bookmark2 = range.createBookmark(),
                current = domUtils.getNextDomNode(bookmark2.start, false, filterFn),
                tmpRange = range.cloneRange(),
                tmpNode;
            while (
                current &&
                !(
                    domUtils.getPosition(current, bookmark2.end) &
                    domUtils.POSITION_FOLLOWING
                )
                ) {
                if (current.nodeType === 3 || !block(current)) {
                    tmpRange.setStartBefore(current);
                    while (current && current !== bookmark2.end && !block(current)) {
                        tmpNode = current;
                        current = domUtils.getNextDomNode(current, false, null, function (
                            node
                        ) {
                            return !block(node);
                        });
                    }
                    tmpRange.setEndAfter(tmpNode);

                    para = range.document.createElement(style);
                    if (attrs) {
                        domUtils.setAttributes(para, attrs);
                        if (
                            sourceCmdName &&
                            sourceCmdName === "customstyle" &&
                            attrs.style
                        ) {
                            para.style.cssText = attrs.style;
                        }
                    }
                    para.appendChild(tmpRange.extractContents());
                    //需要内容占位
                    if (domUtils.isEmptyNode(para)) {
                        domUtils.fillChar(range.document, para);
                    }

                    tmpRange.insertNode(para);

                    var parent = para.parentNode;
                    //如果para上一级是一个block元素且不是body,td就删除它
                    if (
                        block(parent) &&
                        !domUtils.isBody(para.parentNode) &&
                        utils.indexOf(notExchange, parent.tagName) === -1
                    ) {
                        //存储dir,style
                        if (!(sourceCmdName && sourceCmdName === "customstyle")) {
                            parent.getAttribute("dir") &&
                            para.setAttribute("dir", parent.getAttribute("dir"));
                            //trace:1070
                            parent.style.cssText &&
                            (para.style.cssText =
                                parent.style.cssText + ";" + para.style.cssText);
                            //trace:1030
                            parent.style.textAlign &&
                            !para.style.textAlign &&
                            (para.style.textAlign = parent.style.textAlign);
                            parent.style.textIndent &&
                            !para.style.textIndent &&
                            (para.style.textIndent = parent.style.textIndent);
                            parent.style.padding &&
                            !para.style.padding &&
                            (para.style.padding = parent.style.padding);
                        }

                        //trace:1706 选择的就是h1-6要删除
                        if (
                            attrs &&
                            /h\d/i.test(parent.tagName) &&
                            !/h\d/i.test(para.tagName)
                        ) {
                            domUtils.setAttributes(parent, attrs);
                            if (
                                sourceCmdName &&
                                sourceCmdName === "customstyle" &&
                                attrs.style
                            ) {
                                parent.style.cssText = attrs.style;
                            }
                            domUtils.remove(para.parentNode, true);
                            para = parent;
                        } else {
                            domUtils.remove(para.parentNode, true);
                        }
                    }
                    if (utils.indexOf(notExchange, parent.tagName) !== -1) {
                        current = parent;
                    } else {
                        current = para;
                    }

                    current = domUtils.getNextDomNode(current, false, filterFn);
                } else {
                    current = domUtils.getNextDomNode(current, true, filterFn);
                }
            }
            return range.moveToBookmark(bookmark2).moveToBookmark(bookmark);
        };
    me.setOpt("paragraph", {
        p: "",
        h1: "",
        h2: "",
        h3: "",
        h4: "",
        h5: "",
        h6: ""
    });
    me.commands["paragraph"] = {
        execCommand: function (cmdName, style, attrs, sourceCmdName) {
            var range = this.selection.getRange();
            //闭合时单独处理
            if (range.collapsed) {
                var txt = this.document.createTextNode("p");
                range.insertNode(txt);
                //去掉冗余的fillchar
                if (browser.ie) {
                    var node = txt.previousSibling;
                    if (node && domUtils.isWhitespace(node)) {
                        domUtils.remove(node);
                    }
                    node = txt.nextSibling;
                    if (node && domUtils.isWhitespace(node)) {
                        domUtils.remove(node);
                    }
                }
            }
            range = doParagraph(range, style, attrs, sourceCmdName);
            if (txt) {
                range.setStartBefore(txt).collapse(true);
                pN = txt.parentNode;

                domUtils.remove(txt);

                if (domUtils.isBlockElm(pN) && domUtils.isEmptyNode(pN)) {
                    domUtils.fillNode(this.document, pN);
                }
            }

            if (
                browser.gecko &&
                range.collapsed &&
                range.startContainer.nodeType === 1
            ) {
                var child = range.startContainer.childNodes[range.startOffset];
                if (
                    child &&
                    child.nodeType === 1 &&
                    child.tagName.toLowerCase() === style
                ) {
                    range.setStart(child, 0).collapse(true);
                }
            }
            //trace:1097 原来有true,原因忘了,但去了就不能清除多余的占位符了
            range.select();

            return true;
        },
        queryCommandValue: function () {
            var node = domUtils.filterNodeList(
                this.selection.getStartElementPath(),
                "p h1 h2 h3 h4 h5 h6"
            );
            return node ? node.tagName.toLowerCase() : "";
        }
    };
};


// plugins/directionality.js
/**
 * 设置文字输入的方向的插件
 * @file
 * @since 1.2.6.1
 */
(function () {
    var block = domUtils.isBlockElm,
        getObj = function (editor) {
            //            var startNode = editor.selection.getStart(),
            //                parents;
            //            if ( startNode ) {
            //                //查找所有的是block的父亲节点
            //                parents = domUtils.findParents( startNode, true, block, true );
            //                for ( var i = 0,ci; ci = parents[i++]; ) {
            //                    if ( ci.getAttribute( 'dir' ) ) {
            //                        return ci;
            //                    }
            //                }
            //            }
            return domUtils.filterNodeList(
                editor.selection.getStartElementPath(),
                function (n) {
                    return n && n.nodeType == 1 && n.getAttribute("dir");
                }
            );
        },
        doDirectionality = function (range, editor, forward) {
            var bookmark,
                filterFn = function (node) {
                    return node.nodeType == 1
                        ? !domUtils.isBookmarkNode(node)
                        : !domUtils.isWhitespace(node);
                },
                obj = getObj(editor);

            if (obj && range.collapsed) {
                obj.setAttribute("dir", forward);
                return range;
            }
            bookmark = range.createBookmark();
            range.enlarge(true);
            var bookmark2 = range.createBookmark(),
                current = domUtils.getNextDomNode(bookmark2.start, false, filterFn),
                tmpRange = range.cloneRange(),
                tmpNode;
            while (
                current &&
                !(
                    domUtils.getPosition(current, bookmark2.end) &
                    domUtils.POSITION_FOLLOWING
                )
                ) {
                if (current.nodeType == 3 || !block(current)) {
                    tmpRange.setStartBefore(current);
                    while (current && current !== bookmark2.end && !block(current)) {
                        tmpNode = current;
                        current = domUtils.getNextDomNode(current, false, null, function (
                            node
                        ) {
                            return !block(node);
                        });
                    }
                    tmpRange.setEndAfter(tmpNode);
                    var common = tmpRange.getCommonAncestor();
                    if (!domUtils.isBody(common) && block(common)) {
                        //遍历到了block节点
                        common.setAttribute("dir", forward);
                        current = common;
                    } else {
                        //没有遍历到,添加一个block节点
                        var p = range.document.createElement("p");
                        p.setAttribute("dir", forward);
                        var frag = tmpRange.extractContents();
                        p.appendChild(frag);
                        tmpRange.insertNode(p);
                        current = p;
                    }

                    current = domUtils.getNextDomNode(current, false, filterFn);
                } else {
                    current = domUtils.getNextDomNode(current, true, filterFn);
                }
            }
            return range.moveToBookmark(bookmark2).moveToBookmark(bookmark);
        };

    /**
     * 文字输入方向
     * @command directionality
     * @method execCommand
     * @param { String } cmdName 命令字符串
     * @param { String } forward 传入'ltr'表示从左向右输入,传入'rtl'表示从右向左输入
     * @example
     * ```javascript
     * editor.execCommand( 'directionality', 'ltr');
     * ```
     */

    /**
     * 查询当前选区的文字输入方向
     * @command directionality
     * @method queryCommandValue
     * @param { String } cmdName 命令字符串
     * @return { String } 返回'ltr'表示从左向右输入,返回'rtl'表示从右向左输入
     * @example
     * ```javascript
     * editor.queryCommandValue( 'directionality');
     * ```
     */
    UE.commands["directionality"] = {
        execCommand: function (cmdName, forward) {
            var range = this.selection.getRange();
            //闭合时单独处理
            if (range.collapsed) {
                var txt = this.document.createTextNode("d");
                range.insertNode(txt);
            }
            doDirectionality(range, this, forward);
            if (txt) {
                range.setStartBefore(txt).collapse(true);
                domUtils.remove(txt);
            }

            range.select();
            return true;
        },
        queryCommandValue: function () {
            var node = getObj(this);
            return node ? node.getAttribute("dir") : "ltr";
        }
    };
})();


// plugins/horizontal.js
/**
 * 插入分割线插件
 * @file
 * @since 1.2.6.1
 */

/**
 * 插入分割线
 * @command horizontal
 * @method execCommand
 * @param { String } cmdName 命令字符串
 * @example
 * ```javascript
 * editor.execCommand( 'horizontal' );
 * ```
 */
UE.plugins["horizontal"] = function () {
    var me = this;
    me.commands["horizontal"] = {
        execCommand: function (cmdName) {
            var me = this;
            if (me.queryCommandState(cmdName) !== -1) {
                me.execCommand("insertHtml", "<hr>");
                var range = me.selection.getRange(),
                    start = range.startContainer;
                if (start.nodeType == 1 && !start.childNodes[range.startOffset]) {
                    var tmp;
                    if ((tmp = start.childNodes[range.startOffset - 1])) {
                        if (tmp.nodeType == 1 && tmp.tagName == "HR") {
                            if (me.options.enterTag == "p") {
                                tmp = me.document.createElement("p");
                                range.insertNode(tmp);
                                range.setStart(tmp, 0).setCursor();
                            } else {
                                tmp = me.document.createElement("br");
                                range.insertNode(tmp);
                                range.setStartBefore(tmp).setCursor();
                            }
                        }
                    }
                }
                return true;
            }
        },
        //边界在table里不能加分隔线
        queryCommandState: function () {
            return domUtils.filterNodeList(
                this.selection.getStartElementPath(),
                "table"
            )
                ? -1
                : 0;
        }
    };
    //    me.addListener('delkeyup',function(){
    //        var rng = this.selection.getRange();
    //        if(browser.ie && browser.version > 8){
    //            rng.txtToElmBoundary(true);
    //            if(domUtils.isStartInblock(rng)){
    //                var tmpNode = rng.startContainer;
    //                var pre = tmpNode.previousSibling;
    //                if(pre && domUtils.isTagNode(pre,'hr')){
    //                    domUtils.remove(pre);
    //                    rng.select();
    //                    return;
    //                }
    //            }
    //        }
    //        if(domUtils.isBody(rng.startContainer)){
    //            var hr = rng.startContainer.childNodes[rng.startOffset -1];
    //            if(hr && hr.nodeName == 'HR'){
    //                var next = hr.nextSibling;
    //                if(next){
    //                    rng.setStart(next,0)
    //                }else if(hr.previousSibling){
    //                    rng.setStartAtLast(hr.previousSibling)
    //                }else{
    //                    var p = this.document.createElement('p');
    //                    hr.parentNode.insertBefore(p,hr);
    //                    domUtils.fillNode(this.document,p);
    //                    rng.setStart(p,0);
    //                }
    //                domUtils.remove(hr);
    //                rng.setCursor(false,true);
    //            }
    //        }
    //    })
    me.addListener("delkeydown", function (name, evt) {
        var rng = this.selection.getRange();
        rng.txtToElmBoundary(true);
        if (domUtils.isStartInblock(rng)) {
            var tmpNode = rng.startContainer;
            var pre = tmpNode.previousSibling;
            if (pre && domUtils.isTagNode(pre, "hr")) {
                domUtils.remove(pre);
                rng.select();
                domUtils.preventDefault(evt);
                return true;
            }
        }
    });
};


// plugins/time.js
/**
 * 插入时间和日期
 * @file
 * @since 1.2.6.1
 */

/**
 * 插入时间,默认格式:12:59:59
 * @command time
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @example
 * ```javascript
 * editor.execCommand( 'time');
 * ```
 */

/**
 * 插入日期,默认格式:2013-08-30
 * @command date
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @example
 * ```javascript
 * editor.execCommand( 'date');
 * ```
 */
UE.commands["time"] = UE.commands["date"] = {
    execCommand: function (cmd, format) {
        var date = new Date();

        function formatTime(date, format) {
            var hh = ("0" + date.getHours()).slice(-2),
                ii = ("0" + date.getMinutes()).slice(-2),
                ss = ("0" + date.getSeconds()).slice(-2);
            format = format || "hh:ii:ss";
            return format.replace(/hh/gi, hh).replace(/ii/gi, ii).replace(/ss/gi, ss);
        }

        function formatDate(date, format) {
            var yyyy = ("000" + date.getFullYear()).slice(-4),
                yy = yyyy.slice(-2),
                mm = ("0" + (date.getMonth() + 1)).slice(-2),
                dd = ("0" + date.getDate()).slice(-2);
            format = format || "yyyy-mm-dd";
            return format
                .replace(/yyyy/gi, yyyy)
                .replace(/yy/gi, yy)
                .replace(/mm/gi, mm)
                .replace(/dd/gi, dd);
        }

        this.execCommand(
            "insertHtml",
            cmd == "time" ? formatTime(date, format) : formatDate(date, format)
        );
    }
};


// plugins/rowspacing.js
/**
 * 段前段后间距插件
 * @file
 * @since 1.2.6.1
 */

/**
 * 设置段间距
 * @command rowspacing
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @param { String } value 段间距的值,以px为单位
 * @param { String } dir 间距位置,top或bottom,分别表示段前和段后
 * @example
 * ```javascript
 * editor.execCommand( 'rowspacing', '10', 'top' );
 * ```
 */

UE.plugins["rowspacing"] = function () {
    var me = this;
    me.setOpt({
        rowspacingtop: ["5", "10", "15", "20", "25"],
        rowspacingbottom: ["5", "10", "15", "20", "25"]
    });
    me.commands["rowspacing"] = {
        execCommand: function (cmdName, value, dir) {
            this.execCommand("paragraph", "p", {
                style: "margin-" + dir + ":" + value + "px"
            });
            return true;
        },
        queryCommandValue: function (cmdName, dir) {
            var pN = domUtils.filterNodeList(
                this.selection.getStartElementPath(),
                function (node) {
                    return domUtils.isBlockElm(node);
                }
                ),
                value;
            //trace:1026
            if (pN) {
                value = domUtils
                    .getComputedStyle(pN, "margin-" + dir)
                    .replace(/[^\d]/g, "");
                return !value ? 0 : value;
            }
            return 0;
        }
    };
};


// plugins/lineheight.js
/**
 * 设置行内间距
 * @file
 * @since 1.2.6.1
 */
UE.plugins["lineheight"] = function () {
    var me = this;
    me.setOpt({lineheight: ["1", "1.5", "1.75", "2", "3", "4", "5"]});

    /**
     * 行距
     * @command lineheight
     * @method execCommand
     * @param { String } cmdName 命令字符串
     * @param { String } value 传入的行高值, 该值是当前字体的倍数, 例如: 1.5, 1.75
     * @example
     * ```javascript
     * editor.execCommand( 'lineheight', 1.5);
     * ```
     */
    /**
     * 查询当前选区内容的行高大小
     * @command lineheight
     * @method queryCommandValue
     * @param { String } cmd 命令字符串
     * @return { String } 返回当前行高大小
     * @example
     * ```javascript
     * editor.queryCommandValue( 'lineheight' );
     * ```
     */

    me.commands["lineheight"] = {
        execCommand: function (cmdName, value) {
            this.execCommand("paragraph", "p", {
                style: "line-height:" + (value == "1" ? "normal" : value + "em")
            });
            return true;
        },
        queryCommandValue: function () {
            var pN = domUtils.filterNodeList(
                this.selection.getStartElementPath(),
                function (node) {
                    return domUtils.isBlockElm(node);
                }
            );
            if (pN) {
                var value = domUtils.getComputedStyle(pN, "line-height");
                return value == "normal" ? 1 : value.replace(/[^\d.]*/gi, "");
            }
        }
    };
};


// plugins/insertcode.js
/**
 * 插入代码插件
 * @file
 * @since 1.2.6.1
 */

UE.plugins["insertcode"] = function () {
    var me = this;
    me.setOpt("insertcode", {
        as3: "ActionScript3",
        bash: "Bash/Shell",
        cpp: "C/C++",
        css: "Css",
        // cf: "CodeFunction",
        "c#": "C#",
        delphi: "Delphi",
        // diff: "Diff",
        erlang: "Erlang",
        groovy: "Groovy",
        html: "Html",
        java: "Java",
        // jfx: "JavaFx",
        js: "Javascript",
        pl: "Perl",
        php: "PHP",
        plain: "Text",
        ps: "PowerShell",
        python: "Python",
        ruby: "Ruby",
        scala: "Scala",
        sql: "SQL",
        vb: "VB",
        xml: "XML",
        mind: "Mind",
    });

    /**
     * 插入代码
     * @command insertcode
     * @method execCommand
     * @param { String } cmd 命令字符串
     * @param { String } lang 插入代码的语言
     * @example
     * ```javascript
     * editor.execCommand( 'insertcode', 'javascript' );
     * ```
     */

    /**
     * 如果选区所在位置是插入插入代码区域,返回代码的语言
     * @command insertcode
     * @method queryCommandValue
     * @param { String } cmd 命令字符串
     * @return { String } 返回代码的语言
     * @example
     * ```javascript
     * editor.queryCommandValue( 'insertcode' );
     * ```
     */

    me.commands["insertcode"] = {
        execCommand: function (cmd, lang) {
            var me = this,
                rng = me.selection.getRange(),
                pre = domUtils.findParentByTagName(rng.startContainer, "pre", true);
            if (pre) {
                pre.className = "brush:" + lang + ";toolbar:false;";
            } else {
                var code = "";
                if (rng.collapsed) {
                    code = browser.ie && browser.ie11below
                        ? browser.version <= 8 ? "&nbsp;" : ""
                        : "<br/>";
                } else {
                    var frag = rng.extractContents();
                    var div = me.document.createElement("div");
                    div.appendChild(frag);

                    utils.each(
                        UE.filterNode(
                            UE.htmlparser(div.innerHTML.replace(/[\r\t]/g, "")),
                            me.options.filterTxtRules
                        ).children,
                        function (node) {
                            if (browser.ie && browser.ie11below && browser.version > 8) {
                                if (node.type == "element") {
                                    if (node.tagName == "br") {
                                        code += "\n";
                                    } else if (!dtd.$empty[node.tagName]) {
                                        utils.each(node.children, function (cn) {
                                            if (cn.type == "element") {
                                                if (cn.tagName == "br") {
                                                    code += "\n";
                                                } else if (!dtd.$empty[node.tagName]) {
                                                    code += cn.innerText();
                                                }
                                            } else {
                                                code += cn.data;
                                            }
                                        });
                                        if (!/\n$/.test(code)) {
                                            code += "\n";
                                        }
                                    }
                                } else {
                                    code += node.data + "\n";
                                }
                                if (!node.nextSibling() && /\n$/.test(code)) {
                                    code = code.replace(/\n$/, "");
                                }
                            } else {
                                if (browser.ie && browser.ie11below) {
                                    if (node.type == "element") {
                                        if (node.tagName == "br") {
                                            code += "<br>";
                                        } else if (!dtd.$empty[node.tagName]) {
                                            utils.each(node.children, function (cn) {
                                                if (cn.type == "element") {
                                                    if (cn.tagName == "br") {
                                                        code += "<br>";
                                                    } else if (!dtd.$empty[node.tagName]) {
                                                        code += cn.innerText();
                                                    }
                                                } else {
                                                    code += cn.data;
                                                }
                                            });
                                            if (!/br>$/.test(code)) {
                                                code += "<br>";
                                            }
                                        }
                                    } else {
                                        code += node.data + "<br>";
                                    }
                                    if (!node.nextSibling() && /<br>$/.test(code)) {
                                        code = code.replace(/<br>$/, "");
                                    }
                                } else {
                                    code += node.type == "element"
                                        ? dtd.$empty[node.tagName] ? "" : node.innerText()
                                        : node.data;
                                    if (!/br\/?\s*>$/.test(code)) {
                                        if (!node.nextSibling()) return;
                                        code += "<br>";
                                    }
                                }
                            }
                        }
                    );
                }
                me.execCommand(
                    "inserthtml",
                    '<pre id="coder"class="brush:' +
                    lang +
                    ';toolbar:false">' +
                    code +
                    "</pre>",
                    true
                );

                pre = me.document.getElementById("coder");
                domUtils.removeAttributes(pre, "id");
                var tmpNode = pre.previousSibling;

                if (
                    tmpNode &&
                    ((tmpNode.nodeType == 3 &&
                        tmpNode.nodeValue.length == 1 &&
                        browser.ie &&
                        browser.version == 6) ||
                        domUtils.isEmptyBlock(tmpNode))
                ) {
                    domUtils.remove(tmpNode);
                }
                var rng = me.selection.getRange();
                if (domUtils.isEmptyBlock(pre)) {
                    rng.setStart(pre, 0).setCursor(false, true);
                } else {
                    rng.selectNodeContents(pre).select();
                }
            }
        },
        queryCommandValue: function () {
            var path = this.selection.getStartElementPath();
            var lang = "";
            utils.each(path, function (node) {
                if (node.nodeName == "PRE") {
                    var match = node.className.match(/brush:([^;]+)/);
                    lang = match && match[1] ? match[1] : "";
                    return false;
                }
            });
            return lang;
        }
    };

    me.addInputRule(function (root) {
        utils.each(root.getNodesByTagName("pre"), function (pre) {
            var brs = pre.getNodesByTagName("br");
            if (brs.length) {
                browser.ie &&
                browser.ie11below &&
                browser.version > 8 &&
                utils.each(brs, function (br) {
                    var txt = UE.uNode.createText("\n");
                    br.parentNode.insertBefore(txt, br);
                    br.parentNode.removeChild(br);
                });
                return;
            }
            if (browser.ie && browser.ie11below && browser.version > 8) return;
            var code = pre.innerText().split(/\n/);
            pre.innerHTML("");
            utils.each(code, function (c) {
                if (c.length) {
                    pre.appendChild(UE.uNode.createText(c));
                }
                pre.appendChild(UE.uNode.createElement("br"));
            });
        });
    });
    me.addOutputRule(function (root) {
        utils.each(root.getNodesByTagName("pre"), function (pre) {
            var code = "";
            utils.each(pre.children, function (n) {
                if (n.type == "text") {
                    //在ie下文本内容有可能末尾带有\n要去掉
                    //trace:3396
                    code += n.data.replace(/[ ]/g, "&nbsp;").replace(/\n$/, "");
                } else {
                    if (n.tagName == "br") {
                        code += "\n";
                    } else {
                        code += !dtd.$empty[n.tagName] ? "" : n.innerText();
                    }
                }
            });

            pre.innerText(code.replace(/(&nbsp;|\n)+$/, ""));
        });
    });
    //不需要判断highlight的command列表
    me.notNeedCodeQuery = {
        help: 1,
        undo: 1,
        redo: 1,
        source: 1,
        print: 1,
        searchreplace: 1,
        fullscreen: 1,
        preview: 1,
        insertparagraph: 1,
        elementpath: 1,
        insertcode: 1,
        inserthtml: 1,
        selectall: 1
    };
    //将queyCommamndState重置
    var orgQuery = me.queryCommandState;
    me.queryCommandState = function (cmd) {
        var me = this;

        if (
            !me.notNeedCodeQuery[cmd.toLowerCase()] &&
            me.selection &&
            me.queryCommandValue("insertcode")
        ) {
            return -1;
        }
        return UE.Editor.prototype.queryCommandState.apply(this, arguments);
    };
    me.addListener("beforeenterkeydown", function () {
        var rng = me.selection.getRange();
        var pre = domUtils.findParentByTagName(rng.startContainer, "pre", true);
        if (pre) {
            me.fireEvent("saveScene");
            if (!rng.collapsed) {
                rng.deleteContents();
            }
            if (!browser.ie || browser.ie9above) {
                var tmpNode = me.document.createElement("br"),
                    pre;
                rng.insertNode(tmpNode).setStartAfter(tmpNode).collapse(true);
                var next = tmpNode.nextSibling;
                if (!next && (!browser.ie || browser.version > 10)) {
                    rng.insertNode(tmpNode.cloneNode(false));
                } else {
                    rng.setStartAfter(tmpNode);
                }
                pre = tmpNode.previousSibling;
                var tmp;
                while (pre) {
                    tmp = pre;
                    pre = pre.previousSibling;
                    if (!pre || pre.nodeName == "BR") {
                        pre = tmp;
                        break;
                    }
                }
                if (pre) {
                    var str = "";
                    while (
                        pre &&
                        pre.nodeName != "BR" &&
                        new RegExp("^[\\s" + domUtils.fillChar + "]*$").test(pre.nodeValue)
                        ) {
                        str += pre.nodeValue;
                        pre = pre.nextSibling;
                    }
                    if (pre.nodeName != "BR") {
                        var match = pre.nodeValue.match(
                            new RegExp("^([\\s" + domUtils.fillChar + "]+)")
                        );
                        if (match && match[1]) {
                            str += match[1];
                        }
                    }
                    if (str) {
                        str = me.document.createTextNode(str);
                        rng.insertNode(str).setStartAfter(str);
                    }
                }
                rng.collapse(true).select(true);
            } else {
                if (browser.version > 8) {
                    var txt = me.document.createTextNode("\n");
                    var start = rng.startContainer;
                    if (rng.startOffset == 0) {
                        var preNode = start.previousSibling;
                        if (preNode) {
                            rng.insertNode(txt);
                            var fillchar = me.document.createTextNode(" ");
                            rng
                                .setStartAfter(txt)
                                .insertNode(fillchar)
                                .setStart(fillchar, 0)
                                .collapse(true)
                                .select(true);
                        }
                    } else {
                        rng.insertNode(txt).setStartAfter(txt);
                        var fillchar = me.document.createTextNode(" ");
                        start = rng.startContainer.childNodes[rng.startOffset];
                        if (start && !/^\n/.test(start.nodeValue)) {
                            rng.setStartBefore(txt);
                        }
                        rng
                            .insertNode(fillchar)
                            .setStart(fillchar, 0)
                            .collapse(true)
                            .select(true);
                    }
                } else {
                    var tmpNode = me.document.createElement("br");
                    rng.insertNode(tmpNode);
                    rng.insertNode(me.document.createTextNode(domUtils.fillChar));
                    rng.setStartAfter(tmpNode);
                    pre = tmpNode.previousSibling;
                    var tmp;
                    while (pre) {
                        tmp = pre;
                        pre = pre.previousSibling;
                        if (!pre || pre.nodeName == "BR") {
                            pre = tmp;
                            break;
                        }
                    }
                    if (pre) {
                        var str = "";
                        while (
                            pre &&
                            pre.nodeName != "BR" &&
                            new RegExp("^[ " + domUtils.fillChar + "]*$").test(pre.nodeValue)
                            ) {
                            str += pre.nodeValue;
                            pre = pre.nextSibling;
                        }
                        if (pre.nodeName != "BR") {
                            var match = pre.nodeValue.match(
                                new RegExp("^([ " + domUtils.fillChar + "]+)")
                            );
                            if (match && match[1]) {
                                str += match[1];
                            }
                        }

                        str = me.document.createTextNode(str);
                        rng.insertNode(str).setStartAfter(str);
                    }
                    rng.collapse(true).select();
                }
            }
            me.fireEvent("saveScene");
            return true;
        }
    });

    me.addListener("tabkeydown", function (cmd, evt) {
        var rng = me.selection.getRange();
        var pre = domUtils.findParentByTagName(rng.startContainer, "pre", true);
        if (pre) {
            me.fireEvent("saveScene");
            if (evt.shiftKey) {
            } else {
                if (!rng.collapsed) {
                    var bk = rng.createBookmark();
                    var start = bk.start.previousSibling;

                    while (start) {
                        if (pre.firstChild === start && !domUtils.isBr(start)) {
                            pre.insertBefore(me.document.createTextNode("    "), start);

                            break;
                        }
                        if (domUtils.isBr(start)) {
                            pre.insertBefore(
                                me.document.createTextNode("    "),
                                start.nextSibling
                            );

                            break;
                        }
                        start = start.previousSibling;
                    }
                    var end = bk.end;
                    start = bk.start.nextSibling;
                    if (pre.firstChild === bk.start) {
                        pre.insertBefore(
                            me.document.createTextNode("    "),
                            start.nextSibling
                        );
                    }
                    while (start && start !== end) {
                        if (domUtils.isBr(start) && start.nextSibling) {
                            if (start.nextSibling === end) {
                                break;
                            }
                            pre.insertBefore(
                                me.document.createTextNode("    "),
                                start.nextSibling
                            );
                        }

                        start = start.nextSibling;
                    }
                    rng.moveToBookmark(bk).select();
                } else {
                    var tmpNode = me.document.createTextNode("    ");
                    rng
                        .insertNode(tmpNode)
                        .setStartAfter(tmpNode)
                        .collapse(true)
                        .select(true);
                }
            }

            me.fireEvent("saveScene");
            return true;
        }
    });

    me.addListener("beforeinserthtml", function (evtName, html) {
        var me = this,
            rng = me.selection.getRange(),
            pre = domUtils.findParentByTagName(rng.startContainer, "pre", true);
        if (pre) {
            if (!rng.collapsed) {
                rng.deleteContents();
            }
            var htmlstr = "";
            if (browser.ie && browser.version > 8) {
                utils.each(
                    UE.filterNode(UE.htmlparser(html), me.options.filterTxtRules)
                        .children,
                    function (node) {
                        if (node.type == "element") {
                            if (node.tagName == "br") {
                                htmlstr += "\n";
                            } else if (!dtd.$empty[node.tagName]) {
                                utils.each(node.children, function (cn) {
                                    if (cn.type == "element") {
                                        if (cn.tagName == "br") {
                                            htmlstr += "\n";
                                        } else if (!dtd.$empty[node.tagName]) {
                                            htmlstr += cn.innerText();
                                        }
                                    } else {
                                        htmlstr += cn.data;
                                    }
                                });
                                if (!/\n$/.test(htmlstr)) {
                                    htmlstr += "\n";
                                }
                            }
                        } else {
                            htmlstr += node.data + "\n";
                        }
                        if (!node.nextSibling() && /\n$/.test(htmlstr)) {
                            htmlstr = htmlstr.replace(/\n$/, "");
                        }
                    }
                );
                var tmpNode = me.document.createTextNode(
                    utils.html(htmlstr.replace(/&nbsp;/g, " "))
                );
                rng.insertNode(tmpNode).selectNode(tmpNode).select();
            } else {
                var frag = me.document.createDocumentFragment();

                utils.each(
                    UE.filterNode(UE.htmlparser(html), me.options.filterTxtRules)
                        .children,
                    function (node) {
                        if (node.type == "element") {
                            if (node.tagName == "br") {
                                frag.appendChild(me.document.createElement("br"));
                            } else if (!dtd.$empty[node.tagName]) {
                                utils.each(node.children, function (cn) {
                                    if (cn.type == "element") {
                                        if (cn.tagName == "br") {
                                            frag.appendChild(me.document.createElement("br"));
                                        } else if (!dtd.$empty[node.tagName]) {
                                            frag.appendChild(
                                                me.document.createTextNode(
                                                    utils.html(cn.innerText().replace(/&nbsp;/g, " "))
                                                )
                                            );
                                        }
                                    } else {
                                        frag.appendChild(
                                            me.document.createTextNode(
                                                utils.html(cn.data.replace(/&nbsp;/g, " "))
                                            )
                                        );
                                    }
                                });
                                if (frag.lastChild.nodeName != "BR") {
                                    frag.appendChild(me.document.createElement("br"));
                                }
                            }
                        } else {
                            frag.appendChild(
                                me.document.createTextNode(
                                    utils.html(node.data.replace(/&nbsp;/g, " "))
                                )
                            );
                        }
                        if (!node.nextSibling() && frag.lastChild.nodeName == "BR") {
                            frag.removeChild(frag.lastChild);
                        }
                    }
                );
                rng.insertNode(frag).select();
            }

            return true;
        }
    });
    //方向键的处理
    me.addListener("keydown", function (cmd, evt) {
        var me = this,
            keyCode = evt.keyCode || evt.which;
        if (keyCode == 40) {
            var rng = me.selection.getRange(),
                pre,
                start = rng.startContainer;
            if (
                rng.collapsed &&
                (pre = domUtils.findParentByTagName(rng.startContainer, "pre", true)) &&
                !pre.nextSibling
            ) {
                var last = pre.lastChild;
                while (last && last.nodeName == "BR") {
                    last = last.previousSibling;
                }
                if (
                    last === start ||
                    (rng.startContainer === pre &&
                        rng.startOffset == pre.childNodes.length)
                ) {
                    me.execCommand("insertparagraph");
                    domUtils.preventDefault(evt);
                }
            }
        }
    });
    //trace:3395
    me.addListener("delkeydown", function (type, evt) {
        var rng = this.selection.getRange();
        rng.txtToElmBoundary(true);
        var start = rng.startContainer;
        if (
            domUtils.isTagNode(start, "pre") &&
            rng.collapsed &&
            domUtils.isStartInblock(rng)
        ) {
            var p = me.document.createElement("p");
            domUtils.fillNode(me.document, p);
            start.parentNode.insertBefore(p, start);
            domUtils.remove(start);
            rng.setStart(p, 0).setCursor(false, true);
            domUtils.preventDefault(evt);
            return true;
        }
    });
};


// plugins/cleardoc.js
/**
 * 清空文档插件
 * @file
 * @since 1.2.6.1
 */

/**
 * 清空文档
 * @command cleardoc
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @example
 * ```javascript
 * //editor 是编辑器实例
 * editor.execCommand('cleardoc');
 * ```
 */

UE.commands["cleardoc"] = {
    execCommand: function (cmdName) {
        var me = this,
            enterTag = me.options.enterTag,
            range = me.selection.getRange();
        if (enterTag == "br") {
            me.body.innerHTML = "<br/>";
            range.setStart(me.body, 0).setCursor();
        } else {
            me.body.innerHTML = "<p>" + (ie ? "" : "<br/>") + "</p>";
            range.setStart(me.body.firstChild, 0).setCursor(false, true);
        }
        setTimeout(function () {
            me.fireEvent("clearDoc");
        }, 0);
    }
};


// plugins/anchor.js
/**
 * 锚点插件,为UEditor提供插入锚点支持
 * @file
 * @since 1.2.6.1
 */
UE.plugin.register("anchor", function () {
    return {
        bindEvents: {
            ready: function () {
                utils.cssRule(
                    "anchor",
                    ".anchorclass{background: url('" +
                    this.options.themePath +
                    this.options.theme +
                    "/images/anchor.gif') no-repeat scroll left center transparent;cursor: auto;display: inline-block;height: 16px;width: 15px;}",
                    this.document
                );
            }
        },
        outputRule: function (root) {
            utils.each(root.getNodesByTagName("img"), function (a) {
                var val;
                if ((val = a.getAttr("anchorname"))) {
                    a.tagName = "a";
                    a.setAttr({
                        anchorname: "",
                        name: val,
                        class: ""
                    });
                }
            });
        },
        inputRule: function (root) {
            utils.each(root.getNodesByTagName("a"), function (a) {
                var val;
                if ((val = a.getAttr("name")) && !a.getAttr("href")) {
                    //过滤掉word冗余标签
                    //_Toc\d+有可能勿命中
                    if (/^\_Toc\d+$/.test(val)) {
                        a.parentNode.removeChild(a);
                        return;
                    }
                    a.tagName = "img";
                    a.setAttr({
                        anchorname: a.getAttr("name"),
                        class: "anchorclass"
                    });
                    a.setAttr("name");
                }
            });
        },
        commands: {
            /**
             * 插入锚点
             * @command anchor
             * @method execCommand
             * @param { String } cmd 命令字符串
             * @param { String } name 锚点名称字符串
             * @example
             * ```javascript
             * //editor 是编辑器实例
             * editor.execCommand('anchor', 'anchor1');
             * ```
             */
            anchor: {
                execCommand: function (cmd, name) {
                    var range = this.selection.getRange(),
                        img = range.getClosedNode();
                    if (img && img.getAttribute("anchorname")) {
                        if (name) {
                            img.setAttribute("anchorname", name);
                        } else {
                            range.setStartBefore(img).setCursor();
                            domUtils.remove(img);
                        }
                    } else {
                        if (name) {
                            //只在选区的开始插入
                            var anchor = this.document.createElement("img");
                            range.collapse(true);
                            domUtils.setAttributes(anchor, {
                                anchorname: name,
                                class: "anchorclass"
                            });
                            range
                                .insertNode(anchor)
                                .setStartAfter(anchor)
                                .setCursor(false, true);
                        }
                    }
                }
            }
        }
    };
});


// plugins/wordcount.js
///import core
///commands 字数统计
///commandsName  WordCount,wordCount
///commandsTitle  字数统计
/*
 * Created by JetBrains WebStorm.
 * User: taoqili
 * Date: 11-9-7
 * Time: 下午8:18
 * To change this template use File | Settings | File Templates.
 */

UE.plugins["wordcount"] = function () {
    var me = this;
    me.setOpt("wordCount", true);
    me.addListener("contentchange", function () {
        me.fireEvent("wordcount");
    });
    var timer;
    me.addListener("ready", function () {
        var me = this;
        domUtils.on(me.body, "keyup", function (evt) {
            var code = evt.keyCode || evt.which,
                //忽略的按键,ctr,alt,shift,方向键
                ignores = {
                    "16": 1,
                    "18": 1,
                    "20": 1,
                    "37": 1,
                    "38": 1,
                    "39": 1,
                    "40": 1
                };
            if (code in ignores) return;
            clearTimeout(timer);
            timer = setTimeout(function () {
                me.fireEvent("wordcount");
            }, 200);
        });
    });
};


// plugins/pagebreak.js
/**
 * 分页功能插件
 * @file
 * @since 1.2.6.1
 */
UE.plugins["pagebreak"] = function () {
    var me = this,
        notBreakTags = ["td"];
    me.setOpt("pageBreakTag", "_ueditor_page_break_tag_");

    function fillNode(node) {
        if (domUtils.isEmptyBlock(node)) {
            var firstChild = node.firstChild,
                tmpNode;

            while (
                firstChild &&
                firstChild.nodeType == 1 &&
                domUtils.isEmptyBlock(firstChild)
                ) {
                tmpNode = firstChild;
                firstChild = firstChild.firstChild;
            }
            !tmpNode && (tmpNode = node);
            domUtils.fillNode(me.document, tmpNode);
        }
    }

    //分页符样式添加

    me.ready(function () {
        utils.cssRule(
            "pagebreak",
            ".pagebreak{display:block;clear:both !important;cursor:default !important;width: 100% !important;margin:0;}",
            me.document
        );
    });

    function isHr(node) {
        return (
            node &&
            node.nodeType == 1 &&
            node.tagName == "HR" &&
            node.className == "pagebreak"
        );
    }

    me.addInputRule(function (root) {
        root.traversal(function (node) {
            if (node.type == "text" && node.data == me.options.pageBreakTag) {
                var hr = UE.uNode.createElement(
                    '<hr class="pagebreak" noshade="noshade" size="5" style="-webkit-user-select: none;">'
                );
                node.parentNode.insertBefore(hr, node);
                node.parentNode.removeChild(node);
            }
        });
    });
    me.addOutputRule(function (node) {
        utils.each(node.getNodesByTagName("hr"), function (n) {
            if (n.getAttr("class") == "pagebreak") {
                var txt = UE.uNode.createText(me.options.pageBreakTag);
                n.parentNode.insertBefore(txt, n);
                n.parentNode.removeChild(n);
            }
        });
    });

    /**
     * 插入分页符
     * @command pagebreak
     * @method execCommand
     * @param { String } cmd 命令字符串
     * @remind 在表格中插入分页符会把表格切分成两部分
     * @remind 获取编辑器内的数据时, 编辑器会把分页符转换成“_ueditor_page_break_tag_”字符串,
     *          以便于提交数据到服务器端后处理分页。
     * @example
     * ```javascript
     * editor.execCommand( 'pagebreak'); //插入一个hr标签,带有样式类名pagebreak
     * ```
     */

    me.commands["pagebreak"] = {
        execCommand: function () {
            var range = me.selection.getRange(),
                hr = me.document.createElement("hr");
            domUtils.setAttributes(hr, {
                class: "pagebreak",
                noshade: "noshade",
                size: "5"
            });
            domUtils.unSelectable(hr);
            //table单独处理
            var node = domUtils.findParentByTagName(
                range.startContainer,
                notBreakTags,
                true
                ),
                parents = [],
                pN;
            if (node) {
                switch (node.tagName) {
                    case "TD":
                        pN = node.parentNode;
                        if (!pN.previousSibling) {
                            var table = domUtils.findParentByTagName(pN, "table");
                            //                            var tableWrapDiv = table.parentNode;
                            //                            if(tableWrapDiv && tableWrapDiv.nodeType == 1
                            //                                && tableWrapDiv.tagName == 'DIV'
                            //                                && tableWrapDiv.getAttribute('dropdrag')
                            //                                ){
                            //                                domUtils.remove(tableWrapDiv,true);
                            //                            }
                            table.parentNode.insertBefore(hr, table);
                            parents = domUtils.findParents(hr, true);
                        } else {
                            pN.parentNode.insertBefore(hr, pN);
                            parents = domUtils.findParents(hr);
                        }
                        pN = parents[1];
                        if (hr !== pN) {
                            domUtils.breakParent(hr, pN);
                        }
                        //table要重写绑定一下拖拽
                        me.fireEvent("afteradjusttable", me.document);
                }
            } else {
                if (!range.collapsed) {
                    range.deleteContents();
                    var start = range.startContainer;
                    while (
                        !domUtils.isBody(start) &&
                        domUtils.isBlockElm(start) &&
                        domUtils.isEmptyNode(start)
                        ) {
                        range.setStartBefore(start).collapse(true);
                        domUtils.remove(start);
                        start = range.startContainer;
                    }
                }
                range.insertNode(hr);

                var pN = hr.parentNode,
                    nextNode;
                while (!domUtils.isBody(pN)) {
                    domUtils.breakParent(hr, pN);
                    nextNode = hr.nextSibling;
                    if (nextNode && domUtils.isEmptyBlock(nextNode)) {
                        domUtils.remove(nextNode);
                    }
                    pN = hr.parentNode;
                }
                nextNode = hr.nextSibling;
                var pre = hr.previousSibling;
                if (isHr(pre)) {
                    domUtils.remove(pre);
                } else {
                    pre && fillNode(pre);
                }

                if (!nextNode) {
                    var p = me.document.createElement("p");

                    hr.parentNode.appendChild(p);
                    domUtils.fillNode(me.document, p);
                    range.setStart(p, 0).collapse(true);
                } else {
                    if (isHr(nextNode)) {
                        domUtils.remove(nextNode);
                    } else {
                        fillNode(nextNode);
                    }
                    range.setEndAfter(hr).collapse(false);
                }

                range.select(true);
            }
        }
    };
};


// plugins/wordimage.js
///import core
///commands 本地图片引导上传
///commandsName  WordImage
///commandsTitle  本地图片引导上传
///commandsDialog  dialogs\wordimage

UE.plugin.register("wordimage", function () {
    var me = this,
        images = [];

    this.addListener("click", function (type, evt) {
        var el = evt.target || evt.srcElement;
        if ('IMG' == el.tagName && el.getAttribute('data-word-image')) {
            me.ui._dialogs.wordimageDialog && me.ui._dialogs.wordimageDialog.open();
        }
    });

    return {
        commands: {
            wordimage: {
                execCommand: function () {
                    var images = domUtils.getElementsByTagName(me.body, "img");
                    var urlList = [];
                    for (var i = 0, ci; (ci = images[i++]);) {
                        var url = ci.getAttribute("data-word-image");
                        url && urlList.push(url);
                    }
                    return urlList;
                },
                queryCommandState: function () {
                    images = domUtils.getElementsByTagName(me.body, "img");
                    for (var i = 0, ci; (ci = images[i++]);) {
                        if (ci.getAttribute("data-word-image")) {
                            return 1;
                        }
                    }
                    return -1;
                },
                notNeedUndo: true
            }
        },
        inputRule: function (root) {
            utils.each(root.getNodesByTagName("img"), function (img) {
                var attrs = img.attrs,
                    flag = parseInt(attrs.width) < 128 || parseInt(attrs.height) < 43,
                    opt = me.options,
                    src = opt.UEDITOR_HOME_URL + "themes/default/images/spacer.gif";
                if (attrs["src"] && /^(?:(file:\/+))/.test(attrs["src"])) {
                    img.setAttr({
                        width: attrs.width,
                        height: attrs.height,
                        alt: attrs.alt,
                        'data-word-image': attrs.src,
                        src: src,
                        style:
                            "background:url(" +
                            (flag
                                ? opt.themePath + opt.theme + "/images/word.gif"
                                : opt.langPath + opt.lang + "/images/localimage.png") +
                            ") no-repeat center center;border:1px solid #ddd"
                    });
                }
            });
        }
    };
});


// plugins/autosave.js
UE.plugin.register("autosave", function () {
    var me = this, saveKey = null;

    function save(editor) {
        var saveData;

        if (!editor.hasContents()) {
            //这里不能调用命令来删除, 会造成事件死循环
            saveKey && me.removePreferences(saveKey);
            return;
        }

        editor._autoSaveTimer = null;

        saveData = me.body.innerHTML;

        if (
            editor.fireEvent("beforeautosave", {
                content: saveData
            }) === false
        ) {
            return;
        }

        // console.log('autosave', saveKey, saveData);
        me.setPreferences(saveKey, saveData);

        editor.fireEvent("afterautosave", {
            content: saveData
        });
    }

    return {
        defaultOptions: {
            autoSaveEnable: true,
            autoSaveRestore: false,
            autoSaveKey: null,
        },
        bindEvents: {
            ready: function () {
                saveKey = me.getOpt('autoSaveKey');
                if (!saveKey) {
                    var _suffix = "_DraftsData", key = null;

                    if (me.key) {
                        key = me.key + _suffix;
                    } else {
                        key = (me.container.parentNode.id || "ue-common") + _suffix;
                    }
                    saveKey = (location.protocol + location.host + location.pathname).replace(
                        /[.:\/]/g,
                        "_"
                    ) + key;
                }
                if (me.getOpt('autoSaveRestore')) {
                    var data = me.getPreferences(saveKey);
                    // console.log('saveKey', saveKey, data);
                    if (data) {
                        me.body.innerHTML = data;
                        me.fireEvent('showmessage', {
                            type: 'info',
                            content: me.getLang('autosave').autoRestoreTip
                        })
                    }
                }
                // console.log('saveKey', saveKey);
            },
            beforesubmit: function () {
                if (!me.getOpt("autoSaveEnable") || !saveKey) {
                    return;
                }
                me.execCommand('clear_auto_save_content');
            },
            contentchange: function () {
                if (!me.isReady) {
                    return;
                }
                if (!me.getOpt("autoSaveEnable") || !saveKey) {
                    return;
                }

                if (me._autoSaveTimer) {
                    window.clearTimeout(me._autoSaveTimer);
                }

                me._autoSaveTimer = window.setTimeout(function () {
                    save(me);
                }, 1000);
            }
        },
        commands: {
            clear_auto_save_content: {
                execCommand: function (cmd, name) {
                    if (saveKey && me.getPreferences(saveKey)) {
                        me.removePreferences(saveKey);
                    }
                },
                notNeedUndo: true,
                ignoreContentChange: true
            },

            set_auto_save_content: {
                execCommand: function (cmd, name) {
                    save(me);
                },
                notNeedUndo: true,
                ignoreContentChange: true
            },

            get_auto_save_content: {
                execCommand: function (cmd, name) {
                    return me.getPreferences(saveKey) || "";
                },
                notNeedUndo: true,
                ignoreContentChange: true
            },

            auto_save_restore: {
                execCommand: function (cmd, name) {
                    if (saveKey) {
                        me.body.innerHTML =
                            me.getPreferences(saveKey) || "<p>" + domUtils.fillHtml + "</p>";
                        me.focus(true);
                    }
                },
                queryCommandState: function () {
                    return saveKey ? (me.getPreferences(saveKey) === null ? -1 : 0) : -1;
                },
                notNeedUndo: true,
                ignoreContentChange: true
            }
        }
    };
});


// plugins/formula.js
UE.plugin.register("formula", function () {
    var me = this, images = [];

    return {
        commands: {
            formula: {
                execCommand: function (cmdName, value) {
                    var range = me.selection.getRange(),
                        img = range.getClosedNode();

                    value = encodeURIComponent(value);
                    var formulaConfig = me.getOpt('formulaConfig');
                    var src = formulaConfig.imageUrlTemplate.replace(/\{\}/, value);

                    if (img) {
                        img.setAttribute("src", src);
                    } else {
                        me.execCommand("insertHtml", '<img src="' + src + '" data-formula-image="' + value + '" />');
                    }
                },
            }
        },
    };
});


// plugins/dragdrop.js
UE.plugins["dragdrop"] = function () {
    var me = this;
    me.ready(function () {
        domUtils.on(this.body, "dragend", function () {
            var rng = me.selection.getRange();
            var node = rng.getClosedNode() || me.selection.getStart();

            if (node && node.tagName == "IMG") {
                var pre = node.previousSibling,
                    next;
                while ((next = node.nextSibling)) {
                    if (
                        next.nodeType == 1 &&
                        next.tagName == "SPAN" &&
                        !next.firstChild
                    ) {
                        domUtils.remove(next);
                    } else {
                        break;
                    }
                }

                if (
                    ((pre && pre.nodeType == 1 && !domUtils.isEmptyBlock(pre)) || !pre) &&
                    (!next || (next && !domUtils.isEmptyBlock(next)))
                ) {
                    if (pre && pre.tagName == "P" && !domUtils.isEmptyBlock(pre)) {
                        pre.appendChild(node);
                        domUtils.moveChild(next, pre);
                        domUtils.remove(next);
                    } else if (
                        next &&
                        next.tagName == "P" &&
                        !domUtils.isEmptyBlock(next)
                    ) {
                        next.insertBefore(node, next.firstChild);
                    }

                    if (pre && pre.tagName == "P" && domUtils.isEmptyBlock(pre)) {
                        domUtils.remove(pre);
                    }
                    if (next && next.tagName == "P" && domUtils.isEmptyBlock(next)) {
                        domUtils.remove(next);
                    }
                    rng.selectNode(node).select();
                    me.fireEvent("saveScene");
                }
            }
        });
    });
    me.addListener("keyup", function (type, evt) {
        var keyCode = evt.keyCode || evt.which;
        if (keyCode == 13) {
            var rng = me.selection.getRange(),
                node;
            if (
                (node = domUtils.findParentByTagName(rng.startContainer, "p", true))
            ) {
                if (domUtils.getComputedStyle(node, "text-align") == "center") {
                    domUtils.removeStyle(node, "text-align");
                }
            }
        }
    });
};


// plugins/undo.js
/**
 * undo redo
 * @file
 * @since 1.2.6.1
 */

/**
 * 撤销上一次执行的命令
 * @command undo
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @example
 * ```javascript
 * editor.execCommand( 'undo' );
 * ```
 */

/**
 * 重做上一次执行的命令
 * @command redo
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @example
 * ```javascript
 * editor.execCommand( 'redo' );
 * ```
 */

UE.plugins["undo"] = function () {
    var saveSceneTimer;
    var me = this,
        maxUndoCount = me.options.maxUndoCount || 20,
        maxInputCount = me.options.maxInputCount || 20,
        fillchar = new RegExp(domUtils.fillChar + "|</hr>", "gi"); // ie会产生多余的</hr>
    var noNeedFillCharTags = {
        ol: 1,
        ul: 1,
        table: 1,
        tbody: 1,
        tr: 1,
        body: 1
    };
    var orgState = me.options.autoClearEmptyNode;

    function compareAddr(indexA, indexB) {
        if (indexA.length != indexB.length) return 0;
        for (var i = 0, l = indexA.length; i < l; i++) {
            if (indexA[i] != indexB[i]) return 0;
        }
        return 1;
    }

    function compareRangeAddress(rngAddrA, rngAddrB) {
        if (rngAddrA.collapsed != rngAddrB.collapsed) {
            return 0;
        }
        if (
            !compareAddr(rngAddrA.startAddress, rngAddrB.startAddress) ||
            !compareAddr(rngAddrA.endAddress, rngAddrB.endAddress)
        ) {
            return 0;
        }
        return 1;
    }

    function UndoManager() {
        this.list = [];
        this.index = 0;
        this.hasUndo = false;
        this.hasRedo = false;
        this.undo = function () {
            if (this.hasUndo) {
                if (!this.list[this.index - 1] && this.list.length == 1) {
                    this.reset();
                    return;
                }
                while (
                    this.list[this.index].content == this.list[this.index - 1].content
                    ) {
                    this.index--;
                    if (this.index == 0) {
                        return this.restore(0);
                    }
                }
                this.restore(--this.index);
            }
        };
        this.redo = function () {
            if (this.hasRedo) {
                while (
                    this.list[this.index].content == this.list[this.index + 1].content
                    ) {
                    this.index++;
                    if (this.index == this.list.length - 1) {
                        return this.restore(this.index);
                    }
                }
                this.restore(++this.index);
            }
        };

        this.restore = function () {
            var me = this.editor;
            var scene = this.list[this.index];
            var root = UE.htmlparser(scene.content.replace(fillchar, ""));
            me.options.autoClearEmptyNode = false;
            me.filterInputRule(root);
            me.options.autoClearEmptyNode = orgState;
            //trace:873
            //去掉展位符
            me.document.body.innerHTML = root.toHtml();
            me.fireEvent("afterscencerestore");
            //处理undo后空格不展位的问题
            if (browser.ie) {
                utils.each(
                    domUtils.getElementsByTagName(me.document, "td th caption p"),
                    function (node) {
                        if (domUtils.isEmptyNode(node)) {
                            domUtils.fillNode(me.document, node);
                        }
                    }
                );
            }

            try {
                var rng = new dom.Range(me.document).moveToAddress(scene.address);
                rng.select(
                    noNeedFillCharTags[rng.startContainer.nodeName.toLowerCase()]
                );
            } catch (e) {
            }

            this.update();
            this.clearKey();
            //不能把自己reset了
            me.fireEvent("reset", true);
        };

        this.getScene = function () {
            var me = this.editor;
            var rng = me.selection.getRange(),
                rngAddress = rng.createAddress(false, true);
            me.fireEvent("beforegetscene");
            var root = UE.htmlparser(me.body.innerHTML);
            me.options.autoClearEmptyNode = false;
            me.filterOutputRule(root);
            me.options.autoClearEmptyNode = orgState;
            var cont = root.toHtml();
            //trace:3461
            //这个会引起回退时导致空格丢失的情况
            //            browser.ie && (cont = cont.replace(/>&nbsp;</g, '><').replace(/\s*</g, '<').replace(/>\s*/g, '>'));
            me.fireEvent("aftergetscene");

            return {
                address: rngAddress,
                content: cont
            };
        };
        this.save = function (notCompareRange, notSetCursor) {

            clearTimeout(saveSceneTimer);
            var currentScene = this.getScene(notSetCursor),
                lastScene = this.list[this.index];
            if (!lastScene || (lastScene && lastScene.content != currentScene.content)) {
                // 使用异步避免直接在事件中取值滞后一个字符
                setTimeout(function () {
                    me.trigger("contentchange");
                }, 0);
            }
            //内容相同位置相同不存
            if (
                lastScene &&
                lastScene.content == currentScene.content &&
                (notCompareRange
                    ? 1
                    : compareRangeAddress(lastScene.address, currentScene.address))
            ) {
                return;
            }
            this.list = this.list.slice(0, this.index + 1);
            this.list.push(currentScene);
            //如果大于最大数量了,就把最前的剔除
            if (this.list.length > maxUndoCount) {
                this.list.shift();
            }
            this.index = this.list.length - 1;
            this.clearKey();
            //跟新undo/redo状态
            this.update();
        };
        this.update = function () {
            this.hasRedo = !!this.list[this.index + 1];
            this.hasUndo = !!this.list[this.index - 1];
        };
        this.reset = function () {
            this.list = [];
            this.index = 0;
            this.hasUndo = false;
            this.hasRedo = false;
            this.clearKey();
        };
        this.clearKey = function () {
            keycont = 0;
            lastKeyCode = null;
        };
    }

    me.undoManger = new UndoManager();
    me.undoManger.editor = me;

    function saveScene() {
        this.undoManger.save();
    }

    me.addListener("saveScene", function () {
        var args = Array.prototype.splice.call(arguments, 1);
        this.undoManger.save.apply(this.undoManger, args);
    });

    //    me.addListener('beforeexeccommand', saveScene);
    //    me.addListener('afterexeccommand', saveScene);

    me.addListener("reset", function (type, exclude) {
        if (!exclude) {
            this.undoManger.reset();
        }
    });
    me.commands["redo"] = me.commands["undo"] = {
        execCommand: function (cmdName) {
            this.undoManger[cmdName]();
        },
        queryCommandState: function (cmdName) {
            return this.undoManger[
            "has" + (cmdName.toLowerCase() == "undo" ? "Undo" : "Redo")
                ]
                ? 0
                : -1;
        },
        notNeedUndo: 1
    };

    var keys = {
            //  /*Backspace*/ 8:1, /*Delete*/ 46:1,
            /*Shift*/ 16: 1,
            /*Ctrl*/ 17: 1,
            /*Alt*/ 18: 1,
            37: 1,
            38: 1,
            39: 1,
            40: 1
        },
        keycont = 0,
        lastKeyCode;
    //输入法状态下不计算字符数
    var inputType = false;
    me.addListener("ready", function () {
        domUtils.on(this.body, "compositionstart", function () {
            inputType = true;
        });
        domUtils.on(this.body, "compositionend", function () {
            inputType = false;
        });
    });
    //快捷键
    me.addshortcutkey({
        Undo: "ctrl+90", //undo
        Redo: "ctrl+89" //redo
    });
    var isCollapsed = true;
    me.addListener("keyup", function (type, evt) {

        var me = this;
        var keyCode = evt.keyCode || evt.which;
        if (
            !keys[keyCode] &&
            !evt.ctrlKey &&
            !evt.metaKey &&
            !evt.shiftKey &&
            !evt.altKey
        ) {
            if (inputType) return;

            if (!me.selection.getRange().collapsed) {
                me.undoManger.save(false, true);
                isCollapsed = false;
                return;
            }
            if (me.undoManger.list.length === 0) {
                me.undoManger.save(true);
            }
            clearTimeout(saveSceneTimer);

            function save(cont) {
                cont.undoManger.save(false, true);
                cont.fireEvent("selectionchange");
            }

            saveSceneTimer = setTimeout(function () {
                if (inputType) {
                    var intervalTimer = setInterval(function () {
                        if (!inputType) {
                            save(me);
                            clearInterval(intervalTimer);
                        }
                    }, 300);
                    return;
                }
                save(me);
            }, 200);

            lastKeyCode = keyCode;
            keycont++;
            if (keycont >= maxInputCount) {
                save(me);
            }
        }
    });
    me.addListener("keyup", function (type, evt) {
        var keyCode = evt.keyCode || evt.which;
        if (
            !keys[keyCode] &&
            !evt.ctrlKey &&
            !evt.metaKey &&
            !evt.shiftKey &&
            !evt.altKey
        ) {
            if (inputType) return;
            if (!isCollapsed) {
                this.undoManger.save(false, true);
                isCollapsed = true;
            }
        }
    });
    //扩展实例,添加关闭和开启命令undo
    me.stopCmdUndo = function () {
        me.__hasEnterExecCommand = true;
    };
    me.startCmdUndo = function () {
        me.__hasEnterExecCommand = false;
    };
};


// plugins/copy.js
UE.plugin.register("copy", function () {
    var me = this;

    function initZeroClipboard() {
        ZeroClipboard.config({
            debug: false,
            swfPath:
                me.options.UEDITOR_HOME_URL +
                "third-party/zeroclipboard/ZeroClipboard.swf"
        });

        var client = (me.zeroclipboard = new ZeroClipboard());

        // 复制内容
        client.on("copy", function (e) {
            var client = e.client,
                rng = me.selection.getRange(),
                div = document.createElement("div");

            div.appendChild(rng.cloneContents());
            client.setText(div.innerText || div.textContent);
            client.setHtml(div.innerHTML);
            rng.select();
        });
        // hover事件传递到target
        client.on("mouseover mouseout", function (e) {
            var target = e.target;
            if (target) {
                if (e.type == "mouseover") {
                    domUtils.addClass(target, "edui-state-hover");
                } else if (e.type == "mouseout") {
                    domUtils.removeClasses(target, "edui-state-hover");
                }
            }
        });
        // flash加载不成功
        client.on("wrongflash noflash", function () {
            ZeroClipboard.destroy();
        });

        // 触发事件
        me.fireEvent("zeroclipboardready", client);
    }

    return {
        bindEvents: {
            ready: function () {
                if (!browser.ie) {
                    if (window.ZeroClipboard) {
                        initZeroClipboard();
                    } else {
                        utils.loadFile(
                            document,
                            {
                                src:
                                    me.options.UEDITOR_HOME_URL +
                                    "third-party/zeroclipboard/ZeroClipboard.js",
                                tag: "script",
                                type: "text/javascript",
                                defer: "defer"
                            },
                            function () {
                                initZeroClipboard();
                            }
                        );
                    }
                }
            }
        },
        commands: {
            copy: {
                execCommand: function (cmd) {
                    if (!me.document.execCommand("copy")) {
                        alert(me.getLang("copymsg"));
                    }
                }
            }
        }
    };
});


// plugins/paste.js
///import core
///import plugins/inserthtml.js
///import plugins/undo.js
///import plugins/serialize.js
///commands 粘贴
///commandsName  PastePlain
///commandsTitle  纯文本粘贴模式
/**
 * @description 粘贴
 * @author zhanyi
 */
UE.plugins["paste"] = function () {
    function getClipboardData(callback) {
        var doc = this.document;
        if (doc.getElementById("baidu_pastebin")) {
            return;
        }
        var range = this.selection.getRange(),
            bk = range.createBookmark(),
            //创建剪贴的容器div
            pastebin = doc.createElement("div");
        pastebin.id = "baidu_pastebin";
        // Safari 要求div必须有内容,才能粘贴内容进来
        browser.webkit &&
        pastebin.appendChild(
            doc.createTextNode(domUtils.fillChar + domUtils.fillChar)
        );
        doc.body.appendChild(pastebin);
        //trace:717 隐藏的span不能得到top
        //bk.start.innerHTML = '&nbsp;';
        bk.start.style.display = "";
        pastebin.style.cssText =
            "position:absolute;width:1px;height:1px;overflow:hidden;left:-1000px;white-space:nowrap;top:" +
            //要在现在光标平行的位置加入,否则会出现跳动的问题
            domUtils.getXY(bk.start).y +
            "px";

        range.selectNodeContents(pastebin).select(true);

        setTimeout(function () {
            if (browser.webkit) {
                for (
                    var i = 0, pastebins = doc.querySelectorAll("#baidu_pastebin"), pi;
                    (pi = pastebins[i++]);
                ) {
                    if (domUtils.isEmptyNode(pi)) {
                        domUtils.remove(pi);
                    } else {
                        pastebin = pi;
                        break;
                    }
                }
            }
            try {
                pastebin.parentNode.removeChild(pastebin);
            } catch (e) {
            }
            range.moveToBookmark(bk).select(true);
            callback(pastebin);
        }, 0);
    }

    var me = this;

    me.setOpt({
        retainOnlyLabelPasted: false
    });

    var txtContent, htmlContent, address;

    function getPureHtml(html) {
        return html.replace(/<(\/?)([\w\-]+)([^>]*)>/gi, function (
            a,
            b,
            tagName,
            attrs
        ) {
            tagName = tagName.toLowerCase();
            if ({img: 1}[tagName]) {
                return a;
            }
            attrs = attrs.replace(
                /([\w\-]*?)\s*=\s*(("([^"]*)")|('([^']*)')|([^\s>]+))/gi,
                function (str, atr, val) {
                    if (
                        {
                            src: 1,
                            href: 1,
                            name: 1
                        }[atr.toLowerCase()]
                    ) {
                        return atr + "=" + val + " ";
                    }
                    return "";
                }
            );
            if (
                {
                    span: 1,
                    div: 1
                }[tagName]
            ) {
                return "";
            } else {
                return "<" + b + tagName + " " + utils.trim(attrs) + ">";
            }
        });
    }

    function filter(div) {
        var html;
        if (div.firstChild) {
            //去掉cut中添加的边界值
            var nodes = domUtils.getElementsByTagName(div, "span");
            for (var i = 0, ni; (ni = nodes[i++]);) {
                if (ni.id == "_baidu_cut_start" || ni.id == "_baidu_cut_end") {
                    domUtils.remove(ni);
                }
            }

            if (browser.webkit) {
                var brs = div.querySelectorAll("div br");
                for (var i = 0, bi; (bi = brs[i++]);) {
                    var pN = bi.parentNode;
                    if (pN.tagName == "DIV" && pN.childNodes.length == 1) {
                        pN.innerHTML = "<p><br/></p>";
                        domUtils.remove(pN);
                    }
                }
                var divs = div.querySelectorAll("#baidu_pastebin");
                for (var i = 0, di; (di = divs[i++]);) {
                    var tmpP = me.document.createElement("p");
                    di.parentNode.insertBefore(tmpP, di);
                    while (di.firstChild) {
                        tmpP.appendChild(di.firstChild);
                    }
                    domUtils.remove(di);
                }

                var metas = div.querySelectorAll("meta");
                for (var i = 0, ci; (ci = metas[i++]);) {
                    domUtils.remove(ci);
                }

                var brs = div.querySelectorAll("br");
                for (i = 0; (ci = brs[i++]);) {
                    if (/^apple-/i.test(ci.className)) {
                        domUtils.remove(ci);
                    }
                }
            }
            if (browser.gecko) {
                var dirtyNodes = div.querySelectorAll("[_moz_dirty]");
                for (i = 0; (ci = dirtyNodes[i++]);) {
                    ci.removeAttribute("_moz_dirty");
                }
            }
            if (!browser.ie) {
                var spans = div.querySelectorAll("span.Apple-style-span");
                for (var i = 0, ci; (ci = spans[i++]);) {
                    domUtils.remove(ci, true);
                }
            }

            //ie下使用innerHTML会产生多余的\r\n字符,也会产生&nbsp;这里过滤掉
            html = div.innerHTML; //.replace(/>(?:(\s|&nbsp;)*?)</g,'><');

            //过滤word粘贴过来的冗余属性
            html = UE.filterWord(html);
            //取消了忽略空白的第二个参数,粘贴过来的有些是有空白的,会被套上相关的标签
            var root = UE.htmlparser(html);
            //如果给了过滤规则就先进行过滤
            if (me.options.filterRules) {
                UE.filterNode(root, me.options.filterRules);
            }
            //执行默认的处理
            me.filterInputRule(root);
            //针对chrome的处理
            if (browser.webkit) {
                var br = root.lastChild();
                if (br && br.type == "element" && br.tagName == "br") {
                    root.removeChild(br);
                }
                utils.each(me.body.querySelectorAll("div"), function (node) {
                    if (domUtils.isEmptyBlock(node)) {
                        domUtils.remove(node, true);
                    }
                });
            }
            html = {html: root.toHtml()};
            me.fireEvent("beforepaste", html, root);
            //抢了默认的粘贴,那后边的内容就不执行了,比如表格粘贴
            if (!html.html) {
                return;
            }
            root = UE.htmlparser(html.html, true);
            //如果开启了纯文本模式
            if (me.queryCommandState("pasteplain") === 1) {
                me.execCommand(
                    "insertHtml",
                    UE.filterNode(root, me.options.filterTxtRules).toHtml(),
                    true
                );
            } else {
                //文本模式
                UE.filterNode(root, me.options.filterTxtRules);
                txtContent = root.toHtml();
                //完全模式
                htmlContent = html.html;

                address = me.selection.getRange().createAddress(true);
                me.execCommand(
                    "insertHtml",
                    me.getOpt("retainOnlyLabelPasted") === true
                        ? getPureHtml(htmlContent)
                        : htmlContent,
                    true
                );
            }
            me.fireEvent("afterpaste", html);
        }
    }

    me.addListener("pasteTransfer", function (cmd, plainType) {
        if (address && txtContent && htmlContent && txtContent != htmlContent) {
            var range = me.selection.getRange();
            range.moveToAddress(address, true);

            if (!range.collapsed) {
                while (!domUtils.isBody(range.startContainer)) {
                    var start = range.startContainer;
                    if (start.nodeType == 1) {
                        start = start.childNodes[range.startOffset];
                        if (!start) {
                            range.setStartBefore(range.startContainer);
                            continue;
                        }
                        var pre = start.previousSibling;

                        if (
                            pre &&
                            pre.nodeType == 3 &&
                            new RegExp("^[\n\r\t " + domUtils.fillChar + "]*$").test(
                                pre.nodeValue
                            )
                        ) {
                            range.setStartBefore(pre);
                        }
                    }
                    if (range.startOffset == 0) {
                        range.setStartBefore(range.startContainer);
                    } else {
                        break;
                    }
                }
                while (!domUtils.isBody(range.endContainer)) {
                    var end = range.endContainer;
                    if (end.nodeType == 1) {
                        end = end.childNodes[range.endOffset];
                        if (!end) {
                            range.setEndAfter(range.endContainer);
                            continue;
                        }
                        var next = end.nextSibling;
                        if (
                            next &&
                            next.nodeType == 3 &&
                            new RegExp("^[\n\r\t" + domUtils.fillChar + "]*$").test(
                                next.nodeValue
                            )
                        ) {
                            range.setEndAfter(next);
                        }
                    }
                    if (
                        range.endOffset ==
                        range.endContainer[
                            range.endContainer.nodeType == 3 ? "nodeValue" : "childNodes"
                            ].length
                    ) {
                        range.setEndAfter(range.endContainer);
                    } else {
                        break;
                    }
                }
            }

            range.deleteContents();
            range.select(true);
            me.__hasEnterExecCommand = true;
            var html = htmlContent;
            if (plainType === 2) {
                html = getPureHtml(html);
            } else if (plainType) {
                html = txtContent;
            }
            me.execCommand("inserthtml", html, true);
            me.__hasEnterExecCommand = false;
            var rng = me.selection.getRange();
            while (
                !domUtils.isBody(rng.startContainer) &&
                !rng.startOffset &&
                rng.startContainer[
                    rng.startContainer.nodeType == 3 ? "nodeValue" : "childNodes"
                    ].length
                ) {
                rng.setStartBefore(rng.startContainer);
            }
            var tmpAddress = rng.createAddress(true);
            address.endAddress = tmpAddress.startAddress;
        }
    });

    me.addListener("ready", function () {
        domUtils.on(me.body, "cut", function () {
            var range = me.selection.getRange();
            if (!range.collapsed && me.undoManger) {
                me.undoManger.save();
            }
        });

        //ie下beforepaste在点击右键时也会触发,所以用监控键盘才处理
        domUtils.on(
            me.body,
            browser.ie || browser.opera ? "keydown" : "paste",
            function (e) {
                if (
                    (browser.ie || browser.opera) &&
                    ((!e.ctrlKey && !e.metaKey) || e.keyCode != "86")
                ) {
                    return;
                }
                getClipboardData.call(me, function (div) {
                    filter(div);
                });
            }
        );
    });

    me.commands["paste"] = {
        execCommand: function (cmd) {
            if (browser.ie) {
                getClipboardData.call(me, function (div) {
                    filter(div);
                });
                me.document.execCommand("paste");
            } else {
                alert(me.getLang("pastemsg"));
            }
        }
    };
};


// plugins/puretxtpaste.js
/**
 * 纯文本粘贴插件
 * @file
 * @since 1.2.6.1
 */

UE.plugins["pasteplain"] = function () {
    var me = this;
    me.setOpt({
        pasteplain: false,
        filterTxtRules: (function () {
            function transP(node) {
                node.tagName = "p";
                node.setStyle();
            }

            function removeNode(node) {
                node.parentNode.removeChild(node, true);
            }

            return {
                //直接删除及其字节点内容
                "-": "script style object iframe embed input select",
                p: {$: {}},
                br: {$: {}},
                div: function (node) {
                    var tmpNode,
                        p = UE.uNode.createElement("p");
                    while ((tmpNode = node.firstChild())) {
                        if (tmpNode.type == "text" || !UE.dom.dtd.$block[tmpNode.tagName]) {
                            p.appendChild(tmpNode);
                        } else {
                            if (p.firstChild()) {
                                node.parentNode.insertBefore(p, node);
                                p = UE.uNode.createElement("p");
                            } else {
                                node.parentNode.insertBefore(tmpNode, node);
                            }
                        }
                    }
                    if (p.firstChild()) {
                        node.parentNode.insertBefore(p, node);
                    }
                    node.parentNode.removeChild(node);
                },
                ol: removeNode,
                ul: removeNode,
                dl: removeNode,
                dt: removeNode,
                dd: removeNode,
                li: removeNode,
                caption: transP,
                th: transP,
                tr: transP,
                h1: transP,
                h2: transP,
                h3: transP,
                h4: transP,
                h5: transP,
                h6: transP,
                td: function (node) {
                    //没有内容的td直接删掉
                    var txt = !!node.innerText();
                    if (txt) {
                        node.parentNode.insertAfter(
                            UE.uNode.createText(" &nbsp; &nbsp;"),
                            node
                        );
                    }
                    node.parentNode.removeChild(node, node.innerText());
                }
            };
        })()
    });
    //暂时这里支持一下老版本的属性
    var pasteplain = me.options.pasteplain;

    /**
     * 启用或取消纯文本粘贴模式
     * @command pasteplain
     * @method execCommand
     * @param { String } cmd 命令字符串
     * @example
     * ```javascript
     * editor.queryCommandState( 'pasteplain' );
     * ```
     */

    /**
     * 查询当前是否处于纯文本粘贴模式
     * @command pasteplain
     * @method queryCommandState
     * @param { String } cmd 命令字符串
     * @return { int } 如果处于纯文本模式,返回1,否则,返回0
     * @example
     * ```javascript
     * editor.queryCommandState( 'pasteplain' );
     * ```
     */
    me.commands["pasteplain"] = {
        queryCommandState: function () {
            return pasteplain ? 1 : 0;
        },
        execCommand: function () {
            pasteplain = !pasteplain | 0;
        },
        notNeedUndo: 1
    };
};


// plugins/list.js
/**
 * 有序列表,无序列表插件
 * @file
 * @since 1.2.6.1
 */

UE.plugins["list"] = function () {
    var me = this,
        notExchange = {
            TD: 1,
            PRE: 1,
            BLOCKQUOTE: 1
        };
    // var customStyle = {
    //     cn: "cn-1-",
    //     cn1: "cn-2-",
    //     cn2: "cn-3-",
    //     num: "num-1-",
    //     num1: "num-2-",
    //     num2: "num-3-",
    //     dash: "dash",
    //     dot: "dot"
    // };

    me.setOpt({
        autoTransWordToList: false,
        insertorderedlist: {
            // num: "",
            // num1: "",
            // num2: "",
            // cn: "",
            // cn1: "",
            // cn2: "",
            decimal: "",
            "lower-alpha": "",
            "lower-roman": "",
            "upper-alpha": "",
            "upper-roman": ""
        },
        insertunorderedlist: {
            circle: "",
            disc: "",
            square: "",
            // dash: "",
            // dot: ""
        },
        listDefaultPaddingLeft: "30",
        listiconpath: "http://bs.baidu.com/listicon/",
        maxListLevel: -1, //-1不限制
        disablePInList: false
    });

    function listToArray(list) {
        var arr = [];
        for (var p in list) {
            arr.push(p);
        }
        return arr;
    }

    var listStyle = {
        OL: listToArray(me.options.insertorderedlist),
        UL: listToArray(me.options.insertunorderedlist)
    };
    var liiconpath = me.options.listiconpath;

    //根据用户配置,调整customStyle
    // for (var s in customStyle) {
    //     if (
    //         !me.options.insertorderedlist.hasOwnProperty(s) &&
    //         !me.options.insertunorderedlist.hasOwnProperty(s)
    //     ) {
    //         delete customStyle[s];
    //     }
    // }

    me.ready(function () {
        var customCss = [];
        // for (var p in customStyle) {
        //     if (p == "dash" || p == "dot") {
        //         customCss.push(
        //             "li.list-" +
        //             customStyle[p] +
        //             "{background-image:url(" +
        //             liiconpath +
        //             customStyle[p] +
        //             ".gif)}"
        //         );
        //         customCss.push(
        //             "ul.custom_" +
        //             p +
        //             "{list-style:none;}ul.custom_" +
        //             p +
        //             " li{background-position:0 3px;background-repeat:no-repeat}"
        //         );
        //     } else {
        //         for (var i = 0; i < 99; i++) {
        //             customCss.push(
        //                 "li.list-" +
        //                 customStyle[p] +
        //                 i +
        //                 "{background-image:url(" +
        //                 liiconpath +
        //                 "list-" +
        //                 customStyle[p] +
        //                 i +
        //                 ".gif)}"
        //             );
        //         }
        //         customCss.push(
        //             "ol.custom_" +
        //             p +
        //             "{list-style:none;}ol.custom_" +
        //             p +
        //             " li{background-position:0 3px;background-repeat:no-repeat}"
        //         );
        //     }
        //     switch (p) {
        //         case "cn":
        //             customCss.push("li.list-" + p + "-paddingleft-1{padding-left:25px}");
        //             customCss.push("li.list-" + p + "-paddingleft-2{padding-left:40px}");
        //             customCss.push("li.list-" + p + "-paddingleft-3{padding-left:55px}");
        //             break;
        //         case "cn1":
        //             customCss.push("li.list-" + p + "-paddingleft-1{padding-left:30px}");
        //             customCss.push("li.list-" + p + "-paddingleft-2{padding-left:40px}");
        //             customCss.push("li.list-" + p + "-paddingleft-3{padding-left:55px}");
        //             break;
        //         case "cn2":
        //             customCss.push("li.list-" + p + "-paddingleft-1{padding-left:40px}");
        //             customCss.push("li.list-" + p + "-paddingleft-2{padding-left:55px}");
        //             customCss.push("li.list-" + p + "-paddingleft-3{padding-left:68px}");
        //             break;
        //         case "num":
        //         case "num1":
        //             customCss.push("li.list-" + p + "-paddingleft-1{padding-left:25px}");
        //             break;
        //         case "num2":
        //             customCss.push("li.list-" + p + "-paddingleft-1{padding-left:35px}");
        //             customCss.push("li.list-" + p + "-paddingleft-2{padding-left:40px}");
        //             break;
        //         case "dash":
        //             customCss.push("li.list-" + p + "-paddingleft{padding-left:35px}");
        //             break;
        //         case "dot":
        //             customCss.push("li.list-" + p + "-paddingleft{padding-left:20px}");
        //     }
        // }
        customCss.push(".list-paddingleft-1{padding-left:0}");
        customCss.push(
            ".list-paddingleft-2{padding-left:" +
            me.options.listDefaultPaddingLeft +
            "px}"
        );
        customCss.push(
            ".list-paddingleft-3{padding-left:" +
            me.options.listDefaultPaddingLeft * 2 +
            "px}"
        );
        //如果不给宽度会在自定应样式里出现滚动条
        utils.cssRule(
            "list",
            "ol,ul{margin:0;pading:0;" +
            (browser.ie ? "" : "width:95%") +
            "}li{clear:both;}" +
            customCss.join("\n"),
            me.document
        );
    });
    //单独处理剪切的问题
    me.ready(function () {
        domUtils.on(me.body, "cut", function () {
            setTimeout(function () {
                var rng = me.selection.getRange(),
                    li;
                //trace:3416
                if (!rng.collapsed) {
                    if (
                        (li = domUtils.findParentByTagName(rng.startContainer, "li", true))
                    ) {
                        if (!li.nextSibling && domUtils.isEmptyBlock(li)) {
                            var pn = li.parentNode,
                                node;
                            if ((node = pn.previousSibling)) {
                                domUtils.remove(pn);
                                rng.setStartAtLast(node).collapse(true);
                                rng.select(true);
                            } else if ((node = pn.nextSibling)) {
                                domUtils.remove(pn);
                                rng.setStartAtFirst(node).collapse(true);
                                rng.select(true);
                            } else {
                                var tmpNode = me.document.createElement("p");
                                domUtils.fillNode(me.document, tmpNode);
                                pn.parentNode.insertBefore(tmpNode, pn);
                                domUtils.remove(pn);
                                rng.setStart(tmpNode, 0).collapse(true);
                                rng.select(true);
                            }
                        }
                    }
                }
            });
        });
    });

    function getStyle(node) {
        var cls = node.className;
        if (domUtils.hasClass(node, /custom_/)) {
            return cls.match(/custom_(\w+)/)[1];
        }
        return domUtils.getStyle(node, "list-style-type");
    }

    me.addListener("beforepaste", function (type, html) {
        var me = this,
            rng = me.selection.getRange(),
            li;
        var root = UE.htmlparser(html.html, true);
        if ((li = domUtils.findParentByTagName(rng.startContainer, "li", true))) {
            var list = li.parentNode,
                tagName = list.tagName === "OL" ? "ul" : "ol";
            utils.each(root.getNodesByTagName(tagName), function (n) {
                n.tagName = list.tagName;
                n.setAttr();
                if (n.parentNode === root) {
                    type = getStyle(list) || (list.tagName == "OL" ? "decimal" : "disc");
                } else {
                    var className = n.parentNode.getAttr("class");
                    if (className && /custom_/.test(className)) {
                        type = className.match(/custom_(\w+)/)[1];
                    } else {
                        type = n.parentNode.getStyle("list-style-type");
                    }
                    if (!type) {
                        type = list.tagName === "OL" ? "decimal" : "disc";
                    }
                }
                var index = utils.indexOf(listStyle[list.tagName], type);
                if (n.parentNode !== root)
                    index = index + 1 === listStyle[list.tagName].length ? 0 : index + 1;
                var currentStyle = listStyle[list.tagName][index];
                // if (customStyle[currentStyle]) {
                //     n.setAttr("class", "custom_" + currentStyle);
                // } else {
                n.setStyle("list-style-type", currentStyle);
                // }
            });
        }

        html.html = root.toHtml();
    });
    //导出时,去掉p标签
    me.getOpt("disablePInList") === true &&
    me.addOutputRule(function (root) {
        utils.each(root.getNodesByTagName("li"), function (li) {
            var newChildrens = [],
                index = 0;
            utils.each(li.children, function (n) {
                if (n.tagName == "p") {
                    var tmpNode;
                    while ((tmpNode = n.children.pop())) {
                        newChildrens.splice(index, 0, tmpNode);
                        tmpNode.parentNode = li;
                        lastNode = tmpNode;
                    }
                    tmpNode = newChildrens[newChildrens.length - 1];
                    if (
                        !tmpNode ||
                        tmpNode.type !== "element" ||
                        tmpNode.tagName !== "br"
                    ) {
                        var br = UE.uNode.createElement("br");
                        br.parentNode = li;
                        newChildrens.push(br);
                    }

                    index = newChildrens.length;
                }
            });
            if (newChildrens.length) {
                li.children = newChildrens;
            }
        });
    });
    //进入编辑器的li要套p标签
    me.addInputRule(function (root) {
        utils.each(root.getNodesByTagName("li"), function (li) {
            var tmpP = UE.uNode.createElement("p");
            for (var i = 0, ci; (ci = li.children[i]);) {
                if (ci.type === "text" || dtd.p[ci.tagName]) {
                    tmpP.appendChild(ci);
                } else {
                    if (tmpP.firstChild()) {
                        li.insertBefore(tmpP, ci);
                        tmpP = UE.uNode.createElement("p");
                        i = i + 2;
                    } else {
                        i++;
                    }
                }
            }
            if ((tmpP.firstChild() && !tmpP.parentNode) || !li.firstChild()) {
                li.appendChild(tmpP);
            }
            //trace:3357
            //p不能为空
            if (!tmpP.firstChild()) {
                tmpP.innerHTML(browser.ie ? "&nbsp;" : "<br/>");
            }
            //去掉末尾的空白
            var p = li.firstChild();
            var lastChild = p.lastChild();
            if (
                lastChild &&
                lastChild.type === "text" &&
                /^\s*$/.test(lastChild.data)
            ) {
                p.removeChild(lastChild);
            }
        });
        if (me.options.autoTransWordToList) {
            var orderlisttype = {
                    num1: /^\d+\)/,
                    decimal: /^\d+\./,
                    "lower-alpha": /^[a-z]+\)/,
                    "upper-alpha": /^[A-Z]+\./,
                    cn: /^[\u4E00\u4E8C\u4E09\u56DB\u516d\u4e94\u4e03\u516b\u4e5d]+[\u3001]/,
                    cn2: /^\([\u4E00\u4E8C\u4E09\u56DB\u516d\u4e94\u4e03\u516b\u4e5d]+\)/
                },
                unorderlisttype = {
                    square: "n"
                };

            function checkListType(content, container) {
                var span = container.firstChild();
                if (
                    span &&
                    span.type === "element" &&
                    span.tagName === "span" &&
                    /Wingdings|Symbol/.test(span.getStyle("font-family"))
                ) {
                    for (var p in unorderlisttype) {
                        if (unorderlisttype[p] == span.data) {
                            return p;
                        }
                    }
                    return "disc";
                }
                for (var p in orderlisttype) {
                    if (orderlisttype[p].test(content)) {
                        return p;
                    }
                }
            }

            utils.each(root.getNodesByTagName("p"), function (node) {
                if (node.getAttr("class") !== "MsoListParagraph") {
                    return;
                }

                //word粘贴过来的会带有margin要去掉,但这样也可能会误命中一些央视
                node.setStyle("margin", "");
                node.setStyle("margin-left", "");
                node.setAttr("class", "");

                function appendLi(list, p, type) {
                    if (list.tagName === "ol") {
                        if (browser.ie) {
                            var first = p.firstChild();
                            if (
                                first.type === "element" &&
                                first.tagName === "span" &&
                                orderlisttype[type].test(first.innerText())
                            ) {
                                p.removeChild(first);
                            }
                        } else {
                            p.innerHTML(p.innerHTML().replace(orderlisttype[type], ""));
                        }
                    } else {
                        p.removeChild(p.firstChild());
                    }

                    var li = UE.uNode.createElement("li");
                    li.appendChild(p);
                    list.appendChild(li);
                }

                var tmp = node,
                    type,
                    cacheNode = node;

                if (
                    node.parentNode.tagName !== "li" &&
                    (type = checkListType(node.innerText(), node))
                ) {
                    var list = UE.uNode.createElement(
                        me.options.insertorderedlist.hasOwnProperty(type) ? "ol" : "ul"
                    );
                    // if (customStyle[type]) {
                    //     list.setAttr("class", "custom_" + type);
                    // } else {
                    list.setStyle("list-style-type", type);
                    // }
                    while (
                        node &&
                        node.parentNode.tagName !== "li" &&
                        checkListType(node.innerText(), node)
                        ) {
                        tmp = node.nextSibling();
                        if (!tmp) {
                            node.parentNode.insertBefore(list, node);
                        }
                        appendLi(list, node, type);
                        node = tmp;
                    }
                    if (!list.parentNode && node && node.parentNode) {
                        node.parentNode.insertBefore(list, node);
                    }
                }
                var span = cacheNode.firstChild();
                if (
                    span &&
                    span.type == "element" &&
                    span.tagName == "span" &&
                    /^\s*(&nbsp;)+\s*$/.test(span.innerText())
                ) {
                    span.parentNode.removeChild(span);
                }
            });
        }
    });

    //调整索引标签
    me.addListener("contentchange", function () {
        adjustListStyle(me.document);
    });

    function adjustListStyle(doc, ignore) {
        utils.each(domUtils.getElementsByTagName(doc, "ol ul"), function (node) {
            if (!domUtils.inDoc(node, doc)) return;

            var parent = node.parentNode;
            if (parent.tagName === node.tagName) {
                var nodeStyleType =
                    getStyle(node) || (node.tagName === "OL" ? "decimal" : "disc"),
                    parentStyleType =
                        getStyle(parent) || (parent.tagName === "OL" ? "decimal" : "disc");
                if (nodeStyleType === parentStyleType) {
                    var styleIndex = utils.indexOf(
                        listStyle[node.tagName],
                        nodeStyleType
                    );
                    styleIndex = styleIndex + 1 === listStyle[node.tagName].length
                        ? 0
                        : styleIndex + 1;
                    setListStyle(node, listStyle[node.tagName][styleIndex]);
                }
            }
            var index = 0,
                type = 2;
            if (domUtils.hasClass(node, /custom_/)) {
                if (
                    !(
                        /[ou]l/i.test(parent.tagName) &&
                        domUtils.hasClass(parent, /custom_/)
                    )
                ) {
                    type = 1;
                }
            } else {
                if (
                    /[ou]l/i.test(parent.tagName) &&
                    domUtils.hasClass(parent, /custom_/)
                ) {
                    type = 3;
                }
            }

            var style = domUtils.getStyle(node, "list-style-type");
            style && (node.style.cssText = "list-style-type:" + style);
            node.className =
                utils.trim(node.className.replace(/list-paddingleft-\w+/, "")) +
                " list-paddingleft-" +
                type;
            utils.each(domUtils.getElementsByTagName(node, "li"), function (li) {
                li.style.cssText && (li.style.cssText = "");
                if (!li.firstChild) {
                    domUtils.remove(li);
                    return;
                }
                if (li.parentNode !== node) {
                    return;
                }
                index++;
                if (domUtils.hasClass(node, /custom_/)) {
                    var paddingLeft = 1,
                        currentStyle = getStyle(node);
                    if (node.tagName === "OL") {
                        if (currentStyle) {
                            switch (currentStyle) {
                                case "cn":
                                case "cn1":
                                case "cn2":
                                    if (
                                        index > 10 &&
                                        (index % 10 === 0 || (index > 10 && index < 20))
                                    ) {
                                        paddingLeft = 2;
                                    } else if (index > 20) {
                                        paddingLeft = 3;
                                    }
                                    break;
                                case "num2":
                                    if (index > 9) {
                                        paddingLeft = 2;
                                    }
                            }
                        }
                        li.className =
                            // "list-" +
                            // customStyle[currentStyle] +
                            // index +
                            // " " +
                            "list-" +
                            currentStyle +
                            "-paddingleft-" +
                            paddingLeft;
                    } else {
                        li.className =
                            // "list-" +
                            // customStyle[currentStyle] +
                            // " " +
                            "list-" +
                            currentStyle +
                            "-paddingleft";
                    }
                } else {
                    li.className = li.className.replace(/list-[\w\-]+/gi, "");
                }
                var className = li.getAttribute("class");
                if (className !== null && !className.replace(/\s/g, "")) {
                    domUtils.removeAttributes(li, "class");
                }
            });
            !ignore &&
            adjustList(
                node,
                node.tagName.toLowerCase(),
                getStyle(node) || domUtils.getStyle(node, "list-style-type"),
                true
            );
        });
    }

    function adjustList(list, tag, style, ignoreEmpty) {
        var nextList = list.nextSibling;
        if (
            nextList &&
            nextList.nodeType === 1 &&
            nextList.tagName.toLowerCase() === tag &&
            (getStyle(nextList) ||
                domUtils.getStyle(nextList, "list-style-type") ||
                (tag == "ol" ? "decimal" : "disc")) == style
        ) {
            domUtils.moveChild(nextList, list);
            if (nextList.childNodes.length === 0) {
                domUtils.remove(nextList);
            }
        }
        if (nextList && domUtils.isFillChar(nextList)) {
            domUtils.remove(nextList);
        }
        var preList = list.previousSibling;
        if (
            preList &&
            preList.nodeType === 1 &&
            preList.tagName.toLowerCase() == tag &&
            (getStyle(preList) ||
                domUtils.getStyle(preList, "list-style-type") ||
                (tag == "ol" ? "decimal" : "disc")) === style
        ) {
            domUtils.moveChild(list, preList);
        }
        if (preList && domUtils.isFillChar(preList)) {
            domUtils.remove(preList);
        }
        !ignoreEmpty && domUtils.isEmptyBlock(list) && domUtils.remove(list);
        if (getStyle(list)) {
            adjustListStyle(list.ownerDocument, true);
        }
    }

    function setListStyle(list, style) {
        // if (customStyle[style]) {
        //     list.className = "custom_" + style;
        // }
        try {
            domUtils.setStyle(list, "list-style-type", style);
        } catch (e) {
        }
    }

    function clearEmptySibling(node) {
        var tmpNode = node.previousSibling;
        if (tmpNode && domUtils.isEmptyBlock(tmpNode)) {
            domUtils.remove(tmpNode);
        }
        tmpNode = node.nextSibling;
        if (tmpNode && domUtils.isEmptyBlock(tmpNode)) {
            domUtils.remove(tmpNode);
        }
    }

    me.addListener("keydown", function (type, evt) {
        function preventAndSave() {
            evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
            me.fireEvent("contentchange");
            me.undoManger && me.undoManger.save();
        }

        function findList(node, filterFn) {
            while (node && !domUtils.isBody(node)) {
                if (filterFn(node)) {
                    return null;
                }
                if (node.nodeType === 1 && /[ou]l/i.test(node.tagName)) {
                    return node;
                }
                node = node.parentNode;
            }
            return null;
        }

        var keyCode = evt.keyCode || evt.which;
        if (keyCode === 13 && !evt.shiftKey) {
            //回车
            var rng = me.selection.getRange(),
                parent = domUtils.findParent(
                    rng.startContainer,
                    function (node) {
                        return domUtils.isBlockElm(node);
                    },
                    true
                ),
                li = domUtils.findParentByTagName(rng.startContainer, "li", true);
            if (parent && parent.tagName !== "PRE" && !li) {
                var html = parent.innerHTML.replace(
                    new RegExp(domUtils.fillChar, "g"),
                    ""
                );
                if (/^\s*1\s*\.[^\d]/.test(html)) {
                    parent.innerHTML = html.replace(/^\s*1\s*\./, "");
                    rng.setStartAtLast(parent).collapse(true).select();
                    me.__hasEnterExecCommand = true;
                    me.execCommand("insertorderedlist");
                    me.__hasEnterExecCommand = false;
                }
            }
            var range = me.selection.getRange(),
                start = findList(range.startContainer, function (node) {
                    return node.tagName === "TABLE";
                }),
                end = range.collapsed
                    ? start
                    : findList(range.endContainer, function (node) {
                        return node.tagName === "TABLE";
                    });

            if (start && end && start === end) {
                if (!range.collapsed) {
                    start = domUtils.findParentByTagName(
                        range.startContainer,
                        "li",
                        true
                    );
                    end = domUtils.findParentByTagName(range.endContainer, "li", true);
                    if (start && end && start === end) {
                        range.deleteContents();
                        li = domUtils.findParentByTagName(range.startContainer, "li", true);
                        if (li && domUtils.isEmptyBlock(li)) {
                            pre = li.previousSibling;
                            next = li.nextSibling;
                            p = me.document.createElement("p");

                            domUtils.fillNode(me.document, p);
                            parentList = li.parentNode;
                            if (pre && next) {
                                range.setStart(next, 0).collapse(true).select(true);
                                domUtils.remove(li);
                            } else {
                                if ((!pre && !next) || !pre) {
                                    parentList.parentNode.insertBefore(p, parentList);
                                } else {
                                    li.parentNode.parentNode.insertBefore(
                                        p,
                                        parentList.nextSibling
                                    );
                                }
                                domUtils.remove(li);
                                if (!parentList.firstChild) {
                                    domUtils.remove(parentList);
                                }
                                range.setStart(p, 0).setCursor();
                            }
                            preventAndSave();
                            return;
                        }
                    } else {
                        var tmpRange = range.cloneRange(),
                            bk = tmpRange.collapse(false).createBookmark();

                        range.deleteContents();
                        tmpRange.moveToBookmark(bk);
                        var li = domUtils.findParentByTagName(
                            tmpRange.startContainer,
                            "li",
                            true
                        );

                        clearEmptySibling(li);
                        tmpRange.select();
                        preventAndSave();
                        return;
                    }
                }

                li = domUtils.findParentByTagName(range.startContainer, "li", true);

                if (li) {
                    if (domUtils.isEmptyBlock(li)) {
                        bk = range.createBookmark();
                        var parentList = li.parentNode;
                        if (li !== parentList.lastChild) {
                            domUtils.breakParent(li, parentList);
                            clearEmptySibling(li);
                        } else {
                            parentList.parentNode.insertBefore(li, parentList.nextSibling);
                            if (domUtils.isEmptyNode(parentList)) {
                                domUtils.remove(parentList);
                            }
                        }
                        //嵌套不处理
                        if (!dtd.$list[li.parentNode.tagName]) {
                            if (!domUtils.isBlockElm(li.firstChild)) {
                                p = me.document.createElement("p");
                                li.parentNode.insertBefore(p, li);
                                while (li.firstChild) {
                                    p.appendChild(li.firstChild);
                                }
                                domUtils.remove(li);
                            } else {
                                domUtils.remove(li, true);
                            }
                        }
                        range.moveToBookmark(bk).select();
                    } else {
                        var first = li.firstChild;
                        if (!first || !domUtils.isBlockElm(first)) {
                            var p = me.document.createElement("p");

                            !li.firstChild && domUtils.fillNode(me.document, p);
                            while (li.firstChild) {
                                p.appendChild(li.firstChild);
                            }
                            li.appendChild(p);
                            first = p;
                        }

                        var span = me.document.createElement("span");

                        range.insertNode(span);
                        domUtils.breakParent(span, li);

                        var nextLi = span.nextSibling;
                        first = nextLi.firstChild;

                        if (!first) {
                            p = me.document.createElement("p");

                            domUtils.fillNode(me.document, p);
                            nextLi.appendChild(p);
                            first = p;
                        }
                        if (domUtils.isEmptyNode(first)) {
                            first.innerHTML = "";
                            domUtils.fillNode(me.document, first);
                        }

                        range.setStart(first, 0).collapse(true).shrinkBoundary().select();
                        domUtils.remove(span);
                        var pre = nextLi.previousSibling;
                        if (pre && domUtils.isEmptyBlock(pre)) {
                            pre.innerHTML = "<p></p>";
                            domUtils.fillNode(me.document, pre.firstChild);
                        }
                    }
                    //                        }
                    preventAndSave();
                }
            }
        }
        if (keyCode === 8) {
            //修中ie中li下的问题
            range = me.selection.getRange();
            if (range.collapsed && domUtils.isStartInblock(range)) {
                tmpRange = range.cloneRange().trimBoundary();
                li = domUtils.findParentByTagName(range.startContainer, "li", true);
                //要在li的最左边,才能处理
                if (li && domUtils.isStartInblock(tmpRange)) {
                    start = domUtils.findParentByTagName(range.startContainer, "p", true);
                    if (start && start !== li.firstChild) {
                        var parentList = domUtils.findParentByTagName(start, ["ol", "ul"]);
                        domUtils.breakParent(start, parentList);
                        clearEmptySibling(start);
                        me.fireEvent("contentchange");
                        range.setStart(start, 0).setCursor(false, true);
                        me.fireEvent("saveScene");
                        domUtils.preventDefault(evt);
                        return;
                    }

                    if (li && (pre = li.previousSibling)) {
                        if (keyCode === 46 && li.childNodes.length) {
                            return;
                        }
                        //有可能上边的兄弟节点是个2级菜单,要追加到2级菜单的最后的li
                        if (dtd.$list[pre.tagName]) {
                            pre = pre.lastChild;
                        }
                        me.undoManger && me.undoManger.save();
                        first = li.firstChild;
                        if (domUtils.isBlockElm(first)) {
                            if (domUtils.isEmptyNode(first)) {
                                //                                    range.setEnd(pre, pre.childNodes.length).shrinkBoundary().collapse().select(true);
                                pre.appendChild(first);
                                range.setStart(first, 0).setCursor(false, true);
                                //first不是唯一的节点
                                while (li.firstChild) {
                                    pre.appendChild(li.firstChild);
                                }
                            } else {
                                span = me.document.createElement("span");
                                range.insertNode(span);
                                //判断pre是否是空的节点,如果是<p><br/></p>类型的空节点,干掉p标签防止它占位
                                if (domUtils.isEmptyBlock(pre)) {
                                    pre.innerHTML = "";
                                }
                                domUtils.moveChild(li, pre);
                                range.setStartBefore(span).collapse(true).select(true);

                                domUtils.remove(span);
                            }
                        } else {
                            if (domUtils.isEmptyNode(li)) {
                                var p = me.document.createElement("p");
                                pre.appendChild(p);
                                range.setStart(p, 0).setCursor();
                                //                                    range.setEnd(pre, pre.childNodes.length).shrinkBoundary().collapse().select(true);
                            } else {
                                range
                                    .setEnd(pre, pre.childNodes.length)
                                    .collapse()
                                    .select(true);
                                while (li.firstChild) {
                                    pre.appendChild(li.firstChild);
                                }
                            }
                        }
                        domUtils.remove(li);
                        me.fireEvent("contentchange");
                        me.fireEvent("saveScene");
                        domUtils.preventDefault(evt);
                        return;
                    }
                    //trace:980

                    if (li && !li.previousSibling) {
                        var parentList = li.parentNode;
                        var bk = range.createBookmark();
                        if (domUtils.isTagNode(parentList.parentNode, "ol ul")) {
                            parentList.parentNode.insertBefore(li, parentList);
                            if (domUtils.isEmptyNode(parentList)) {
                                domUtils.remove(parentList);
                            }
                        } else {
                            while (li.firstChild) {
                                parentList.parentNode.insertBefore(li.firstChild, parentList);
                            }

                            domUtils.remove(li);
                            if (domUtils.isEmptyNode(parentList)) {
                                domUtils.remove(parentList);
                            }
                        }
                        range.moveToBookmark(bk).setCursor(false, true);
                        me.fireEvent("contentchange");
                        me.fireEvent("saveScene");
                        domUtils.preventDefault(evt);
                        return;
                    }
                }
            }
        }
    });

    me.addListener("keyup", function (type, evt) {
        var keyCode = evt.keyCode || evt.which;
        if (keyCode == 8) {
            var rng = me.selection.getRange(),
                list;
            if (
                (list = domUtils.findParentByTagName(
                    rng.startContainer,
                    ["ol", "ul"],
                    true
                ))
            ) {
                adjustList(
                    list,
                    list.tagName.toLowerCase(),
                    getStyle(list) || domUtils.getComputedStyle(list, "list-style-type"),
                    true
                );
            }
        }
    });
    //处理tab键
    me.addListener("tabkeydown", function () {
        var range = me.selection.getRange();

        //控制级数
        function checkLevel(li) {
            if (me.options.maxListLevel != -1) {
                var level = li.parentNode,
                    levelNum = 0;
                while (/[ou]l/i.test(level.tagName)) {
                    levelNum++;
                    level = level.parentNode;
                }
                if (levelNum >= me.options.maxListLevel) {
                    return true;
                }
            }
        }

        //只以开始为准
        //todo 后续改进
        var li = domUtils.findParentByTagName(range.startContainer, "li", true);
        if (li) {
            var bk;
            if (range.collapsed) {
                if (checkLevel(li)) return true;
                var parentLi = li.parentNode,
                    list = me.document.createElement(parentLi.tagName),
                    index = utils.indexOf(
                        listStyle[list.tagName],
                        getStyle(parentLi) ||
                        domUtils.getComputedStyle(parentLi, "list-style-type")
                    );
                index = index + 1 == listStyle[list.tagName].length ? 0 : index + 1;
                var currentStyle = listStyle[list.tagName][index];
                setListStyle(list, currentStyle);
                if (domUtils.isStartInblock(range)) {
                    me.fireEvent("saveScene");
                    bk = range.createBookmark();
                    parentLi.insertBefore(list, li);
                    list.appendChild(li);
                    adjustList(list, list.tagName.toLowerCase(), currentStyle);
                    me.fireEvent("contentchange");
                    range.moveToBookmark(bk).select(true);
                    return true;
                }
            } else {
                me.fireEvent("saveScene");
                bk = range.createBookmark();
                for (
                    var i = 0, closeList, parents = domUtils.findParents(li), ci;
                    (ci = parents[i++]);
                ) {
                    if (domUtils.isTagNode(ci, "ol ul")) {
                        closeList = ci;
                        break;
                    }
                }
                var current = li;
                if (bk.end) {
                    while (
                        current &&
                        !(
                            domUtils.getPosition(current, bk.end) &
                            domUtils.POSITION_FOLLOWING
                        )
                        ) {
                        if (checkLevel(current)) {
                            current = domUtils.getNextDomNode(current, false, null, function (
                                node
                            ) {
                                return node !== closeList;
                            });
                            continue;
                        }
                        var parentLi = current.parentNode,
                            list = me.document.createElement(parentLi.tagName),
                            index = utils.indexOf(
                                listStyle[list.tagName],
                                getStyle(parentLi) ||
                                domUtils.getComputedStyle(parentLi, "list-style-type")
                            );
                        var currentIndex = index + 1 == listStyle[list.tagName].length
                            ? 0
                            : index + 1;
                        var currentStyle = listStyle[list.tagName][currentIndex];
                        setListStyle(list, currentStyle);
                        parentLi.insertBefore(list, current);
                        while (
                            current &&
                            !(
                                domUtils.getPosition(current, bk.end) &
                                domUtils.POSITION_FOLLOWING
                            )
                            ) {
                            li = current.nextSibling;
                            list.appendChild(current);
                            if (!li || domUtils.isTagNode(li, "ol ul")) {
                                if (li) {
                                    while ((li = li.firstChild)) {
                                        if (li.tagName == "LI") {
                                            break;
                                        }
                                    }
                                } else {
                                    li = domUtils.getNextDomNode(current, false, null, function (
                                        node
                                    ) {
                                        return node !== closeList;
                                    });
                                }
                                break;
                            }
                            current = li;
                        }
                        adjustList(list, list.tagName.toLowerCase(), currentStyle);
                        current = li;
                    }
                }
                me.fireEvent("contentchange");
                range.moveToBookmark(bk).select();
                return true;
            }
        }
    });

    function getLi(start) {
        while (start && !domUtils.isBody(start)) {
            if (start.nodeName == "TABLE") {
                return null;
            }
            if (start.nodeName == "LI") {
                return start;
            }
            start = start.parentNode;
        }
    }

    /**
     * 有序列表,与“insertunorderedlist”命令互斥
     * @command insertorderedlist
     * @method execCommand
     * @param { String } command 命令字符串
     * @param { String } style 插入的有序列表类型,值为:decimal,lower-alpha,lower-roman,upper-alpha,upper-roman,cn,cn1,cn2,num,num1,num2
     * @example
     * ```javascript
     * editor.execCommand( 'insertorderedlist','decimal');
     * ```
     */
    /**
     * 查询当前选区内容是否有序列表
     * @command insertorderedlist
     * @method queryCommandState
     * @param { String } cmd 命令字符串
     * @return { int } 如果当前选区是有序列表返回1,否则返回0
     * @example
     * ```javascript
     * editor.queryCommandState( 'insertorderedlist' );
     * ```
     */
    /**
     * 查询当前选区内容是否有序列表
     * @command insertorderedlist
     * @method queryCommandValue
     * @param { String } cmd 命令字符串
     * @return { String } 返回当前有序列表的类型,值为null或decimal,lower-alpha,lower-roman,upper-alpha,upper-roman,cn,cn1,cn2,num,num1,num2
     * @example
     * ```javascript
     * editor.queryCommandValue( 'insertorderedlist' );
     * ```
     */

    /**
     * 无序列表,与“insertorderedlist”命令互斥
     * @command insertunorderedlist
     * @method execCommand
     * @param { String } command 命令字符串
     * @param { String } style 插入的无序列表类型,值为:circle,disc,square,dash,dot
     * @example
     * ```javascript
     * editor.execCommand( 'insertunorderedlist','circle');
     * ```
     */
    /**
     * 查询当前是否有word文档粘贴进来的图片
     * @command insertunorderedlist
     * @method insertunorderedlist
     * @param { String } command 命令字符串
     * @return { int } 如果当前选区是无序列表返回1,否则返回0
     * @example
     * ```javascript
     * editor.queryCommandState( 'insertunorderedlist' );
     * ```
     */
    /**
     * 查询当前选区内容是否有序列表
     * @command insertunorderedlist
     * @method queryCommandValue
     * @param { String } command 命令字符串
     * @return { String } 返回当前无序列表的类型,值为null或circle,disc,square,dash,dot
     * @example
     * ```javascript
     * editor.queryCommandValue( 'insertunorderedlist' );
     * ```
     */

    me.commands["insertorderedlist"] = me.commands["insertunorderedlist"] = {
        execCommand: function (command, style) {
            if (!style) {
                style = command.toLowerCase() == "insertorderedlist"
                    ? "decimal"
                    : "disc";
            }
            var me = this,
                range = this.selection.getRange(),
                filterFn = function (node) {
                    return node.nodeType == 1
                        ? node.tagName.toLowerCase() != "br"
                        : !domUtils.isWhitespace(node);
                },
                tag = command.toLowerCase() == "insertorderedlist" ? "ol" : "ul",
                frag = me.document.createDocumentFragment();
            //去掉是因为会出现选到末尾,导致adjustmentBoundary缩到ol/ul的位置
            //range.shrinkBoundary();//.adjustmentBoundary();
            range.adjustmentBoundary().shrinkBoundary();
            var bko = range.createBookmark(true),
                start = getLi(me.document.getElementById(bko.start)),
                modifyStart = 0,
                end = getLi(me.document.getElementById(bko.end)),
                modifyEnd = 0,
                startParent,
                endParent,
                list,
                tmp;

            if (start || end) {
                start && (startParent = start.parentNode);
                if (!bko.end) {
                    end = start;
                }
                end && (endParent = end.parentNode);

                if (startParent === endParent) {
                    while (start !== end) {
                        tmp = start;
                        start = start.nextSibling;
                        if (!domUtils.isBlockElm(tmp.firstChild)) {
                            var p = me.document.createElement("p");
                            while (tmp.firstChild) {
                                p.appendChild(tmp.firstChild);
                            }
                            tmp.appendChild(p);
                        }
                        frag.appendChild(tmp);
                    }
                    tmp = me.document.createElement("span");
                    startParent.insertBefore(tmp, end);
                    if (!domUtils.isBlockElm(end.firstChild)) {
                        p = me.document.createElement("p");
                        while (end.firstChild) {
                            p.appendChild(end.firstChild);
                        }
                        end.appendChild(p);
                    }
                    frag.appendChild(end);
                    domUtils.breakParent(tmp, startParent);
                    if (domUtils.isEmptyNode(tmp.previousSibling)) {
                        domUtils.remove(tmp.previousSibling);
                    }
                    if (domUtils.isEmptyNode(tmp.nextSibling)) {
                        domUtils.remove(tmp.nextSibling);
                    }
                    var nodeStyle =
                        getStyle(startParent) ||
                        domUtils.getComputedStyle(startParent, "list-style-type") ||
                        (command.toLowerCase() == "insertorderedlist" ? "decimal" : "disc");
                    if (startParent.tagName.toLowerCase() == tag && nodeStyle == style) {
                        for (
                            var i = 0, ci, tmpFrag = me.document.createDocumentFragment();
                            (ci = frag.firstChild);
                        ) {
                            if (domUtils.isTagNode(ci, "ol ul")) {
                                //                                  删除时,子列表不处理
                                //                                  utils.each(domUtils.getElementsByTagName(ci,'li'),function(li){
                                //                                        while(li.firstChild){
                                //                                            tmpFrag.appendChild(li.firstChild);
                                //                                        }
                                //
                                //                                    });
                                tmpFrag.appendChild(ci);
                            } else {
                                while (ci.firstChild) {
                                    tmpFrag.appendChild(ci.firstChild);
                                    domUtils.remove(ci);
                                }
                            }
                        }
                        tmp.parentNode.insertBefore(tmpFrag, tmp);
                    } else {
                        list = me.document.createElement(tag);
                        setListStyle(list, style);
                        list.appendChild(frag);
                        tmp.parentNode.insertBefore(list, tmp);
                    }

                    domUtils.remove(tmp);
                    list && adjustList(list, tag, style);
                    range.moveToBookmark(bko).select();
                    return;
                }
                //开始
                if (start) {
                    while (start) {
                        tmp = start.nextSibling;
                        if (domUtils.isTagNode(start, "ol ul")) {
                            frag.appendChild(start);
                        } else {
                            var tmpfrag = me.document.createDocumentFragment(),
                                hasBlock = 0;
                            while (start.firstChild) {
                                if (domUtils.isBlockElm(start.firstChild)) {
                                    hasBlock = 1;
                                }
                                tmpfrag.appendChild(start.firstChild);
                            }
                            if (!hasBlock) {
                                var tmpP = me.document.createElement("p");
                                tmpP.appendChild(tmpfrag);
                                frag.appendChild(tmpP);
                            } else {
                                frag.appendChild(tmpfrag);
                            }
                            domUtils.remove(start);
                        }

                        start = tmp;
                    }
                    startParent.parentNode.insertBefore(frag, startParent.nextSibling);
                    if (domUtils.isEmptyNode(startParent)) {
                        range.setStartBefore(startParent);
                        domUtils.remove(startParent);
                    } else {
                        range.setStartAfter(startParent);
                    }
                    modifyStart = 1;
                }

                if (end && domUtils.inDoc(endParent, me.document)) {
                    //结束
                    start = endParent.firstChild;
                    while (start && start !== end) {
                        tmp = start.nextSibling;
                        if (domUtils.isTagNode(start, "ol ul")) {
                            frag.appendChild(start);
                        } else {
                            tmpfrag = me.document.createDocumentFragment();
                            hasBlock = 0;
                            while (start.firstChild) {
                                if (domUtils.isBlockElm(start.firstChild)) {
                                    hasBlock = 1;
                                }
                                tmpfrag.appendChild(start.firstChild);
                            }
                            if (!hasBlock) {
                                tmpP = me.document.createElement("p");
                                tmpP.appendChild(tmpfrag);
                                frag.appendChild(tmpP);
                            } else {
                                frag.appendChild(tmpfrag);
                            }
                            domUtils.remove(start);
                        }
                        start = tmp;
                    }
                    var tmpDiv = domUtils.createElement(me.document, "div", {
                        tmpDiv: 1
                    });
                    domUtils.moveChild(end, tmpDiv);

                    frag.appendChild(tmpDiv);
                    domUtils.remove(end);
                    endParent.parentNode.insertBefore(frag, endParent);
                    range.setEndBefore(endParent);
                    if (domUtils.isEmptyNode(endParent)) {
                        domUtils.remove(endParent);
                    }

                    modifyEnd = 1;
                }
            }

            if (!modifyStart) {
                range.setStartBefore(me.document.getElementById(bko.start));
            }
            if (bko.end && !modifyEnd) {
                range.setEndAfter(me.document.getElementById(bko.end));
            }
            range.enlarge(true, function (node) {
                return notExchange[node.tagName];
            });

            frag = me.document.createDocumentFragment();

            var bk = range.createBookmark(),
                current = domUtils.getNextDomNode(bk.start, false, filterFn),
                tmpRange = range.cloneRange(),
                tmpNode,
                block = domUtils.isBlockElm;

            while (
                current &&
                current !== bk.end &&
                domUtils.getPosition(current, bk.end) & domUtils.POSITION_PRECEDING
                ) {
                if (current.nodeType == 3 || dtd.li[current.tagName]) {
                    if (current.nodeType == 1 && dtd.$list[current.tagName]) {
                        while (current.firstChild) {
                            frag.appendChild(current.firstChild);
                        }
                        tmpNode = domUtils.getNextDomNode(current, false, filterFn);
                        domUtils.remove(current);
                        current = tmpNode;
                        continue;
                    }
                    tmpNode = current;
                    tmpRange.setStartBefore(current);

                    while (
                        current &&
                        current !== bk.end &&
                        (!block(current) || domUtils.isBookmarkNode(current))
                        ) {
                        tmpNode = current;
                        current = domUtils.getNextDomNode(current, false, null, function (
                            node
                        ) {
                            return !notExchange[node.tagName];
                        });
                    }

                    if (current && block(current)) {
                        tmp = domUtils.getNextDomNode(tmpNode, false, filterFn);
                        if (tmp && domUtils.isBookmarkNode(tmp)) {
                            current = domUtils.getNextDomNode(tmp, false, filterFn);
                            tmpNode = tmp;
                        }
                    }
                    tmpRange.setEndAfter(tmpNode);

                    current = domUtils.getNextDomNode(tmpNode, false, filterFn);

                    var li = range.document.createElement("li");

                    li.appendChild(tmpRange.extractContents());
                    if (domUtils.isEmptyNode(li)) {
                        var tmpNode = range.document.createElement("p");
                        while (li.firstChild) {
                            tmpNode.appendChild(li.firstChild);
                        }
                        li.appendChild(tmpNode);
                    }
                    frag.appendChild(li);
                } else {
                    current = domUtils.getNextDomNode(current, true, filterFn);
                }
            }
            range.moveToBookmark(bk).collapse(true);
            list = me.document.createElement(tag);
            setListStyle(list, style);
            list.appendChild(frag);
            range.insertNode(list);
            //当前list上下看能否合并
            adjustList(list, tag, style);
            //去掉冗余的tmpDiv
            for (
                var i = 0, ci, tmpDivs = domUtils.getElementsByTagName(list, "div");
                (ci = tmpDivs[i++]);
            ) {
                if (ci.getAttribute("tmpDiv")) {
                    domUtils.remove(ci, true);
                }
            }
            range.moveToBookmark(bko).select();
        },
        queryCommandState: function (command) {
            var tag = command.toLowerCase() == "insertorderedlist" ? "ol" : "ul";
            var path = this.selection.getStartElementPath();
            for (var i = 0, ci; (ci = path[i++]);) {
                if (ci.nodeName == "TABLE") {
                    return 0;
                }
                if (tag == ci.nodeName.toLowerCase()) {
                    return 1;
                }
            }
            return 0;
        },
        queryCommandValue: function (command) {
            var tag = command.toLowerCase() == "insertorderedlist" ? "ol" : "ul";
            var path = this.selection.getStartElementPath(),
                node;
            for (var i = 0, ci; (ci = path[i++]);) {
                if (ci.nodeName == "TABLE") {
                    node = null;
                    break;
                }
                if (tag == ci.nodeName.toLowerCase()) {
                    node = ci;
                    break;
                }
            }
            return node
                ? getStyle(node) || domUtils.getComputedStyle(node, "list-style-type")
                : null;
        }
    };
};


// plugins/source.js
/**
 * 源码编辑插件
 * @file
 * @since 1.2.6.1
 */

(function () {
    var sourceEditors = {
        textarea: function (editor, holder) {
            var textarea = holder.ownerDocument.createElement("textarea");
            textarea.style.cssText =
                "position:absolute;resize:none;width:100%;height:100%;border:0;padding:0;margin:0;overflow-y:auto;";
            // todo: IE下只有onresize属性可用... 很纠结
            if (browser.ie && browser.version < 8) {
                textarea.style.width = holder.offsetWidth + "px";
                textarea.style.height = holder.offsetHeight + "px";
                holder.onresize = function () {
                    textarea.style.width = holder.offsetWidth + "px";
                    textarea.style.height = holder.offsetHeight + "px";
                };
            }
            holder.appendChild(textarea);
            return {
                setContent: function (content) {
                    textarea.value = content;
                },
                getContent: function () {
                    return textarea.value;
                },
                select: function () {
                    var range;
                    if (browser.ie) {
                        range = textarea.createTextRange();
                        range.collapse(true);
                        range.select();
                    } else {
                        //todo: chrome下无法设置焦点
                        textarea.setSelectionRange(0, 0);
                        textarea.focus();
                    }
                },
                dispose: function () {
                    holder.removeChild(textarea);
                    // todo
                    holder.onresize = null;
                    textarea = null;
                    holder = null;
                },
                focus: function () {
                    textarea.focus();
                },
                blur: function () {
                    textarea.blur();
                }
            };
        },
        codemirror: function (editor, holder) {
            var codeEditor = window.CodeMirror(holder, {
                mode: "text/html",
                tabMode: "indent",
                lineNumbers: true,
                lineWrapping: true,
                onChange: function (v) {
                    editor.sync();
                    editor.fireEvent("contentchange");
                    // console.log('CodeMirror.onChange',v.getValue());
                }
            });
            // console.log('sourceEditor',codeEditor);
            var dom = codeEditor.getWrapperElement();
            dom.style.cssText =
                'position:absolute;left:0;top:0;width:100%;height:100%;font-family:consolas,"Courier new",monospace;font-size:13px;';
            codeEditor.getScrollerElement().style.cssText =
                "position:absolute;left:0;top:0;width:100%;height:100%;";
            codeEditor.refresh();
            return {
                getCodeMirror: function () {
                    return codeEditor;
                },
                setContent: function (content) {
                    codeEditor.setValue(content);
                },
                getContent: function () {
                    return codeEditor.getValue();
                },
                select: function () {
                    codeEditor.focus();
                },
                dispose: function () {
                    holder.removeChild(dom);
                    dom = null;
                    codeEditor = null;
                },
                focus: function () {
                    codeEditor.focus();
                },
                blur: function () {
                    // codeEditor.blur();
                    // since codemirror not support blur()
                    codeEditor.setOption('readOnly', true);
                    codeEditor.setOption('readOnly', false);
                }
            };
        }
    };

    UE.plugins["source"] = function () {
        var me = this;
        var opt = this.options;
        var sourceMode = false;
        var sourceEditor;
        var orgSetContent;
        var orgFocus;
        var orgBlur;
        opt.sourceEditor = browser.ie
            ? "textarea"
            : opt.sourceEditor || "codemirror";

        me.setOpt({
            sourceEditorFirst: false
        });

        function createSourceEditor(holder) {
            return sourceEditors[
                opt.sourceEditor == "codemirror" && window.CodeMirror
                    ? "codemirror"
                    : "textarea"
                ](me, holder);
        }

        var bakCssText;
        //解决在源码模式下getContent不能得到最新的内容问题
        var oldGetContent, bakAddress;

        /**
         * 切换源码模式和编辑模式
         * @command source
         * @method execCommand
         * @param { String } cmd 命令字符串
         * @example
         * ```javascript
         * editor.execCommand( 'source');
         * ```
         */

        /**
         * 查询当前编辑区域的状态是源码模式还是可视化模式
         * @command source
         * @method queryCommandState
         * @param { String } cmd 命令字符串
         * @return { int } 如果当前是源码编辑模式,返回1,否则返回0
         * @example
         * ```javascript
         * editor.queryCommandState( 'source' );
         * ```
         */

        me.commands["source"] = {
            execCommand: function () {
                sourceMode = !sourceMode;
                if (sourceMode) {
                    bakAddress = me.selection.getRange().createAddress(false, true);
                    me.undoManger && me.undoManger.save(true);
                    if (browser.gecko) {
                        me.body.contentEditable = false;
                    }

                    bakCssText = me.iframe.style.cssText;
                    me.iframe.style.cssText +=
                        "position:absolute;left:-32768px;top:-32768px;";

                    me.fireEvent("beforegetcontent");
                    var root = UE.htmlparser(me.body.innerHTML);
                    me.filterOutputRule(root);
                    root.traversal(function (node) {
                        if (node.type == "element") {
                            switch (node.tagName) {
                                case "td":
                                case "th":
                                case "caption":
                                    if (node.children && node.children.length == 1) {
                                        if (node.firstChild().tagName == "br") {
                                            node.removeChild(node.firstChild());
                                        }
                                    }
                                    break;
                                case "pre":
                                    node.innerText(node.innerText().replace(/&nbsp;/g, " "));
                            }
                        }
                    });

                    me.fireEvent("aftergetcontent");

                    var content = root.toHtml(true);

                    sourceEditor = createSourceEditor(me.iframe.parentNode);

                    sourceEditor.setContent(content);

                    orgSetContent = me.setContent;

                    me.setContent = function (html) {
                        //这里暂时不触发事件,防止报错
                        var root = UE.htmlparser(html);
                        me.filterInputRule(root);
                        html = root.toHtml();
                        sourceEditor.setContent(html);
                    };

                    setTimeout(function () {
                        sourceEditor.select();
                        me.addListener("fullscreenchanged", function () {
                            try {
                                sourceEditor.getCodeMirror().refresh();
                            } catch (e) {
                            }
                        });
                    });

                    //重置getContent,源码模式下取值也能是最新的数据
                    oldGetContent = me.getContent;
                    me.getContent = function () {
                        return (
                            sourceEditor.getContent() ||
                            "<p>" + (browser.ie ? "" : "<br/>") + "</p>"
                        );
                    };

                    orgFocus = me.focus;
                    orgBlur = me.blur;

                    me.focus = function () {
                        sourceEditor.focus();
                    };

                    me.blur = function () {
                        orgBlur.call(me);
                        sourceEditor.blur();
                    };
                } else {
                    me.iframe.style.cssText = bakCssText;
                    var cont =
                        sourceEditor.getContent() ||
                        "<p>" + (browser.ie ? "" : "<br/>") + "</p>";
                    //处理掉block节点前后的空格,有可能会误命中,暂时不考虑
                    cont = cont.replace(
                        new RegExp("[\\r\\t\\n ]*</?(\\w+)\\s*(?:[^>]*)>", "g"),
                        function (a, b) {
                            if (b && !dtd.$inlineWithA[b.toLowerCase()]) {
                                return a.replace(/(^[\n\r\t ]*)|([\n\r\t ]*$)/g, "");
                            }
                            return a.replace(/(^[\n\r\t]*)|([\n\r\t]*$)/g, "");
                        }
                    );

                    me.setContent = orgSetContent;

                    me.setContent(cont);
                    sourceEditor.dispose();
                    sourceEditor = null;
                    //还原getContent方法
                    me.getContent = oldGetContent;

                    me.focus = orgFocus;
                    me.blur = orgBlur;

                    var first = me.body.firstChild;
                    //trace:1106 都删除空了,下边会报错,所以补充一个p占位
                    if (!first) {
                        me.body.innerHTML = "<p>" + (browser.ie ? "" : "<br/>") + "</p>";
                        first = me.body.firstChild;
                    }

                    //要在ifm为显示时ff才能取到selection,否则报错
                    //这里不能比较位置了
                    me.undoManger && me.undoManger.save(true);

                    if (browser.gecko) {
                        var input = document.createElement("input");
                        input.style.cssText = "position:absolute;left:0;top:-32768px";

                        document.body.appendChild(input);

                        me.body.contentEditable = false;
                        setTimeout(function () {
                            domUtils.setViewportOffset(input, {left: -32768, top: 0});
                            input.focus();
                            setTimeout(function () {
                                me.body.contentEditable = true;
                                me.selection.getRange().moveToAddress(bakAddress).select(true);
                                domUtils.remove(input);
                            });
                        });
                    } else {
                        //ie下有可能报错,比如在代码顶头的情况
                        try {
                            me.selection.getRange().moveToAddress(bakAddress).select(true);
                        } catch (e) {
                        }
                    }
                }
                this.fireEvent("sourcemodechanged", sourceMode);
            },
            queryCommandState: function () {
                return sourceMode | 0;
            },
            notNeedUndo: 1
        };
        var oldQueryCommandState = me.queryCommandState;

        me.queryCommandState = function (cmdName) {
            cmdName = cmdName.toLowerCase();
            if (sourceMode) {
                //源码模式下可以开启的命令
                return cmdName in
                {
                    source: 1,
                    fullscreen: 1
                }
                    ? 1
                    : -1;
            }
            return oldQueryCommandState.apply(this, arguments);
        };

        if (opt.sourceEditor == "codemirror") {
            me.addListener("ready", function () {
                utils.loadFile(
                    document,
                    {
                        src:
                            opt.codeMirrorJsUrl ||
                            opt.UEDITOR_HOME_URL + "third-party/codemirror/codemirror.js",
                        tag: "script",
                        type: "text/javascript",
                        defer: "defer"
                    },
                    function () {
                        if (opt.sourceEditorFirst) {
                            setTimeout(function () {
                                me.execCommand("source");
                            }, 0);
                        }
                    }
                );
                utils.loadFile(document, {
                    tag: "link",
                    rel: "stylesheet",
                    type: "text/css",
                    href:
                        opt.codeMirrorCssUrl ||
                        opt.UEDITOR_HOME_URL + "third-party/codemirror/codemirror.css?221123"
                });
            });
        }
    };
})();


// plugins/enterkey.js
///import core
///import plugins/undo.js
///commands 设置回车标签p或br
///commandsName  EnterKey
///commandsTitle  设置回车标签p或br
/**
 * @description 处理回车
 * @author zhanyi
 */
UE.plugins["enterkey"] = function () {
    var hTag,
        me = this,
        tag = me.options.enterTag;
    me.addListener("keyup", function (type, evt) {
        var keyCode = evt.keyCode || evt.which;
        if (keyCode == 13) {
            var range = me.selection.getRange(),
                start = range.startContainer,
                doSave;

            //修正在h1-h6里边回车后不能嵌套p的问题
            if (!browser.ie) {
                if (/h\d/i.test(hTag)) {
                    if (browser.gecko) {
                        var h = domUtils.findParentByTagName(
                            start,
                            [
                                "h1",
                                "h2",
                                "h3",
                                "h4",
                                "h5",
                                "h6",
                                "blockquote",
                                "caption",
                                "table"
                            ],
                            true
                        );
                        if (!h) {
                            me.document.execCommand("formatBlock", false, "<p>");
                            doSave = 1;
                        }
                    } else {
                        //chrome remove div
                        if (start.nodeType == 1) {
                            var tmp = me.document.createTextNode(""),
                                div;
                            range.insertNode(tmp);
                            div = domUtils.findParentByTagName(tmp, "div", true);
                            if (div) {
                                var p = me.document.createElement("p");
                                while (div.firstChild) {
                                    p.appendChild(div.firstChild);
                                }
                                div.parentNode.insertBefore(p, div);
                                domUtils.remove(div);
                                range.setStartBefore(tmp).setCursor();
                                doSave = 1;
                            }
                            domUtils.remove(tmp);
                        }
                    }

                    if (me.undoManger && doSave) {
                        me.undoManger.save();
                    }
                }
                //没有站位符,会出现多行的问题
                browser.opera && range.select();
            } else {
                me.fireEvent("saveScene", true, true);
            }
        }
    });

    me.addListener("keydown", function (type, evt) {
        var keyCode = evt.keyCode || evt.which;
        if (keyCode == 13) {
            //回车
            if (me.fireEvent("beforeenterkeydown")) {
                domUtils.preventDefault(evt);
                return;
            }
            me.fireEvent("saveScene", true, true);
            hTag = "";

            var range = me.selection.getRange();

            if (!range.collapsed) {
                //跨td不能删
                var start = range.startContainer,
                    end = range.endContainer,
                    startTd = domUtils.findParentByTagName(start, "td", true),
                    endTd = domUtils.findParentByTagName(end, "td", true);
                if (
                    (startTd && endTd && startTd !== endTd) ||
                    (!startTd && endTd) ||
                    (startTd && !endTd)
                ) {
                    evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
                    return;
                }
            }
            if (tag == "p") {
                if (!browser.ie) {
                    start = domUtils.findParentByTagName(
                        range.startContainer,
                        [
                            "ol",
                            "ul",
                            "p",
                            "h1",
                            "h2",
                            "h3",
                            "h4",
                            "h5",
                            "h6",
                            "blockquote",
                            "caption"
                        ],
                        true
                    );

                    //opera下执行formatblock会在table的场景下有问题,回车在opera原生支持很好,所以暂时在opera去掉调用这个原生的command
                    //trace:2431
                    if (!start && !browser.opera) {
                        me.document.execCommand("formatBlock", false, "<p>");

                        if (browser.gecko) {
                            range = me.selection.getRange();
                            start = domUtils.findParentByTagName(
                                range.startContainer,
                                "p",
                                true
                            );
                            start && domUtils.removeDirtyAttr(start);
                        }
                    } else {
                        hTag = start.tagName;
                        start.tagName.toLowerCase() == "p" &&
                        browser.gecko &&
                        domUtils.removeDirtyAttr(start);
                    }
                }
            } else {
                evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);

                if (!range.collapsed) {
                    range.deleteContents();
                    start = range.startContainer;
                    if (
                        start.nodeType == 1 &&
                        (start = start.childNodes[range.startOffset])
                    ) {
                        while (start.nodeType == 1) {
                            if (dtd.$empty[start.tagName]) {
                                range.setStartBefore(start).setCursor();
                                if (me.undoManger) {
                                    me.undoManger.save();
                                }
                                return false;
                            }
                            if (!start.firstChild) {
                                var br = range.document.createElement("br");
                                start.appendChild(br);
                                range.setStart(start, 0).setCursor();
                                if (me.undoManger) {
                                    me.undoManger.save();
                                }
                                return false;
                            }
                            start = start.firstChild;
                        }
                        if (start === range.startContainer.childNodes[range.startOffset]) {
                            br = range.document.createElement("br");
                            range.insertNode(br).setCursor();
                        } else {
                            range.setStart(start, 0).setCursor();
                        }
                    } else {
                        br = range.document.createElement("br");
                        range.insertNode(br).setStartAfter(br).setCursor();
                    }
                } else {
                    br = range.document.createElement("br");
                    range.insertNode(br);
                    var parent = br.parentNode;
                    if (parent.lastChild === br) {
                        br.parentNode.insertBefore(br.cloneNode(true), br);
                        range.setStartBefore(br);
                    } else {
                        range.setStartAfter(br);
                    }
                    range.setCursor();
                }
            }
        }
    });
};


// plugins/keystrokes.js
/* 处理特殊键的兼容性问题 */
UE.plugins["keystrokes"] = function () {
    var me = this;
    var collapsed = true;
    me.addListener("keydown", function (type, evt) {
        var keyCode = evt.keyCode || evt.which,
            rng = me.selection.getRange();

        //处理全选的情况
        if (
            !rng.collapsed &&
            !(evt.ctrlKey || evt.shiftKey || evt.altKey || evt.metaKey) &&
            ((keyCode >= 65 && keyCode <= 90) ||
                (keyCode >= 48 && keyCode <= 57) ||
                (keyCode >= 96 && keyCode <= 111) ||
                {
                    13: 1,
                    8: 1,
                    46: 1
                }[keyCode])
        ) {
            var tmpNode = rng.startContainer;
            if (domUtils.isFillChar(tmpNode)) {
                rng.setStartBefore(tmpNode);
            }
            tmpNode = rng.endContainer;
            if (domUtils.isFillChar(tmpNode)) {
                rng.setEndAfter(tmpNode);
            }
            rng.txtToElmBoundary();
            //结束边界可能放到了br的前边,要把br包含进来
            // x[xxx]<br/>
            if (rng.endContainer && rng.endContainer.nodeType == 1) {
                tmpNode = rng.endContainer.childNodes[rng.endOffset];
                if (tmpNode && domUtils.isBr(tmpNode)) {
                    rng.setEndAfter(tmpNode);
                }
            }
            if (rng.startOffset == 0) {
                tmpNode = rng.startContainer;
                if (domUtils.isBoundaryNode(tmpNode, "firstChild")) {
                    tmpNode = rng.endContainer;
                    if (
                        rng.endOffset ==
                        (tmpNode.nodeType == 3
                            ? tmpNode.nodeValue.length
                            : tmpNode.childNodes.length) &&
                        domUtils.isBoundaryNode(tmpNode, "lastChild")
                    ) {
                        me.fireEvent("saveScene");
                        me.body.innerHTML = "<p>" + (browser.ie ? "" : "<br/>") + "</p>";
                        rng.setStart(me.body.firstChild, 0).setCursor(false, true);
                        me._selectionChange();
                        return;
                    }
                }
            }
        }

        //处理backspace
        if (keyCode == keymap.Backspace) {
            rng = me.selection.getRange();
            collapsed = rng.collapsed;
            if (me.fireEvent("delkeydown", evt)) {
                return;
            }
            var start, end;
            //避免按两次删除才能生效的问题
            if (rng.collapsed && rng.inFillChar()) {
                start = rng.startContainer;

                if (domUtils.isFillChar(start)) {
                    rng.setStartBefore(start).shrinkBoundary(true).collapse(true);
                    domUtils.remove(start);
                } else {
                    start.nodeValue = start.nodeValue.replace(
                        new RegExp("^" + domUtils.fillChar),
                        ""
                    );
                    rng.startOffset--;
                    rng.collapse(true).select(true);
                }
            }

            //解决选中control元素不能删除的问题
            if ((start = rng.getClosedNode())) {
                me.fireEvent("saveScene");
                rng.setStartBefore(start);
                domUtils.remove(start);
                rng.setCursor();
                me.fireEvent("saveScene");
                domUtils.preventDefault(evt);
                return;
            }
            //阻止在table上的删除
            if (!browser.ie) {
                start = domUtils.findParentByTagName(rng.startContainer, "table", true);
                end = domUtils.findParentByTagName(rng.endContainer, "table", true);
                if ((start && !end) || (!start && end) || start !== end) {
                    evt.preventDefault();
                    return;
                }
            }
        }
        //处理tab键的逻辑
        if (keyCode == keymap.Tab) {
            //不处理以下标签
            var excludeTagNameForTabKey = {
                ol: 1,
                ul: 1,
                table: 1
            };
            //处理组件里的tab按下事件
            if (me.fireEvent("tabkeydown", evt)) {
                domUtils.preventDefault(evt);
                return;
            }
            var range = me.selection.getRange();
            me.fireEvent("saveScene");
            for (
                var i = 0,
                    txt = "",
                    tabSize = me.options.tabSize || 4,
                    tabNode = me.options.tabNode || "&nbsp;";
                i < tabSize;
                i++
            ) {
                txt += tabNode;
            }
            var span = me.document.createElement("span");
            span.innerHTML = txt + domUtils.fillChar;
            if (range.collapsed) {
                range.insertNode(span.cloneNode(true).firstChild).setCursor(true);
            } else {
                var filterFn = function (node) {
                    return (
                        domUtils.isBlockElm(node) &&
                        !excludeTagNameForTabKey[node.tagName.toLowerCase()]
                    );
                };
                //普通的情况
                start = domUtils.findParent(range.startContainer, filterFn, true);
                end = domUtils.findParent(range.endContainer, filterFn, true);
                if (start && end && start === end) {
                    range.deleteContents();
                    range.insertNode(span.cloneNode(true).firstChild).setCursor(true);
                } else {
                    var bookmark = range.createBookmark();
                    range.enlarge(true);
                    var bookmark2 = range.createBookmark(),
                        current = domUtils.getNextDomNode(bookmark2.start, false, filterFn);
                    while (
                        current &&
                        !(
                            domUtils.getPosition(current, bookmark2.end) &
                            domUtils.POSITION_FOLLOWING
                        )
                        ) {
                        current.insertBefore(
                            span.cloneNode(true).firstChild,
                            current.firstChild
                        );
                        current = domUtils.getNextDomNode(current, false, filterFn);
                    }
                    range.moveToBookmark(bookmark2).moveToBookmark(bookmark).select();
                }
            }
            domUtils.preventDefault(evt);
        }
        //trace:1634
        //ff的del键在容器空的时候,也会删除
        if (browser.gecko && keyCode == 46) {
            range = me.selection.getRange();
            if (range.collapsed) {
                start = range.startContainer;
                if (domUtils.isEmptyBlock(start)) {
                    var parent = start.parentNode;
                    while (
                        domUtils.getChildCount(parent) == 1 &&
                        !domUtils.isBody(parent)
                        ) {
                        start = parent;
                        parent = parent.parentNode;
                    }
                    if (start === parent.lastChild) evt.preventDefault();
                    return;
                }
            }
        }

        /* 修复在编辑区域快捷键 (Mac:meta+alt+I; Win:ctrl+shift+I) 打不开 chrome 控制台的问题 */
        browser.chrome &&
        me.on("keydown", function (type, e) {
            var keyCode = e.keyCode || e.which;
            if (
                ((e.metaKey && e.altKey) || (e.ctrlKey && e.shiftKey)) &&
                keyCode == 73
            ) {
                return true;
            }
        });
    });
    me.addListener("keyup", function (type, evt) {
        var keyCode = evt.keyCode || evt.which,
            rng,
            me = this;
        if (keyCode == keymap.Backspace) {
            if (me.fireEvent("delkeyup")) {
                return;
            }
            rng = me.selection.getRange();
            if (rng.collapsed) {
                var tmpNode,
                    autoClearTagName = ["h1", "h2", "h3", "h4", "h5", "h6"];
                if (
                    (tmpNode = domUtils.findParentByTagName(
                        rng.startContainer,
                        autoClearTagName,
                        true
                    ))
                ) {
                    if (domUtils.isEmptyBlock(tmpNode)) {
                        var pre = tmpNode.previousSibling;
                        if (pre && pre.nodeName != "TABLE") {
                            domUtils.remove(tmpNode);
                            rng.setStartAtLast(pre).setCursor(false, true);
                            return;
                        } else {
                            var next = tmpNode.nextSibling;
                            if (next && next.nodeName != "TABLE") {
                                domUtils.remove(tmpNode);
                                rng.setStartAtFirst(next).setCursor(false, true);
                                return;
                            }
                        }
                    }
                }
                //处理当删除到body时,要重新给p标签展位
                if (domUtils.isBody(rng.startContainer)) {
                    var tmpNode = domUtils.createElement(me.document, "p", {
                        innerHTML: browser.ie ? domUtils.fillChar : "<br/>"
                    });
                    rng.insertNode(tmpNode).setStart(tmpNode, 0).setCursor(false, true);
                }
            }

            //chrome下如果删除了inline标签,浏览器会有记忆,在输入文字还是会套上刚才删除的标签,所以这里再选一次就不会了
            if (
                !collapsed &&
                (rng.startContainer.nodeType == 3 ||
                    (rng.startContainer.nodeType == 1 &&
                        domUtils.isEmptyBlock(rng.startContainer)))
            ) {
                if (browser.ie) {
                    var span = rng.document.createElement("span");
                    rng.insertNode(span).setStartBefore(span).collapse(true);
                    rng.select();
                    domUtils.remove(span);
                } else {
                    rng.select();
                }
            }
        }
    });
};


// plugins/fiximgclick.js
///import core
///commands 修复chrome下图片不能点击的问题,出现八个角可改变大小
///commandsName  FixImgClick
///commandsTitle  修复chrome下图片不能点击的问题,出现八个角可改变大小
//修复chrome下图片不能点击的问题,出现八个角可改变大小

UE.plugins["fiximgclick"] = (function () {
    var elementUpdated = false;

    function Scale() {
        this.editor = null;
        this.resizer = null;
        this.cover = null;
        this.doc = document;
        this.prePos = {x: 0, y: 0};
        this.startPos = {x: 0, y: 0};
    }

    (function () {
        var rect = [
            //[left, top, width, height]
            [0, 0, -1, -1],
            [0, 0, 0, -1],
            [0, 0, 1, -1],
            [0, 0, -1, 0],
            [0, 0, 1, 0],
            [0, 0, -1, 1],
            [0, 0, 0, 1],
            [0, 0, 1, 1]
        ];

        Scale.prototype = {
            init: function (editor) {
                var me = this;
                me.editor = editor;
                me.startPos = this.prePos = {x: 0, y: 0};
                me.dragId = -1;

                var hands = [],
                    cover = (me.cover = document.createElement("div")),
                    resizer = (me.resizer = document.createElement("div"));

                cover.id = me.editor.ui.id + "_imagescale_cover";
                cover.style.cssText =
                    "position:absolute;display:none;z-index:" +
                    me.editor.options.zIndex +
                    ";filter:alpha(opacity=0); opacity:0;background:#CCC;";
                domUtils.on(cover, "mousedown", function (e) {
                    me.hide();
                });

                for (var i = 0; i < 8; i++) {
                    hands.push(
                        '<span class="edui-editor-imagescale-hand' + i + '"></span>'
                    );
                }
                resizer.id = me.editor.ui.id + "_imagescale";
                resizer.className = "edui-editor-imagescale";
                resizer.innerHTML = hands.join("");
                resizer.style.cssText +=
                    ";display:none;border:1px solid #3b77ff;z-index:" +
                    me.editor.options.zIndex +
                    ";";

                me.editor.ui.getDom().appendChild(cover);
                me.editor.ui.getDom().appendChild(resizer);

                me.initStyle();
                me.initEvents();
            },
            initStyle: function () {
                utils.cssRule(
                    "imagescale",
                    ".edui-editor-imagescale{display:none;position:absolute;border:1px solid #38B2CE;cursor:hand;-webkit-box-sizing: content-box;-moz-box-sizing: content-box;box-sizing: content-box;}" +
                    ".edui-editor-imagescale span{position:absolute;width:6px;height:6px;overflow:hidden;font-size:0px;display:block;background-color:#3C9DD0;}" +
                    ".edui-editor-imagescale .edui-editor-imagescale-hand0{cursor:nw-resize;top:0;margin-top:-4px;left:0;margin-left:-4px;}" +
                    ".edui-editor-imagescale .edui-editor-imagescale-hand1{cursor:n-resize;top:0;margin-top:-4px;left:50%;margin-left:-4px;}" +
                    ".edui-editor-imagescale .edui-editor-imagescale-hand2{cursor:ne-resize;top:0;margin-top:-4px;left:100%;margin-left:-3px;}" +
                    ".edui-editor-imagescale .edui-editor-imagescale-hand3{cursor:w-resize;top:50%;margin-top:-4px;left:0;margin-left:-4px;}" +
                    ".edui-editor-imagescale .edui-editor-imagescale-hand4{cursor:e-resize;top:50%;margin-top:-4px;left:100%;margin-left:-3px;}" +
                    ".edui-editor-imagescale .edui-editor-imagescale-hand5{cursor:sw-resize;top:100%;margin-top:-3px;left:0;margin-left:-4px;}" +
                    ".edui-editor-imagescale .edui-editor-imagescale-hand6{cursor:s-resize;top:100%;margin-top:-3px;left:50%;margin-left:-4px;}" +
                    ".edui-editor-imagescale .edui-editor-imagescale-hand7{cursor:se-resize;top:100%;margin-top:-3px;left:100%;margin-left:-3px;}"
                );
            },
            initEvents: function () {
                var me = this;

                me.startPos.x = me.startPos.y = 0;
                me.isDraging = false;
            },
            _eventHandler: function (e) {
                var me = this;
                switch (e.type) {
                    case "mousedown":
                        var hand = e.target || e.srcElement,
                            hand;
                        if (
                            hand.className.indexOf("edui-editor-imagescale-hand") !== -1 &&
                            me.dragId === -1
                        ) {
                            me.dragId = hand.className.slice(-1);
                            me.startPos.x = me.prePos.x = e.clientX;
                            me.startPos.y = me.prePos.y = e.clientY;
                            domUtils.on(me.doc, "mousemove", me.proxy(me._eventHandler, me));
                        }
                        break;
                    case "mousemove":
                        if (me.dragId !== -1) {
                            me.updateContainerStyle(me.dragId, {
                                x: e.clientX - me.prePos.x,
                                y: e.clientY - me.prePos.y
                            });
                            me.prePos.x = e.clientX;
                            me.prePos.y = e.clientY;
                            elementUpdated = true;
                            me.updateTargetElement();
                        }
                        break;
                    case "mouseup":
                        if (me.dragId !== -1) {
                            me.updateContainerStyle(me.dragId, {
                                x: e.clientX - me.prePos.x,
                                y: e.clientY - me.prePos.y
                            });
                            me.updateTargetElement();
                            if (me.target.parentNode) {
                                me.attachTo(me.target);
                            }
                            me.dragId = -1;
                        }
                        domUtils.un(me.doc, "mousemove", me.proxy(me._eventHandler, me));
                        //修复只是点击挪动点,但没有改变大小,不应该触发contentchange
                        if (elementUpdated) {
                            elementUpdated = false;
                            me.editor.fireEvent("contentchange");
                        }

                        break;
                    default:
                        break;
                }
            },
            updateTargetElement: function () {
                var me = this;
                domUtils.setStyles(me.target, {
                    width: me.resizer.style.width,
                    height: me.resizer.style.height
                });
                me.target.width = parseInt(me.resizer.style.width);
                me.target.height = parseInt(me.resizer.style.height);
                me.attachTo(me.target);
            },
            updateContainerStyle: function (dir, offset) {
                var me = this,
                    dom = me.resizer,
                    tmp;

                if (rect[dir][0] != 0) {
                    tmp = parseInt(dom.style.left) + offset.x;
                    dom.style.left = me._validScaledProp("left", tmp) + "px";
                }
                if (rect[dir][1] != 0) {
                    tmp = parseInt(dom.style.top) + offset.y;
                    dom.style.top = me._validScaledProp("top", tmp) + "px";
                }
                if (rect[dir][2] != 0) {
                    tmp = dom.clientWidth + rect[dir][2] * offset.x;
                    dom.style.width = me._validScaledProp("width", tmp) + "px";
                }
                if (rect[dir][3] != 0) {
                    tmp = dom.clientHeight + rect[dir][3] * offset.y;
                    dom.style.height = me._validScaledProp("height", tmp) + "px";
                }
            },
            _validScaledProp: function (prop, value) {
                var ele = this.resizer,
                    wrap = document;

                value = isNaN(value) ? 0 : value;
                switch (prop) {
                    case "left":
                        return value < 0
                            ? 0
                            : value + ele.clientWidth > wrap.clientWidth
                                ? wrap.clientWidth - ele.clientWidth
                                : value;
                    case "top":
                        return value < 0
                            ? 0
                            : value + ele.clientHeight > wrap.clientHeight
                                ? wrap.clientHeight - ele.clientHeight
                                : value;
                    case "width":
                        return value <= 0
                            ? 1
                            : value + ele.offsetLeft > wrap.clientWidth
                                ? wrap.clientWidth - ele.offsetLeft
                                : value;
                    case "height":
                        return value <= 0
                            ? 1
                            : value + ele.offsetTop > wrap.clientHeight
                                ? wrap.clientHeight - ele.offsetTop
                                : value;
                }
            },
            hideCover: function () {
                this.cover.style.display = "none";
            },
            showCover: function () {
                var me = this,
                    editorPos = domUtils.getXY(me.editor.ui.getDom()),
                    iframePos = domUtils.getXY(me.editor.iframe);

                domUtils.setStyles(me.cover, {
                    width: me.editor.iframe.offsetWidth + "px",
                    height: me.editor.iframe.offsetHeight + "px",
                    top: iframePos.y - editorPos.y + "px",
                    left: iframePos.x - editorPos.x + "px",
                    position: "absolute",
                    display: ""
                });
            },
            show: function (targetObj) {
                var me = this;
                me.resizer.style.display = "block";
                if (targetObj) {
                    me.attachTo(targetObj);
                }

                domUtils.on(this.resizer, "mousedown", me.proxy(me._eventHandler, me));
                domUtils.on(me.doc, "mouseup", me.proxy(me._eventHandler, me));

                me.showCover();
                me.editor.fireEvent("afterscaleshow", me);
                me.editor.fireEvent("saveScene");
            },
            hide: function () {
                var me = this;
                me.hideCover();
                me.resizer.style.display = "none";

                domUtils.un(me.resizer, "mousedown", me.proxy(me._eventHandler, me));
                domUtils.un(me.doc, "mouseup", me.proxy(me._eventHandler, me));
                me.editor.fireEvent("afterscalehide", me);
            },
            proxy: function (fn, context) {
                return function (e) {
                    return fn.apply(context || this, arguments);
                };
            },
            attachTo: function (targetObj) {
                var me = this,
                    target = (me.target = targetObj),
                    resizer = this.resizer,
                    imgPos = domUtils.getXY(target),
                    iframePos = domUtils.getXY(me.editor.iframe),
                    editorPos = domUtils.getXY(resizer.parentNode);

                domUtils.setStyles(resizer, {
                    width: target.width + "px",
                    height: target.height + "px",
                    left:
                        iframePos.x +
                        imgPos.x -
                        me.editor.getScrollLeft() -
                        editorPos.x -
                        parseInt(resizer.style.borderLeftWidth) +
                        "px",
                    top:
                        iframePos.y +
                        imgPos.y -
                        me.editor.getScrollTop() -
                        editorPos.y -
                        parseInt(resizer.style.borderTopWidth) +
                        "px"
                });
            }
        };
    })();

    return function () {
        var me = this,
            imageScale;

        me.setOpt("imageScaleEnabled", true);

        if (!browser.ie && me.options.imageScaleEnabled) {
            me.addListener("click", function (type, e) {
                var range = me.selection.getRange(),
                    img = range.getClosedNode();

                if (img
                    && img.tagName === "IMG"
                    && me.body.contentEditable !== "false"
                    && img === e.target
                ) {
                    if (
                        img.getAttribute("anchorname") ||
                        domUtils.hasClass(img, "uep-loading") ||
                        domUtils.hasClass(img, "uep-loading-error")
                    ) {
                        return;
                    }

                    if (!imageScale) {
                        imageScale = new Scale();
                        imageScale.init(me);
                        me.ui.getDom().appendChild(imageScale.resizer);

                        var _keyDownHandler = function (e) {
                                imageScale.hide();
                                if (imageScale.target) {
                                    me.selection.getRange().selectNode(imageScale.target).select();
                                }
                            },
                            _mouseDownHandler = function (e) {
                                var ele = e.target || e.srcElement;
                                if (
                                    ele &&
                                    (ele.className === undefined ||
                                        ele.className.indexOf("edui-editor-imagescale") === -1)
                                ) {
                                    _keyDownHandler(e);
                                }
                            },
                            timer;

                        me.addListener("afterscaleshow", function (e) {
                            me.addListener("beforekeydown", _keyDownHandler);
                            me.addListener("beforemousedown", _mouseDownHandler);
                            domUtils.on(document, "keydown", _keyDownHandler);
                            domUtils.on(document, "mousedown", _mouseDownHandler);
                            me.selection.getNative().removeAllRanges();
                        });
                        me.addListener("afterscalehide", function (e) {
                            me.removeListener("beforekeydown", _keyDownHandler);
                            me.removeListener("beforemousedown", _mouseDownHandler);
                            domUtils.un(document, "keydown", _keyDownHandler);
                            domUtils.un(document, "mousedown", _mouseDownHandler);
                            var target = imageScale.target;
                            if (target.parentNode) {
                                me.selection.getRange().selectNode(target).select();
                            }
                        });
                        //TODO 有iframe的情况,mousedown不能往下传。。
                        domUtils.on(imageScale.resizer, "mousedown", function (e) {
                            me.selection.getNative().removeAllRanges();
                            var ele = e.target || e.srcElement;
                            if (
                                ele &&
                                ele.className.indexOf("edui-editor-imagescale-hand") === -1
                            ) {
                                timer = setTimeout(function () {
                                    imageScale.hide();
                                    if (imageScale.target)
                                        me.selection.getRange().selectNode(ele).select();
                                }, 200);
                            }
                        });
                        domUtils.on(imageScale.resizer, "mouseup", function (e) {
                            var ele = e.target || e.srcElement;
                            if (
                                ele &&
                                ele.className.indexOf("edui-editor-imagescale-hand") === -1
                            ) {
                                clearTimeout(timer);
                            }
                        });
                    }
                    imageScale.show(img);
                } else {
                    if (imageScale && imageScale.resizer.style.display !== "none") {
                        imageScale.hide();
                    }
                }
            });
        }

        if (browser.webkit) {
            me.addListener("click", function (type, e) {
                if (e.target.tagName === "IMG" && me.body.contentEditable !== "false") {
                    var range = new dom.Range(me.document);
                    range.selectNode(e.target).select();
                }
            });
        }
    };
})();


// plugins/autolink.js
///import core
///commands 为非ie浏览器自动添加a标签
///commandsName  AutoLink
///commandsTitle  自动增加链接
/**
 * @description 为非ie浏览器自动添加a标签
 * @author zhanyi
 */

UE.plugin.register(
    "autolink",
    function () {
        var cont = 0;

        return !browser.ie
            ? {
                bindEvents: {
                    reset: function () {
                        cont = 0;
                    },
                    keydown: function (type, evt) {
                        var me = this;
                        var keyCode = evt.keyCode || evt.which;

                        if (keyCode == 32 || keyCode == 13) {
                            var sel = me.selection.getNative(),
                                range = sel.getRangeAt(0).cloneRange(),
                                offset,
                                charCode;

                            var start = range.startContainer;
                            while (start.nodeType == 1 && range.startOffset > 0) {
                                start =
                                    range.startContainer.childNodes[range.startOffset - 1];
                                if (!start) {
                                    break;
                                }
                                range.setStart(
                                    start,
                                    start.nodeType == 1
                                        ? start.childNodes.length
                                        : start.nodeValue.length
                                );
                                range.collapse(true);
                                start = range.startContainer;
                            }

                            do {
                                if (range.startOffset == 0) {
                                    start = range.startContainer.previousSibling;

                                    while (start && start.nodeType == 1) {
                                        start = start.lastChild;
                                    }
                                    if (!start || domUtils.isFillChar(start)) {
                                        break;
                                    }
                                    offset = start.nodeValue.length;
                                } else {
                                    start = range.startContainer;
                                    offset = range.startOffset;
                                }
                                range.setStart(start, offset - 1);
                                charCode = range.toString().charCodeAt(0);
                            } while (charCode != 160 && charCode != 32);

                            if (
                                range
                                    .toString()
                                    .replace(new RegExp(domUtils.fillChar, "g"), "")
                                    .match(/(?:https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.)/i)
                            ) {
                                while (range.toString().length) {
                                    if (
                                        /^(?:https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.)/i.test(
                                            range.toString()
                                        )
                                    ) {
                                        break;
                                    }
                                    try {
                                        range.setStart(
                                            range.startContainer,
                                            range.startOffset + 1
                                        );
                                    } catch (e) {
                                        //trace:2121
                                        var start = range.startContainer;
                                        while (!(next = start.nextSibling)) {
                                            if (domUtils.isBody(start)) {
                                                return;
                                            }
                                            start = start.parentNode;
                                        }
                                        range.setStart(next, 0);
                                    }
                                }
                                //range的开始边界已经在a标签里的不再处理
                                if (
                                    domUtils.findParentByTagName(
                                        range.startContainer,
                                        "a",
                                        true
                                    )
                                ) {
                                    return;
                                }
                                var a = me.document.createElement("a"),
                                    text = me.document.createTextNode(" "),
                                    href;

                                me.undoManger && me.undoManger.save();
                                a.appendChild(range.extractContents());
                                a.href = a.innerHTML = a.innerHTML.replace(/<[^>]+>/g, "");
                                href = a
                                    .getAttribute("href")
                                    .replace(new RegExp(domUtils.fillChar, "g"), "");
                                href = /^(?:https?:\/\/)/gi.test(href)
                                    ? href
                                    : "http://" + href;
                                a.setAttribute("_src", utils.html(href));
                                a.href = utils.html(href);

                                range.insertNode(a);
                                a.parentNode.insertBefore(text, a.nextSibling);
                                range.setStart(text, 0);
                                range.collapse(true);
                                sel.removeAllRanges();
                                sel.addRange(range);
                                me.undoManger && me.undoManger.save();
                            }
                        }
                    }
                }
            }
            : {};
    },
    function () {
        var keyCodes = {
            37: 1,
            38: 1,
            39: 1,
            40: 1,
            13: 1,
            32: 1
        };

        function checkIsCludeLink(node) {
            if (node.nodeType == 3) {
                return null;
            }
            if (node.nodeName == "A") {
                return node;
            }
            var lastChild = node.lastChild;

            while (lastChild) {
                if (lastChild.nodeName == "A") {
                    return lastChild;
                }
                if (lastChild.nodeType == 3) {
                    if (domUtils.isWhitespace(lastChild)) {
                        lastChild = lastChild.previousSibling;
                        continue;
                    }
                    return null;
                }
                lastChild = lastChild.lastChild;
            }
        }

        browser.ie &&
        this.addListener("keyup", function (cmd, evt) {
            var me = this,
                keyCode = evt.keyCode;
            if (keyCodes[keyCode]) {
                var rng = me.selection.getRange();
                var start = rng.startContainer;

                if (keyCode == 13) {
                    while (
                        start &&
                        !domUtils.isBody(start) &&
                        !domUtils.isBlockElm(start)
                        ) {
                        start = start.parentNode;
                    }
                    if (start && !domUtils.isBody(start) && start.nodeName == "P") {
                        var pre = start.previousSibling;
                        if (pre && pre.nodeType == 1) {
                            var pre = checkIsCludeLink(pre);
                            if (pre && !pre.getAttribute("_href")) {
                                domUtils.remove(pre, true);
                            }
                        }
                    }
                } else if (keyCode == 32) {
                    if (start.nodeType == 3 && /^\s$/.test(start.nodeValue)) {
                        start = start.previousSibling;
                        if (
                            start &&
                            start.nodeName == "A" &&
                            !start.getAttribute("_href")
                        ) {
                            domUtils.remove(start, true);
                        }
                    }
                } else {
                    start = domUtils.findParentByTagName(start, "a", true);
                    if (start && !start.getAttribute("_href")) {
                        var bk = rng.createBookmark();

                        domUtils.remove(start, true);
                        rng.moveToBookmark(bk).select(true);
                    }
                }
            }
        });
    }
);


// plugins/autoheight.js
///import core
///commands 当输入内容超过编辑器高度时,编辑器自动增高
///commandsName  AutoHeight,autoHeightEnabled
///commandsTitle  自动增高
/**
 * @description 自动伸展
 * @author zhanyi
 */
UE.plugins["autoheight"] = function () {
    var me = this;
    //提供开关,就算加载也可以关闭
    me.autoHeightEnabled = me.options.autoHeightEnabled !== false;
    if (!me.autoHeightEnabled) {
        return;
    }

    var bakOverflow,
        lastHeight = 0,
        options = me.options,
        currentHeight,
        timer;

    function adjustHeight() {
        var me = this;
        clearTimeout(timer);
        if (isFullscreen) return;
        if (
            !me.queryCommandState ||
            (me.queryCommandState && me.queryCommandState("source") != 1)
        ) {
            timer = setTimeout(function () {
                var node = me.body.lastChild;
                while (node && node.nodeType != 1) {
                    node = node.previousSibling;
                }
                if (node && node.nodeType == 1) {
                    node.style.clear = "both";
                    currentHeight = Math.max(
                        domUtils.getXY(node).y + node.offsetHeight + 25,
                        Math.max(options.minFrameHeight, options.initialFrameHeight)
                    );
                    if (currentHeight !== lastHeight) {
                        me.iframe.parentNode.style.transition = 'width 0.3s, height 0.3s, easy-in-out';
                        if (currentHeight !== parseInt(me.iframe.parentNode.style.height)) {
                            me.iframe.parentNode.style.height = currentHeight + "px";
                        }
                        me.body.style.height = currentHeight + "px";
                        lastHeight = currentHeight;
                    }
                    domUtils.removeStyle(node, "clear");
                }
            }, 50);
        }
    }

    var isFullscreen;
    me.addListener("fullscreenchanged", function (cmd, f) {
        isFullscreen = f;
    });
    me.addListener("destroy", function () {
        domUtils.un(me.window, "scroll", fixedScrollTop);
        me.removeListener(
            "contentchange afterinserthtml keyup mouseup",
            adjustHeight
        );
    });
    me.enableAutoHeight = function () {
        var me = this;
        if (!me.autoHeightEnabled) {
            return;
        }
        var doc = me.document;
        me.autoHeightEnabled = true;
        bakOverflow = doc.body.style.overflowY;
        doc.body.style.overflowY = "hidden";
        me.addListener("contentchange afterinserthtml keyup mouseup", adjustHeight);
        //ff不给事件算得不对

        setTimeout(function () {
            adjustHeight.call(me);
        }, browser.gecko ? 100 : 0);
        me.fireEvent("autoheightchanged", me.autoHeightEnabled);
    };
    me.disableAutoHeight = function () {
        me.body.style.overflowY = bakOverflow || "";

        me.removeListener("contentchange", adjustHeight);
        me.removeListener("keyup", adjustHeight);
        me.removeListener("mouseup", adjustHeight);
        me.autoHeightEnabled = false;
        me.fireEvent("autoheightchanged", me.autoHeightEnabled);
    };

    me.on("setHeight", function () {
        me.disableAutoHeight();
    });
    me.addListener("ready", function () {
        me.enableAutoHeight();
        //trace:1764
        var timer;
        domUtils.on(
            browser.ie ? me.body : me.document,
            browser.webkit ? "dragover" : "drop",
            function () {
                clearTimeout(timer);
                timer = setTimeout(function () {
                    //trace:3681
                    adjustHeight.call(me);
                }, 100);
            }
        );
        //修复内容过多时,回到顶部,顶部内容被工具栏遮挡问题
        domUtils.on(me.window, "scroll", fixedScrollTop);
    });

    var lastScrollY;

    function fixedScrollTop() {
        if (!me.window) return;
        if (lastScrollY === null) {
            lastScrollY = me.window.scrollY;
        } else if (me.window.scrollY == 0 && lastScrollY != 0) {
            me.window.scrollTo(0, 0);
            lastScrollY = null;
        }
    }
};


// plugins/autofloat.js
///import core
///commands 悬浮工具栏
///commandsName  AutoFloat,autoFloatEnabled
///commandsTitle  悬浮工具栏
/**
 *  modified by chengchao01
 *  注意: 引入此功能后,在IE6下会将body的背景图片覆盖掉!
 */
UE.plugins["autofloat"] = function () {
    var me = this,
        lang = me.getLang();
    me.setOpt({
        topOffset: 0
    });
    var optsAutoFloatEnabled = me.options.autoFloatEnabled !== false,
        topOffset = me.options.topOffset;

    //如果不固定toolbar的位置,则直接退出
    if (!optsAutoFloatEnabled) {
        return;
    }
    var uiUtils = UE.ui.uiUtils,
        LteIE6 = browser.ie && browser.version <= 6,
        quirks = browser.quirks;

    function checkHasUI() {
        if (!UE.ui) {
            alert(lang.autofloatMsg);
            return 0;
        }
        return 1;
    }

    function fixIE6FixedPos() {
        var docStyle = document.body.style;
        docStyle.backgroundImage = 'url("about:blank")';
        docStyle.backgroundAttachment = "fixed";
    }

    var bakCssText,
        placeHolder = document.createElement("div"),
        toolbarBox,
        orgTop,
        getPosition,
        flag = true; //ie7模式下需要偏移
    function setFloating() {
        var toobarBoxPos = domUtils.getXY(toolbarBox),
            origalFloat = domUtils.getComputedStyle(toolbarBox, "position"),
            origalLeft = domUtils.getComputedStyle(toolbarBox, "left");
        toolbarBox.style.width = toolbarBox.offsetWidth + "px";
        toolbarBox.style.zIndex = me.options.zIndex * 1 + 1;
        toolbarBox.parentNode.insertBefore(placeHolder, toolbarBox);
        if (LteIE6 || (quirks && browser.ie)) {
            if (toolbarBox.style.position != "absolute") {
                toolbarBox.style.position = "absolute";
            }
            toolbarBox.style.top =
                (document.body.scrollTop || document.documentElement.scrollTop) -
                orgTop +
                topOffset +
                "px";
        } else {
            if (browser.ie7Compat && flag) {
                flag = false;
                toolbarBox.style.left =
                    domUtils.getXY(toolbarBox).x -
                    document.documentElement.getBoundingClientRect().left +
                    2 +
                    "px";
            }
            if (toolbarBox.style.position != "fixed") {
                toolbarBox.style.position = "fixed";
                toolbarBox.style.top = topOffset + "px";
                (origalFloat == "absolute" || origalFloat == "relative") &&
                parseFloat(origalLeft) &&
                (toolbarBox.style.left = toobarBoxPos.x + "px");
            }
        }
    }

    function unsetFloating() {
        flag = true;
        if (placeHolder.parentNode) {
            placeHolder.parentNode.removeChild(placeHolder);
        }

        toolbarBox.style.cssText = bakCssText;
    }

    me.unsetFloating = unsetFloating;

    function updateFloating() {
        var rect3 = getPosition(me.container);
        var offset = me.options.toolbarTopOffset || 0;
        if (rect3.top < 0 && rect3.bottom - toolbarBox.offsetHeight > offset) {
            setFloating();
        } else {
            unsetFloating();
        }
    }

    var defer_updateFloating = utils.defer(
        function () {
            updateFloating();
        },
        browser.ie ? 200 : 100,
        true
    );

    me.addListener("destroy", function () {
        domUtils.un(window, ["scroll", "resize"], updateFloating);
        me.removeListener("keydown", defer_updateFloating);
    });

    me.addListener("ready", function () {
        if (checkHasUI(me)) {
            //加载了ui组件,但在new时,没有加载ui,导致编辑器实例上没有ui类,所以这里做判断
            if (!me.ui) {
                return;
            }
            getPosition = uiUtils.getClientRect;
            toolbarBox = me.ui.getDom("toolbarbox");
            orgTop = getPosition(toolbarBox).top;
            bakCssText = toolbarBox.style.cssText;
            placeHolder.style.height = toolbarBox.offsetHeight + "px";
            if (LteIE6) {
                fixIE6FixedPos();
            }
            domUtils.on(window, ["scroll", "resize"], updateFloating);
            me.addListener("keydown", defer_updateFloating);

            me.addListener("beforefullscreenchange", function (t, enabled) {
                if (enabled) {
                    unsetFloating();
                }
            });
            me.addListener("fullscreenchanged", function (t, enabled) {
                if (!enabled) {
                    updateFloating();
                }
            });
            me.addListener("sourcemodechanged", function (t, enabled) {
                setTimeout(function () {
                    updateFloating();
                }, 0);
            });
            me.addListener("clearDoc", function () {
                setTimeout(function () {
                    updateFloating();
                }, 0);
            });
        }
    });
};


// plugins/video.js
/**
 * video插件, 为UEditor提供视频插入支持
 * @file
 * @since 1.2.6.1
 */

UE.plugins["video"] = function () {
    var me = this;

    /**
     * 创建插入视频字符窜
     * @param url 视频地址
     * @param width 视频宽度
     * @param height 视频高度
     * @param align 视频对齐
     * @param toEmbed 是否以flash代替显示
     * @param addParagraph  是否需要添加P 标签
     */
    function creatInsertStr(url, width, height, id, align, classname, type) {
        var str;
        switch (type) {
            case 'iframe':
                str = '<iframe class="' + classname + '" ' +
                    ' src="' + utils.html(url) + '" width="' + width + '" height="' + height + '"' +
                    ' frameborder=0 allowfullscreen>';
                break;
            case "image":
                str =
                    "<img " +
                    (id ? 'id="' + id + '"' : "") +
                    ' width="' +
                    width +
                    '" height="' +
                    height +
                    '" _url="' +
                    url +
                    '" class="' +
                    '"' +
                    ' src=' +
                    me.options.UEDITOR_HOME_URL +
                    '"themes/default/images/spacer.gif" style="background:url(' +
                    me.options.UEDITOR_HOME_URL +
                    "themes/default/images/videologo.gif) no-repeat center center; border:1px solid gray;" +
                    (align ? "float:" + align + ";" : "") +
                    '" />';
                break;
            case "embed":
                str =
                    '<embed type="application/x-shockwave-flash" class="' +
                    classname +
                    '" pluginspage="http://www.macromedia.com/go/getflashplayer"' +
                    ' src="' +
                    utils.html(url) +
                    '" width="' +
                    width +
                    '" height="' +
                    height +
                    '"' +
                    (align ? ' style="float:' + align + '"' : "") +
                    ' wmode="transparent" play="true" loop="false" menu="false" allowscriptaccess="never" allowfullscreen="true" >';
                break;
            case "video":
                var ext = url.substr(url.lastIndexOf(".") + 1);
                if (ext == "ogv") ext = "ogg";
                str =
                    "<video" +
                    (id ? ' id="' + id + '"' : "") +
                    ' class="' +
                    classname +
                    '" ' +
                    (align ? ' style="float:' + align + '"' : "") +
                    ' controls preload="none" width="' +
                    width +
                    '" height="' +
                    height +
                    '" src="' +
                    url +
                    '" data-setup="{}">' +
                    '<source src="' +
                    url +
                    '" type="video/' +
                    ext +
                    '" /></video>';
                break;
        }
        return str;
    }

    function switchImgAndVideo(root, img2video) {
        utils.each(
            root.getNodesByTagName(img2video ? "img" : "embed video"),
            function (node) {
                var className = node.getAttr("class");
                if (className && className.indexOf("edui-faked-video") != -1) {
                    var html = creatInsertStr(
                        img2video ? node.getAttr("_url") : node.getAttr("src"),
                        node.getAttr("width"),
                        node.getAttr("height"),
                        null,
                        node.getStyle("float") || "",
                        className,
                        img2video ? "embed" : "image"
                    );
                    node.parentNode.replaceChild(UE.uNode.createElement(html), node);
                }
                if (className && className.indexOf("edui-upload-video") != -1) {
                    var html = creatInsertStr(
                        img2video ? node.getAttr("_url") : node.getAttr("src"),
                        node.getAttr("width"),
                        node.getAttr("height"),
                        null,
                        node.getStyle("float") || "",
                        className,
                        img2video ? "video" : "image"
                    );
                    node.parentNode.replaceChild(UE.uNode.createElement(html), node);
                }
            }
        );
    }

    me.addOutputRule(function (root) {
        switchImgAndVideo(root, true);
    });
    me.addInputRule(function (root) {
        switchImgAndVideo(root);
    });

    /**
     * 插入视频
     * @command insertvideo
     * @method execCommand
     * @param { String } cmd 命令字符串
     * @param { Object } videoAttr 键值对对象, 描述一个视频的所有属性
     * @example
     * ```javascript
     *
     * var videoAttr = {
     *      //视频地址
     *      url: 'http://www.youku.com/xxx',
     *      //视频宽高值, 单位px
     *      width: 200,
     *      height: 100
     * };
     *
     * //editor 是编辑器实例
     * //向编辑器插入单个视频
     * editor.execCommand( 'insertvideo', videoAttr );
     * ```
     */

    /**
     * 插入视频
     * @command insertvideo
     * @method execCommand
     * @param { String } cmd 命令字符串
     * @param { Array } videoArr 需要插入的视频的数组, 其中的每一个元素都是一个键值对对象, 描述了一个视频的所有属性
     * @example
     * ```javascript
     *
     * var videoAttr1 = {
     *      //视频地址
     *      url: 'http://www.youku.com/xxx',
     *      //视频宽高值, 单位px
     *      width: 200,
     *      height: 100
     * },
     * videoAttr2 = {
     *      //视频地址
     *      url: 'http://www.youku.com/xxx',
     *      //视频宽高值, 单位px
     *      width: 200,
     *      height: 100
     * }
     *
     * //editor 是编辑器实例
     * //该方法将会向编辑器内插入两个视频
     * editor.execCommand( 'insertvideo', [ videoAttr1, videoAttr2 ] );
     * ```
     */

    /**
     * 查询当前光标所在处是否是一个视频
     * @command insertvideo
     * @method queryCommandState
     * @param { String } cmd 需要查询的命令字符串
     * @return { int } 如果当前光标所在处的元素是一个视频对象, 则返回1,否则返回0
     * @example
     * ```javascript
     *
     * //editor 是编辑器实例
     * editor.queryCommandState( 'insertvideo' );
     * ```
     */
    me.commands["insertvideo"] = {
        execCommand: function (cmd, videoObjs, type) {
            videoObjs = utils.isArray(videoObjs) ? videoObjs : [videoObjs];

            if (me.fireEvent("beforeinsertvideo", videoObjs) === true) {
                return;
            }

            var html = [],
                id = "tmpVideo",
                cl;
            for (var i = 0, vi, len = videoObjs.length; i < len; i++) {
                vi = videoObjs[i];
                var videoType = 'iframe';
                if (vi.url.match(/.mp4$/)) {
                    videoType = 'video';
                }
                cl = videoType == "iframe"
                    ? "edui-video-iframe"
                    : "edui-video-video";
                html.push(
                    creatInsertStr(
                        vi.url,
                        vi.width || 420,
                        vi.height || 280,
                        id + i,
                        null,
                        cl,
                        videoType
                    )
                );
            }
            me.execCommand("inserthtml", html.join(""), true);
            var rng = this.selection.getRange();
            // for (var i = 0, len = videoObjs.length; i < len; i++) {
            //   var img = this.document.getElementById("tmpVideo" + i);
            //   domUtils.removeAttributes(img, "id");
            //   rng.selectNode(img).select();
            //   me.execCommand("imagefloat", videoObjs[i].align);
            // }

            me.fireEvent("afterinsertvideo", videoObjs);
        },
        queryCommandState: function () {
            var img = me.selection.getRange().getClosedNode(),
                flag =
                    img &&
                    (img.className == "edui-video-iframe" ||
                        img.className.indexOf("edui-video-iframe") != -1 ||
                        img.className == "edui-video-video" ||
                        img.className.indexOf("edui-video-video") != -1);
            return flag ? 1 : 0;
        }
    };
};


// plugins/audio.js
UE.plugins["audio"] = function () {
    var me = this;

    function createAudioHtml(url, param) {
        param = param || {};
        var str = [
            "<audio",
            (param.id ? ' id="' + param.id + '"' : ""),
            (param.cls ? ' class="' + param.cls + '"' : ''),
            ' controls >',
            '<source src="' + url + '" type="audio/mpeg' + '" />',
            '</audio>',
        ];
        return str.join('');
    }

    function switchImgAndAudio(root, img2audio) {
        // utils.each(
        //     root.getNodesByTagName(img2audio ? "img" : "embed audio"),
        //     function (node) {
        //         var className = node.getAttr("class");
        //         if (className && className.indexOf("edui-faked-audio") != -1) {
        //             var html = createAudioHtml(
        //                 img2audio ? node.getAttr("_url") : node.getAttr("src"),
        //                 node.getAttr("width"),
        //                 node.getAttr("height"),
        //                 null,
        //                 node.getStyle("float") || "",
        //                 className,
        //                 img2audio ? "embed" : "image"
        //             );
        //             node.parentNode.replaceChild(UE.uNode.createElement(html), node);
        //         }
        //         if (className && className.indexOf("edui-upload-audio") != -1) {
        //             var html = createAudioHtml(
        //                 img2audio ? node.getAttr("_url") : node.getAttr("src"),
        //                 node.getAttr("width"),
        //                 node.getAttr("height"),
        //                 null,
        //                 node.getStyle("float") || "",
        //                 className,
        //                 img2audio ? "audio" : "image"
        //             );
        //             node.parentNode.replaceChild(UE.uNode.createElement(html), node);
        //         }
        //     }
        // );
    }

    me.addOutputRule(function (root) {
        switchImgAndAudio(root, true);
    });
    me.addInputRule(function (root) {
        switchImgAndAudio(root);
    });

    me.commands["insertaudio"] = {
        execCommand: function (cmd, audioObjs, type) {
            audioObjs = utils.isArray(audioObjs) ? audioObjs : [audioObjs];

            if (me.fireEvent("beforeinsertaudio", audioObjs) === true) {
                return;
            }

            var html = [];
            for (var i = 0, vi, len = audioObjs.length; i < len; i++) {
                vi = audioObjs[i];
                html.push(
                    createAudioHtml(
                        vi.url,
                        {
                            cls: 'edui-audio-audio'
                        }
                    )
                );
            }
            me.execCommand("inserthtml", html.join(""), true);
            var rng = this.selection.getRange();
            // for (var i = 0, len = audioObjs.length; i < len; i++) {
            //   var img = this.document.getElementById("tmpAudio" + i);
            //   domUtils.removeAttributes(img, "id");
            //   rng.selectNode(img).select();
            //   me.execCommand("imagefloat", audioObjs[i].align);
            // }

            me.fireEvent("afterinsertaudio", audioObjs);
        },
        queryCommandState: function () {
            var img = me.selection.getRange().getClosedNode(),
                flag = img &&
                    (img.className == "edui-audio-audio" || img.className.indexOf("edui-audio-audio") != -1);
            return flag ? 1 : 0;
        }
    };
};


// plugins/table.core.js
/**
 * Created with JetBrains WebStorm.
 * User: taoqili
 * Date: 13-1-18
 * Time: 上午11:09
 * To change this template use File | Settings | File Templates.
 */
/**
 * UE表格操作类
 * @param table
 * @constructor
 */
(function () {
    var UETable = (UE.UETable = function (table) {
        this.table = table;
        this.indexTable = [];
        this.selectedTds = [];
        this.cellsRange = {};
        this.update(table);
    });

    //===以下为静态工具方法===
    UETable.removeSelectedClass = function (cells) {
        utils.each(cells, function (cell) {
            domUtils.removeClasses(cell, "selectTdClass");
        });
    };
    UETable.addSelectedClass = function (cells) {
        utils.each(cells, function (cell) {
            domUtils.addClass(cell, "selectTdClass");
        });
    };
    UETable.isEmptyBlock = function (node) {
        var reg = new RegExp(domUtils.fillChar, "g");
        if (
            node[browser.ie ? "innerText" : "textContent"]
                .replace(/^\s*$/, "")
                .replace(reg, "").length > 0
        ) {
            return 0;
        }
        for (var i in dtd.$isNotEmpty)
            if (dtd.$isNotEmpty.hasOwnProperty(i)) {
                if (node.getElementsByTagName(i).length) {
                    return 0;
                }
            }
        return 1;
    };
    UETable.getWidth = function (cell) {
        if (!cell) return 0;
        return parseInt(domUtils.getComputedStyle(cell, "width"), 10);
    };

    /**
     * 获取单元格或者单元格组的“对齐”状态。 如果当前的检测对象是一个单元格组, 只有在满足所有单元格的 水平和竖直 对齐属性都相同的
     * 条件时才会返回其状态值,否则将返回null; 如果当前只检测了一个单元格, 则直接返回当前单元格的对齐状态;
     * @param table cell or table cells , 支持单个单元格dom对象 或者 单元格dom对象数组
     * @return { align: 'left' || 'right' || 'center', valign: 'top' || 'middle' || 'bottom' } 或者 null
     */
    UETable.getTableCellAlignState = function (cells) {
        !utils.isArray(cells) && (cells = [cells]);

        var result = {},
            status = ["align", "valign"],
            tempStatus = null,
            isSame = true; //状态是否相同

        utils.each(cells, function (cellNode) {
            utils.each(status, function (currentState) {
                tempStatus = cellNode.getAttribute(currentState);

                if (!result[currentState] && tempStatus) {
                    result[currentState] = tempStatus;
                } else if (
                    !result[currentState] ||
                    tempStatus !== result[currentState]
                ) {
                    isSame = false;
                    return false;
                }
            });

            return isSame;
        });

        return isSame ? result : null;
    };

    /**
     * 根据当前选区获取相关的table信息
     * @return {Object}
     */
    UETable.getTableItemsByRange = function (editor) {
        var start = editor.selection.getStart();

        //ff下会选中bookmark
        if (
            start &&
            start.id &&
            start.id.indexOf("_baidu_bookmark_start_") === 0 &&
            start.nextSibling
        ) {
            start = start.nextSibling;
        }

        //在table或者td边缘有可能存在选中tr的情况
        var cell = start && domUtils.findParentByTagName(start, ["td", "th"], true),
            tr = cell && cell.parentNode,
            table = tr && domUtils.findParentByTagName(tr, ["table"]),
            caption = table && table.getElementsByTagName("caption")[0];

        return {
            cell: cell,
            tr: tr,
            table: table,
            caption: caption
        };
    };
    UETable.getUETableBySelected = function (editor) {
        var table = UETable.getTableItemsByRange(editor).table;
        if (table && table.ueTable && table.ueTable.selectedTds.length) {
            return table.ueTable;
        }
        return null;
    };

    UETable.getDefaultValue = function (editor, table) {
        var borderMap = {
                thin: "0px",
                medium: "1px",
                thick: "2px"
            },
            tableBorder,
            tdPadding,
            tdBorder,
            tmpValue;
        if (!table) {
            table = editor.document.createElement("table");
            table.insertRow(0).insertCell(0).innerHTML = "xxx";
            editor.body.appendChild(table);
            var td = table.getElementsByTagName("td")[0];
            tmpValue = domUtils.getComputedStyle(table, "border-left-width");
            tableBorder = parseInt(borderMap[tmpValue] || tmpValue, 10);
            tmpValue = domUtils.getComputedStyle(td, "padding-left");
            tdPadding = parseInt(borderMap[tmpValue] || tmpValue, 10);
            tmpValue = domUtils.getComputedStyle(td, "border-left-width");
            tdBorder = parseInt(borderMap[tmpValue] || tmpValue, 10);
            domUtils.remove(table);
            return {
                tableBorder: tableBorder,
                tdPadding: tdPadding,
                tdBorder: tdBorder
            };
        } else {
            td = table.getElementsByTagName("td")[0];
            tmpValue = domUtils.getComputedStyle(table, "border-left-width");
            tableBorder = parseInt(borderMap[tmpValue] || tmpValue, 10);
            tmpValue = domUtils.getComputedStyle(td, "padding-left");
            tdPadding = parseInt(borderMap[tmpValue] || tmpValue, 10);
            tmpValue = domUtils.getComputedStyle(td, "border-left-width");
            tdBorder = parseInt(borderMap[tmpValue] || tmpValue, 10);
            return {
                tableBorder: tableBorder,
                tdPadding: tdPadding,
                tdBorder: tdBorder
            };
        }
    };
    /**
     * 根据当前点击的td或者table获取索引对象
     * @param tdOrTable
     */
    UETable.getUETable = function (tdOrTable) {
        var tag = tdOrTable.tagName.toLowerCase();
        tdOrTable = tag == "td" || tag == "th" || tag == "caption"
            ? domUtils.findParentByTagName(tdOrTable, "table", true)
            : tdOrTable;
        if (!tdOrTable.ueTable) {
            tdOrTable.ueTable = new UETable(tdOrTable);
        }
        return tdOrTable.ueTable;
    };

    UETable.cloneCell = function (cell, ignoreMerge, keepPro) {
        if (!cell || utils.isString(cell)) {
            return this.table.ownerDocument.createElement(cell || "td");
        }
        var flag = domUtils.hasClass(cell, "selectTdClass");
        flag && domUtils.removeClasses(cell, "selectTdClass");
        var tmpCell = cell.cloneNode(true);
        if (ignoreMerge) {
            tmpCell.rowSpan = tmpCell.colSpan = 1;
        }
        //去掉宽高
        !keepPro && domUtils.removeAttributes(tmpCell, "width height");
        !keepPro && domUtils.removeAttributes(tmpCell, "style");

        tmpCell.style.borderLeftStyle = "";
        tmpCell.style.borderTopStyle = "";
        tmpCell.style.borderLeftColor = cell.style.borderRightColor;
        tmpCell.style.borderLeftWidth = cell.style.borderRightWidth;
        tmpCell.style.borderTopColor = cell.style.borderBottomColor;
        tmpCell.style.borderTopWidth = cell.style.borderBottomWidth;
        flag && domUtils.addClass(cell, "selectTdClass");
        return tmpCell;
    };

    UETable.prototype = {
        getMaxRows: function () {
            var rows = this.table.rows,
                maxLen = 1;
            for (var i = 0, row; (row = rows[i]); i++) {
                var currentMax = 1;
                for (var j = 0, cj; (cj = row.cells[j++]);) {
                    currentMax = Math.max(cj.rowSpan || 1, currentMax);
                }
                maxLen = Math.max(currentMax + i, maxLen);
            }
            return maxLen;
        },
        /**
         * 获取当前表格的最大列数
         */
        getMaxCols: function () {
            var rows = this.table.rows,
                maxLen = 0,
                cellRows = {};
            for (var i = 0, row; (row = rows[i]); i++) {
                var cellsNum = 0;
                for (var j = 0, cj; (cj = row.cells[j++]);) {
                    cellsNum += cj.colSpan || 1;
                    if (cj.rowSpan && cj.rowSpan > 1) {
                        for (var k = 1; k < cj.rowSpan; k++) {
                            if (!cellRows["row_" + (i + k)]) {
                                cellRows["row_" + (i + k)] = cj.colSpan || 1;
                            } else {
                                cellRows["row_" + (i + k)]++;
                            }
                        }
                    }
                }
                cellsNum += cellRows["row_" + i] || 0;
                maxLen = Math.max(cellsNum, maxLen);
            }
            return maxLen;
        },
        getCellColIndex: function (cell) {
        },
        /**
         * 获取当前cell旁边的单元格,
         * @param cell
         * @param right
         */
        getHSideCell: function (cell, right) {
            try {
                var cellInfo = this.getCellInfo(cell),
                    previewRowIndex,
                    previewColIndex;
                var len = this.selectedTds.length,
                    range = this.cellsRange;
                //首行或者首列没有前置单元格
                if (
                    (!right && (!len ? !cellInfo.colIndex : !range.beginColIndex)) ||
                    (right &&
                        (!len
                            ? cellInfo.colIndex == this.colsNum - 1
                            : range.endColIndex == this.colsNum - 1))
                )
                    return null;

                previewRowIndex = !len ? cellInfo.rowIndex : range.beginRowIndex;
                previewColIndex = !right
                    ? !len
                        ? cellInfo.colIndex < 1 ? 0 : cellInfo.colIndex - 1
                        : range.beginColIndex - 1
                    : !len ? cellInfo.colIndex + 1 : range.endColIndex + 1;
                return this.getCell(
                    this.indexTable[previewRowIndex][previewColIndex].rowIndex,
                    this.indexTable[previewRowIndex][previewColIndex].cellIndex
                );
            } catch (e) {
                showError(e);
            }
        },
        getTabNextCell: function (cell, preRowIndex) {
            var cellInfo = this.getCellInfo(cell),
                rowIndex = preRowIndex || cellInfo.rowIndex,
                colIndex = cellInfo.colIndex + 1 + (cellInfo.colSpan - 1),
                nextCell;
            try {
                nextCell = this.getCell(
                    this.indexTable[rowIndex][colIndex].rowIndex,
                    this.indexTable[rowIndex][colIndex].cellIndex
                );
            } catch (e) {
                try {
                    rowIndex = rowIndex * 1 + 1;
                    colIndex = 0;
                    nextCell = this.getCell(
                        this.indexTable[rowIndex][colIndex].rowIndex,
                        this.indexTable[rowIndex][colIndex].cellIndex
                    );
                } catch (e) {
                }
            }
            return nextCell;
        },
        /**
         * 获取视觉上的后置单元格
         * @param cell
         * @param bottom
         */
        getVSideCell: function (cell, bottom, ignoreRange) {
            try {
                var cellInfo = this.getCellInfo(cell),
                    nextRowIndex,
                    nextColIndex;
                var len = this.selectedTds.length && !ignoreRange,
                    range = this.cellsRange;
                //末行或者末列没有后置单元格
                if (
                    (!bottom && cellInfo.rowIndex == 0) ||
                    (bottom &&
                        (!len
                            ? cellInfo.rowIndex + cellInfo.rowSpan > this.rowsNum - 1
                            : range.endRowIndex == this.rowsNum - 1))
                )
                    return null;

                nextRowIndex = !bottom
                    ? !len ? cellInfo.rowIndex - 1 : range.beginRowIndex - 1
                    : !len ? cellInfo.rowIndex + cellInfo.rowSpan : range.endRowIndex + 1;
                nextColIndex = !len ? cellInfo.colIndex : range.beginColIndex;
                return this.getCell(
                    this.indexTable[nextRowIndex][nextColIndex].rowIndex,
                    this.indexTable[nextRowIndex][nextColIndex].cellIndex
                );
            } catch (e) {
                showError(e);
            }
        },
        /**
         * 获取相同结束位置的单元格,xOrY指代了是获取x轴相同还是y轴相同
         */
        getSameEndPosCells: function (cell, xOrY) {
            try {
                var flag = xOrY.toLowerCase() === "x",
                    end =
                        domUtils.getXY(cell)[flag ? "x" : "y"] +
                        cell["offset" + (flag ? "Width" : "Height")],
                    rows = this.table.rows,
                    cells = null,
                    returns = [];
                for (var i = 0; i < this.rowsNum; i++) {
                    cells = rows[i].cells;
                    for (var j = 0, tmpCell; (tmpCell = cells[j++]);) {
                        var tmpEnd =
                            domUtils.getXY(tmpCell)[flag ? "x" : "y"] +
                            tmpCell["offset" + (flag ? "Width" : "Height")];
                        //对应行的td已经被上面行rowSpan了
                        if (tmpEnd > end && flag) break;
                        if (cell == tmpCell || end == tmpEnd) {
                            //只获取单一的单元格
                            //todo 仅获取单一单元格在特定情况下会造成returns为空,从而影响后续的拖拽实现,修正这个。需考虑性能
                            if (tmpCell[flag ? "colSpan" : "rowSpan"] == 1) {
                                returns.push(tmpCell);
                            }
                            if (flag) break;
                        }
                    }
                }
                return returns;
            } catch (e) {
                showError(e);
            }
        },
        setCellContent: function (cell, content) {
            cell.innerHTML = content || (browser.ie ? domUtils.fillChar : "<br />");
        },
        cloneCell: UETable.cloneCell,
        /**
         * 获取跟当前单元格的右边竖线为左边的所有未合并单元格
         */
        getSameStartPosXCells: function (cell) {
            try {
                var start = domUtils.getXY(cell).x + cell.offsetWidth,
                    rows = this.table.rows,
                    cells,
                    returns = [];
                for (var i = 0; i < this.rowsNum; i++) {
                    cells = rows[i].cells;
                    for (var j = 0, tmpCell; (tmpCell = cells[j++]);) {
                        var tmpStart = domUtils.getXY(tmpCell).x;
                        if (tmpStart > start) break;
                        if (tmpStart == start && tmpCell.colSpan == 1) {
                            returns.push(tmpCell);
                            break;
                        }
                    }
                }
                return returns;
            } catch (e) {
                showError(e);
            }
        },
        /**
         * 更新table对应的索引表
         */
        update: function (table) {
            this.table = table || this.table;
            this.selectedTds = [];
            this.cellsRange = {};
            this.indexTable = [];
            var rows = this.table.rows,
                rowsNum = this.getMaxRows(),
                dNum = rowsNum - rows.length,
                colsNum = this.getMaxCols();
            while (dNum--) {
                this.table.insertRow(rows.length);
            }
            this.rowsNum = rowsNum;
            this.colsNum = colsNum;
            for (var i = 0, len = rows.length; i < len; i++) {
                this.indexTable[i] = new Array(colsNum);
            }
            //填充索引表
            for (var rowIndex = 0, row; (row = rows[rowIndex]); rowIndex++) {
                for (
                    var cellIndex = 0, cell, cells = row.cells;
                    (cell = cells[cellIndex]);
                    cellIndex++
                ) {
                    //修正整行被rowSpan时导致的行数计算错误
                    if (cell.rowSpan > rowsNum) {
                        cell.rowSpan = rowsNum;
                    }
                    var colIndex = cellIndex,
                        rowSpan = cell.rowSpan || 1,
                        colSpan = cell.colSpan || 1;
                    //当已经被上一行rowSpan或者被前一列colSpan了,则跳到下一个单元格进行
                    while (this.indexTable[rowIndex][colIndex]) colIndex++;
                    for (var j = 0; j < rowSpan; j++) {
                        for (var k = 0; k < colSpan; k++) {
                            this.indexTable[rowIndex + j][colIndex + k] = {
                                rowIndex: rowIndex,
                                cellIndex: cellIndex,
                                colIndex: colIndex,
                                rowSpan: rowSpan,
                                colSpan: colSpan
                            };
                        }
                    }
                }
            }
            //修复残缺td
            for (j = 0; j < rowsNum; j++) {
                for (k = 0; k < colsNum; k++) {
                    if (this.indexTable[j][k] === undefined) {
                        row = rows[j];
                        cell = row.cells[row.cells.length - 1];
                        cell = cell
                            ? cell.cloneNode(true)
                            : this.table.ownerDocument.createElement("td");
                        this.setCellContent(cell);
                        if (cell.colSpan !== 1) cell.colSpan = 1;
                        if (cell.rowSpan !== 1) cell.rowSpan = 1;
                        row.appendChild(cell);
                        this.indexTable[j][k] = {
                            rowIndex: j,
                            cellIndex: cell.cellIndex,
                            colIndex: k,
                            rowSpan: 1,
                            colSpan: 1
                        };
                    }
                }
            }
            //当框选后删除行或者列后撤销,需要重建选区。
            var tds = domUtils.getElementsByTagName(this.table, "td"),
                selectTds = [];
            utils.each(tds, function (td) {
                if (domUtils.hasClass(td, "selectTdClass")) {
                    selectTds.push(td);
                }
            });
            if (selectTds.length) {
                var start = selectTds[0],
                    end = selectTds[selectTds.length - 1],
                    startInfo = this.getCellInfo(start),
                    endInfo = this.getCellInfo(end);
                this.selectedTds = selectTds;
                this.cellsRange = {
                    beginRowIndex: startInfo.rowIndex,
                    beginColIndex: startInfo.colIndex,
                    endRowIndex: endInfo.rowIndex + endInfo.rowSpan - 1,
                    endColIndex: endInfo.colIndex + endInfo.colSpan - 1
                };
            }
            //给第一行设置firstRow的样式名称,在排序图标的样式上使用到
            if (!domUtils.hasClass(this.table.rows[0], "firstRow")) {
                domUtils.addClass(this.table.rows[0], "firstRow");
                for (var i = 1; i < this.table.rows.length; i++) {
                    domUtils.removeClasses(this.table.rows[i], "firstRow");
                }
            }
        },
        /**
         * 获取单元格的索引信息
         */
        getCellInfo: function (cell) {
            if (!cell) return;
            var cellIndex = cell.cellIndex,
                rowIndex = cell.parentNode.rowIndex,
                rowInfo = this.indexTable[rowIndex],
                numCols = this.colsNum;
            for (var colIndex = cellIndex; colIndex < numCols; colIndex++) {
                var cellInfo = rowInfo[colIndex];
                if (
                    cellInfo.rowIndex === rowIndex &&
                    cellInfo.cellIndex === cellIndex
                ) {
                    return cellInfo;
                }
            }
        },
        /**
         * 根据行列号获取单元格
         */
        getCell: function (rowIndex, cellIndex) {
            return (
                (rowIndex < this.rowsNum &&
                    this.table.rows[rowIndex].cells[cellIndex]) ||
                null
            );
        },
        /**
         * 删除单元格
         */
        deleteCell: function (cell, rowIndex) {
            rowIndex = typeof rowIndex == "number"
                ? rowIndex
                : cell.parentNode.rowIndex;
            var row = this.table.rows[rowIndex];
            row.deleteCell(cell.cellIndex);
        },
        /**
         * 根据始末两个单元格获取被框选的所有单元格范围
         */
        getCellsRange: function (cellA, cellB) {
            function checkRange(
                beginRowIndex,
                beginColIndex,
                endRowIndex,
                endColIndex
            ) {
                var tmpBeginRowIndex = beginRowIndex,
                    tmpBeginColIndex = beginColIndex,
                    tmpEndRowIndex = endRowIndex,
                    tmpEndColIndex = endColIndex,
                    cellInfo,
                    colIndex,
                    rowIndex;
                // 通过indexTable检查是否存在超出TableRange上边界的情况
                if (beginRowIndex > 0) {
                    for (colIndex = beginColIndex; colIndex < endColIndex; colIndex++) {
                        cellInfo = me.indexTable[beginRowIndex][colIndex];
                        rowIndex = cellInfo.rowIndex;
                        if (rowIndex < beginRowIndex) {
                            tmpBeginRowIndex = Math.min(rowIndex, tmpBeginRowIndex);
                        }
                    }
                }
                // 通过indexTable检查是否存在超出TableRange右边界的情况
                if (endColIndex < me.colsNum) {
                    for (rowIndex = beginRowIndex; rowIndex < endRowIndex; rowIndex++) {
                        cellInfo = me.indexTable[rowIndex][endColIndex];
                        colIndex = cellInfo.colIndex + cellInfo.colSpan - 1;
                        if (colIndex > endColIndex) {
                            tmpEndColIndex = Math.max(colIndex, tmpEndColIndex);
                        }
                    }
                }
                // 检查是否有超出TableRange下边界的情况
                if (endRowIndex < me.rowsNum) {
                    for (colIndex = beginColIndex; colIndex < endColIndex; colIndex++) {
                        cellInfo = me.indexTable[endRowIndex][colIndex];
                        rowIndex = cellInfo.rowIndex + cellInfo.rowSpan - 1;
                        if (rowIndex > endRowIndex) {
                            tmpEndRowIndex = Math.max(rowIndex, tmpEndRowIndex);
                        }
                    }
                }
                // 检查是否有超出TableRange左边界的情况
                if (beginColIndex > 0) {
                    for (rowIndex = beginRowIndex; rowIndex < endRowIndex; rowIndex++) {
                        cellInfo = me.indexTable[rowIndex][beginColIndex];
                        colIndex = cellInfo.colIndex;
                        if (colIndex < beginColIndex) {
                            tmpBeginColIndex = Math.min(cellInfo.colIndex, tmpBeginColIndex);
                        }
                    }
                }
                //递归调用直至所有完成所有框选单元格的扩展
                if (
                    tmpBeginRowIndex != beginRowIndex ||
                    tmpBeginColIndex != beginColIndex ||
                    tmpEndRowIndex != endRowIndex ||
                    tmpEndColIndex != endColIndex
                ) {
                    return checkRange(
                        tmpBeginRowIndex,
                        tmpBeginColIndex,
                        tmpEndRowIndex,
                        tmpEndColIndex
                    );
                } else {
                    // 不需要扩展TableRange的情况
                    return {
                        beginRowIndex: beginRowIndex,
                        beginColIndex: beginColIndex,
                        endRowIndex: endRowIndex,
                        endColIndex: endColIndex
                    };
                }
            }

            try {
                var me = this,
                    cellAInfo = me.getCellInfo(cellA);
                if (cellA === cellB) {
                    return {
                        beginRowIndex: cellAInfo.rowIndex,
                        beginColIndex: cellAInfo.colIndex,
                        endRowIndex: cellAInfo.rowIndex + cellAInfo.rowSpan - 1,
                        endColIndex: cellAInfo.colIndex + cellAInfo.colSpan - 1
                    };
                }
                var cellBInfo = me.getCellInfo(cellB);
                // 计算TableRange的四个边
                var beginRowIndex = Math.min(cellAInfo.rowIndex, cellBInfo.rowIndex),
                    beginColIndex = Math.min(cellAInfo.colIndex, cellBInfo.colIndex),
                    endRowIndex = Math.max(
                        cellAInfo.rowIndex + cellAInfo.rowSpan - 1,
                        cellBInfo.rowIndex + cellBInfo.rowSpan - 1
                    ),
                    endColIndex = Math.max(
                        cellAInfo.colIndex + cellAInfo.colSpan - 1,
                        cellBInfo.colIndex + cellBInfo.colSpan - 1
                    );

                return checkRange(
                    beginRowIndex,
                    beginColIndex,
                    endRowIndex,
                    endColIndex
                );
            } catch (e) {
                //throw e;
            }
        },
        /**
         * 依据cellsRange获取对应的单元格集合
         */
        getCells: function (range) {
            //每次获取cells之前必须先清除上次的选择,否则会对后续获取操作造成影响
            this.clearSelected();
            var beginRowIndex = range.beginRowIndex,
                beginColIndex = range.beginColIndex,
                endRowIndex = range.endRowIndex,
                endColIndex = range.endColIndex,
                cellInfo,
                rowIndex,
                colIndex,
                tdHash = {},
                returnTds = [];
            for (var i = beginRowIndex; i <= endRowIndex; i++) {
                for (var j = beginColIndex; j <= endColIndex; j++) {
                    cellInfo = this.indexTable[i][j];
                    rowIndex = cellInfo.rowIndex;
                    colIndex = cellInfo.colIndex;
                    // 如果Cells里已经包含了此Cell则跳过
                    var key = rowIndex + "|" + colIndex;
                    if (tdHash[key]) continue;
                    tdHash[key] = 1;
                    if (
                        rowIndex < i ||
                        colIndex < j ||
                        rowIndex + cellInfo.rowSpan - 1 > endRowIndex ||
                        colIndex + cellInfo.colSpan - 1 > endColIndex
                    ) {
                        return null;
                    }
                    returnTds.push(this.getCell(rowIndex, cellInfo.cellIndex));
                }
            }
            return returnTds;
        },
        /**
         * 清理已经选中的单元格
         */
        clearSelected: function () {
            UETable.removeSelectedClass(this.selectedTds);
            this.selectedTds = [];
            this.cellsRange = {};
        },
        /**
         * 根据range设置已经选中的单元格
         */
        setSelected: function (range) {
            var cells = this.getCells(range);
            UETable.addSelectedClass(cells);
            this.selectedTds = cells;
            this.cellsRange = range;
        },
        isFullRow: function () {
            var range = this.cellsRange;
            return range.endColIndex - range.beginColIndex + 1 == this.colsNum;
        },
        isFullCol: function () {
            var range = this.cellsRange,
                table = this.table,
                ths = table.getElementsByTagName("th"),
                rows = range.endRowIndex - range.beginRowIndex + 1;
            return !ths.length
                ? rows == this.rowsNum
                : rows == this.rowsNum || rows == this.rowsNum - 1;
        },
        /**
         * 获取视觉上的前置单元格,默认是左边,top传入时
         * @param cell
         * @param top
         */
        getNextCell: function (cell, bottom, ignoreRange) {
            try {
                var cellInfo = this.getCellInfo(cell),
                    nextRowIndex,
                    nextColIndex;
                var len = this.selectedTds.length && !ignoreRange,
                    range = this.cellsRange;
                //末行或者末列没有后置单元格
                if (
                    (!bottom && cellInfo.rowIndex == 0) ||
                    (bottom &&
                        (!len
                            ? cellInfo.rowIndex + cellInfo.rowSpan > this.rowsNum - 1
                            : range.endRowIndex == this.rowsNum - 1))
                )
                    return null;

                nextRowIndex = !bottom
                    ? !len ? cellInfo.rowIndex - 1 : range.beginRowIndex - 1
                    : !len ? cellInfo.rowIndex + cellInfo.rowSpan : range.endRowIndex + 1;
                nextColIndex = !len ? cellInfo.colIndex : range.beginColIndex;
                return this.getCell(
                    this.indexTable[nextRowIndex][nextColIndex].rowIndex,
                    this.indexTable[nextRowIndex][nextColIndex].cellIndex
                );
            } catch (e) {
                showError(e);
            }
        },
        getPreviewCell: function (cell, top) {
            try {
                var cellInfo = this.getCellInfo(cell),
                    previewRowIndex,
                    previewColIndex;
                var len = this.selectedTds.length,
                    range = this.cellsRange;
                //首行或者首列没有前置单元格
                if (
                    (!top && (!len ? !cellInfo.colIndex : !range.beginColIndex)) ||
                    (top &&
                        (!len
                            ? cellInfo.rowIndex > this.colsNum - 1
                            : range.endColIndex == this.colsNum - 1))
                )
                    return null;

                previewRowIndex = !top
                    ? !len ? cellInfo.rowIndex : range.beginRowIndex
                    : !len
                        ? cellInfo.rowIndex < 1 ? 0 : cellInfo.rowIndex - 1
                        : range.beginRowIndex;
                previewColIndex = !top
                    ? !len
                        ? cellInfo.colIndex < 1 ? 0 : cellInfo.colIndex - 1
                        : range.beginColIndex - 1
                    : !len ? cellInfo.colIndex : range.endColIndex + 1;
                return this.getCell(
                    this.indexTable[previewRowIndex][previewColIndex].rowIndex,
                    this.indexTable[previewRowIndex][previewColIndex].cellIndex
                );
            } catch (e) {
                showError(e);
            }
        },
        /**
         * 移动单元格中的内容
         */
        moveContent: function (cellTo, cellFrom) {
            if (UETable.isEmptyBlock(cellFrom)) return;
            if (UETable.isEmptyBlock(cellTo)) {
                cellTo.innerHTML = cellFrom.innerHTML;
                return;
            }
            var child = cellTo.lastChild;
            if (child.nodeType == 3 || !dtd.$block[child.tagName]) {
                cellTo.appendChild(cellTo.ownerDocument.createElement("br"));
            }
            while ((child = cellFrom.firstChild)) {
                cellTo.appendChild(child);
            }
        },
        /**
         * 向右合并单元格
         */
        mergeRight: function (cell) {
            var cellInfo = this.getCellInfo(cell),
                rightColIndex = cellInfo.colIndex + cellInfo.colSpan,
                rightCellInfo = this.indexTable[cellInfo.rowIndex][rightColIndex],
                rightCell = this.getCell(
                    rightCellInfo.rowIndex,
                    rightCellInfo.cellIndex
                );
            //合并
            cell.colSpan = cellInfo.colSpan + rightCellInfo.colSpan;
            //被合并的单元格不应存在宽度属性
            cell.removeAttribute("width");
            //移动内容
            this.moveContent(cell, rightCell);
            //删掉被合并的Cell
            this.deleteCell(rightCell, rightCellInfo.rowIndex);
            this.update();
        },
        /**
         * 向下合并单元格
         */
        mergeDown: function (cell) {
            var cellInfo = this.getCellInfo(cell),
                downRowIndex = cellInfo.rowIndex + cellInfo.rowSpan,
                downCellInfo = this.indexTable[downRowIndex][cellInfo.colIndex],
                downCell = this.getCell(downCellInfo.rowIndex, downCellInfo.cellIndex);
            cell.rowSpan = cellInfo.rowSpan + downCellInfo.rowSpan;
            cell.removeAttribute("height");
            this.moveContent(cell, downCell);
            this.deleteCell(downCell, downCellInfo.rowIndex);
            this.update();
        },
        /**
         * 合并整个range中的内容
         */
        mergeRange: function () {
            //由于合并操作可以在任意时刻进行,所以无法通过鼠标位置等信息实时生成range,只能通过缓存实例中的cellsRange对象来访问
            var range = this.cellsRange,
                leftTopCell = this.getCell(
                    range.beginRowIndex,
                    this.indexTable[range.beginRowIndex][range.beginColIndex].cellIndex
                );

            // 这段关于行表头或者列表头的特殊处理会导致表头合并范围错误
            // 为什么有这段代码的原因未明,暂且注释掉,希望原作者看到后出面说明下
            // if (
            //   leftTopCell.tagName == "TH" &&
            //   range.endRowIndex !== range.beginRowIndex
            // ) {
            //   var index = this.indexTable,
            //     info = this.getCellInfo(leftTopCell);
            //   leftTopCell = this.getCell(1, index[1][info.colIndex].cellIndex);
            //   range = this.getCellsRange(
            //     leftTopCell,
            //     this.getCell(
            //       index[this.rowsNum - 1][info.colIndex].rowIndex,
            //       index[this.rowsNum - 1][info.colIndex].cellIndex
            //     )
            //   );
            // }

            // 删除剩余的Cells
            var cells = this.getCells(range);
            for (var i = 0, ci; (ci = cells[i++]);) {
                if (ci !== leftTopCell) {
                    this.moveContent(leftTopCell, ci);
                    this.deleteCell(ci);
                }
            }
            // 修改左上角Cell的rowSpan和colSpan,并调整宽度属性设置
            leftTopCell.rowSpan = range.endRowIndex - range.beginRowIndex + 1;
            leftTopCell.rowSpan > 1 && leftTopCell.removeAttribute("height");
            leftTopCell.colSpan = range.endColIndex - range.beginColIndex + 1;
            leftTopCell.colSpan > 1 && leftTopCell.removeAttribute("width");
            if (leftTopCell.rowSpan == this.rowsNum && leftTopCell.colSpan != 1) {
                leftTopCell.colSpan = 1;
            }

            if (leftTopCell.colSpan == this.colsNum && leftTopCell.rowSpan != 1) {
                var rowIndex = leftTopCell.parentNode.rowIndex;
                //解决IE下的表格操作问题
                if (this.table.deleteRow) {
                    for (
                        var i = rowIndex + 1,
                            curIndex = rowIndex + 1,
                            len = leftTopCell.rowSpan;
                        i < len;
                        i++
                    ) {
                        this.table.deleteRow(curIndex);
                    }
                } else {
                    for (var i = 0, len = leftTopCell.rowSpan - 1; i < len; i++) {
                        var row = this.table.rows[rowIndex + 1];
                        row.parentNode.removeChild(row);
                    }
                }
                leftTopCell.rowSpan = 1;
            }
            this.update();
        },
        /**
         * 插入一行单元格
         */
        insertRow: function (rowIndex, sourceCell) {
            var numCols = this.colsNum,
                table = this.table,
                row = table.insertRow(rowIndex),
                cell,
                thead = null,
                isInsertTitle =
                    typeof sourceCell == "string" && sourceCell.toUpperCase() == "TH";

            function replaceTdToTh(colIndex, cell, tableRow) {
                if (colIndex == 0) {
                    var tr = tableRow.nextSibling || tableRow.previousSibling,
                        th = tr.cells[colIndex];
                    if (th.tagName == "TH") {
                        th = cell.ownerDocument.createElement("th");
                        th.appendChild(cell.firstChild);
                        tableRow.insertBefore(th, cell);
                        domUtils.remove(cell);
                    }
                } else {
                    if (cell.tagName == "TH") {
                        var td = cell.ownerDocument.createElement("td");
                        td.appendChild(cell.firstChild);
                        tableRow.insertBefore(td, cell);
                        domUtils.remove(cell);
                    }
                }
            }

            //首行直接插入,无需考虑部分单元格被rowspan的情况
            if (rowIndex == 0 || rowIndex == this.rowsNum) {
                for (var colIndex = 0; colIndex < numCols; colIndex++) {
                    cell = this.cloneCell(sourceCell, true);
                    this.setCellContent(cell);
                    cell.getAttribute("vAlign") &&
                    cell.setAttribute("vAlign", cell.getAttribute("vAlign"));
                    row.appendChild(cell);
                    if (!isInsertTitle) replaceTdToTh(colIndex, cell, row);
                }

                if (isInsertTitle) {
                    thead = table.createTHead();
                    thead.insertBefore(row, thead.firstChild);
                }
            } else {
                var infoRow = this.indexTable[rowIndex],
                    cellIndex = 0;
                for (colIndex = 0; colIndex < numCols; colIndex++) {
                    var cellInfo = infoRow[colIndex];
                    //如果存在某个单元格的rowspan穿过待插入行的位置,则修改该单元格的rowspan即可,无需插入单元格
                    if (cellInfo.rowIndex < rowIndex) {
                        cell = this.getCell(cellInfo.rowIndex, cellInfo.cellIndex);
                        cell.rowSpan = cellInfo.rowSpan + 1;
                    } else {
                        cell = this.cloneCell(sourceCell, true);
                        this.setCellContent(cell);
                        row.appendChild(cell);
                    }
                    if (!isInsertTitle) replaceTdToTh(colIndex, cell, row);
                }
            }
            //框选时插入不触发contentchange,需要手动更新索引。
            this.update();
            return row;
        },
        /**
         * 删除一行单元格
         * @param rowIndex
         */
        deleteRow: function (rowIndex) {
            var row = this.table.rows[rowIndex],
                infoRow = this.indexTable[rowIndex],
                colsNum = this.colsNum,
                count = 0; //处理计数
            for (var colIndex = 0; colIndex < colsNum;) {
                var cellInfo = infoRow[colIndex],
                    cell = this.getCell(cellInfo.rowIndex, cellInfo.cellIndex);
                if (cell.rowSpan > 1) {
                    if (cellInfo.rowIndex == rowIndex) {
                        var clone = cell.cloneNode(true);
                        clone.rowSpan = cell.rowSpan - 1;
                        clone.innerHTML = "";
                        cell.rowSpan = 1;
                        var nextRowIndex = rowIndex + 1,
                            nextRow = this.table.rows[nextRowIndex],
                            insertCellIndex,
                            preMerged =
                                this.getPreviewMergedCellsNum(nextRowIndex, colIndex) - count;
                        if (preMerged < colIndex) {
                            insertCellIndex = colIndex - preMerged - 1;
                            //nextRow.insertCell(insertCellIndex);
                            domUtils.insertAfter(nextRow.cells[insertCellIndex], clone);
                        } else {
                            if (nextRow.cells.length)
                                nextRow.insertBefore(clone, nextRow.cells[0]);
                        }
                        count += 1;
                        //cell.parentNode.removeChild(cell);
                    }
                }
                colIndex += cell.colSpan || 1;
            }
            var deleteTds = [],
                cacheMap = {};
            for (colIndex = 0; colIndex < colsNum; colIndex++) {
                var tmpRowIndex = infoRow[colIndex].rowIndex,
                    tmpCellIndex = infoRow[colIndex].cellIndex,
                    key = tmpRowIndex + "_" + tmpCellIndex;
                if (cacheMap[key]) continue;
                cacheMap[key] = 1;
                cell = this.getCell(tmpRowIndex, tmpCellIndex);
                deleteTds.push(cell);
            }
            var mergeTds = [];
            utils.each(deleteTds, function (td) {
                if (td.rowSpan == 1) {
                    td.parentNode.removeChild(td);
                } else {
                    mergeTds.push(td);
                }
            });
            utils.each(mergeTds, function (td) {
                td.rowSpan--;
            });
            row.parentNode.removeChild(row);
            //浏览器方法本身存在bug,采用自定义方法删除
            //this.table.deleteRow(rowIndex);
            this.update();
        },
        insertCol: function (colIndex, sourceCell, defaultValue) {
            var rowsNum = this.rowsNum,
                rowIndex = 0,
                tableRow,
                cell,
                backWidth = parseInt(
                    (this.table.offsetWidth -
                        (this.colsNum + 1) * 20 -
                        (this.colsNum + 1)) /
                    (this.colsNum + 1),
                    10
                ),
                isInsertTitleCol =
                    typeof sourceCell == "string" && sourceCell.toUpperCase() == "TH";

            function replaceTdToTh(rowIndex, cell, tableRow) {
                if (rowIndex == 0) {
                    var th = cell.nextSibling || cell.previousSibling;
                    if (th.tagName == "TH") {
                        th = cell.ownerDocument.createElement("th");
                        th.appendChild(cell.firstChild);
                        tableRow.insertBefore(th, cell);
                        domUtils.remove(cell);
                    }
                } else {
                    if (cell.tagName == "TH") {
                        var td = cell.ownerDocument.createElement("td");
                        td.appendChild(cell.firstChild);
                        tableRow.insertBefore(td, cell);
                        domUtils.remove(cell);
                    }
                }
            }

            var preCell;
            if (colIndex == 0 || colIndex == this.colsNum) {
                for (; rowIndex < rowsNum; rowIndex++) {
                    tableRow = this.table.rows[rowIndex];
                    preCell =
                        tableRow.cells[colIndex == 0 ? colIndex : tableRow.cells.length];
                    cell = this.cloneCell(sourceCell, true); //tableRow.insertCell(colIndex == 0 ? colIndex : tableRow.cells.length);
                    this.setCellContent(cell);
                    cell.setAttribute("vAlign", cell.getAttribute("vAlign"));
                    preCell && cell.setAttribute("width", preCell.getAttribute("width"));
                    if (!colIndex) {
                        tableRow.insertBefore(cell, tableRow.cells[0]);
                    } else {
                        domUtils.insertAfter(
                            tableRow.cells[tableRow.cells.length - 1],
                            cell
                        );
                    }
                    if (!isInsertTitleCol) replaceTdToTh(rowIndex, cell, tableRow);
                }
            } else {
                for (; rowIndex < rowsNum; rowIndex++) {
                    var cellInfo = this.indexTable[rowIndex][colIndex];
                    if (cellInfo.colIndex < colIndex) {
                        cell = this.getCell(cellInfo.rowIndex, cellInfo.cellIndex);
                        cell.colSpan = cellInfo.colSpan + 1;
                    } else {
                        tableRow = this.table.rows[rowIndex];
                        preCell = tableRow.cells[cellInfo.cellIndex];

                        cell = this.cloneCell(sourceCell, true); //tableRow.insertCell(cellInfo.cellIndex);
                        this.setCellContent(cell);
                        cell.setAttribute("vAlign", cell.getAttribute("vAlign"));
                        preCell &&
                        cell.setAttribute("width", preCell.getAttribute("width"));
                        //防止IE下报错
                        preCell
                            ? tableRow.insertBefore(cell, preCell)
                            : tableRow.appendChild(cell);
                    }
                    if (!isInsertTitleCol) replaceTdToTh(rowIndex, cell, tableRow);
                }
            }
            //框选时插入不触发contentchange,需要手动更新索引
            this.update();
            this.updateWidth(
                backWidth,
                defaultValue || {tdPadding: 10, tdBorder: 1}
            );
        },
        updateWidth: function (width, defaultValue) {
            var table = this.table,
                tmpWidth =
                    UETable.getWidth(table) -
                    defaultValue.tdPadding * 2 -
                    defaultValue.tdBorder +
                    width;
            if (tmpWidth < table.ownerDocument.body.offsetWidth) {
                table.setAttribute("width", tmpWidth);
                return;
            }
            var tds = domUtils.getElementsByTagName(this.table, "td th");
            utils.each(tds, function (td) {
                td.setAttribute("width", width);
            });
        },
        deleteCol: function (colIndex) {
            var indexTable = this.indexTable,
                tableRows = this.table.rows,
                backTableWidth = this.table.getAttribute("width"),
                backTdWidth = 0,
                rowsNum = this.rowsNum,
                cacheMap = {};
            for (var rowIndex = 0; rowIndex < rowsNum;) {
                var infoRow = indexTable[rowIndex],
                    cellInfo = infoRow[colIndex],
                    key = cellInfo.rowIndex + "_" + cellInfo.colIndex;
                // 跳过已经处理过的Cell
                if (cacheMap[key]) continue;
                cacheMap[key] = 1;
                var cell = this.getCell(cellInfo.rowIndex, cellInfo.cellIndex);
                if (!backTdWidth)
                    backTdWidth =
                        cell && parseInt(cell.offsetWidth / cell.colSpan, 10).toFixed(0);
                // 如果Cell的colSpan大于1, 就修改colSpan, 否则就删掉这个Cell
                if (cell.colSpan > 1) {
                    cell.colSpan--;
                } else {
                    tableRows[rowIndex].deleteCell(cellInfo.cellIndex);
                }
                rowIndex += cellInfo.rowSpan || 1;
            }
            this.table.setAttribute("width", backTableWidth - backTdWidth);
            this.update();
        },
        splitToCells: function (cell) {
            var me = this,
                cells = this.splitToRows(cell);
            utils.each(cells, function (cell) {
                me.splitToCols(cell);
            });
        },
        splitToRows: function (cell) {
            var cellInfo = this.getCellInfo(cell),
                rowIndex = cellInfo.rowIndex,
                colIndex = cellInfo.colIndex,
                results = [];
            // 修改Cell的rowSpan
            cell.rowSpan = 1;
            results.push(cell);
            // 补齐单元格
            for (
                var i = rowIndex, endRow = rowIndex + cellInfo.rowSpan;
                i < endRow;
                i++
            ) {
                if (i == rowIndex) continue;
                var tableRow = this.table.rows[i],
                    tmpCell = tableRow.insertCell(
                        colIndex - this.getPreviewMergedCellsNum(i, colIndex)
                    );
                tmpCell.colSpan = cellInfo.colSpan;
                this.setCellContent(tmpCell);
                tmpCell.setAttribute("vAlign", cell.getAttribute("vAlign"));
                tmpCell.setAttribute("align", cell.getAttribute("align"));
                if (cell.style.cssText) {
                    tmpCell.style.cssText = cell.style.cssText;
                }
                results.push(tmpCell);
            }
            this.update();
            return results;
        },
        getPreviewMergedCellsNum: function (rowIndex, colIndex) {
            var indexRow = this.indexTable[rowIndex],
                num = 0;
            for (var i = 0; i < colIndex;) {
                var colSpan = indexRow[i].colSpan,
                    tmpRowIndex = indexRow[i].rowIndex;
                num += colSpan - (tmpRowIndex == rowIndex ? 1 : 0);
                i += colSpan;
            }
            return num;
        },
        splitToCols: function (cell) {
            var backWidth = (cell.offsetWidth / cell.colSpan - 22).toFixed(0),
                cellInfo = this.getCellInfo(cell),
                rowIndex = cellInfo.rowIndex,
                colIndex = cellInfo.colIndex,
                results = [];
            // 修改Cell的rowSpan
            cell.colSpan = 1;
            cell.setAttribute("width", backWidth);
            results.push(cell);
            // 补齐单元格
            for (
                var j = colIndex, endCol = colIndex + cellInfo.colSpan;
                j < endCol;
                j++
            ) {
                if (j == colIndex) continue;
                var tableRow = this.table.rows[rowIndex],
                    tmpCell = tableRow.insertCell(
                        this.indexTable[rowIndex][j].cellIndex + 1
                    );
                tmpCell.rowSpan = cellInfo.rowSpan;
                this.setCellContent(tmpCell);
                tmpCell.setAttribute("vAlign", cell.getAttribute("vAlign"));
                tmpCell.setAttribute("align", cell.getAttribute("align"));
                tmpCell.setAttribute("width", backWidth);
                if (cell.style.cssText) {
                    tmpCell.style.cssText = cell.style.cssText;
                }
                //处理th的情况
                if (cell.tagName == "TH") {
                    var th = cell.ownerDocument.createElement("th");
                    th.appendChild(tmpCell.firstChild);
                    th.setAttribute("vAlign", cell.getAttribute("vAlign"));
                    th.rowSpan = tmpCell.rowSpan;
                    tableRow.insertBefore(th, tmpCell);
                    domUtils.remove(tmpCell);
                }
                results.push(tmpCell);
            }
            this.update();
            return results;
        },
        isLastCell: function (cell, rowsNum, colsNum) {
            rowsNum = rowsNum || this.rowsNum;
            colsNum = colsNum || this.colsNum;
            var cellInfo = this.getCellInfo(cell);
            return (
                cellInfo.rowIndex + cellInfo.rowSpan == rowsNum &&
                cellInfo.colIndex + cellInfo.colSpan == colsNum
            );
        },
        getLastCell: function (cells) {
            cells = cells || this.table.getElementsByTagName("td");
            var firstInfo = this.getCellInfo(cells[0]);
            var me = this,
                last = cells[0],
                tr = last.parentNode,
                cellsNum = 0,
                cols = 0,
                rows;
            utils.each(cells, function (cell) {
                if (cell.parentNode == tr) cols += cell.colSpan || 1;
                cellsNum += cell.rowSpan * cell.colSpan || 1;
            });
            rows = cellsNum / cols;
            utils.each(cells, function (cell) {
                if (me.isLastCell(cell, rows, cols)) {
                    last = cell;
                    return false;
                }
            });
            return last;
        },
        selectRow: function (rowIndex) {
            var indexRow = this.indexTable[rowIndex],
                start = this.getCell(indexRow[0].rowIndex, indexRow[0].cellIndex),
                end = this.getCell(
                    indexRow[this.colsNum - 1].rowIndex,
                    indexRow[this.colsNum - 1].cellIndex
                ),
                range = this.getCellsRange(start, end);
            this.setSelected(range);
        },
        selectTable: function () {
            var tds = this.table.getElementsByTagName("td"),
                range = this.getCellsRange(tds[0], tds[tds.length - 1]);
            this.setSelected(range);
        },
        setBackground: function (cells, value) {
            if (typeof value === "string") {
                utils.each(cells, function (cell) {
                    cell.style.backgroundColor = value;
                });
            } else if (typeof value === "object") {
                value = utils.extend(
                    {
                        repeat: true,
                        colorList: ["#ddd", "#fff"]
                    },
                    value
                );
                var rowIndex = this.getCellInfo(cells[0]).rowIndex,
                    count = 0,
                    colors = value.colorList,
                    getColor = function (list, index, repeat) {
                        return list[index]
                            ? list[index]
                            : repeat ? list[index % list.length] : "";
                    };
                for (var i = 0, cell; (cell = cells[i++]);) {
                    var cellInfo = this.getCellInfo(cell);
                    cell.style.backgroundColor = getColor(
                        colors,
                        rowIndex + count == cellInfo.rowIndex ? count : ++count,
                        value.repeat
                    );
                }
            }
        },
        removeBackground: function (cells) {
            utils.each(cells, function (cell) {
                cell.style.backgroundColor = "";
            });
        }
    };

    function showError(e) {
    }
})();


// plugins/table.cmds.js
/**
 * Created with JetBrains PhpStorm.
 * User: taoqili
 * Date: 13-2-20
 * Time: 下午6:25
 * To change this template use File | Settings | File Templates.
 */
(function () {
    var UT = UE.UETable,
        getTableItemsByRange = function (editor) {
            return UT.getTableItemsByRange(editor);
        },
        getUETableBySelected = function (editor) {
            return UT.getUETableBySelected(editor);
        },
        getDefaultValue = function (editor, table) {
            return UT.getDefaultValue(editor, table);
        },
        getUETable = function (tdOrTable) {
            return UT.getUETable(tdOrTable);
        };

    UE.commands["inserttable"] = {
        queryCommandState: function () {
            return getTableItemsByRange(this).table ? -1 : 0;
        },
        execCommand: function (cmd, opt) {
            function createTable(opt, tdWidth) {
                var html = [],
                    rowsNum = opt.numRows,
                    colsNum = opt.numCols;
                for (var r = 0; r < rowsNum; r++) {
                    html.push("<tr" + (r == 0 ? ' class="firstRow"' : "") + ">");
                    for (var c = 0; c < colsNum; c++) {
                        html.push(
                            '<td width="' +
                            tdWidth +
                            '"  vAlign="' +
                            opt.tdvalign +
                            '" >' +
                            (browser.ie && browser.version < 11
                                ? domUtils.fillChar
                                : "<br/>") +
                            "</td>"
                        );
                    }
                    html.push("</tr>");
                }
                //禁止指定table-width
                return "<table><tbody>" + html.join("") + "</tbody></table>";
            }

            if (!opt) {
                opt = utils.extend(
                    {},
                    {
                        numCols: this.options.defaultCols,
                        numRows: this.options.defaultRows,
                        tdvalign: this.options.tdvalign
                    }
                );
            }
            var me = this;
            var range = this.selection.getRange(),
                start = range.startContainer,
                firstParentBlock =
                    domUtils.findParent(
                        start,
                        function (node) {
                            return domUtils.isBlockElm(node);
                        },
                        true
                    ) || me.body;

            var defaultValue = getDefaultValue(me),
                tableWidth = firstParentBlock.offsetWidth,
                tdWidth = Math.floor(
                    tableWidth / opt.numCols -
                    defaultValue.tdPadding * 2 -
                    defaultValue.tdBorder
                );

            //todo其他属性
            !opt.tdvalign && (opt.tdvalign = me.options.tdvalign);
            me.execCommand("inserthtml", createTable(opt, tdWidth));
        }
    };

    UE.commands["insertparagraphbeforetable"] = {
        queryCommandState: function () {
            return getTableItemsByRange(this).cell ? 0 : -1;
        },
        execCommand: function () {
            var table = getTableItemsByRange(this).table;
            if (table) {
                var p = this.document.createElement("p");
                p.innerHTML = browser.ie ? "&nbsp;" : "<br />";
                table.parentNode.insertBefore(p, table);
                this.selection.getRange().setStart(p, 0).setCursor();
            }
        }
    };

    UE.commands["deletetable"] = {
        queryCommandState: function () {
            var rng = this.selection.getRange();
            return domUtils.findParentByTagName(rng.startContainer, "table", true)
                ? 0
                : -1;
        },
        execCommand: function (cmd, table) {
            var rng = this.selection.getRange();
            table =
                table ||
                domUtils.findParentByTagName(rng.startContainer, "table", true);
            if (table) {
                var next = table.nextSibling;
                if (!next) {
                    next = domUtils.createElement(this.document, "p", {
                        innerHTML: browser.ie ? domUtils.fillChar : "<br/>"
                    });
                    table.parentNode.insertBefore(next, table);
                }
                domUtils.remove(table);
                rng = this.selection.getRange();
                if (next.nodeType == 3) {
                    rng.setStartBefore(next);
                } else {
                    rng.setStart(next, 0);
                }
                rng.setCursor(false, true);
                this.fireEvent("tablehasdeleted");
            }
        }
    };
    UE.commands["cellalign"] = {
        queryCommandState: function () {
            return getSelectedArr(this).length ? 0 : -1;
        },
        execCommand: function (cmd, align) {
            var selectedTds = getSelectedArr(this);
            if (selectedTds.length) {
                for (var i = 0, ci; (ci = selectedTds[i++]);) {
                    ci.setAttribute("align", align);
                }
            }
        }
    };
    UE.commands["cellvalign"] = {
        queryCommandState: function () {
            return getSelectedArr(this).length ? 0 : -1;
        },
        execCommand: function (cmd, valign) {
            var selectedTds = getSelectedArr(this);
            if (selectedTds.length) {
                for (var i = 0, ci; (ci = selectedTds[i++]);) {
                    ci.setAttribute("vAlign", valign);
                }
            }
        }
    };
    UE.commands["insertcaption"] = {
        queryCommandState: function () {
            var table = getTableItemsByRange(this).table;
            if (table) {
                return table.getElementsByTagName("caption").length == 0 ? 1 : -1;
            }
            return -1;
        },
        execCommand: function () {
            var table = getTableItemsByRange(this).table;
            if (table) {
                var caption = this.document.createElement("caption");
                caption.innerHTML = browser.ie ? domUtils.fillChar : "<br/>";
                table.insertBefore(caption, table.firstChild);
                var range = this.selection.getRange();
                range.setStart(caption, 0).setCursor();
            }
        }
    };
    UE.commands["deletecaption"] = {
        queryCommandState: function () {
            var rng = this.selection.getRange(),
                table = domUtils.findParentByTagName(rng.startContainer, "table");
            if (table) {
                return table.getElementsByTagName("caption").length == 0 ? -1 : 1;
            }
            return -1;
        },
        execCommand: function () {
            var rng = this.selection.getRange(),
                table = domUtils.findParentByTagName(rng.startContainer, "table");
            if (table) {
                domUtils.remove(table.getElementsByTagName("caption")[0]);
                var range = this.selection.getRange();
                range.setStart(table.rows[0].cells[0], 0).setCursor();
            }
        }
    };
    UE.commands["inserttitle"] = {
        queryCommandState: function () {
            var table = getTableItemsByRange(this).table;
            if (table) {
                var firstRow = table.rows[0];
                return firstRow.cells[
                firstRow.cells.length - 1
                    ].tagName.toLowerCase() != "th"
                    ? 0
                    : -1;
            }
            return -1;
        },
        execCommand: function () {
            var table = getTableItemsByRange(this).table;
            if (table) {
                getUETable(table).insertRow(0, "th");
            }
            var th = table.getElementsByTagName("th")[0];
            this.selection.getRange().setStart(th, 0).setCursor(false, true);
        }
    };
    UE.commands["deletetitle"] = {
        queryCommandState: function () {
            var table = getTableItemsByRange(this).table;
            if (table) {
                var firstRow = table.rows[0];
                return firstRow.cells[
                firstRow.cells.length - 1
                    ].tagName.toLowerCase() == "th"
                    ? 0
                    : -1;
            }
            return -1;
        },
        execCommand: function () {
            var table = getTableItemsByRange(this).table;
            if (table) {
                domUtils.remove(table.rows[0]);
            }
            var td = table.getElementsByTagName("td")[0];
            this.selection.getRange().setStart(td, 0).setCursor(false, true);
        }
    };
    UE.commands["inserttitlecol"] = {
        queryCommandState: function () {
            var table = getTableItemsByRange(this).table;
            if (table) {
                var lastRow = table.rows[table.rows.length - 1];
                return lastRow.getElementsByTagName("th").length ? -1 : 0;
            }
            return -1;
        },
        execCommand: function (cmd) {
            var table = getTableItemsByRange(this).table;
            if (table) {
                getUETable(table).insertCol(0, "th");
            }
            resetTdWidth(table, this);
            var th = table.getElementsByTagName("th")[0];
            this.selection.getRange().setStart(th, 0).setCursor(false, true);
        }
    };
    UE.commands["deletetitlecol"] = {
        queryCommandState: function () {
            var table = getTableItemsByRange(this).table;
            if (table) {
                var lastRow = table.rows[table.rows.length - 1];
                return lastRow.getElementsByTagName("th").length ? 0 : -1;
            }
            return -1;
        },
        execCommand: function () {
            var table = getTableItemsByRange(this).table;
            if (table) {
                for (var i = 0; i < table.rows.length; i++) {
                    domUtils.remove(table.rows[i].children[0]);
                }
            }
            resetTdWidth(table, this);
            var td = table.getElementsByTagName("td")[0];
            this.selection.getRange().setStart(td, 0).setCursor(false, true);
        }
    };

    UE.commands["mergeright"] = {
        queryCommandState: function (cmd) {
            var tableItems = getTableItemsByRange(this),
                table = tableItems.table,
                cell = tableItems.cell;

            if (!table || !cell) return -1;
            var ut = getUETable(table);
            if (ut.selectedTds.length) return -1;

            var cellInfo = ut.getCellInfo(cell),
                rightColIndex = cellInfo.colIndex + cellInfo.colSpan;
            if (rightColIndex >= ut.colsNum) return -1; // 如果处于最右边则不能向右合并

            var rightCellInfo = ut.indexTable[cellInfo.rowIndex][rightColIndex],
                rightCell =
                    table.rows[rightCellInfo.rowIndex].cells[rightCellInfo.cellIndex];
            if (!rightCell || cell.tagName != rightCell.tagName) return -1; // TH和TD不能相互合并

            // 当且仅当两个Cell的开始列号和结束列号一致时能进行合并
            return rightCellInfo.rowIndex == cellInfo.rowIndex &&
            rightCellInfo.rowSpan == cellInfo.rowSpan
                ? 0
                : -1;
        },
        execCommand: function (cmd) {
            var rng = this.selection.getRange(),
                bk = rng.createBookmark(true);
            var cell = getTableItemsByRange(this).cell,
                ut = getUETable(cell);
            ut.mergeRight(cell);
            rng.moveToBookmark(bk).select();
        }
    };
    UE.commands["mergedown"] = {
        queryCommandState: function (cmd) {
            var tableItems = getTableItemsByRange(this),
                table = tableItems.table,
                cell = tableItems.cell;

            if (!table || !cell) return -1;
            var ut = getUETable(table);
            if (ut.selectedTds.length) return -1;

            var cellInfo = ut.getCellInfo(cell),
                downRowIndex = cellInfo.rowIndex + cellInfo.rowSpan;
            if (downRowIndex >= ut.rowsNum) return -1; // 如果处于最下边则不能向下合并

            var downCellInfo = ut.indexTable[downRowIndex][cellInfo.colIndex],
                downCell =
                    table.rows[downCellInfo.rowIndex].cells[downCellInfo.cellIndex];
            if (!downCell || cell.tagName != downCell.tagName) return -1; // TH和TD不能相互合并

            // 当且仅当两个Cell的开始列号和结束列号一致时能进行合并
            return downCellInfo.colIndex == cellInfo.colIndex &&
            downCellInfo.colSpan == cellInfo.colSpan
                ? 0
                : -1;
        },
        execCommand: function () {
            var rng = this.selection.getRange(),
                bk = rng.createBookmark(true);
            var cell = getTableItemsByRange(this).cell,
                ut = getUETable(cell);
            ut.mergeDown(cell);
            rng.moveToBookmark(bk).select();
        }
    };
    UE.commands["mergecells"] = {
        queryCommandState: function () {
            return getUETableBySelected(this) ? 0 : -1;
        },
        execCommand: function () {
            var ut = getUETableBySelected(this);
            if (ut && ut.selectedTds.length) {
                var cell = ut.selectedTds[0];
                ut.mergeRange();
                var rng = this.selection.getRange();
                if (domUtils.isEmptyBlock(cell)) {
                    rng.setStart(cell, 0).collapse(true);
                } else {
                    rng.selectNodeContents(cell);
                }
                rng.select();
            }
        }
    };
    UE.commands["insertrow"] = {
        queryCommandState: function () {
            var tableItems = getTableItemsByRange(this),
                cell = tableItems.cell;
            return cell &&
            (cell.tagName == "TD" ||
                (cell.tagName == "TH" &&
                    tableItems.tr !== tableItems.table.rows[0])) &&
            getUETable(tableItems.table).rowsNum < this.options.maxRowNum
                ? 0
                : -1;
        },
        execCommand: function () {
            var rng = this.selection.getRange(),
                bk = rng.createBookmark(true);
            var tableItems = getTableItemsByRange(this),
                cell = tableItems.cell,
                table = tableItems.table,
                ut = getUETable(table),
                cellInfo = ut.getCellInfo(cell);
            //ut.insertRow(!ut.selectedTds.length ? cellInfo.rowIndex:ut.cellsRange.beginRowIndex,'');
            if (!ut.selectedTds.length) {
                ut.insertRow(cellInfo.rowIndex, cell);
            } else {
                var range = ut.cellsRange;
                for (
                    var i = 0, len = range.endRowIndex - range.beginRowIndex + 1;
                    i < len;
                    i++
                ) {
                    ut.insertRow(range.beginRowIndex, cell);
                }
            }
            rng.moveToBookmark(bk).select();
            if (table.getAttribute("interlaced") === "enabled")
                this.fireEvent("interlacetable", table);
        }
    };
    //后插入行
    UE.commands["insertrownext"] = {
        queryCommandState: function () {
            var tableItems = getTableItemsByRange(this),
                cell = tableItems.cell;
            return cell &&
            cell.tagName == "TD" &&
            getUETable(tableItems.table).rowsNum < this.options.maxRowNum
                ? 0
                : -1;
        },
        execCommand: function () {
            var rng = this.selection.getRange(),
                bk = rng.createBookmark(true);
            var tableItems = getTableItemsByRange(this),
                cell = tableItems.cell,
                table = tableItems.table,
                ut = getUETable(table),
                cellInfo = ut.getCellInfo(cell);
            //ut.insertRow(!ut.selectedTds.length? cellInfo.rowIndex + cellInfo.rowSpan : ut.cellsRange.endRowIndex + 1,'');
            if (!ut.selectedTds.length) {
                ut.insertRow(cellInfo.rowIndex + cellInfo.rowSpan, cell);
            } else {
                var range = ut.cellsRange;
                for (
                    var i = 0, len = range.endRowIndex - range.beginRowIndex + 1;
                    i < len;
                    i++
                ) {
                    ut.insertRow(range.endRowIndex + 1, cell);
                }
            }
            rng.moveToBookmark(bk).select();
            if (table.getAttribute("interlaced") === "enabled")
                this.fireEvent("interlacetable", table);
        }
    };
    UE.commands["deleterow"] = {
        queryCommandState: function () {
            var tableItems = getTableItemsByRange(this);
            return tableItems.cell ? 0 : -1;
        },
        execCommand: function () {
            var cell = getTableItemsByRange(this).cell,
                ut = getUETable(cell),
                cellsRange = ut.cellsRange,
                cellInfo = ut.getCellInfo(cell),
                preCell = ut.getVSideCell(cell),
                nextCell = ut.getVSideCell(cell, true),
                rng = this.selection.getRange();
            if (utils.isEmptyObject(cellsRange)) {
                ut.deleteRow(cellInfo.rowIndex);
            } else {
                for (
                    var i = cellsRange.beginRowIndex;
                    i < cellsRange.endRowIndex + 1;
                    i++
                ) {
                    ut.deleteRow(cellsRange.beginRowIndex);
                }
            }
            var table = ut.table;
            if (!table.getElementsByTagName("td").length) {
                var nextSibling = table.nextSibling;
                domUtils.remove(table);
                if (nextSibling) {
                    rng.setStart(nextSibling, 0).setCursor(false, true);
                }
            } else {
                if (
                    cellInfo.rowSpan == 1 ||
                    cellInfo.rowSpan ==
                    cellsRange.endRowIndex - cellsRange.beginRowIndex + 1
                ) {
                    if (nextCell || preCell)
                        rng.selectNodeContents(nextCell || preCell).setCursor(false, true);
                } else {
                    var newCell = ut.getCell(
                        cellInfo.rowIndex,
                        ut.indexTable[cellInfo.rowIndex][cellInfo.colIndex].cellIndex
                    );
                    if (newCell) rng.selectNodeContents(newCell).setCursor(false, true);
                }
            }
            if (table.getAttribute("interlaced") === "enabled")
                this.fireEvent("interlacetable", table);
        }
    };
    UE.commands["insertcol"] = {
        queryCommandState: function (cmd) {
            var tableItems = getTableItemsByRange(this),
                cell = tableItems.cell;
            return cell &&
            (cell.tagName == "TD" ||
                (cell.tagName == "TH" && cell !== tableItems.tr.cells[0])) &&
            getUETable(tableItems.table).colsNum < this.options.maxColNum
                ? 0
                : -1;
        },
        execCommand: function (cmd) {
            var rng = this.selection.getRange(),
                bk = rng.createBookmark(true);
            if (this.queryCommandState(cmd) == -1) return;
            var cell = getTableItemsByRange(this).cell,
                ut = getUETable(cell),
                cellInfo = ut.getCellInfo(cell);

            //ut.insertCol(!ut.selectedTds.length ? cellInfo.colIndex:ut.cellsRange.beginColIndex);
            if (!ut.selectedTds.length) {
                ut.insertCol(cellInfo.colIndex, cell);
            } else {
                var range = ut.cellsRange;
                for (
                    var i = 0, len = range.endColIndex - range.beginColIndex + 1;
                    i < len;
                    i++
                ) {
                    ut.insertCol(range.beginColIndex, cell);
                }
            }
            rng.moveToBookmark(bk).select(true);
        }
    };
    UE.commands["insertcolnext"] = {
        queryCommandState: function () {
            var tableItems = getTableItemsByRange(this),
                cell = tableItems.cell;
            return cell &&
            getUETable(tableItems.table).colsNum < this.options.maxColNum
                ? 0
                : -1;
        },
        execCommand: function () {
            var rng = this.selection.getRange(),
                bk = rng.createBookmark(true);
            var cell = getTableItemsByRange(this).cell,
                ut = getUETable(cell),
                cellInfo = ut.getCellInfo(cell);
            //ut.insertCol(!ut.selectedTds.length ? cellInfo.colIndex + cellInfo.colSpan:ut.cellsRange.endColIndex +1);
            if (!ut.selectedTds.length) {
                ut.insertCol(cellInfo.colIndex + cellInfo.colSpan, cell);
            } else {
                var range = ut.cellsRange;
                for (
                    var i = 0, len = range.endColIndex - range.beginColIndex + 1;
                    i < len;
                    i++
                ) {
                    ut.insertCol(range.endColIndex + 1, cell);
                }
            }
            rng.moveToBookmark(bk).select();
        }
    };

    UE.commands["deletecol"] = {
        queryCommandState: function () {
            var tableItems = getTableItemsByRange(this);
            return tableItems.cell ? 0 : -1;
        },
        execCommand: function () {
            var cell = getTableItemsByRange(this).cell,
                ut = getUETable(cell),
                range = ut.cellsRange,
                cellInfo = ut.getCellInfo(cell),
                preCell = ut.getHSideCell(cell),
                nextCell = ut.getHSideCell(cell, true);
            if (utils.isEmptyObject(range)) {
                ut.deleteCol(cellInfo.colIndex);
            } else {
                for (var i = range.beginColIndex; i < range.endColIndex + 1; i++) {
                    ut.deleteCol(range.beginColIndex);
                }
            }
            var table = ut.table,
                rng = this.selection.getRange();

            if (!table.getElementsByTagName("td").length) {
                var nextSibling = table.nextSibling;
                domUtils.remove(table);
                if (nextSibling) {
                    rng.setStart(nextSibling, 0).setCursor(false, true);
                }
            } else {
                if (domUtils.inDoc(cell, this.document)) {
                    rng.setStart(cell, 0).setCursor(false, true);
                } else {
                    if (nextCell && domUtils.inDoc(nextCell, this.document)) {
                        rng.selectNodeContents(nextCell).setCursor(false, true);
                    } else {
                        if (preCell && domUtils.inDoc(preCell, this.document)) {
                            rng.selectNodeContents(preCell).setCursor(true, true);
                        }
                    }
                }
            }
        }
    };
    UE.commands["splittocells"] = {
        queryCommandState: function () {
            var tableItems = getTableItemsByRange(this),
                cell = tableItems.cell;
            if (!cell) return -1;
            var ut = getUETable(tableItems.table);
            if (ut.selectedTds.length > 0) return -1;
            return cell && (cell.colSpan > 1 || cell.rowSpan > 1) ? 0 : -1;
        },
        execCommand: function () {
            var rng = this.selection.getRange(),
                bk = rng.createBookmark(true);
            var cell = getTableItemsByRange(this).cell,
                ut = getUETable(cell);
            ut.splitToCells(cell);
            rng.moveToBookmark(bk).select();
        }
    };
    UE.commands["splittorows"] = {
        queryCommandState: function () {
            var tableItems = getTableItemsByRange(this),
                cell = tableItems.cell;
            if (!cell) return -1;
            var ut = getUETable(tableItems.table);
            if (ut.selectedTds.length > 0) return -1;
            return cell && cell.rowSpan > 1 ? 0 : -1;
        },
        execCommand: function () {
            var rng = this.selection.getRange(),
                bk = rng.createBookmark(true);
            var cell = getTableItemsByRange(this).cell,
                ut = getUETable(cell);
            ut.splitToRows(cell);
            rng.moveToBookmark(bk).select();
        }
    };
    UE.commands["splittocols"] = {
        queryCommandState: function () {
            var tableItems = getTableItemsByRange(this),
                cell = tableItems.cell;
            if (!cell) return -1;
            var ut = getUETable(tableItems.table);
            if (ut.selectedTds.length > 0) return -1;
            return cell && cell.colSpan > 1 ? 0 : -1;
        },
        execCommand: function () {
            var rng = this.selection.getRange(),
                bk = rng.createBookmark(true);
            var cell = getTableItemsByRange(this).cell,
                ut = getUETable(cell);
            ut.splitToCols(cell);
            rng.moveToBookmark(bk).select();
        }
    };

    // UE.commands["adaptbytext"] = UE.commands["adaptbywindow"] = {
    //     queryCommandState: function () {
    //         return getTableItemsByRange(this).table ? 0 : -1;
    //     },
    //     execCommand: function (cmd) {
    //         var tableItems = getTableItemsByRange(this),
    //             table = tableItems.table;
    //         if (table) {
    //             if (cmd == "adaptbywindow") {
    //                 resetTdWidth(table, this);
    //             } else {
    //                 var cells = domUtils.getElementsByTagName(table, "td th");
    //                 utils.each(cells, function (cell) {
    //                     cell.removeAttribute("width");
    //                 });
    //                 table.removeAttribute("width");
    //             }
    //         }
    //     }
    // };

  //修改表格属性 宽度设置
  UE.commands["adaptbytext"] =
    UE.commands["adaptbywindow"] = {
      queryCommandState: function () {
        return getTableItemsByRange(this).table ? 0 : -1
      },
      execCommand: function (cmd) {
        var tableItems = getTableItemsByRange(this),
          table = tableItems.table;
        if (table) {
          if (cmd == 'adaptbywindow') {
            table.removeAttribute("width");
            resetTdWidth(table, this);
          } else {
            var cells = domUtils.getElementsByTagName(table, "td th");
            utils.each(cells, function (cell) {
              cell.removeAttribute("width");
            });
            table.removeAttribute("width");
            table.width = "100%";
          }
        }
      }
    };

    //平均分配各列
    UE.commands["averagedistributecol"] = {
        queryCommandState: function () {
            var ut = getUETableBySelected(this);
            if (!ut) return -1;
            return ut.isFullRow() || ut.isFullCol() ? 0 : -1;
        },
        execCommand: function (cmd) {
            var me = this,
                ut = getUETableBySelected(me);

            function getAverageWidth() {
                var tb = ut.table,
                    averageWidth,
                    sumWidth = 0,
                    colsNum = 0,
                    tbAttr = getDefaultValue(me, tb);

                if (ut.isFullRow()) {
                    sumWidth = tb.offsetWidth;
                    colsNum = ut.colsNum;
                } else {
                    var begin = ut.cellsRange.beginColIndex,
                        end = ut.cellsRange.endColIndex,
                        node;
                    for (var i = begin; i <= end;) {
                        node = ut.selectedTds[i];
                        sumWidth += node.offsetWidth;
                        i += node.colSpan;
                        colsNum += 1;
                    }
                }
                averageWidth =
                    Math.ceil(sumWidth / colsNum) -
                    tbAttr.tdBorder * 2 -
                    tbAttr.tdPadding * 2;
                return averageWidth;
            }

            function setAverageWidth(averageWidth) {
                utils.each(domUtils.getElementsByTagName(ut.table, "th"), function (
                    node
                ) {
                    node.setAttribute("width", "");
                });
                var cells = ut.isFullRow()
                    ? domUtils.getElementsByTagName(ut.table, "td")
                    : ut.selectedTds;

                utils.each(cells, function (node) {
                    if (node.colSpan == 1) {
                        node.setAttribute("width", averageWidth);
                    }
                });
            }

            if (ut && ut.selectedTds.length) {
                setAverageWidth(getAverageWidth());
            }
        }
    };
    //平均分配各行
    UE.commands["averagedistributerow"] = {
        queryCommandState: function () {
            var ut = getUETableBySelected(this);
            if (!ut) return -1;
            if (ut.selectedTds && /th/gi.test(ut.selectedTds[0].tagName)) return -1;
            return ut.isFullRow() || ut.isFullCol() ? 0 : -1;
        },
        execCommand: function (cmd) {
            var me = this,
                ut = getUETableBySelected(me);

            function getAverageHeight() {
                var averageHeight,
                    rowNum,
                    sumHeight = 0,
                    tb = ut.table,
                    tbAttr = getDefaultValue(me, tb),
                    tdpadding = parseInt(
                        domUtils.getComputedStyle(
                            tb.getElementsByTagName("td")[0],
                            "padding-top"
                        )
                    );

                if (ut.isFullCol()) {
                    var captionArr = domUtils.getElementsByTagName(tb, "caption"),
                        thArr = domUtils.getElementsByTagName(tb, "th"),
                        captionHeight,
                        thHeight;

                    if (captionArr.length > 0) {
                        captionHeight = captionArr[0].offsetHeight;
                    }
                    if (thArr.length > 0) {
                        thHeight = thArr[0].offsetHeight;
                    }

                    sumHeight = tb.offsetHeight - (captionHeight || 0) - (thHeight || 0);
                    rowNum = thArr.length == 0 ? ut.rowsNum : ut.rowsNum - 1;
                } else {
                    var begin = ut.cellsRange.beginRowIndex,
                        end = ut.cellsRange.endRowIndex,
                        count = 0,
                        trs = domUtils.getElementsByTagName(tb, "tr");
                    for (var i = begin; i <= end; i++) {
                        sumHeight += trs[i].offsetHeight;
                        count += 1;
                    }
                    rowNum = count;
                }
                //ie8下是混杂模式
                if (browser.ie && browser.version < 9) {
                    averageHeight = Math.ceil(sumHeight / rowNum);
                } else {
                    averageHeight =
                        Math.ceil(sumHeight / rowNum) - tbAttr.tdBorder * 2 - tdpadding * 2;
                }
                return averageHeight;
            }

            function setAverageHeight(averageHeight) {
                var cells = ut.isFullCol()
                    ? domUtils.getElementsByTagName(ut.table, "td")
                    : ut.selectedTds;
                utils.each(cells, function (node) {
                    if (node.rowSpan == 1) {
                        node.setAttribute("height", averageHeight);
                    }
                });
            }

            if (ut && ut.selectedTds.length) {
                setAverageHeight(getAverageHeight());
            }
        }
    };

    //单元格对齐方式
    UE.commands["cellalignment"] = {
        queryCommandState: function () {
            return getTableItemsByRange(this).table ? 0 : -1;
        },
        execCommand: function (cmd, data) {
            var me = this,
                ut = getUETableBySelected(me);

            if (!ut) {
                var start = me.selection.getStart(),
                    cell =
                        start &&
                        domUtils.findParentByTagName(start, ["td", "th", "caption"], true);
                if (!/caption/gi.test(cell.tagName)) {
                    domUtils.setAttributes(cell, data);
                } else {
                    cell.style.textAlign = data.align;
                    cell.style.verticalAlign = data.vAlign;
                }
                me.selection.getRange().setCursor(true);
            } else {
                utils.each(ut.selectedTds, function (cell) {
                    domUtils.setAttributes(cell, data);
                });
            }
        },
        /**
         * 查询当前点击的单元格的对齐状态, 如果当前已经选择了多个单元格, 则会返回所有单元格经过统一协调过后的状态
         * @see UE.UETable.getTableCellAlignState
         */
        queryCommandValue: function (cmd) {
            var activeMenuCell = getTableItemsByRange(this).cell;

            if (!activeMenuCell) {
                activeMenuCell = getSelectedArr(this)[0];
            }

            if (!activeMenuCell) {
                return null;
            } else {
                //获取同时选中的其他单元格
                var cells = UE.UETable.getUETable(activeMenuCell).selectedTds;

                !cells.length && (cells = activeMenuCell);

                return UE.UETable.getTableCellAlignState(cells);
            }
        }
    };
    //表格对齐方式
    UE.commands["tablealignment"] = {
        queryCommandState: function () {
            if (browser.ie && browser.version < 8) {
                return -1;
            }
            return getTableItemsByRange(this).table ? 0 : -1;
        },
        execCommand: function (cmd, value) {
            var me = this,
                start = me.selection.getStart(),
                table = start && domUtils.findParentByTagName(start, ["table"], true);

            if (table) {
                table.setAttribute("align", value);
            }
        }
    };

    //表格属性
    UE.commands["edittable"] = {
        queryCommandState: function () {
            return getTableItemsByRange(this).table ? 0 : -1;
        },
        execCommand: function (cmd, color) {
            var rng = this.selection.getRange(),
                table = domUtils.findParentByTagName(rng.startContainer, "table");
            if (table) {
                var arr = domUtils
                    .getElementsByTagName(table, "td")
                    .concat(
                        domUtils.getElementsByTagName(table, "th"),
                        domUtils.getElementsByTagName(table, "caption")
                    );
                utils.each(arr, function (node) {
                    node.style.borderColor = color;
                });
            }
        }
    };
    //表格属性  宽度设置
    UE.commands['settablesize'] = {
      queryCommandState: function () {
        return getTableItemsByRange(this).table ? 0 : -1
      },
      execCommand: function (cmd, width) {
        var rng = this.selection.getRange(),
          table = domUtils.findParentByTagName(rng.startContainer, 'table');
        if (table) {
          table.width = width;
        }
      }
    };
    //单元格属性
    UE.commands["edittd"] = {
        queryCommandState: function () {
            return getTableItemsByRange(this).table ? 0 : -1;
        },
        execCommand: function (cmd, bkColor) {
            var me = this,
                ut = getUETableBySelected(me);

            if (!ut) {
                var start = me.selection.getStart(),
                    cell =
                        start &&
                        domUtils.findParentByTagName(start, ["td", "th", "caption"], true);
                if (cell) {
                    cell.style.backgroundColor = bkColor;
                }
            } else {
                utils.each(ut.selectedTds, function (cell) {
                    cell.style.backgroundColor = bkColor;
                });
            }
        }
    };

    UE.commands["settablebackground"] = {
        queryCommandState: function () {
            return getSelectedArr(this).length > 1 ? 0 : -1;
        },
        execCommand: function (cmd, value) {
            var cells, ut;
            cells = getSelectedArr(this);
            ut = getUETable(cells[0]);
            ut.setBackground(cells, value);
        }
    };

    UE.commands["cleartablebackground"] = {
        queryCommandState: function () {
            var cells = getSelectedArr(this);
            if (!cells.length) return -1;
            for (var i = 0, cell; (cell = cells[i++]);) {
                if (cell.style.backgroundColor !== "") return 0;
            }
            return -1;
        },
        execCommand: function () {
            var cells = getSelectedArr(this),
                ut = getUETable(cells[0]);
            ut.removeBackground(cells);
        }
    };

    UE.commands["interlacetable"] = UE.commands["uninterlacetable"] = {
        queryCommandState: function (cmd) {
            var table = getTableItemsByRange(this).table;
            if (!table) return -1;
            var interlaced = table.getAttribute("interlaced");
            if (cmd == "interlacetable") {
                //TODO 待定
                //是否需要待定,如果设置,则命令只能单次执行成功,但反射具备toggle效果;否则可以覆盖前次命令,但反射将不存在toggle效果
                return interlaced === "enabled" ? -1 : 0;
            } else {
                return !interlaced || interlaced === "disabled" ? -1 : 0;
            }
        },
        execCommand: function (cmd, classList) {
            var table = getTableItemsByRange(this).table;
            if (cmd == "interlacetable") {
                table.setAttribute("interlaced", "enabled");
                this.fireEvent("interlacetable", table, classList);
            } else {
                table.setAttribute("interlaced", "disabled");
                this.fireEvent("uninterlacetable", table);
            }
        }
    };
    UE.commands["setbordervisible"] = {
        queryCommandState: function (cmd) {
            var table = getTableItemsByRange(this).table;
            if (!table) return -1;
            return 0;
        },
        execCommand: function () {
            var table = getTableItemsByRange(this).table;
            utils.each(domUtils.getElementsByTagName(table, "td"), function (td) {
                td.style.borderWidth = "1px";
                td.style.borderStyle = "solid";
            });
            //增加下面一段
            utils.each(domUtils.getElementsByTagName(table,'th'),function(th){
              th.style.borderWidth = domUtils.getComputedStyle(th, "border-width");
              th.style.borderStyle = 'solid';
              th.style.borderColor = "black";
            });
        }
    };

    function resetTdWidth(table, editor) {
        var tds = domUtils.getElementsByTagName(table, "td th");
        utils.each(tds, function (td) {
            td.removeAttribute("width");
        });
        table.setAttribute(
            "width",
            getTableWidth(editor, true, getDefaultValue(editor, table))
        );
        var tdsWidths = [];
        setTimeout(function () {
            utils.each(tds, function (td) {
                td.colSpan == 1 && tdsWidths.push(td.offsetWidth);
            });
            utils.each(tds, function (td, i) {
                td.colSpan == 1 && td.setAttribute("width", tdsWidths[i] + "");
            });
        }, 0);
    }

    function getTableWidth(editor, needIEHack, defaultValue) {
        var body = editor.body;
        return (
            body.offsetWidth -
            (needIEHack
                ? parseInt(domUtils.getComputedStyle(body, "margin-left"), 10) * 2
                : 0) -
            defaultValue.tableBorder * 2 -
            (editor.options.offsetWidth || 0)
        );
    }

    function getSelectedArr(editor) {
        var cell = getTableItemsByRange(editor).cell;
        if (cell) {
            var ut = getUETable(cell);
            return ut.selectedTds.length ? ut.selectedTds : [cell];
        } else {
            return [];
        }
    }
})();


// plugins/table.action.js
/**
 * Created with JetBrains PhpStorm.
 * User: taoqili
 * Date: 12-10-12
 * Time: 上午10:05
 * To change this template use File | Settings | File Templates.
 */
UE.plugins["table"] = function () {
    var me = this,
        tabTimer = null,
        //拖动计时器
        tableDragTimer = null,
        //双击计时器
        tableResizeTimer = null,
        //单元格最小宽度
        cellMinWidth = 5,
        isInResizeBuffer = false,
        //单元格边框大小
        cellBorderWidth = 5,
        //鼠标偏移距离
        offsetOfTableCell = 10,
        //记录在有限时间内的点击状态, 共有3个取值, 0, 1, 2。 0代表未初始化, 1代表单击了1次,2代表2次
        singleClickState = 0,
        userActionStatus = null,
        //双击允许的时间范围
        dblclickTime = 200,
        UT = UE.UETable,
        getUETable = function (tdOrTable) {
            return UT.getUETable(tdOrTable);
        },
        getUETableBySelected = function (editor) {
            return UT.getUETableBySelected(editor);
        },
        getDefaultValue = function (editor, table) {
            return UT.getDefaultValue(editor, table);
        },
        removeSelectedClass = function (cells) {
            return UT.removeSelectedClass(cells);
        };

    function showError(e) {
        //        throw e;
    }

    me.ready(function () {
        var me = this;
        var orgGetText = me.selection.getText;
        me.selection.getText = function () {
            var table = getUETableBySelected(me);
            if (table) {
                var str = "";
                utils.each(table.selectedTds, function (td) {
                    str += td[browser.ie ? "innerText" : "textContent"];
                });
                return str;
            } else {
                return orgGetText.call(me.selection);
            }
        };
    });

    //处理拖动及框选相关方法
    var startTd = null, //鼠标按下时的锚点td
        currentTd = null, //当前鼠标经过时的td
        onDrag = "", //指示当前拖动状态,其值可为"","h","v" ,分别表示未拖动状态,横向拖动状态,纵向拖动状态,用于鼠标移动过程中的判断
        onBorder = false, //检测鼠标按下时是否处在单元格边缘位置
        dragButton = null,
        dragOver = false,
        dragLine = null, //模拟的拖动线
        dragTd = null; //发生拖动的目标td

    var mousedown = false,
        //todo 判断混乱模式
        needIEHack = true;

    me.setOpt({
        maxColNum: 20,
        maxRowNum: 100,
        defaultCols: 5,
        defaultRows: 5,
        tdvalign: "top",
        cursorpath: me.options.UEDITOR_HOME_URL + "themes/default/images/cursor_",
        tableDragable: false,
        classList: [
            "ue-table-interlace-color-single",
            "ue-table-interlace-color-double"
        ]
    });
    me.getUETable = getUETable;
    var commands = {
        deletetable: 1,
        inserttable: 1,
        cellvalign: 1,
        insertcaption: 1,
        deletecaption: 1,
        inserttitle: 1,
        deletetitle: 1,
        mergeright: 1,
        mergedown: 1,
        mergecells: 1,
        insertrow: 1,
        insertrownext: 1,
        deleterow: 1,
        insertcol: 1,
        insertcolnext: 1,
        deletecol: 1,
        splittocells: 1,
        splittorows: 1,
        splittocols: 1,
        adaptbytext: 1,
        adaptbywindow: 1,
        adaptbycustomer: 1,
        insertparagraph: 1,
        insertparagraphbeforetable: 1,
        averagedistributecol: 1,
        averagedistributerow: 1
    };
    me.ready(function () {
        utils.cssRule(
            "table",
            //选中的td上的样式
            ".selectTdClass{background-color:#edf5fa !important}" +
            "table.noBorderTable td,table.noBorderTable th,table.noBorderTable caption{border:1px dashed #ddd !important}" +
            //插入的表格的默认样式
            "table{margin-bottom:10px;border-collapse:collapse;display:table;}" +
            // "td,th{padding: 5px 10px;border: 1px solid #DDD;}" +
            "td,th{padding: 5px 10px;border: 1px dashed black}" +
            "caption{border:1px dashed #DDD;border-bottom:0;padding:3px;text-align:center;}" +
            // "th{border-top:1px solid #BBB;background-color:#F7F7F7;}" +
            "th{border-top:1px dashed black;background-color:#F7F7F7;}" +
            "table tr.firstRow th{border-top-width:2px;}" +
            ".ue-table-interlace-color-single{ background-color: #fcfcfc; } .ue-table-interlace-color-double{ background-color: #f7faff; }" +
            "td p{margin:0;padding:0;}",
            me.document
        );

        var tableCopyList, isFullCol, isFullRow;
        //注册del/backspace事件
        me.addListener("keydown", function (cmd, evt) {
            var me = this;
            var keyCode = evt.keyCode || evt.which;

            if (keyCode == 8) {
                var ut = getUETableBySelected(me);
                if (ut && ut.selectedTds.length) {
                    if (ut.isFullCol()) {
                        me.execCommand("deletecol");
                    } else if (ut.isFullRow()) {
                        me.execCommand("deleterow");
                    } else {
                        me.fireEvent("delcells");
                    }
                    domUtils.preventDefault(evt);
                }

                var caption = domUtils.findParentByTagName(
                    me.selection.getStart(),
                    "caption",
                    true
                    ),
                    range = me.selection.getRange();
                if (range.collapsed && caption && isEmptyBlock(caption)) {
                    me.fireEvent("saveScene");
                    var table = caption.parentNode;
                    domUtils.remove(caption);
                    if (table) {
                        range.setStart(table.rows[0].cells[0], 0).setCursor(false, true);
                    }
                    me.fireEvent("saveScene");
                }
            }

            if (keyCode == 46) {
                ut = getUETableBySelected(me);
                if (ut) {
                    me.fireEvent("saveScene");
                    for (var i = 0, ci; (ci = ut.selectedTds[i++]);) {
                        domUtils.fillNode(me.document, ci);
                    }
                    me.fireEvent("saveScene");
                    domUtils.preventDefault(evt);
                }
            }
            if (keyCode == 13) {
                var rng = me.selection.getRange(),
                    caption = domUtils.findParentByTagName(
                        rng.startContainer,
                        "caption",
                        true
                    );
                if (caption) {
                    var table = domUtils.findParentByTagName(caption, "table");
                    if (!rng.collapsed) {
                        rng.deleteContents();
                        me.fireEvent("saveScene");
                    } else {
                        if (caption) {
                            rng.setStart(table.rows[0].cells[0], 0).setCursor(false, true);
                        }
                    }
                    domUtils.preventDefault(evt);
                    return;
                }
                if (rng.collapsed) {
                    var table = domUtils.findParentByTagName(rng.startContainer, "table");
                    if (table) {
                        var cell = table.rows[0].cells[0],
                            start = domUtils.findParentByTagName(
                                me.selection.getStart(),
                                ["td", "th"],
                                true
                            ),
                            preNode = table.previousSibling;
                        if (
                            cell === start &&
                            (!preNode ||
                                (preNode.nodeType == 1 && preNode.tagName == "TABLE")) &&
                            domUtils.isStartInblock(rng)
                        ) {
                            var first = domUtils.findParent(
                                me.selection.getStart(),
                                function (n) {
                                    return domUtils.isBlockElm(n);
                                },
                                true
                            );
                            if (
                                first &&
                                (/t(h|d)/i.test(first.tagName) || first === start.firstChild)
                            ) {
                                me.execCommand("insertparagraphbeforetable");
                                domUtils.preventDefault(evt);
                            }
                        }
                    }
                }
            }

            if ((evt.ctrlKey || evt.metaKey) && evt.keyCode == "67") {
                tableCopyList = null;
                var ut = getUETableBySelected(me);
                if (ut) {
                    var tds = ut.selectedTds;
                    isFullCol = ut.isFullCol();
                    isFullRow = ut.isFullRow();
                    tableCopyList = [[ut.cloneCell(tds[0], null, true)]];
                    for (var i = 1, ci; (ci = tds[i]); i++) {
                        if (ci.parentNode !== tds[i - 1].parentNode) {
                            tableCopyList.push([ut.cloneCell(ci, null, true)]);
                        } else {
                            tableCopyList[tableCopyList.length - 1].push(
                                ut.cloneCell(ci, null, true)
                            );
                        }
                    }
                }
            }
        });
        me.addListener("tablehasdeleted", function () {
            toggleDraggableState(this, false, "", null);
            if (dragButton) domUtils.remove(dragButton);
        });

        me.addListener("beforepaste", function (cmd, html) {
            var me = this;
            var rng = me.selection.getRange();
            if (domUtils.findParentByTagName(rng.startContainer, "caption", true)) {
                var div = me.document.createElement("div");
                div.innerHTML = html.html;
                //trace:3729
                html.html = div[browser.ie9below ? "innerText" : "textContent"];
                return;
            }
            var table = getUETableBySelected(me);
            if (tableCopyList) {
                me.fireEvent("saveScene");
                var rng = me.selection.getRange();
                var td = domUtils.findParentByTagName(
                    rng.startContainer,
                    ["td", "th"],
                    true
                    ),
                    tmpNode,
                    preNode;
                if (td) {
                    var ut = getUETable(td);
                    if (isFullRow) {
                        var rowIndex = ut.getCellInfo(td).rowIndex;
                        if (td.tagName == "TH") {
                            rowIndex++;
                        }
                        for (var i = 0, ci; (ci = tableCopyList[i++]);) {
                            var tr = ut.insertRow(rowIndex++, "td");
                            for (var j = 0, cj; (cj = ci[j]); j++) {
                                var cell = tr.cells[j];
                                if (!cell) {
                                    cell = tr.insertCell(j);
                                }
                                cell.innerHTML = cj.innerHTML;
                                cj.getAttribute("width") &&
                                cell.setAttribute("width", cj.getAttribute("width"));
                                cj.getAttribute("vAlign") &&
                                cell.setAttribute("vAlign", cj.getAttribute("vAlign"));
                                cj.getAttribute("align") &&
                                cell.setAttribute("align", cj.getAttribute("align"));
                                cj.style.cssText && (cell.style.cssText = cj.style.cssText);
                            }
                            for (var j = 0, cj; (cj = tr.cells[j]); j++) {
                                if (!ci[j]) break;
                                cj.innerHTML = ci[j].innerHTML;
                                ci[j].getAttribute("width") &&
                                cj.setAttribute("width", ci[j].getAttribute("width"));
                                ci[j].getAttribute("vAlign") &&
                                cj.setAttribute("vAlign", ci[j].getAttribute("vAlign"));
                                ci[j].getAttribute("align") &&
                                cj.setAttribute("align", ci[j].getAttribute("align"));
                                ci[j].style.cssText && (cj.style.cssText = ci[j].style.cssText);
                            }
                        }
                    } else {
                        if (isFullCol) {
                            cellInfo = ut.getCellInfo(td);
                            var maxColNum = 0;
                            for (var j = 0, ci = tableCopyList[0], cj; (cj = ci[j++]);) {
                                maxColNum += cj.colSpan || 1;
                            }
                            me.__hasEnterExecCommand = true;
                            for (i = 0; i < maxColNum; i++) {
                                me.execCommand("insertcol");
                            }
                            me.__hasEnterExecCommand = false;
                            td = ut.table.rows[0].cells[cellInfo.cellIndex];
                            if (td.tagName == "TH") {
                                td = ut.table.rows[1].cells[cellInfo.cellIndex];
                            }
                        }
                        for (var i = 0, ci; (ci = tableCopyList[i++]);) {
                            tmpNode = td;
                            for (var j = 0, cj; (cj = ci[j++]);) {
                                if (td) {
                                    td.innerHTML = cj.innerHTML;
                                    //todo 定制处理
                                    cj.getAttribute("width") &&
                                    td.setAttribute("width", cj.getAttribute("width"));
                                    cj.getAttribute("vAlign") &&
                                    td.setAttribute("vAlign", cj.getAttribute("vAlign"));
                                    cj.getAttribute("align") &&
                                    td.setAttribute("align", cj.getAttribute("align"));
                                    cj.style.cssText && (td.style.cssText = cj.style.cssText);
                                    preNode = td;
                                    td = td.nextSibling;
                                } else {
                                    var cloneTd = cj.cloneNode(true);
                                    domUtils.removeAttributes(cloneTd, [
                                        "class",
                                        "rowSpan",
                                        "colSpan"
                                    ]);

                                    preNode.parentNode.appendChild(cloneTd);
                                }
                            }
                            td = ut.getNextCell(tmpNode, true, true);
                            if (!tableCopyList[i]) break;
                            if (!td) {
                                var cellInfo = ut.getCellInfo(tmpNode);
                                ut.table.insertRow(ut.table.rows.length);
                                ut.update();
                                td = ut.getVSideCell(tmpNode, true);
                            }
                        }
                    }
                    ut.update();
                } else {
                    table = me.document.createElement("table");
                    for (var i = 0, ci; (ci = tableCopyList[i++]);) {
                        var tr = table.insertRow(table.rows.length);
                        for (var j = 0, cj; (cj = ci[j++]);) {
                            cloneTd = UT.cloneCell(cj, null, true);
                            domUtils.removeAttributes(cloneTd, ["class"]);
                            tr.appendChild(cloneTd);
                        }
                        if (j == 2 && cloneTd.rowSpan > 1) {
                            cloneTd.rowSpan = 1;
                        }
                    }

                    var defaultValue = getDefaultValue(me),
                        width =
                            me.body.offsetWidth -
                            (needIEHack
                                ? parseInt(
                                domUtils.getComputedStyle(me.body, "margin-left"),
                                10
                            ) * 2
                                : 0) -
                            defaultValue.tableBorder * 2 -
                            (me.options.offsetWidth || 0);
                    me.execCommand(
                        "insertHTML",
                        "<table  " +
                        (isFullCol && isFullRow ? 'width="' + width + '"' : "") +
                        ">" +
                        table.innerHTML
                            .replace(/>\s*</g, "><")
                            .replace(/\bth\b/gi, "td") +
                        "</table>"
                    );
                }
                me.fireEvent("contentchange");
                me.fireEvent("saveScene");
                html.html = "";
                return true;
            } else {
                var div = me.document.createElement("div"),
                    tables;
                div.innerHTML = html.html;
                tables = div.getElementsByTagName("table");
                if (domUtils.findParentByTagName(me.selection.getStart(), "table")) {
                    utils.each(tables, function (t) {
                        domUtils.remove(t);
                    });
                    if (
                        domUtils.findParentByTagName(
                            me.selection.getStart(),
                            "caption",
                            true
                        )
                    ) {
                        div.innerHTML = div[browser.ie ? "innerText" : "textContent"];
                    }
                } else {
                    utils.each(tables, function (table) {
                        removeStyleSize(table, true);
                        // domUtils.removeAttributes(table, ["style", "border"]);
                        domUtils.removeAttributes(table, ["style"]);
                        utils.each(domUtils.getElementsByTagName(table, "td"), function (
                            td
                        ) {
                            if (isEmptyBlock(td)) {
                                domUtils.fillNode(me.document, td);
                            }
                            removeStyleSize(td, true);
                            //                            domUtils.removeAttributes(td, ['style'])
                        });
                    });
                }
                html.html = div.innerHTML;
            }
        });

        me.addListener("afterpaste", function () {
            utils.each(domUtils.getElementsByTagName(me.body, "table"), function (
                table
            ) {
                if (table.offsetWidth > me.body.offsetWidth) {
                    var defaultValue = getDefaultValue(me, table);
                    table.style.width =
                        me.body.offsetWidth -
                        (needIEHack
                            ? parseInt(
                            domUtils.getComputedStyle(me.body, "margin-left"),
                            10
                        ) * 2
                            : 0) -
                        defaultValue.tableBorder * 2 -
                        (me.options.offsetWidth || 0) +
                        "px";
                }
            });
        });
        me.addListener("blur", function () {
            tableCopyList = null;
        });
        var timer;
        me.addListener("keydown", function () {
            clearTimeout(timer);
            timer = setTimeout(function () {
                var rng = me.selection.getRange(),
                    cell = domUtils.findParentByTagName(
                        rng.startContainer,
                        ["th", "td"],
                        true
                    );
                if (cell) {
                    var table = cell.parentNode.parentNode.parentNode;
                    if (table.offsetWidth > table.getAttribute("width")) {
                        cell.style.wordBreak = "break-all";
                    }
                }
            }, 100);
        });
        me.addListener("selectionchange", function () {
            toggleDraggableState(me, false, "", null);
        });

        //内容变化时触发索引更新
        //todo 可否考虑标记检测,如果不涉及表格的变化就不进行索引重建和更新
        me.addListener("contentchange", function () {
            var me = this;
            //尽可能排除一些不需要更新的状况
            hideDragLine(me);
            if (getUETableBySelected(me)) return;
            var rng = me.selection.getRange();
            var start = rng.startContainer;
            start = domUtils.findParentByTagName(start, ["td", "th"], true);
            utils.each(domUtils.getElementsByTagName(me.document, "table"), function (
                table
            ) {
                if (me.fireEvent("excludetable", table) === true) return;
                table.ueTable = new UT(table);
                //trace:3742
                //                utils.each(domUtils.getElementsByTagName(me.document, 'td'), function (td) {
                //
                //                    if (domUtils.isEmptyBlock(td) && td !== start) {
                //                        domUtils.fillNode(me.document, td);
                //                        if (browser.ie && browser.version == 6) {
                //                            td.innerHTML = '&nbsp;'
                //                        }
                //                    }
                //                });
                //                utils.each(domUtils.getElementsByTagName(me.document, 'th'), function (th) {
                //                    if (domUtils.isEmptyBlock(th) && th !== start) {
                //                        domUtils.fillNode(me.document, th);
                //                        if (browser.ie && browser.version == 6) {
                //                            th.innerHTML = '&nbsp;'
                //                        }
                //                    }
                //                });
                table.onmouseover = function () {
                    me.fireEvent("tablemouseover", table);
                };
                table.onmousemove = function () {
                    me.fireEvent("tablemousemove", table);
                    me.options.tableDragable && toggleDragButton(true, this, me);
                    utils.defer(function () {
                        me.fireEvent("contentchange", 50);
                    }, true);
                };
                table.onmouseout = function () {
                    me.fireEvent("tablemouseout", table);
                    toggleDraggableState(me, false, "", null);
                    hideDragLine(me);
                };
                table.onclick = function (evt) {
                    evt = me.window.event || evt;
                    var target = getParentTdOrTh(evt.target || evt.srcElement);
                    if (!target) return;
                    var ut = getUETable(target),
                        table = ut.table,
                        cellInfo = ut.getCellInfo(target),
                        cellsRange,
                        rng = me.selection.getRange();
                    //                    if ("topLeft" == inPosition(table, mouseCoords(evt))) {
                    //                        cellsRange = ut.getCellsRange(ut.table.rows[0].cells[0], ut.getLastCell());
                    //                        ut.setSelected(cellsRange);
                    //                        return;
                    //                    }
                    //                    if ("bottomRight" == inPosition(table, mouseCoords(evt))) {
                    //
                    //                        return;
                    //                    }
                    if (inTableSide(table, target, evt, true)) {
                        var endTdCol = ut.getCell(
                            ut.indexTable[ut.rowsNum - 1][cellInfo.colIndex].rowIndex,
                            ut.indexTable[ut.rowsNum - 1][cellInfo.colIndex].cellIndex
                        );
                        if (evt.shiftKey && ut.selectedTds.length) {
                            if (ut.selectedTds[0] !== endTdCol) {
                                cellsRange = ut.getCellsRange(ut.selectedTds[0], endTdCol);
                                ut.setSelected(cellsRange);
                            } else {
                                rng && rng.selectNodeContents(endTdCol).select();
                            }
                        } else {
                            if (target !== endTdCol) {
                                cellsRange = ut.getCellsRange(target, endTdCol);
                                ut.setSelected(cellsRange);
                            } else {
                                rng && rng.selectNodeContents(endTdCol).select();
                            }
                        }
                        return;
                    }
                    if (inTableSide(table, target, evt)) {
                        var endTdRow = ut.getCell(
                            ut.indexTable[cellInfo.rowIndex][ut.colsNum - 1].rowIndex,
                            ut.indexTable[cellInfo.rowIndex][ut.colsNum - 1].cellIndex
                        );
                        if (evt.shiftKey && ut.selectedTds.length) {
                            if (ut.selectedTds[0] !== endTdRow) {
                                cellsRange = ut.getCellsRange(ut.selectedTds[0], endTdRow);
                                ut.setSelected(cellsRange);
                            } else {
                                rng && rng.selectNodeContents(endTdRow).select();
                            }
                        } else {
                            if (target !== endTdRow) {
                                cellsRange = ut.getCellsRange(target, endTdRow);
                                ut.setSelected(cellsRange);
                            } else {
                                rng && rng.selectNodeContents(endTdRow).select();
                            }
                        }
                    }
                };
            });

            switchBorderColor(me, true);
        });

        domUtils.on(me.document, "mousemove", mouseMoveEvent);

        domUtils.on(me.document, "mouseout", function (evt) {
            var target = evt.target || evt.srcElement;
            if (target.tagName == "TABLE") {
                toggleDraggableState(me, false, "", null);
            }
        });
        /**
         * 表格隔行变色
         */
        me.addListener("interlacetable", function (type, table, classList) {
            if (!table) return;
            var me = this,
                rows = table.rows,
                len = rows.length,
                getClass = function (list, index, repeat) {
                    return list[index]
                        ? list[index]
                        : repeat ? list[index % list.length] : "";
                };
            for (var i = 0; i < len; i++) {
                rows[i].className = getClass(
                    classList || me.options.classList,
                    i,
                    true
                );
            }
        });
        me.addListener("uninterlacetable", function (type, table) {
            if (!table) return;
            var me = this,
                rows = table.rows,
                classList = me.options.classList,
                len = rows.length;
            for (var i = 0; i < len; i++) {
                domUtils.removeClasses(rows[i], classList);
            }
        });

        me.addListener("mousedown", mouseDownEvent);
        me.addListener("mouseup", mouseUpEvent);
        //拖动的时候触发mouseup
        domUtils.on(me.body, "dragstart", function (evt) {
            mouseUpEvent.call(me, "dragstart", evt);
        });
        me.addOutputRule(function (root) {
            utils.each(root.getNodesByTagName("div"), function (n) {
                if (n.getAttr("id") == "ue_tableDragLine") {
                    n.parentNode.removeChild(n);
                }
            });
        });

        var currentRowIndex = 0;
        me.addListener("mousedown", function () {
            currentRowIndex = 0;
        });
        me.addListener("tabkeydown", function () {
            var range = this.selection.getRange(),
                common = range.getCommonAncestor(true, true),
                table = domUtils.findParentByTagName(common, "table");
            if (table) {
                if (domUtils.findParentByTagName(common, "caption", true)) {
                    var cell = domUtils.getElementsByTagName(table, "th td");
                    if (cell && cell.length) {
                        range.setStart(cell[0], 0).setCursor(false, true);
                    }
                } else {
                    var cell = domUtils.findParentByTagName(common, ["td", "th"], true),
                        ua = getUETable(cell);
                    currentRowIndex = cell.rowSpan > 1
                        ? currentRowIndex
                        : ua.getCellInfo(cell).rowIndex;
                    var nextCell = ua.getTabNextCell(cell, currentRowIndex);
                    if (nextCell) {
                        if (isEmptyBlock(nextCell)) {
                            range.setStart(nextCell, 0).setCursor(false, true);
                        } else {
                            range.selectNodeContents(nextCell).select();
                        }
                    } else {
                        me.fireEvent("saveScene");
                        me.__hasEnterExecCommand = true;
                        this.execCommand("insertrownext");
                        me.__hasEnterExecCommand = false;
                        range = this.selection.getRange();
                        range
                            .setStart(table.rows[table.rows.length - 1].cells[0], 0)
                            .setCursor();
                        me.fireEvent("saveScene");
                    }
                }
                return true;
            }
        });
        browser.ie &&
        me.addListener("selectionchange", function () {
            toggleDraggableState(this, false, "", null);
        });
        me.addListener("keydown", function (type, evt) {
            var me = this;
            //处理在表格的最后一个输入tab产生新的表格
            var keyCode = evt.keyCode || evt.which;
            if (keyCode == 8 || keyCode == 46) {
                return;
            }
            var notCtrlKey =
                !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey;
            notCtrlKey &&
            removeSelectedClass(domUtils.getElementsByTagName(me.body, "td"));
            var ut = getUETableBySelected(me);
            if (!ut) return;
            notCtrlKey && ut.clearSelected();
        });

        me.addListener("beforegetcontent", function () {
            switchBorderColor(this, false);
            browser.ie &&
            utils.each(this.document.getElementsByTagName("caption"), function (ci) {
                if (domUtils.isEmptyNode(ci)) {
                    ci.innerHTML = "&nbsp;";
                }
            });
        });
        me.addListener("aftergetcontent", function () {
            switchBorderColor(this, true);
        });
        me.addListener("getAllHtml", function () {
            removeSelectedClass(me.document.getElementsByTagName("td"));
        });
        //修正全屏状态下插入的表格宽度在非全屏状态下撑开编辑器的情况
        me.addListener("fullscreenchanged", function (type, fullscreen) {
            if (!fullscreen) {
                var ratio = this.body.offsetWidth / document.body.offsetWidth,
                    tables = domUtils.getElementsByTagName(this.body, "table");
                utils.each(tables, function (table) {
                    if (table.offsetWidth < me.body.offsetWidth) return false;
                    var tds = domUtils.getElementsByTagName(table, "td"),
                        backWidths = [];
                    utils.each(tds, function (td) {
                        backWidths.push(td.offsetWidth);
                    });
                    for (var i = 0, td; (td = tds[i]); i++) {
                        td.setAttribute("width", Math.floor(backWidths[i] * ratio));
                    }
                    table.setAttribute(
                        "width",
                        Math.floor(getTableWidth(me, needIEHack, getDefaultValue(me)))
                    );
                });
            }
        });

        //重写execCommand命令,用于处理框选时的处理
        var oldExecCommand = me.execCommand;
        me.execCommand = function (cmd, datatat) {
            var me = this,
                args = arguments;

            cmd = cmd.toLowerCase();
            var ut = getUETableBySelected(me),
                tds,
                range = new dom.Range(me.document),
                cmdFun = me.commands[cmd] || UE.commands[cmd],
                result;
            if (!cmdFun) return;
            if (
                ut &&
                !commands[cmd] &&
                !cmdFun.notNeedUndo &&
                !me.__hasEnterExecCommand
            ) {
                me.__hasEnterExecCommand = true;
                me.fireEvent("beforeexeccommand", cmd);
                tds = ut.selectedTds;
                var lastState = -2,
                    lastValue = -2,
                    value,
                    state;
                for (var i = 0, td; (td = tds[i]); i++) {
                    if (isEmptyBlock(td)) {
                        range.setStart(td, 0).setCursor(false, true);
                    } else {
                        range.selectNode(td).select(true);
                    }
                    state = me.queryCommandState(cmd);
                    value = me.queryCommandValue(cmd);
                    if (state != -1) {
                        if (lastState !== state || lastValue !== value) {
                            me._ignoreContentChange = true;
                            result = oldExecCommand.apply(me, arguments);
                            me._ignoreContentChange = false;
                        }
                        lastState = me.queryCommandState(cmd);
                        lastValue = me.queryCommandValue(cmd);
                        if (domUtils.isEmptyBlock(td)) {
                            domUtils.fillNode(me.document, td);
                        }
                    }
                }
                range.setStart(tds[0], 0).shrinkBoundary(true).setCursor(false, true);
                me.fireEvent("contentchange");
                me.fireEvent("afterexeccommand", cmd);
                me.__hasEnterExecCommand = false;
                me._selectionChange();
            } else {
                result = oldExecCommand.apply(me, arguments);
            }
            return result;
        };
    });

    /**
     * 删除obj的宽高style,改成属性宽高
     * @param obj
     * @param replaceToProperty
     */
    function removeStyleSize(obj, replaceToProperty) {
        removeStyle(obj, "width", true);
        removeStyle(obj, "height", true);
    }

    function removeStyle(obj, styleName, replaceToProperty) {
        if (obj.style[styleName]) {
            replaceToProperty &&
            obj.setAttribute(styleName, parseInt(obj.style[styleName], 10));
            obj.style[styleName] = "";
        }
    }

    function getParentTdOrTh(ele) {
        if (ele.tagName == "TD" || ele.tagName == "TH") return ele;
        var td;
        if (
            (td =
                domUtils.findParentByTagName(ele, "td", true) ||
                domUtils.findParentByTagName(ele, "th", true))
        )
            return td;
        return null;
    }

    function isEmptyBlock(node) {
        var reg = new RegExp(domUtils.fillChar, "g");
        if (
            node[browser.ie ? "innerText" : "textContent"]
                .replace(/^\s*$/, "")
                .replace(reg, "").length > 0
        ) {
            return 0;
        }
        for (var n in dtd.$isNotEmpty) {
            if (node.getElementsByTagName(n).length) {
                return 0;
            }
        }
        return 1;
    }

    function mouseCoords(evt) {
        if (evt.pageX || evt.pageY) {
            return {x: evt.pageX, y: evt.pageY};
        }
        return {
            x:
                evt.clientX + me.document.body.scrollLeft - me.document.body.clientLeft,
            y: evt.clientY + me.document.body.scrollTop - me.document.body.clientTop
        };
    }

    function mouseMoveEvent(evt) {
        if (isEditorDisabled()) {
            return;
        }

        try {
            //普通状态下鼠标移动
            var target = getParentTdOrTh(evt.target || evt.srcElement),
                pos;

            //区分用户的行为是拖动还是双击
            if (isInResizeBuffer) {
                me.body.style.webkitUserSelect = "none";

                if (
                    Math.abs(userActionStatus.x - evt.clientX) > offsetOfTableCell ||
                    Math.abs(userActionStatus.y - evt.clientY) > offsetOfTableCell
                ) {
                    clearTableDragTimer();
                    isInResizeBuffer = false;
                    singleClickState = 0;
                    //drag action
                    tableBorderDrag(evt);
                }
            }

            //修改单元格大小时的鼠标移动
            if (onDrag && dragTd) {
                singleClickState = 0;
                me.body.style.webkitUserSelect = "none";
                me.selection.getNative()[
                    browser.ie9below ? "empty" : "removeAllRanges"
                    ]();
                pos = mouseCoords(evt);
                toggleDraggableState(me, true, onDrag, pos, target);
                if (onDrag == "h") {
                    dragLine.style.left = getPermissionX(dragTd, evt) + "px";
                } else if (onDrag == "v") {
                    dragLine.style.top = getPermissionY(dragTd, evt) + "px";
                }
                return;
            }
            //当鼠标处于table上时,修改移动过程中的光标状态
            if (target) {
                //针对使用table作为容器的组件不触发拖拽效果
                if (me.fireEvent("excludetable", target) === true) return;
                pos = mouseCoords(evt);
                var state = getRelation(target, pos),
                    table = domUtils.findParentByTagName(target, "table", true);

                if (inTableSide(table, target, evt, true)) {
                    if (me.fireEvent("excludetable", table) === true) return;
                    me.body.style.cursor =
                        "url(" + me.options.cursorpath + "h.png),pointer";
                } else if (inTableSide(table, target, evt)) {
                    if (me.fireEvent("excludetable", table) === true) return;
                    me.body.style.cursor =
                        "url(" + me.options.cursorpath + "v.png),pointer";
                } else {
                    me.body.style.cursor = "text";
                    var curCell = target;
                    if (/\d/.test(state)) {
                        state = state.replace(/\d/, "");
                        target = getUETable(target).getPreviewCell(target, state == "v");
                    }
                    //位于第一行的顶部或者第一列的左边时不可拖动
                    toggleDraggableState(
                        me,
                        target ? !!state : false,
                        target ? state : "",
                        pos,
                        target
                    );
                }
            } else {
                toggleDragButton(false, table, me);
            }
        } catch (e) {
            showError(e);
        }
    }

    var dragButtonTimer;

    function toggleDragButton(show, table, editor) {
        if (!show) {
            if (dragOver) return;
            dragButtonTimer = setTimeout(function () {
                !dragOver &&
                dragButton &&
                dragButton.parentNode &&
                dragButton.parentNode.removeChild(dragButton);
            }, 2000);
        } else {
            createDragButton(table, editor);
        }
    }

    function createDragButton(table, editor) {
        var pos = domUtils.getXY(table),
            doc = table.ownerDocument;
        if (dragButton && dragButton.parentNode) return dragButton;
        dragButton = doc.createElement("div");
        dragButton.contentEditable = false;
        dragButton.innerHTML = "";
        dragButton.style.cssText =
            "width:15px;height:15px;background-image:url(" +
            editor.options.UEDITOR_HOME_URL +
            "dialogs/table/dragicon.png);position: absolute;cursor:move;top:" +
            (pos.y - 15) +
            "px;left:" +
            pos.x +
            "px;";
        domUtils.unSelectable(dragButton);
        dragButton.onmouseover = function (evt) {
            dragOver = true;
        };
        dragButton.onmouseout = function (evt) {
            dragOver = false;
        };
        domUtils.on(dragButton, "click", function (type, evt) {
            doClick(evt, this);
        });
        domUtils.on(dragButton, "dblclick", function (type, evt) {
            doDblClick(evt);
        });
        domUtils.on(dragButton, "dragstart", function (type, evt) {
            domUtils.preventDefault(evt);
        });
        var timer;

        function doClick(evt, button) {
            // 部分浏览器下需要清理
            clearTimeout(timer);
            timer = setTimeout(function () {
                editor.fireEvent("tableClicked", table, button);
            }, 300);
        }

        function doDblClick(evt) {
            clearTimeout(timer);
            var ut = getUETable(table),
                start = table.rows[0].cells[0],
                end = ut.getLastCell(),
                range = ut.getCellsRange(start, end);
            editor.selection.getRange().setStart(start, 0).setCursor(false, true);
            ut.setSelected(range);
        }

        doc.body.appendChild(dragButton);
    }

    //    function inPosition(table, pos) {
    //        var tablePos = domUtils.getXY(table),
    //            width = table.offsetWidth,
    //            height = table.offsetHeight;
    //        if (pos.x - tablePos.x < 5 && pos.y - tablePos.y < 5) {
    //            return "topLeft";
    //        } else if (tablePos.x + width - pos.x < 5 && tablePos.y + height - pos.y < 5) {
    //            return "bottomRight";
    //        }
    //    }

    function inTableSide(table, cell, evt, top) {
        var pos = mouseCoords(evt),
            state = getRelation(cell, pos);

        if (top) {
            var caption = table.getElementsByTagName("caption")[0],
                capHeight = caption ? caption.offsetHeight : 0;
            return state == "v1" && pos.y - domUtils.getXY(table).y - capHeight < 8;
        } else {
            return state == "h1" && pos.x - domUtils.getXY(table).x < 8;
        }
    }

    /**
     * 获取拖动时允许的X轴坐标
     * @param dragTd
     * @param evt
     */
    function getPermissionX(dragTd, evt) {
        var ut = getUETable(dragTd);
        if (ut) {
            var preTd = ut.getSameEndPosCells(dragTd, "x")[0],
                nextTd = ut.getSameStartPosXCells(dragTd)[0],
                mouseX = mouseCoords(evt).x,
                left =
                    (preTd ? domUtils.getXY(preTd).x : domUtils.getXY(ut.table).x) + 20,
                right = nextTd
                    ? domUtils.getXY(nextTd).x + nextTd.offsetWidth - 20
                    : me.body.offsetWidth + 5 ||
                    parseInt(domUtils.getComputedStyle(me.body, "width"), 10);

            left += cellMinWidth;
            right -= cellMinWidth;

            return mouseX < left ? left : mouseX > right ? right : mouseX;
        }
    }

    /**
     * 获取拖动时允许的Y轴坐标
     */
    function getPermissionY(dragTd, evt) {
        try {
            var top = domUtils.getXY(dragTd).y,
                mousePosY = mouseCoords(evt).y;
            return mousePosY < top ? top : mousePosY;
        } catch (e) {
            showError(e);
        }
    }

    /**
     * 移动状态切换
     */
    function toggleDraggableState(editor, draggable, dir, mousePos, cell) {
        try {
            editor.body.style.cursor = dir == "h"
                ? "col-resize"
                : dir == "v" ? "row-resize" : "text";
            if (browser.ie) {
                if (dir && !mousedown && !getUETableBySelected(editor)) {
                    getDragLine(editor, editor.document);
                    showDragLineAt(dir, cell);
                } else {
                    hideDragLine(editor);
                }
            }
            onBorder = draggable;
        } catch (e) {
            showError(e);
        }
    }

    /**
     * 获取与UETable相关的resize line
     * @param uetable UETable对象
     */
    function getResizeLineByUETable() {
        var lineId = "_UETableResizeLine",
            line = this.document.getElementById(lineId);

        if (!line) {
            line = this.document.createElement("div");
            line.id = lineId;
            line.contnetEditable = false;
            line.setAttribute("unselectable", "on");

            var styles = {
                width: 2 * cellBorderWidth + 1 + "px",
                position: "absolute",
                "z-index": 100000,
                cursor: "col-resize",
                background: "red",
                display: "none"
            };

            //切换状态
            line.onmouseout = function () {
                this.style.display = "none";
            };

            utils.extend(line.style, styles);

            this.document.body.appendChild(line);
        }

        return line;
    }

    /**
     * 更新resize-line
     */
    function updateResizeLine(cell, uetable) {
        var line = getResizeLineByUETable.call(this),
            table = uetable.table,
            styles = {
                top: domUtils.getXY(table).y + "px",
                left:
                    domUtils.getXY(cell).x + cell.offsetWidth - cellBorderWidth + "px",
                display: "block",
                height: table.offsetHeight + "px"
            };

        utils.extend(line.style, styles);
    }

    /**
     * 显示resize-line
     */
    function showResizeLine(cell) {
        var uetable = getUETable(cell);

        updateResizeLine.call(this, cell, uetable);
    }

    /**
     * 获取鼠标与当前单元格的相对位置
     * @param ele
     * @param mousePos
     */
    function getRelation(ele, mousePos) {
        var elePos = domUtils.getXY(ele);

        if (!elePos) {
            return "";
        }

        if (elePos.x + ele.offsetWidth - mousePos.x < cellBorderWidth) {
            return "h";
        }
        if (mousePos.x - elePos.x < cellBorderWidth) {
            return "h1";
        }
        if (elePos.y + ele.offsetHeight - mousePos.y < cellBorderWidth) {
            return "v";
        }
        if (mousePos.y - elePos.y < cellBorderWidth) {
            return "v1";
        }
        return "";
    }

    function mouseDownEvent(type, evt) {
        if (isEditorDisabled()) {
            return;
        }

        userActionStatus = {
            x: evt.clientX,
            y: evt.clientY
        };

        //右键菜单单独处理
        if (evt.button == 2) {
            var ut = getUETableBySelected(me),
                flag = false;

            if (ut) {
                var td = getTargetTd(me, evt);
                utils.each(ut.selectedTds, function (ti) {
                    if (ti === td) {
                        flag = true;
                    }
                });
                if (!flag) {
                    removeSelectedClass(domUtils.getElementsByTagName(me.body, "th td"));
                    ut.clearSelected();
                } else {
                    td = ut.selectedTds[0];
                    setTimeout(function () {
                        me.selection.getRange().setStart(td, 0).setCursor(false, true);
                    }, 0);
                }
            }
        } else {
            tableClickHander(evt);
        }
    }

    //清除表格的计时器
    function clearTableTimer() {
        tabTimer && clearTimeout(tabTimer);
        tabTimer = null;
    }

    //双击收缩
    function tableDbclickHandler(evt) {
        singleClickState = 0;
        evt = evt || me.window.event;
        var target = getParentTdOrTh(evt.target || evt.srcElement);
        if (target) {
            var h;
            if ((h = getRelation(target, mouseCoords(evt)))) {
                hideDragLine(me);

                if (h == "h1") {
                    h = "h";
                    if (
                        inTableSide(
                            domUtils.findParentByTagName(target, "table"),
                            target,
                            evt
                        )
                    ) {
                        me.execCommand("adaptbywindow");
                    } else {
                        target = getUETable(target).getPreviewCell(target);
                        if (target) {
                            var rng = me.selection.getRange();
                            rng.selectNodeContents(target).setCursor(true, true);
                        }
                    }
                }
                if (h == "h") {
                    var ut = getUETable(target),
                        table = ut.table,
                        cells = getCellsByMoveBorder(target, table, true);

                    cells = extractArray(cells, "left");

                    ut.width = ut.offsetWidth;

                    var oldWidth = [],
                        newWidth = [];

                    utils.each(cells, function (cell) {
                        oldWidth.push(cell.offsetWidth);
                    });

                    utils.each(cells, function (cell) {
                        cell.removeAttribute("width");
                    });

                    window.setTimeout(function () {
                        //是否允许改变
                        var changeable = true;

                        utils.each(cells, function (cell, index) {
                            var width = cell.offsetWidth;

                            if (width > oldWidth[index]) {
                                changeable = false;
                                return false;
                            }

                            newWidth.push(width);
                        });

                        var change = changeable ? newWidth : oldWidth;

                        utils.each(cells, function (cell, index) {
                            cell.width = change[index] - getTabcellSpace();
                        });
                    }, 0);

                    //                    minWidth -= cellMinWidth;
                    //
                    //                    table.removeAttribute("width");
                    //                    utils.each(cells, function (cell) {
                    //                        cell.style.width = "";
                    //                        cell.width -= minWidth;
                    //                    });
                }
            }
        }
    }

    function tableClickHander(evt) {
        removeSelectedClass(domUtils.getElementsByTagName(me.body, "td th"));
        //trace:3113
        //选中单元格,点击table外部,不会清掉table上挂的ueTable,会引起getUETableBySelected方法返回值
        utils.each(me.document.getElementsByTagName("table"), function (t) {
            t.ueTable = null;
        });
        startTd = getTargetTd(me, evt);
        if (!startTd) return;
        var table = domUtils.findParentByTagName(startTd, "table", true);
        ut = getUETable(table);
        ut && ut.clearSelected();

        //判断当前鼠标状态
        if (!onBorder) {
            me.document.body.style.webkitUserSelect = "";
            mousedown = true;
            me.addListener("mouseover", mouseOverEvent);
        } else {
            //边框上的动作处理
            borderActionHandler(evt);
        }
    }

    //处理表格边框上的动作, 这里做延时处理,避免两种动作互相影响
    function borderActionHandler(evt) {
        if (browser.ie) {
            evt = reconstruct(evt);
        }

        clearTableDragTimer();

        //是否正在等待resize的缓冲中
        isInResizeBuffer = true;

        tableDragTimer = setTimeout(function () {
            tableBorderDrag(evt);
        }, dblclickTime);
    }

    function extractArray(originArr, key) {
        var result = [],
            tmp = null;

        for (var i = 0, len = originArr.length; i < len; i++) {
            tmp = originArr[i][key];

            if (tmp) {
                result.push(tmp);
            }
        }

        return result;
    }

    function clearTableDragTimer() {
        tableDragTimer && clearTimeout(tableDragTimer);
        tableDragTimer = null;
    }

    function reconstruct(obj) {
        var attrs = [
                "pageX",
                "pageY",
                "clientX",
                "clientY",
                "srcElement",
                "target"
            ],
            newObj = {};

        if (obj) {
            for (var i = 0, key, val; (key = attrs[i]); i++) {
                val = obj[key];
                val && (newObj[key] = val);
            }
        }

        return newObj;
    }

    //边框拖动
    function tableBorderDrag(evt) {
        isInResizeBuffer = false;

        startTd = evt.target || evt.srcElement;
        if (!startTd) return;
        var state = getRelation(startTd, mouseCoords(evt));
        if (/\d/.test(state)) {
            state = state.replace(/\d/, "");
            startTd = getUETable(startTd).getPreviewCell(startTd, state == "v");
        }
        hideDragLine(me);
        getDragLine(me, me.document);
        me.fireEvent("saveScene");
        showDragLineAt(state, startTd);
        mousedown = true;
        //拖动开始
        onDrag = state;
        dragTd = startTd;
    }

    function mouseUpEvent(type, evt) {
        if (isEditorDisabled()) {
            return;
        }

        clearTableDragTimer();

        isInResizeBuffer = false;

        if (onBorder) {
            singleClickState = ++singleClickState % 3;

            userActionStatus = {
                x: evt.clientX,
                y: evt.clientY
            };

            tableResizeTimer = setTimeout(function () {
                singleClickState > 0 && singleClickState--;
            }, dblclickTime);

            if (singleClickState === 2) {
                singleClickState = 0;
                tableDbclickHandler(evt);
                return;
            }
        }

        if (evt.button == 2) return;
        var me = this;
        //清除表格上原生跨选问题
        var range = me.selection.getRange(),
            start = domUtils.findParentByTagName(range.startContainer, "table", true),
            end = domUtils.findParentByTagName(range.endContainer, "table", true);

        if (start || end) {
            if (start === end) {
                start = domUtils.findParentByTagName(
                    range.startContainer,
                    ["td", "th", "caption"],
                    true
                );
                end = domUtils.findParentByTagName(
                    range.endContainer,
                    ["td", "th", "caption"],
                    true
                );
                if (start !== end) {
                    me.selection.clearRange();
                }
            } else {
                me.selection.clearRange();
            }
        }
        mousedown = false;
        me.document.body.style.webkitUserSelect = "";
        //拖拽状态下的mouseUP
        if (onDrag && dragTd) {
            me.selection.getNative()[
                browser.ie9below ? "empty" : "removeAllRanges"
                ]();

            singleClickState = 0;
            dragLine = me.document.getElementById("ue_tableDragLine");

            // trace 3973
            if (dragLine) {
                var dragTdPos = domUtils.getXY(dragTd),
                    dragLinePos = domUtils.getXY(dragLine);

                switch (onDrag) {
                    case "h":
                        changeColWidth(dragTd, dragLinePos.x - dragTdPos.x);
                        break;
                    case "v":
                        changeRowHeight(
                            dragTd,
                            dragLinePos.y - dragTdPos.y - dragTd.offsetHeight
                        );
                        break;
                    default:
                }
                onDrag = "";
                dragTd = null;

                hideDragLine(me);
                me.fireEvent("saveScene");
                return;
            }
        }
        //正常状态下的mouseup
        if (!startTd) {
            var target = domUtils.findParentByTagName(
                evt.target || evt.srcElement,
                "td",
                true
            );
            if (!target)
                target = domUtils.findParentByTagName(
                    evt.target || evt.srcElement,
                    "th",
                    true
                );
            if (target && (target.tagName == "TD" || target.tagName == "TH")) {
                if (me.fireEvent("excludetable", target) === true) return;
                range = new dom.Range(me.document);
                range.setStart(target, 0).setCursor(false, true);
            }
        } else {
            var ut = getUETable(startTd),
                cell = ut ? ut.selectedTds[0] : null;
            if (cell) {
                range = new dom.Range(me.document);
                if (domUtils.isEmptyBlock(cell)) {
                    range.setStart(cell, 0).setCursor(false, true);
                } else {
                    range
                        .selectNodeContents(cell)
                        .shrinkBoundary()
                        .setCursor(false, true);
                }
            } else {
                range = me.selection.getRange().shrinkBoundary();
                if (!range.collapsed) {
                    var start = domUtils.findParentByTagName(
                        range.startContainer,
                        ["td", "th"],
                        true
                        ),
                        end = domUtils.findParentByTagName(
                            range.endContainer,
                            ["td", "th"],
                            true
                        );
                    //在table里边的不能清除
                    if (
                        (start && !end) ||
                        (!start && end) ||
                        (start && end && start !== end)
                    ) {
                        range.setCursor(false, true);
                    }
                }
            }
            startTd = null;
            me.removeListener("mouseover", mouseOverEvent);
        }
        me._selectionChange(250, evt);
    }

    function mouseOverEvent(type, evt) {
        if (isEditorDisabled()) {
            return;
        }

        var me = this,
            tar = evt.target || evt.srcElement;
        currentTd =
            domUtils.findParentByTagName(tar, "td", true) ||
            domUtils.findParentByTagName(tar, "th", true);
        //需要判断两个TD是否位于同一个表格内
        if (
            startTd &&
            currentTd &&
            ((startTd.tagName == "TD" && currentTd.tagName == "TD") ||
                (startTd.tagName == "TH" && currentTd.tagName == "TH")) &&
            domUtils.findParentByTagName(startTd, "table") ==
            domUtils.findParentByTagName(currentTd, "table")
        ) {
            var ut = getUETable(currentTd);
            if (startTd != currentTd) {
                me.document.body.style.webkitUserSelect = "none";
                me.selection.getNative()[
                    browser.ie9below ? "empty" : "removeAllRanges"
                    ]();
                var range = ut.getCellsRange(startTd, currentTd);
                ut.setSelected(range);
            } else {
                me.document.body.style.webkitUserSelect = "";
                ut.clearSelected();
            }
        }
        evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
    }

    function setCellHeight(cell, height, backHeight) {
        var lineHight = parseInt(
            domUtils.getComputedStyle(cell, "line-height"),
            10
            ),
            tmpHeight = backHeight + height;
        height = tmpHeight < lineHight ? lineHight : tmpHeight;
        if (cell.style.height) cell.style.height = "";
        cell.rowSpan == 1
            ? cell.setAttribute("height", height)
            : cell.removeAttribute && cell.removeAttribute("height");
    }

    function getWidth(cell) {
        if (!cell) return 0;
        return parseInt(domUtils.getComputedStyle(cell, "width"), 10);
    }

    function changeColWidth(cell, changeValue) {
        var ut = getUETable(cell);
        if (ut) {
            //根据当前移动的边框获取相关的单元格
            var table = ut.table,
                cells = getCellsByMoveBorder(cell, table);

            table.style.width = "";
            table.removeAttribute("width");

            //修正改变量
            changeValue = correctChangeValue(changeValue, cell, cells);

            if (cell.nextSibling) {
                var i = 0;

                utils.each(cells, function (cellGroup) {
                    cellGroup.left.width = +cellGroup.left.width + changeValue;
                    cellGroup.right &&
                    (cellGroup.right.width = +cellGroup.right.width - changeValue);
                });
            } else {
                utils.each(cells, function (cellGroup) {
                    cellGroup.left.width -= -changeValue;
                });
            }
        }
    }

    function isEditorDisabled() {
        return me.body.contentEditable === "false";
    }

    function changeRowHeight(td, changeValue) {
        if (Math.abs(changeValue) < 10) return;
        var ut = getUETable(td);
        if (ut) {
            var cells = ut.getSameEndPosCells(td, "y"),
                //备份需要连带变化的td的原始高度,否则后期无法获取正确的值
                backHeight = cells[0] ? cells[0].offsetHeight : 0;
            for (var i = 0, cell; (cell = cells[i++]);) {
                setCellHeight(cell, changeValue, backHeight);
            }
        }
    }

    /**
     * 获取调整单元格大小的相关单元格
     * @isContainMergeCell 返回的结果中是否包含发生合并后的单元格
     */
    function getCellsByMoveBorder(cell, table, isContainMergeCell) {
        if (!table) {
            table = domUtils.findParentByTagName(cell, "table");
        }

        if (!table) {
            return null;
        }

        //获取到该单元格所在行的序列号
        var index = domUtils.getNodeIndex(cell),
            temp = cell,
            rows = table.rows,
            colIndex = 0;

        while (temp) {
            //获取到当前单元格在未发生单元格合并时的序列
            if (temp.nodeType === 1) {
                colIndex += temp.colSpan || 1;
            }
            temp = temp.previousSibling;
        }

        temp = null;

        //记录想关的单元格
        var borderCells = [];

        utils.each(rows, function (tabRow) {
            var cells = tabRow.cells,
                currIndex = 0;

            utils.each(cells, function (tabCell) {
                currIndex += tabCell.colSpan || 1;

                if (currIndex === colIndex) {
                    borderCells.push({
                        left: tabCell,
                        right: tabCell.nextSibling || null
                    });

                    return false;
                } else if (currIndex > colIndex) {
                    if (isContainMergeCell) {
                        borderCells.push({
                            left: tabCell
                        });
                    }

                    return false;
                }
            });
        });

        return borderCells;
    }

    /**
     * 通过给定的单元格集合获取最小的单元格width
     */
    function getMinWidthByTableCells(cells) {
        var minWidth = Number.MAX_VALUE;

        for (var i = 0, curCell; (curCell = cells[i]); i++) {
            minWidth = Math.min(
                minWidth,
                curCell.width || getTableCellWidth(curCell)
            );
        }

        return minWidth;
    }

    function correctChangeValue(changeValue, relatedCell, cells) {
        //为单元格的paading预留空间
        changeValue -= getTabcellSpace();

        if (changeValue < 0) {
            return 0;
        }

        changeValue -= getTableCellWidth(relatedCell);

        //确定方向
        var direction = changeValue < 0 ? "left" : "right";

        changeValue = Math.abs(changeValue);

        //只关心非最后一个单元格就可以
        utils.each(cells, function (cellGroup) {
            var curCell = cellGroup[direction];

            //为单元格保留最小空间
            if (curCell) {
                changeValue = Math.min(
                    changeValue,
                    getTableCellWidth(curCell) - cellMinWidth
                );
            }
        });

        //修正越界
        changeValue = changeValue < 0 ? 0 : changeValue;

        return direction === "left" ? -changeValue : changeValue;
    }

    function getTableCellWidth(cell) {
        var width = 0,
            //偏移纠正量
            offset = 0,
            width = cell.offsetWidth - getTabcellSpace();

        //最后一个节点纠正一下
        if (!cell.nextSibling) {
            width -= getTableCellOffset(cell);
        }

        width = width < 0 ? 0 : width;

        try {
            cell.width = width;
        } catch (e) {
        }

        return width;
    }

    /**
     * 获取单元格所在表格的最末单元格的偏移量
     */
    function getTableCellOffset(cell) {
        tab = domUtils.findParentByTagName(cell, "table", false);

        if (tab.offsetVal === undefined) {
            var prev = cell.previousSibling;

            if (prev) {
                //最后一个单元格和前一个单元格的width diff结果 如果恰好为一个border width, 则条件成立
                tab.offsetVal = cell.offsetWidth - prev.offsetWidth === UT.borderWidth
                    ? UT.borderWidth
                    : 0;
            } else {
                tab.offsetVal = 0;
            }
        }

        return tab.offsetVal;
    }

    function getTabcellSpace() {
        if (UT.tabcellSpace === undefined) {
            var cell = null,
                tab = me.document.createElement("table"),
                tbody = me.document.createElement("tbody"),
                trow = me.document.createElement("tr"),
                tabcell = me.document.createElement("td"),
                mirror = null;

            tabcell.style.cssText = "border: 0;";
            tabcell.width = 1;

            trow.appendChild(tabcell);
            trow.appendChild((mirror = tabcell.cloneNode(false)));

            tbody.appendChild(trow);

            tab.appendChild(tbody);

            tab.style.cssText = "visibility: hidden;";

            me.body.appendChild(tab);

            UT.paddingSpace = tabcell.offsetWidth - 1;

            var tmpTabWidth = tab.offsetWidth;

            tabcell.style.cssText = "";
            mirror.style.cssText = "";

            UT.borderWidth = (tab.offsetWidth - tmpTabWidth) / 3;

            UT.tabcellSpace = UT.paddingSpace + UT.borderWidth;

            me.body.removeChild(tab);
        }

        getTabcellSpace = function () {
            return UT.tabcellSpace;
        };

        return UT.tabcellSpace;
    }

    function getDragLine(editor, doc) {
        if (mousedown) return;
        dragLine = editor.document.createElement("div");
        domUtils.setAttributes(dragLine, {
            id: "ue_tableDragLine",
            unselectable: "on",
            contenteditable: false,
            onresizestart: "return false",
            ondragstart: "return false",
            onselectstart: "return false",
            style:
                "background-color:blue;position:absolute;padding:0;margin:0;background-image:none;border:0px none;opacity:0;filter:alpha(opacity=0)"
        });
        editor.body.appendChild(dragLine);
    }

    function hideDragLine(editor) {
        if (mousedown) return;
        var line;
        while ((line = editor.document.getElementById("ue_tableDragLine"))) {
            domUtils.remove(line);
        }
    }

    /**
     * 依据state(v|h)在cell位置显示横线
     * @param state
     * @param cell
     */
    function showDragLineAt(state, cell) {
        if (!cell) return;
        var table = domUtils.findParentByTagName(cell, "table"),
            caption = table.getElementsByTagName("caption"),
            width = table.offsetWidth,
            height =
                table.offsetHeight - (caption.length > 0 ? caption[0].offsetHeight : 0),
            tablePos = domUtils.getXY(table),
            cellPos = domUtils.getXY(cell),
            css;
        switch (state) {
            case "h":
                css =
                    "height:" +
                    height +
                    "px;top:" +
                    (tablePos.y + (caption.length > 0 ? caption[0].offsetHeight : 0)) +
                    "px;left:" +
                    (cellPos.x + cell.offsetWidth);
                dragLine.style.cssText =
                    css +
                    "px;position: absolute;display:block;background-color:blue;width:1px;border:0; color:blue;opacity:.3;filter:alpha(opacity=30)";
                break;
            case "v":
                css =
                    "width:" +
                    width +
                    "px;left:" +
                    tablePos.x +
                    "px;top:" +
                    (cellPos.y + cell.offsetHeight);
                //必须加上border:0和color:blue,否则低版ie不支持背景色显示
                dragLine.style.cssText =
                    css +
                    "px;overflow:hidden;position: absolute;display:block;background-color:blue;height:1px;border:0;color:blue;opacity:.2;filter:alpha(opacity=20)";
                break;
            default:
        }
    }

    /**
     * 当表格边框颜色为白色时设置为虚线,true为添加虚线
     * @param editor
     * @param flag
     */
    function switchBorderColor(editor, flag) {
        var tableArr = domUtils.getElementsByTagName(editor.body, "table"),
            color;
        for (var i = 0, node; (node = tableArr[i++]);) {
            var td = domUtils.getElementsByTagName(node, "td");
            if (td[0]) {
                if (flag) {
                    color = td[0].style.borderColor.replace(/\s/g, "");
                    if (/(#ffffff)|(rgb\(255,255,255\))/gi.test(color))
                        domUtils.addClass(node, "noBorderTable");
                } else {
                    domUtils.removeClasses(node, "noBorderTable");
                }
            }
        }
    }

    function getTableWidth(editor, needIEHack, defaultValue) {
        var body = editor.body;
        return (
            body.offsetWidth -
            (needIEHack
                ? parseInt(domUtils.getComputedStyle(body, "margin-left"), 10) * 2
                : 0) -
            defaultValue.tableBorder * 2 -
            (editor.options.offsetWidth || 0)
        );
    }

    /**
     * 获取当前拖动的单元格
     */
    function getTargetTd(editor, evt) {
        var target = domUtils.findParentByTagName(
            evt.target || evt.srcElement,
            ["td", "th"],
            true
            ),
            dir = null;

        if (!target) {
            return null;
        }

        dir = getRelation(target, mouseCoords(evt));

        //如果有前一个节点, 需要做一个修正, 否则可能会得到一个错误的td

        if (!target) {
            return null;
        }

        if (dir === "h1" && target.previousSibling) {
            var position = domUtils.getXY(target),
                cellWidth = target.offsetWidth;

            if (Math.abs(position.x + cellWidth - evt.clientX) > cellWidth / 3) {
                target = target.previousSibling;
            }
        } else if (dir === "v1" && target.parentNode.previousSibling) {
            var position = domUtils.getXY(target),
                cellHeight = target.offsetHeight;

            if (Math.abs(position.y + cellHeight - evt.clientY) > cellHeight / 3) {
                target = target.parentNode.previousSibling.firstChild;
            }
        }

        //排除了非td内部以及用于代码高亮部分的td
        return target && !(editor.fireEvent("excludetable", target) === true)
            ? target
            : null;
    }
};


// plugins/table.sort.js
/**
 * Created with JetBrains PhpStorm.
 * User: Jinqn
 * Date: 13-10-12
 * Time: 上午10:20
 * To change this template use File | Settings | File Templates.
 */

UE.UETable.prototype.sortTable = function (sortByCellIndex, compareFn) {
    var table = this.table,
        rows = table.rows,
        trArray = [],
        flag = rows[0].cells[0].tagName === "TH",
        lastRowIndex = 0;
    if (this.selectedTds.length) {
        var range = this.cellsRange,
            len = range.endRowIndex + 1;
        for (var i = range.beginRowIndex; i < len; i++) {
            trArray[i] = rows[i];
        }
        trArray.splice(0, range.beginRowIndex);
        lastRowIndex = range.endRowIndex + 1 === this.rowsNum
            ? 0
            : range.endRowIndex + 1;
    } else {
        for (var i = 0, len = rows.length; i < len; i++) {
            trArray[i] = rows[i];
        }
    }

    var Fn = {
        reversecurrent: function (td1, td2) {
            return 1;
        },
        orderbyasc: function (td1, td2) {
            var value1 = td1.innerText || td1.textContent,
                value2 = td2.innerText || td2.textContent;
            return value1.localeCompare(value2);
        },
        reversebyasc: function (td1, td2) {
            var value1 = td1.innerHTML,
                value2 = td2.innerHTML;
            return value2.localeCompare(value1);
        },
        orderbynum: function (td1, td2) {
            var value1 = td1[browser.ie ? "innerText" : "textContent"].match(/\d+/),
                value2 = td2[browser.ie ? "innerText" : "textContent"].match(/\d+/);
            if (value1) value1 = +value1[0];
            if (value2) value2 = +value2[0];
            return (value1 || 0) - (value2 || 0);
        },
        reversebynum: function (td1, td2) {
            var value1 = td1[browser.ie ? "innerText" : "textContent"].match(/\d+/),
                value2 = td2[browser.ie ? "innerText" : "textContent"].match(/\d+/);
            if (value1) value1 = +value1[0];
            if (value2) value2 = +value2[0];
            return (value2 || 0) - (value1 || 0);
        }
    };

    //对表格设置排序的标记data-sort-type
    table.setAttribute(
        "data-sort-type",
        compareFn && typeof compareFn === "string" && Fn[compareFn] ? compareFn : ""
    );

    //th不参与排序
    flag && trArray.splice(0, 1);
    trArray = utils.sort(trArray, function (tr1, tr2) {
        var result;
        if (compareFn && typeof compareFn === "function") {
            result = compareFn.call(
                this,
                tr1.cells[sortByCellIndex],
                tr2.cells[sortByCellIndex]
            );
        } else if (compareFn && typeof compareFn === "number") {
            result = 1;
        } else if (compareFn && typeof compareFn === "string" && Fn[compareFn]) {
            result = Fn[compareFn].call(
                this,
                tr1.cells[sortByCellIndex],
                tr2.cells[sortByCellIndex]
            );
        } else {
            result = Fn["orderbyasc"].call(
                this,
                tr1.cells[sortByCellIndex],
                tr2.cells[sortByCellIndex]
            );
        }
        return result;
    });
    var fragment = table.ownerDocument.createDocumentFragment();
    for (var j = 0, len = trArray.length; j < len; j++) {
        fragment.appendChild(trArray[j]);
    }
    var tbody = table.getElementsByTagName("tbody")[0];
    if (!lastRowIndex) {
        tbody.appendChild(fragment);
    } else {
        tbody.insertBefore(
            fragment,
            rows[lastRowIndex - range.endRowIndex + range.beginRowIndex - 1]
        );
    }
};

UE.plugins["tablesort"] = function () {
    var me = this,
        UT = UE.UETable,
        getUETable = function (tdOrTable) {
            return UT.getUETable(tdOrTable);
        },
        getTableItemsByRange = function (editor) {
            return UT.getTableItemsByRange(editor);
        };

    me.ready(function () {
        //添加表格可排序的样式
        utils.cssRule(
            "tablesort",
            "table.sortEnabled tr.firstRow th,table.sortEnabled tr.firstRow td{padding-right:20px;background-repeat: no-repeat;background-position: center right;" +
            "   background-image:url(" +
            me.options.themePath +
            me.options.theme +
            "/images/sortable.png);}",
            me.document
        );

        //做单元格合并操作时,清除可排序标识
        me.addListener("afterexeccommand", function (type, cmd) {
            if (cmd == "mergeright" || cmd == "mergedown" || cmd == "mergecells") {
                this.execCommand("disablesort");
            }
        });
    });

    //表格排序
    UE.commands["sorttable"] = {
        queryCommandState: function () {
            var me = this,
                tableItems = getTableItemsByRange(me);
            if (!tableItems.cell) return -1;
            var table = tableItems.table,
                cells = table.getElementsByTagName("td");
            for (var i = 0, cell; (cell = cells[i++]);) {
                if (cell.rowSpan != 1 || cell.colSpan != 1) return -1;
            }
            return 0;
        },
        execCommand: function (cmd, fn) {
            var me = this,
                range = me.selection.getRange(),
                bk = range.createBookmark(true),
                tableItems = getTableItemsByRange(me),
                cell = tableItems.cell,
                ut = getUETable(tableItems.table),
                cellInfo = ut.getCellInfo(cell);
            ut.sortTable(cellInfo.cellIndex, fn);
            range.moveToBookmark(bk);
            try {
                range.select();
            } catch (e) {
            }
        }
    };

    //设置表格可排序,清除表格可排序
    UE.commands["enablesort"] = UE.commands["disablesort"] = {
        queryCommandState: function (cmd) {
            var table = getTableItemsByRange(this).table;
            if (table && cmd == "enablesort") {
                var cells = domUtils.getElementsByTagName(table, "th td");
                for (var i = 0; i < cells.length; i++) {
                    if (
                        cells[i].getAttribute("colspan") > 1 ||
                        cells[i].getAttribute("rowspan") > 1
                    )
                        return -1;
                }
            }

            return !table
                ? -1
                : (cmd == "enablesort") ^
                (table.getAttribute("data-sort") != "sortEnabled")
                    ? -1
                    : 0;
        },
        execCommand: function (cmd) {
            var table = getTableItemsByRange(this).table;
            table.setAttribute(
                "data-sort",
                cmd == "enablesort" ? "sortEnabled" : "sortDisabled"
            );
            cmd == "enablesort"
                ? domUtils.addClass(table, "sortEnabled")
                : domUtils.removeClasses(table, "sortEnabled");
        }
    };
};


// plugins/contextmenu.js
///import core
///commands 右键菜单
///commandsName  ContextMenu
///commandsTitle  右键菜单
/**
 * 右键菜单
 * @function
 * @name baidu.editor.plugins.contextmenu
 * @author zhanyi
 */

UE.plugins["contextmenu"] = function () {
    var me = this;

    me.setOpt("enableContextMenu", me.getOpt("enableContextMenu") || true);

    if (me.getOpt("enableContextMenu") === false) {
        return;
    }
    var lang = me.getLang("contextMenu"),
        menu,
        items = me.options.contextMenu || [
            {label: lang["selectall"], cmdName: "selectall"},
            {
                label: lang.cleardoc,
                cmdName: "cleardoc",
                exec: function () {
                    if (confirm(lang.confirmclear)) {
                        this.execCommand("cleardoc");
                    }
                }
            },
            "-",
            {
                label: lang.unlink,
                cmdName: "unlink"
            },
            "-",
            {
                group: lang.paragraph,
                icon: "justifyjustify",
                subMenu: [
                    {
                        label: lang.justifyleft,
                        cmdName: "justify",
                        value: "left"
                    },
                    {
                        label: lang.justifyright,
                        cmdName: "justify",
                        value: "right"
                    },
                    {
                        label: lang.justifycenter,
                        cmdName: "justify",
                        value: "center"
                    },
                    {
                        label: lang.justifyjustify,
                        cmdName: "justify",
                        value: "justify"
                    }
                ]
            },
            "-",
            {
                group: lang.table,
                icon: "table",
                subMenu: [
                    {
                        label: lang.inserttable,
                        cmdName: "inserttable"
                    },
                    {
                        label: lang.deletetable,
                        cmdName: "deletetable"
                    },
                    "-",
                    {
                        label: lang.deleterow,
                        cmdName: "deleterow"
                    },
                    {
                        label: lang.deletecol,
                        cmdName: "deletecol"
                    },
                    {
                        label: lang.insertcol,
                        cmdName: "insertcol"
                    },
                    {
                        label: lang.insertcolnext,
                        cmdName: "insertcolnext"
                    },
                    {
                        label: lang.insertrow,
                        cmdName: "insertrow"
                    },
                    {
                        label: lang.insertrownext,
                        cmdName: "insertrownext"
                    },
                    "-",
                    {
                        label: lang.insertcaption,
                        cmdName: "insertcaption"
                    },
                    {
                        label: lang.deletecaption,
                        cmdName: "deletecaption"
                    },
                    {
                        label: lang.inserttitle,
                        cmdName: "inserttitle"
                    },
                    {
                        label: lang.deletetitle,
                        cmdName: "deletetitle"
                    },
                    {
                        label: lang.inserttitlecol,
                        cmdName: "inserttitlecol"
                    },
                    {
                        label: lang.deletetitlecol,
                        cmdName: "deletetitlecol"
                    },
                    "-",
                    {
                        label: lang.mergecells,
                        cmdName: "mergecells"
                    },
                    {
                        label: lang.mergeright,
                        cmdName: "mergeright"
                    },
                    {
                        label: lang.mergedown,
                        cmdName: "mergedown"
                    },
                    "-",
                    {
                        label: lang.splittorows,
                        cmdName: "splittorows"
                    },
                    {
                        label: lang.splittocols,
                        cmdName: "splittocols"
                    },
                    {
                        label: lang.splittocells,
                        cmdName: "splittocells"
                    },
                    "-",
                    {
                        label: lang.averageDiseRow,
                        cmdName: "averagedistributerow"
                    },
                    {
                        label: lang.averageDisCol,
                        cmdName: "averagedistributecol"
                    },
                    "-",
                    {
                        label: lang.edittd,
                        cmdName: "edittd",
                        exec: function () {
                            if (UE.ui["edittd"]) {
                                new UE.ui["edittd"](this);
                            }
                            this.getDialog("edittd").open();
                        }
                    },
                    {
                        label: lang.edittable,
                        cmdName: "edittable",
                        exec: function () {
                            if (UE.ui["edittable"]) {
                                new UE.ui["edittable"](this);
                            }
                            this.getDialog("edittable").open();
                        }
                    },
                    {
                        label: lang.setbordervisible,
                        cmdName: "setbordervisible"
                    }
                ]
            },
            {
                group: lang.tablesort,
                icon: "tablesort",
                subMenu: [
                    {
                        label: lang.enablesort,
                        cmdName: "enablesort"
                    },
                    {
                        label: lang.disablesort,
                        cmdName: "disablesort"
                    },
                    "-",
                    {
                        label: lang.reversecurrent,
                        cmdName: "sorttable",
                        value: "reversecurrent"
                    },
                    {
                        label: lang.orderbyasc,
                        cmdName: "sorttable",
                        value: "orderbyasc"
                    },
                    {
                        label: lang.reversebyasc,
                        cmdName: "sorttable",
                        value: "reversebyasc"
                    },
                    {
                        label: lang.orderbynum,
                        cmdName: "sorttable",
                        value: "orderbynum"
                    },
                    {
                        label: lang.reversebynum,
                        cmdName: "sorttable",
                        value: "reversebynum"
                    }
                ]
            },
            {
                group: lang.borderbk,
                icon: "borderBack",
                subMenu: [
                    {
                        label: lang.setcolor,
                        cmdName: "interlacetable",
                        exec: function () {
                            this.execCommand("interlacetable");
                        }
                    },
                    {
                        label: lang.unsetcolor,
                        cmdName: "uninterlacetable",
                        exec: function () {
                            this.execCommand("uninterlacetable");
                        }
                    },
                    {
                        label: lang.setbackground,
                        cmdName: "settablebackground",
                        exec: function () {
                            this.execCommand("settablebackground", {
                                repeat: true,
                                colorList: ["#bbb", "#ccc"]
                            });
                        }
                    },
                    {
                        label: lang.unsetbackground,
                        cmdName: "cleartablebackground",
                        exec: function () {
                            this.execCommand("cleartablebackground");
                        }
                    },
                    {
                        label: lang.redandblue,
                        cmdName: "settablebackground",
                        exec: function () {
                            this.execCommand("settablebackground", {
                                repeat: true,
                                colorList: ["red", "blue"]
                            });
                        }
                    },
                    {
                        label: lang.threecolorgradient,
                        cmdName: "settablebackground",
                        exec: function () {
                            this.execCommand("settablebackground", {
                                repeat: true,
                                colorList: ["#aaa", "#bbb", "#ccc"]
                            });
                        }
                    }
                ]
            },
            {
                group: lang.aligntd,
                icon: "aligntd",
                subMenu: [
                    {
                        cmdName: "cellalignment",
                        value: {align: "left", vAlign: "top"}
                    },
                    {
                        cmdName: "cellalignment",
                        value: {align: "center", vAlign: "top"}
                    },
                    {
                        cmdName: "cellalignment",
                        value: {align: "right", vAlign: "top"}
                    },
                    {
                        cmdName: "cellalignment",
                        value: {align: "left", vAlign: "middle"}
                    },
                    {
                        cmdName: "cellalignment",
                        value: {align: "center", vAlign: "middle"}
                    },
                    {
                        cmdName: "cellalignment",
                        value: {align: "right", vAlign: "middle"}
                    },
                    {
                        cmdName: "cellalignment",
                        value: {align: "left", vAlign: "bottom"}
                    },
                    {
                        cmdName: "cellalignment",
                        value: {align: "center", vAlign: "bottom"}
                    },
                    {
                        cmdName: "cellalignment",
                        value: {align: "right", vAlign: "bottom"}
                    }
                ]
            },
            {
                group: lang.aligntable,
                icon: "aligntable",
                subMenu: [
                    {
                        cmdName: "tablealignment",
                        className: "left",
                        label: lang.tableleft,
                        value: "left"
                    },
                    {
                        cmdName: "tablealignment",
                        className: "center",
                        label: lang.tablecenter,
                        value: "center"
                    },
                    {
                        cmdName: "tablealignment",
                        className: "right",
                        label: lang.tableright,
                        value: "right"
                    }
                ]
            },
            "-",
            {
                label: lang.insertparagraphbefore,
                cmdName: "insertparagraph",
                value: true
            },
            {
                label: lang.insertparagraphafter,
                cmdName: "insertparagraph"
            },
            {
                label: lang["copy"],
                cmdName: "copy"
            },
            {
                label: lang["paste"],
                cmdName: "paste"
            }
        ];
    if (!items.length) {
        return;
    }
    var uiUtils = UE.ui.uiUtils;

    me.addListener("contextmenu", function (type, evt) {
        var offset = uiUtils.getViewportOffsetByEvent(evt);
        me.fireEvent("beforeselectionchange");
        if (menu) {
            menu.destroy();
        }
        for (var i = 0, ti, contextItems = []; (ti = items[i]); i++) {
            var last;
            (function (item) {
                if (item == "-") {
                    if ((last = contextItems[contextItems.length - 1]) && last !== "-") {
                        contextItems.push("-");
                    }
                } else if (item.hasOwnProperty("group")) {
                    for (var j = 0, cj, subMenu = []; (cj = item.subMenu[j]); j++) {
                        (function (subItem) {
                            if (subItem == "-") {
                                if ((last = subMenu[subMenu.length - 1]) && last !== "-") {
                                    subMenu.push("-");
                                } else {
                                    subMenu.splice(subMenu.length - 1);
                                }
                            } else {
                                if (
                                    (me.commands[subItem.cmdName] ||
                                        UE.commands[subItem.cmdName] ||
                                        subItem.query) &&
                                    (subItem.query
                                        ? subItem.query()
                                        : me.queryCommandState(subItem.cmdName)) > -1
                                ) {
                                    subMenu.push({
                                        label:
                                            subItem.label ||
                                            me.getLang(
                                                "contextMenu." +
                                                subItem.cmdName +
                                                (subItem.value || "")
                                            ) ||
                                            "",
                                        className:
                                            "edui-for-" +
                                            subItem.cmdName +
                                            (subItem.className
                                                ? " edui-for-" +
                                                subItem.cmdName +
                                                "-" +
                                                subItem.className
                                                : ""),
                                        onclick: subItem.exec
                                            ? function () {
                                                subItem.exec.call(me);
                                            }
                                            : function () {
                                                me.execCommand(subItem.cmdName, subItem.value);
                                            }
                                    });
                                }
                            }
                        })(cj);
                    }
                    if (subMenu.length) {
                        function getLabel() {
                            switch (item.icon) {
                                case "table":
                                    return me.getLang("contextMenu.table");
                                case "justifyjustify":
                                    return me.getLang("contextMenu.paragraph");
                                case "aligntd":
                                    return me.getLang("contextMenu.aligntd");
                                case "aligntable":
                                    return me.getLang("contextMenu.aligntable");
                                case "tablesort":
                                    return lang.tablesort;
                                case "borderBack":
                                    return lang.borderbk;
                                default:
                                    return "";
                            }
                        }

                        contextItems.push({
                            //todo 修正成自动获取方式
                            label: getLabel(),
                            className: "edui-for-" + item.icon,
                            subMenu: {
                                items: subMenu,
                                editor: me
                            }
                        });
                    }
                } else {
                    //有可能commmand没有加载右键不能出来,或者没有command也想能展示出来添加query方法
                    if (
                        (me.commands[item.cmdName] ||
                            UE.commands[item.cmdName] ||
                            item.query) &&
                        (item.query
                            ? item.query.call(me)
                            : me.queryCommandState(item.cmdName)) > -1
                    ) {
                        contextItems.push({
                            label: item.label || me.getLang("contextMenu." + item.cmdName),
                            className:
                                "edui-for-" +
                                (item.icon ? item.icon : item.cmdName + (item.value || "")),
                            onclick: item.exec
                                ? function () {
                                    item.exec.call(me);
                                }
                                : function () {
                                    me.execCommand(item.cmdName, item.value);
                                }
                        });
                    }
                }
            })(ti);
        }
        if (contextItems[contextItems.length - 1] == "-") {
            contextItems.pop();
        }

        menu = new UE.ui.Menu({
            items: contextItems,
            className: "edui-contextmenu",
            editor: me
        });
        menu.render();
        menu.showAt(offset);

        me.fireEvent("aftershowcontextmenu", menu);

        domUtils.preventDefault(evt);
        if (browser.ie) {
            var ieRange;
            try {
                ieRange = me.selection.getNative().createRange();
            } catch (e) {
                return;
            }
            if (ieRange.item) {
                var range = new dom.Range(me.document);
                range.selectNode(ieRange.item(0)).select(true, true);
            }
        }
    });

    // 添加复制的flash按钮
    me.addListener("aftershowcontextmenu", function (type, menu) {
        if (me.zeroclipboard) {
            var items = menu.items;
            for (var key in items) {
                if (items[key].className == "edui-for-copy") {
                    me.zeroclipboard.clip(items[key].getDom());
                }
            }
        }
    });
};


// plugins/shortcutmenu.js
///import core
///commands       弹出菜单
// commandsName  popupmenu
///commandsTitle  弹出菜单
/**
 * 弹出菜单
 * @function
 * @name baidu.editor.plugins.popupmenu
 * @author xuheng
 */

UE.plugins["shortcutmenu"] = function () {
    var me = this,
        menu,
        items = me.options.shortcutMenu || [];

    if (!items.length) {
        return;
    }

    // contextmenu
    me.addListener("mouseup", function (type, e) {
        var me = this,
            customEvt = {
                type: type,
                target: e.target || e.srcElement,
                screenX: e.screenX,
                screenY: e.screenY,
                clientX: e.clientX,
                clientY: e.clientY
            };
        // console.log('shortcutmenu.mouseup', e, e.target, me.selection.getRange());

        setTimeout(function () {
            // console.log(e, me.selection.getRange());
            // var rng = me.selection.getRange();
            // if (rng.collapsed) {
            //     return;
            // }
            // if (rng.collapsed === false || type === "contextmenu") {
            // 未选中文字情况下不显示
            // if (!me.selection.getText()) {
            //     return
            // }
            if (!menu) {
                menu = new baidu.editor.ui.ShortCutMenu({
                    editor: me,
                    items: items.concat([]),
                    theme: me.options.theme,
                    className: "edui-shortcutmenu"
                });

                menu.render();
                me.fireEvent("afterrendershortcutmenu", menu);
            }
            menu.show(customEvt, !!UE.plugins["contextmenu"]);
            // }
        });

        if (type === "contextmenu") {
            domUtils.preventDefault(e);
            if (browser.ie9below) {
                var ieRange;
                try {
                    ieRange = me.selection.getNative().createRange();
                } catch (e) {
                    return;
                }
                if (ieRange.item) {
                    var range = new dom.Range(me.document);
                    range.selectNode(ieRange.item(0)).select(true, true);
                }
            }
        }
    });

    me.addListener("keydown", function (type) {
        if (type === "keydown") {
            menu && !menu.isHidden && menu.hide();
        }
    });
};


// plugins/basestyle.js
/**
 * B、I、sub、super命令支持
 * @file
 * @since 1.2.6.1
 */

UE.plugins["basestyle"] = function () {
    /**
     * 字体加粗
     * @command bold
     * @param { String } cmd 命令字符串
     * @remind 对已加粗的文本内容执行该命令, 将取消加粗
     * @method execCommand
     * @example
     * ```javascript
     * //editor是编辑器实例
     * //对当前选中的文本内容执行加粗操作
     * //第一次执行, 文本内容加粗
     * editor.execCommand( 'bold' );
     *
     * //第二次执行, 文本内容取消加粗
     * editor.execCommand( 'bold' );
     * ```
     */

    /**
     * 字体倾斜
     * @command italic
     * @method execCommand
     * @param { String } cmd 命令字符串
     * @remind 对已倾斜的文本内容执行该命令, 将取消倾斜
     * @example
     * ```javascript
     * //editor是编辑器实例
     * //对当前选中的文本内容执行斜体操作
     * //第一次操作, 文本内容将变成斜体
     * editor.execCommand( 'italic' );
     *
     * //再次对同一文本内容执行, 则文本内容将恢复正常
     * editor.execCommand( 'italic' );
     * ```
     */

    /**
     * 下标文本,与“superscript”命令互斥
     * @command subscript
     * @method execCommand
     * @remind  把选中的文本内容切换成下标文本, 如果当前选中的文本已经是下标, 则该操作会把文本内容还原成正常文本
     * @param { String } cmd 命令字符串
     * @example
     * ```javascript
     * //editor是编辑器实例
     * //对当前选中的文本内容执行下标操作
     * //第一次操作, 文本内容将变成下标文本
     * editor.execCommand( 'subscript' );
     *
     * //再次对同一文本内容执行, 则文本内容将恢复正常
     * editor.execCommand( 'subscript' );
     * ```
     */

    /**
     * 上标文本,与“subscript”命令互斥
     * @command superscript
     * @method execCommand
     * @remind 把选中的文本内容切换成上标文本, 如果当前选中的文本已经是上标, 则该操作会把文本内容还原成正常文本
     * @param { String } cmd 命令字符串
     * @example
     * ```javascript
     * //editor是编辑器实例
     * //对当前选中的文本内容执行上标操作
     * //第一次操作, 文本内容将变成上标文本
     * editor.execCommand( 'superscript' );
     *
     * //再次对同一文本内容执行, 则文本内容将恢复正常
     * editor.execCommand( 'superscript' );
     * ```
     */
    var basestyles = {
            bold: ["strong", "b"],
            italic: ["em", "i"],
            subscript: ["sub"],
            superscript: ["sup"]
        },
        getObj = function (editor, tagNames) {
            return domUtils.filterNodeList(
                editor.selection.getStartElementPath(),
                tagNames
            );
        },
        me = this;
    //添加快捷键
    me.addshortcutkey({
        Bold: "ctrl+66", //^B
        Italic: "ctrl+73", //^I
        Underline: "ctrl+85" //^U
    });
    me.addInputRule(function (root) {
        utils.each(root.getNodesByTagName("b i"), function (node) {
            switch (node.tagName) {
                case "b":
                    node.tagName = "strong";
                    break;
                case "i":
                    node.tagName = "em";
            }
        });
    });
    for (var style in basestyles) {
        (function (cmd, tagNames) {
            me.commands[cmd] = {
                execCommand: function (cmdName) {
                    var range = me.selection.getRange(),
                        obj = getObj(this, tagNames);
                    if (range.collapsed) {
                        if (obj) {
                            var tmpText = me.document.createTextNode("");
                            range.insertNode(tmpText).removeInlineStyle(tagNames);
                            range.setStartBefore(tmpText);
                            domUtils.remove(tmpText);
                        } else {
                            var tmpNode = range.document.createElement(tagNames[0]);
                            if (cmdName == "superscript" || cmdName == "subscript") {
                                tmpText = me.document.createTextNode("");
                                range
                                    .insertNode(tmpText)
                                    .removeInlineStyle(["sub", "sup"])
                                    .setStartBefore(tmpText)
                                    .collapse(true);
                            }
                            range.insertNode(tmpNode).setStart(tmpNode, 0);
                        }
                        range.collapse(true);
                    } else {
                        if (cmdName == "superscript" || cmdName == "subscript") {
                            if (!obj || obj.tagName.toLowerCase() != cmdName) {
                                range.removeInlineStyle(["sub", "sup"]);
                            }
                        }
                        obj
                            ? range.removeInlineStyle(tagNames)
                            : range.applyInlineStyle(tagNames[0]);
                    }
                    range.select();
                },
                queryCommandState: function () {
                    return getObj(this, tagNames) ? 1 : 0;
                }
            };
        })(style, basestyles[style]);
    }
};


// plugins/elementpath.js
/**
 * 选取路径命令
 * @file
 */
UE.plugins["elementpath"] = function () {
    var currentLevel,
        tagNames,
        me = this;
    me.setOpt("elementPathEnabled", true);
    if (!me.options.elementPathEnabled) {
        return;
    }
    me.commands["elementpath"] = {
        execCommand: function (cmdName, level) {
            var start = tagNames[level],
                range = me.selection.getRange();
            currentLevel = level * 1;
            range.selectNode(start).select();
        },
        queryCommandValue: function () {
            //产生一个副本,不能修改原来的startElementPath;
            var parents = [].concat(this.selection.getStartElementPath()).reverse(),
                names = [];
            tagNames = parents;
            for (var i = 0, ci; (ci = parents[i]); i++) {
                if (ci.nodeType == 3) {
                    continue;
                }
                var name = ci.tagName.toLowerCase();
                if (name == "img" && ci.getAttribute("anchorname")) {
                    name = "anchor";
                }
                names[i] = name;
                if (currentLevel == i) {
                    currentLevel = -1;
                    break;
                }
            }
            return names;
        }
    };
};


// plugins/formatmatch.js
/**
 * 格式刷,只格式inline的
 * @file
 * @since 1.2.6.1
 */

/**
 * 格式刷
 * @command formatmatch
 * @method execCommand
 * @remind 该操作不能复制段落格式
 * @param { String } cmd 命令字符串
 * @example
 * ```javascript
 * //editor是编辑器实例
 * //获取格式刷
 * editor.execCommand( 'formatmatch' );
 * ```
 */
UE.plugins["formatmatch"] = function () {
    var me = this,
        list = [],
        img,
        flag = 0;

    me.addListener("reset", function () {
        list = [];
        flag = 0;
    });

    function addList(type, evt) {
        if (browser.webkit) {
            var target = evt.target.tagName == "IMG" ? evt.target : null;
        }

        function addFormat(range) {
            if (text) {
                range.selectNode(text);
            }
            return range.applyInlineStyle(list[list.length - 1].tagName, null, list);
        }

        me.undoManger && me.undoManger.save();

        var range = me.selection.getRange(),
            imgT = target || range.getClosedNode();
        if (img && imgT && imgT.tagName == "IMG") {
            //trace:964

            imgT.style.cssText +=
                ";float:" +
                (img.style.cssFloat || img.style.styleFloat || "none") +
                ";display:" +
                (img.style.display || "inline");

            img = null;
        } else {
            if (!img) {
                var collapsed = range.collapsed;
                if (collapsed) {
                    var text = me.document.createTextNode("match");
                    range.insertNode(text).select();
                }
                me.__hasEnterExecCommand = true;
                //不能把block上的属性干掉
                //trace:1553
                var removeFormatAttributes = me.options.removeFormatAttributes;
                me.options.removeFormatAttributes = "";
                me.execCommand("removeformat");
                me.options.removeFormatAttributes = removeFormatAttributes;
                me.__hasEnterExecCommand = false;
                //trace:969
                range = me.selection.getRange();
                if (list.length) {
                    addFormat(range);
                }
                if (text) {
                    range.setStartBefore(text).collapse(true);
                }
                range.select();
                text && domUtils.remove(text);
            }
        }

        me.undoManger && me.undoManger.save();
        me.removeListener("mouseup", addList);
        flag = 0;
    }

    me.commands["formatmatch"] = {
        execCommand: function (cmdName) {
            if (flag) {
                flag = 0;
                list = [];
                me.removeListener("mouseup", addList);
                return;
            }

            var range = me.selection.getRange();
            img = range.getClosedNode();
            if (!img || img.tagName != "IMG") {
                range.collapse(true).shrinkBoundary();
                var start = range.startContainer;
                list = domUtils.findParents(start, true, function (node) {
                    return !domUtils.isBlockElm(node) && node.nodeType == 1;
                });
                //a不能加入格式刷, 并且克隆节点
                for (var i = 0, ci; (ci = list[i]); i++) {
                    if (ci.tagName == "A") {
                        list.splice(i, 1);
                        break;
                    }
                }
            }

            me.addListener("mouseup", addList);
            flag = 1;
        },
        queryCommandState: function () {
            return flag;
        },
        notNeedUndo: 1
    };
};


// plugins/searchreplace.js
///import core
///commands 查找替换
///commandsName  SearchReplace
///commandsTitle  查询替换
///commandsDialog  dialogs\searchreplace
/**
 * @description 查找替换
 * @author zhanyi
 */

UE.plugin.register("searchreplace", function () {
    var me = this;

    var _blockElm = {table: 1, tbody: 1, tr: 1, ol: 1, ul: 1};

    var lastRng = null;

    function getText(node) {
        var text = node.nodeType == 3
            ? node.nodeValue
            : node[browser.ie ? "innerText" : "textContent"];
        return text.replace(domUtils.fillChar, "");
    }

    function findTextInString(textContent, opt, currentIndex) {
        var str = opt.searchStr;

        var reg = new RegExp(str, "g" + (opt.casesensitive ? "" : "i")),
            match;

        if (opt.dir == -1) {
            textContent = textContent.substr(0, currentIndex);
            textContent = textContent.split("").reverse().join("");
            str = str.split("").reverse().join("");
            match = reg.exec(textContent);
            if (match) {
                return currentIndex - match.index - str.length;
            }
        } else {
            textContent = textContent.substr(currentIndex);
            match = reg.exec(textContent);
            if (match) {
                return match.index + currentIndex;
            }
        }

        return -1;
    }

    function findTextBlockElm(node, currentIndex, opt) {
        var textContent,
            index,
            methodName = opt.all || opt.dir == 1 ? "getNextDomNode" : "getPreDomNode";
        if (domUtils.isBody(node)) {
            node = node.firstChild;
        }
        var first = 1;
        while (node) {
            textContent = getText(node);
            index = findTextInString(textContent, opt, currentIndex);
            first = 0;
            if (index != -1) {
                return {
                    node: node,
                    index: index
                };
            }
            node = domUtils[methodName](node);
            while (node && _blockElm[node.nodeName.toLowerCase()]) {
                node = domUtils[methodName](node, true);
            }
            if (node) {
                currentIndex = opt.dir == -1 ? getText(node).length : 0;
            }
        }
    }

    function findNTextInBlockElm(node, index, str) {
        var currentIndex = 0,
            currentNode = node.firstChild,
            currentNodeLength = 0,
            result;
        while (currentNode) {
            if (currentNode.nodeType == 3) {
                currentNodeLength = getText(currentNode).replace(
                    /(^[\t\r\n]+)|([\t\r\n]+$)/,
                    ""
                ).length;
                currentIndex += currentNodeLength;
                if (currentIndex >= index) {
                    return {
                        node: currentNode,
                        index: currentNodeLength - (currentIndex - index)
                    };
                }
            } else if (!dtd.$empty[currentNode.tagName]) {
                currentNodeLength = getText(currentNode).replace(
                    /(^[\t\r\n]+)|([\t\r\n]+$)/,
                    ""
                ).length;
                currentIndex += currentNodeLength;
                if (currentIndex >= index) {
                    result = findNTextInBlockElm(
                        currentNode,
                        currentNodeLength - (currentIndex - index),
                        str
                    );
                    if (result) {
                        return result;
                    }
                }
            }
            currentNode = domUtils.getNextDomNode(currentNode);
        }
    }

    function searchReplace(me, opt) {
        var rng = lastRng || me.selection.getRange(),
            startBlockNode,
            searchStr = opt.searchStr,
            span = me.document.createElement("span");
        span.innerHTML = "$$ueditor_searchreplace_key$$";

        rng.shrinkBoundary(true);

        //判断是不是第一次选中
        if (!rng.collapsed) {
            rng.select();
            var rngText = me.selection.getText();
            if (
                new RegExp(
                    "^" + opt.searchStr + "$",
                    opt.casesensitive ? "" : "i"
                ).test(rngText)
            ) {
                if (opt.replaceStr != undefined) {
                    replaceText(rng, opt.replaceStr);
                    rng.select();
                    return true;
                } else {
                    rng.collapse(opt.dir == -1);
                }
            }
        }

        rng.insertNode(span);
        rng.enlargeToBlockElm(true);
        startBlockNode = rng.startContainer;
        var currentIndex = getText(startBlockNode).indexOf(
            "$$ueditor_searchreplace_key$$"
        );
        rng.setStartBefore(span);
        domUtils.remove(span);
        var result = findTextBlockElm(startBlockNode, currentIndex, opt);
        if (result) {
            var rngStart = findNTextInBlockElm(result.node, result.index, searchStr);
            var rngEnd = findNTextInBlockElm(
                result.node,
                result.index + searchStr.length,
                searchStr
            );
            rng
                .setStart(rngStart.node, rngStart.index)
                .setEnd(rngEnd.node, rngEnd.index);

            if (opt.replaceStr !== undefined) {
                replaceText(rng, opt.replaceStr);
            }
            rng.select();
            return true;
        } else {
            rng.setCursor();
        }
    }

    function replaceText(rng, str) {
        str = me.document.createTextNode(str);
        rng.deleteContents().insertNode(str);
    }

    return {
        commands: {
            searchreplace: {
                execCommand: function (cmdName, opt) {
                    utils.extend(
                        opt,
                        {
                            all: false,
                            casesensitive: false,
                            dir: 1
                        },
                        true
                    );
                    var num = 0;
                    if (opt.all) {
                        lastRng = null;
                        var rng = me.selection.getRange(),
                            first = me.body.firstChild;
                        if (first && first.nodeType == 1) {
                            rng.setStart(first, 0);
                            rng.shrinkBoundary(true);
                        } else if (first.nodeType == 3) {
                            rng.setStartBefore(first);
                        }
                        rng.collapse(true).select(true);
                        if (opt.replaceStr !== undefined) {
                            me.fireEvent("saveScene");
                        }
                        while (searchReplace(this, opt)) {
                            num++;
                            lastRng = me.selection.getRange();
                            lastRng.collapse(opt.dir == -1);
                        }
                        if (num) {
                            me.fireEvent("saveScene");
                        }
                    } else {
                        if (opt.replaceStr !== undefined) {
                            me.fireEvent("saveScene");
                        }
                        if (searchReplace(this, opt)) {
                            num++;
                            lastRng = me.selection.getRange();
                            lastRng.collapse(opt.dir == -1);
                        }
                        if (num) {
                            me.fireEvent("saveScene");
                        }
                    }

                    return num;
                },
                notNeedUndo: 1
            }
        },
        bindEvents: {
            clearlastSearchResult: function () {
                lastRng = null;
            }
        }
    };
});


// plugins/customstyle.js
/**
 * 自定义样式
 * @file
 * @since 1.2.6.1
 */

/**
 * 根据config配置文件里“customstyle”选项的值对匹配的标签执行样式替换。
 * @command customstyle
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @example
 * ```javascript
 * editor.execCommand( 'customstyle' );
 * ```
 */
UE.plugins["customstyle"] = function () {
    var me = this;
    me.setOpt({
        customstyle: [
            {
                tag: "h1",
                name: "tc",
                style:
                    "font-size:32px;line-height:40px;font-weight:bold;border-bottom:#ccc 2px solid;padding:0 4px 0 0;text-align:center;"
            },
            {
                tag: "h1",
                name: "tl",
                style:
                    "font-size:32px;line-height:40px;font-weight:bold;border-bottom:#ccc 2px solid;padding:0 4px 0 0;text-align:left;"
            },
            {
                tag: "span",
                name: "im",
                style:
                    "font-size:16px;font-style:italic;font-weight:bold;line-height:18px;"
            },
            {
                tag: "span",
                name: "hi",
                style:
                    "font-size:16px;font-style:italic;font-weight:bold;color:rgb(51, 153, 204);line-height:18px;"
            }
        ]
    });
    me.commands["customstyle"] = {
        execCommand: function (cmdName, obj) {
            var me = this,
                tagName = obj.tag,
                node = domUtils.findParent(
                    me.selection.getStart(),
                    function (node) {
                        return node.getAttribute("label");
                    },
                    true
                ),
                range,
                bk,
                tmpObj = {};
            for (var p in obj) {
                if (obj[p] !== undefined) tmpObj[p] = obj[p];
            }
            delete tmpObj.tag;
            if (node && node.getAttribute("label") == obj.label) {
                range = this.selection.getRange();
                bk = range.createBookmark();
                if (range.collapsed) {
                    //trace:1732 删掉自定义标签,要有p来回填站位
                    if (dtd.$block[node.tagName]) {
                        var fillNode = me.document.createElement("p");
                        domUtils.moveChild(node, fillNode);
                        node.parentNode.insertBefore(fillNode, node);
                        domUtils.remove(node);
                    } else {
                        domUtils.remove(node, true);
                    }
                } else {
                    var common = domUtils.getCommonAncestor(bk.start, bk.end),
                        nodes = domUtils.getElementsByTagName(common, tagName);
                    if (new RegExp(tagName, "i").test(common.tagName)) {
                        nodes.push(common);
                    }
                    for (var i = 0, ni; (ni = nodes[i++]);) {
                        if (ni.getAttribute("label") == obj.label) {
                            var ps = domUtils.getPosition(ni, bk.start),
                                pe = domUtils.getPosition(ni, bk.end);
                            if (
                                (ps & domUtils.POSITION_FOLLOWING ||
                                    ps & domUtils.POSITION_CONTAINS) &&
                                (pe & domUtils.POSITION_PRECEDING ||
                                    pe & domUtils.POSITION_CONTAINS)
                            )
                                if (dtd.$block[tagName]) {
                                    var fillNode = me.document.createElement("p");
                                    domUtils.moveChild(ni, fillNode);
                                    ni.parentNode.insertBefore(fillNode, ni);
                                }
                            domUtils.remove(ni, true);
                        }
                    }
                    node = domUtils.findParent(
                        common,
                        function (node) {
                            return node.getAttribute("label") == obj.label;
                        },
                        true
                    );
                    if (node) {
                        domUtils.remove(node, true);
                    }
                }
                range.moveToBookmark(bk).select();
            } else {
                if (dtd.$block[tagName]) {
                    this.execCommand("paragraph", tagName, tmpObj, "customstyle");
                    range = me.selection.getRange();
                    if (!range.collapsed) {
                        range.collapse();
                        node = domUtils.findParent(
                            me.selection.getStart(),
                            function (node) {
                                return node.getAttribute("label") == obj.label;
                            },
                            true
                        );
                        var pNode = me.document.createElement("p");
                        domUtils.insertAfter(node, pNode);
                        domUtils.fillNode(me.document, pNode);
                        range.setStart(pNode, 0).setCursor();
                    }
                } else {
                    range = me.selection.getRange();
                    if (range.collapsed) {
                        node = me.document.createElement(tagName);
                        domUtils.setAttributes(node, tmpObj);
                        range.insertNode(node).setStart(node, 0).setCursor();

                        return;
                    }

                    bk = range.createBookmark();
                    range.applyInlineStyle(tagName, tmpObj).moveToBookmark(bk).select();
                }
            }
        },
        queryCommandValue: function () {
            var parent = domUtils.filterNodeList(
                this.selection.getStartElementPath(),
                function (node) {
                    return node.getAttribute("label");
                }
            );
            return parent ? parent.getAttribute("label") : "";
        }
    };
    //当去掉customstyle是,如果是块元素,用p代替
    me.addListener("keyup", function (type, evt) {
        var keyCode = evt.keyCode || evt.which;

        if (keyCode == 32 || keyCode == 13) {
            var range = me.selection.getRange();
            if (range.collapsed) {
                var node = domUtils.findParent(
                    me.selection.getStart(),
                    function (node) {
                        return node.getAttribute("label");
                    },
                    true
                );
                if (node && dtd.$block[node.tagName] && domUtils.isEmptyNode(node)) {
                    var p = me.document.createElement("p");
                    domUtils.insertAfter(node, p);
                    domUtils.fillNode(me.document, p);
                    domUtils.remove(node);
                    range.setStart(p, 0).setCursor();
                }
            }
        }
    });
};


// plugins/catchremoteimage.js
///import core
///commands 远程图片抓取
///commandsName  catchRemoteImage,catchremoteimageenable
///commandsTitle  远程图片抓取
/**
 * 远程图片抓取,当开启本插件时所有不符合本地域名的图片都将被抓取成为本地服务器上的图片
 */
UE.plugins["catchremoteimage"] = function () {
    var me = this,
        ajax = UE.ajax;

    /* 设置默认值 */
    if (me.options.catchRemoteImageEnable === false) {
        return;
    }
    me.setOpt({
        catchRemoteImageEnable: false
    });

    var catcherLocalDomain = me.getOpt("catcherLocalDomain"),
        catcherActionUrl = me.getActionUrl(me.getOpt("catcherActionName")),
        catcherUrlPrefix = me.getOpt("catcherUrlPrefix"),
        catcherFieldName = me.getOpt("catcherFieldName");

    me.addListener('serverConfigLoaded', function () {
        catcherLocalDomain = me.getOpt("catcherLocalDomain");
        catcherActionUrl = me.getActionUrl(me.getOpt("catcherActionName"));
        catcherUrlPrefix = me.getOpt("catcherUrlPrefix");
        catcherFieldName = me.getOpt("catcherFieldName");
    });

    me.addListener("afterpaste", function () {
        me.fireEvent("catchremoteimage");
    });

    var catchRemoteImageCatching = false;

    function sendApi(imgs, callbacks) {
        var params = utils.serializeParam(me.queryCommandValue("serverparam")) || "",
            url = utils.formatUrl(
                catcherActionUrl +
                (catcherActionUrl.indexOf("?") === -1 ? "?" : "&") +
                params
            ),
            isJsonp = utils.isCrossDomainUrl(url),
            // isJsonp = false,
            opt = {
                method: "POST",
                dataType: isJsonp ? "jsonp" : "",
                timeout: 60000, //单位:毫秒,回调请求超时设置。目标用户如果网速不是很快的话此处建议设置一个较大的数值
                headers: me.options.serverHeaders || {},
                onsuccess: callbacks["success"],
                onerror: callbacks["error"]
            };
        opt[catcherFieldName] = imgs;
        ajax.request(url, opt);
    }

    function catchElement(type, ele, imageUrl) {
        sendApi([imageUrl], {
            //成功抓取
            success: function (r) {
                try {
                    var info = r.state !== undefined
                        ? r
                        : eval("(" + r.responseText + ")");
                } catch (e) {
                    return;
                }

                info = me.options.serverResponsePrepare(info);

                /* 获取源路径和新路径 */
                var oldSrc,
                    newSrc,
                    oldBgIMG,
                    newBgIMG,
                    list = info.list;
                var catchFailList = [];
                var catchSuccessList = [];
                var failIMG = me.options.themePath + me.options.theme + '/images/img-cracked.png';
                var loadingIMG = me.options.themePath + me.options.theme + '/images/spacer.gif';

                var cj = list[0];
                switch (type) {
                    case 'image':
                        oldSrc = ele.getAttribute("_src") || ele.src || "";
                        if (cj.state === "SUCCESS") {
                            newSrc = catcherUrlPrefix + cj.url;
                            // 上传成功是删除uploading动画
                            domUtils.removeClasses(ele, "uep-loading");
                            domUtils.setAttributes(ele, {
                                "src": newSrc,
                                "_src": newSrc,
                                "data-catch-result": "success"
                            });
                            catchSuccessList.push(ele);
                        } else {
                            // 替换成统一的失败图片
                            domUtils.removeClasses(ele, "uep-loading");
                            domUtils.setAttributes(ele, {
                                "src": failIMG,
                                "_src": failIMG,
                                "data-catch-result": "fail" // 添加catch失败标记
                            });
                            catchFailList.push(ele);
                        }
                        break;
                    case 'background':
                        oldBgIMG = ele.getAttribute("data-background") || "";
                        if (cj.state === "SUCCESS") {
                            newBgIMG = catcherUrlPrefix + cj.url;
                            ele.style.cssText = ele.style.cssText.replace(loadingIMG, newBgIMG);
                            domUtils.removeAttributes(ele, "data-background");
                            domUtils.setAttributes(ele, {
                                "data-catch-result": "success"   // 添加catch成功标记
                            });
                            catchSuccessList.push(ele);
                        } else {
                            ele.style.cssText = ele.style.cssText.replace(loadingIMG, failIMG);
                            domUtils.removeAttributes(ele, "data-background");
                            domUtils.setAttributes(ele, {
                                "data-catch-result": "fail"   // 添加catch失败标记
                            });
                            catchFailList.push(ele);
                        }
                        break;
                }
                // 监听事件添加成功抓取和抓取失败的dom列表参数
                me.fireEvent('catchremotesuccess', catchSuccessList, catchFailList);
                catchRemoteImageCatching = false;
                setTimeout(function () {
                    me.fireEvent('catchremoteimage');
                }, 0);
            },
            //回调失败,本次请求超时
            error: function () {
                me.fireEvent('catchremoteerror');
                catchRemoteImageCatching = false;
                setTimeout(function () {
                    me.fireEvent('catchremoteimage');
                }, 0);
            }
        });
    }

    function catchRemoteImage() {
        if (catchRemoteImageCatching) {
            return;
        }
        catchRemoteImageCatching = true;

        var loadingIMG = me.options.themePath + me.options.theme + '/images/spacer.gif',
            imgs = me.document.querySelectorAll('[style*="url"],img'),
            test = function (src, urls) {
                if (src.indexOf(location.host) !== -1 || /(^\.)|(^\/)/.test(src)) {
                    return true;
                }
                if (urls) {
                    for (var j = 0, url; (url = urls[j++]);) {
                        if (src.indexOf(url) !== -1) {
                            return true;
                        }
                    }
                }
                return false;
            };

        for (var i = 0, ci; (ci = imgs[i++]);) {
            if (ci.getAttribute("data-word-image") || ci.getAttribute('data-catch-result')) {
                continue;
            }
            if (ci.nodeName === "IMG") {
                var src = ci.getAttribute("_src") || ci.src || "";
                if (/^(https?|ftp):/i.test(src) && !test(src, catcherLocalDomain)) {
                    catchElement('image', ci, src);
                    domUtils.setAttributes(ci, {
                        class: "loadingclass",
                        _src: src,
                        src: loadingIMG
                    })
                    return;
                }
            } else {
                var backgroundImageurl = ci.style.cssText.replace(/.*\s?url\([\'\"]?/, '').replace(/[\'\"]?\).*/, '');
                if (/^(https?|ftp):/i.test(backgroundImageurl) && !test(backgroundImageurl, catcherLocalDomain)) {
                    catchElement('background', ci, backgroundImageurl);
                    ci.style.cssText = ci.style.cssText.replace(backgroundImageurl, loadingIMG);
                    domUtils.setAttributes(ci, {
                        "data-background": backgroundImageurl
                    })
                    return;
                }
            }
        }

    };

    me.addListener("catchremoteimage", function () {
        catchRemoteImage();
    });
};


// plugins/insertparagraph.js
/**
 * 插入段落
 * @file
 * @since 1.2.6.1
 */

/**
 * 插入段落
 * @command insertparagraph
 * @method execCommand
 * @param { String } cmd 命令字符串
 * @example
 * ```javascript
 * //editor是编辑器实例
 * editor.execCommand( 'insertparagraph' );
 * ```
 */

UE.commands["insertparagraph"] = {
    execCommand: function (cmdName, front) {
        var me = this,
            range = me.selection.getRange(),
            start = range.startContainer,
            tmpNode;
        while (start) {
            if (domUtils.isBody(start)) {
                break;
            }
            tmpNode = start;
            start = start.parentNode;
        }
        if (tmpNode) {
            var p = me.document.createElement("p");
            if (front) {
                tmpNode.parentNode.insertBefore(p, tmpNode);
            } else {
                tmpNode.parentNode.insertBefore(p, tmpNode.nextSibling);
            }
            domUtils.fillNode(me.document, p);
            range.setStart(p, 0).setCursor(false, true);
        }
    }
};


// plugins/template.js
///import core
///import plugins\inserthtml.js
///import plugins\cleardoc.js
///commands 模板
///commandsName  template
///commandsTitle  模板
///commandsDialog  dialogs\template
UE.plugins["template"] = function () {
    UE.commands["template"] = {
        execCommand: function (cmd, obj) {
            obj.html && this.execCommand("inserthtml", obj.html);
        }
    };
    this.addListener("click", function (type, evt) {
        var el = evt.target || evt.srcElement,
            range = this.selection.getRange();
        var tnode = domUtils.findParent(
            el,
            function (node) {
                if (node.className && domUtils.hasClass(node, "ue_t")) {
                    return node;
                }
            },
            true
        );
        tnode && range.selectNode(tnode).shrinkBoundary().select();
    });
    this.addListener("keydown", function (type, evt) {
        var range = this.selection.getRange();
        if (!range.collapsed) {
            if (!evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) {
                var tnode = domUtils.findParent(
                    range.startContainer,
                    function (node) {
                        if (node.className && domUtils.hasClass(node, "ue_t")) {
                            return node;
                        }
                    },
                    true
                );
                if (tnode) {
                    domUtils.removeClasses(tnode, ["ue_t"]);
                }
            }
        }
    });
};


// plugins/autoupload.js
/**
 * @description
 * 1.拖放文件到编辑区域,自动上传并插入到选区
 * 2.插入粘贴板的图片,自动上传并插入到选区
 * @author Jinqn
 * @date 2013-10-14
 */
UE.plugin.register("autoupload", function () {
    function sendAndInsertFile(file, editor) {
        var me = editor;
        //模拟数据
        var fieldName,
            urlPrefix,
            maxSize,
            allowFiles,
            actionUrl,
            loadingHtml,
            errorHandler,
            successHandler,
            filetype = /image\/\w+/i.test(file.type) ? "image" : "file",
            loadingId = "loading_" + (+new Date()).toString(36);

        fieldName = me.getOpt(filetype + "FieldName");
        urlPrefix = me.getOpt(filetype + "UrlPrefix");
        maxSize = me.getOpt(filetype + "MaxSize");
        allowFiles = me.getOpt(filetype + "AllowFiles");
        actionUrl = me.getActionUrl(me.getOpt(filetype + "ActionName"));
        errorHandler = function (title) {
            var loader = me.document.getElementById(loadingId);
            loader && domUtils.remove(loader);
            me.fireEvent("showmessage", {
                id: loadingId,
                content: title,
                type: "error",
                timeout: 4000
            });
        };

        if (filetype === "image") {
            loadingHtml =
                '<img class="uep-loading" id="' +
                loadingId +
                '" src="' +
                me.options.themePath +
                me.options.theme +
                '/images/spacer.gif">';
            successHandler = function (data) {
                var link = urlPrefix + data.url,
                    loader = me.document.getElementById(loadingId);
                if (loader) {
                    domUtils.removeClasses(loader, "uep-loading");
                    loader.setAttribute("src", link);
                    loader.setAttribute("_src", link);
                    loader.setAttribute("alt", data.original || "");
                    loader.removeAttribute("id");
                    me.trigger("contentchange", loader);
                }
            };
        } else {
            loadingHtml =
                "<p>" +
                '<img class="uep-loading" id="' +
                loadingId +
                '" src="' +
                me.options.themePath +
                me.options.theme +
                '/images/spacer.gif">' +
                "</p>";
            successHandler = function (data) {
                var link = urlPrefix + data.url,
                    loader = me.document.getElementById(loadingId);

                var rng = me.selection.getRange(),
                    bk = rng.createBookmark();
                rng.selectNode(loader).select();
                me.execCommand("insertfile", {url: link});
                rng.moveToBookmark(bk).select();
            };
        }

        /* 插入loading的占位符 */
        me.execCommand("inserthtml", loadingHtml);
        /* 判断后端配置是否没有加载成功 */
        if (!me.getOpt(filetype + "ActionName")) {
            errorHandler(me.getLang("autoupload.errorLoadConfig"));
            return;
        }
        /* 判断文件大小是否超出限制 */
        if (file.size > maxSize) {
            errorHandler(me.getLang("autoupload.exceedSizeError"));
            return;
        }
        /* 判断文件格式是否超出允许 */
        var fileext = file.name ? file.name.substr(file.name.lastIndexOf(".")) : "";
        if (
            (fileext && filetype != "image") ||
            (allowFiles &&
                (allowFiles.join("") + ".").indexOf(fileext.toLowerCase() + ".") == -1)
        ) {
            errorHandler(me.getLang("autoupload.exceedTypeError"));
            return;
        }

        var upload = function (file) {
            const formData = new FormData();
            formData.append(fieldName, file, file.name);
            UE.api.requestAction(me, me.getOpt(filetype + "ActionName"), {
                data: formData
            }).then(function (res) {
                successHandler(res.data);
            }).catch(function (err) {
                errorHandler(me.getLang("autoupload.loadError"));
            });
        };

        var imageCompressEnable = me.getOpt('imageCompressEnable'),
            imageMaxSize = me.getOpt('imageMaxSize'),
            imageCompressBorder = me.getOpt('imageCompressBorder');
        if ('image' === filetype && imageCompressEnable) {
            UE.image.compress(file, {
                maxSizeMB: imageMaxSize / 1024 / 1024,
                maxWidthOrHeight: imageCompressBorder
            }).then(function (compressedFile) {
                if (me.options.debug) {
                    console.log('AutoUpload.CompressImage', (compressedFile.size / file.size * 100).toFixed(2) + '%');
                }
                upload(compressedFile);
            }).catch(function (err) {
                console.error('AutoUpload.CompressImage.error', err)
                upload(file);
            });
        } else {
            upload(file);
        }

        /* 创建Ajax并提交 */
        // var xhr = new XMLHttpRequest(),
        //   fd = new FormData(),
        //   params = utils.serializeParam(me.queryCommandValue("serverparam")) || "",
        //   url = utils.formatUrl(
        //     actionUrl + (actionUrl.indexOf("?") == -1 ? "?" : "&") + params
        //   );
        //
        // fd.append(
        //   fieldName,
        //   file,
        //   file.name || "blob." + file.type.substr("image/".length)
        // );
        // fd.append("type", "ajax");
        // xhr.open("post", url, true);
        // xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
        // xhr.addEventListener("load", function(e) {
        //   try {
        //     var json = new Function("return " + utils.trim(e.target.response))();
        //     if (json.state == "SUCCESS" && json.url) {
        //       successHandler(json);
        //     } else {
        //       errorHandler(json.state);
        //     }
        //   } catch (er) {
        //     errorHandler(me.getLang("autoupload.loadError"));
        //   }
        // });
        // xhr.send(fd);
    }

    function getPasteImage(e) {
        return e.clipboardData &&
        e.clipboardData.items &&
        e.clipboardData.items.length == 1 &&
        /^image\//.test(e.clipboardData.items[0].type)
            ? e.clipboardData.items
            : null;
    }

    function getDropImage(e) {
        return e.dataTransfer && e.dataTransfer.files ? e.dataTransfer.files : null;
    }

    return {
        outputRule: function (root) {
            utils.each(root.getNodesByTagName("img"), function (n) {
                if (/\b(uep\-loading\-error)|(bloaderrorclass)\b/.test(n.getAttr("class"))) {
                    n.parentNode.removeChild(n);
                }
            });
            utils.each(root.getNodesByTagName("p"), function (n) {
                if (/\bloadpara\b/.test(n.getAttr("class"))) {
                    n.parentNode.removeChild(n);
                }
            });
        },
        bindEvents: {
            defaultOptions: {
                //默认间隔时间
                enableDragUpload: true,
                enablePasteUpload: true
            },
            //插入粘贴板的图片,拖放插入图片
            ready: function (e) {
                var me = this;
                if (window.FormData && window.FileReader) {
                    var handler = function (e) {
                        var hasImg = false,
                            items;
                        //获取粘贴板文件列表或者拖放文件列表
                        items = e.type == "paste" ? getPasteImage(e) : getDropImage(e);
                        if (items) {
                            var len = items.length,
                                file;
                            while (len--) {
                                file = items[len];
                                if (file.getAsFile) file = file.getAsFile();
                                if (file && file.size > 0) {
                                    sendAndInsertFile(file, me);
                                    hasImg = true;
                                }
                            }
                            hasImg && e.preventDefault();
                        }
                    };

                    if (me.getOpt("enablePasteUpload") !== false) {
                        domUtils.on(me.body, "paste ", handler);
                    }
                    if (me.getOpt("enableDragUpload") !== false) {
                        domUtils.on(me.body, "drop", handler);
                        //取消拖放图片时出现的文字光标位置提示
                        domUtils.on(me.body, "dragover", function (e) {
                            if (e.dataTransfer.types[0] == "Files") {
                                e.preventDefault();
                            }
                        });
                    } else {
                        if (browser.gecko) {
                            domUtils.on(me.body, "drop", function (e) {
                                if (getDropImage(e)) {
                                    e.preventDefault();
                                }
                            });
                        }
                    }

                    //设置loading的样式
                    utils.cssRule(
                        "loading",
                        ".uep-loading{display:inline-block;cursor:default;background: url('" +
                        this.options.themePath +
                        this.options.theme +
                        "/images/loading.gif') no-repeat center center transparent;border-radius:3px;outline:1px solid #EEE;margin-left:1px;height:22px;width:22px;}\n" +
                        ".uep-loading-error{display:inline-block;cursor:default;background: url('" +
                        this.options.themePath +
                        this.options.theme +
                        "/images/loaderror.png') no-repeat center center transparent;border-radius:3px;outline:1px solid #EEE;margin-right:1px;height:22px;width:22px;" +
                        "}",
                        this.document
                    );
                }
            }
        }
    };
});


// plugins/section.js
/**
 * 目录大纲支持插件
 * @file
 * @since 1.3.0
 */
UE.plugin.register("section", function () {
    /* 目录节点对象 */
    function Section(option) {
        this.tag = "";
        (this.level = -1), (this.dom = null);
        this.nextSection = null;
        this.previousSection = null;
        this.parentSection = null;
        this.startAddress = [];
        this.endAddress = [];
        this.children = [];
    }

    function getSection(option) {
        var section = new Section();
        return utils.extend(section, option);
    }

    function getNodeFromAddress(startAddress, root) {
        var current = root;
        for (var i = 0; i < startAddress.length; i++) {
            if (!current.childNodes) return null;
            current = current.childNodes[startAddress[i]];
        }
        return current;
    }

    var me = this;

    return {
        bindMultiEvents: {
            type: "aftersetcontent afterscencerestore",
            handler: function () {
                me.fireEvent("updateSections");
            }
        },
        bindEvents: {
            /* 初始化、拖拽、粘贴、执行setcontent之后 */
            ready: function () {
                me.fireEvent("updateSections");
                domUtils.on(me.body, "drop paste", function () {
                    me.fireEvent("updateSections");
                });
            },
            /* 执行paragraph命令之后 */
            afterexeccommand: function (type, cmd) {
                if (cmd == "paragraph") {
                    me.fireEvent("updateSections");
                }
            },
            /* 部分键盘操作,触发updateSections事件 */
            keyup: function (type, e) {
                var me = this,
                    range = me.selection.getRange();
                if (range.collapsed != true) {
                    me.fireEvent("updateSections");
                } else {
                    var keyCode = e.keyCode || e.which;
                    if (keyCode == 13 || keyCode == 8 || keyCode == 46) {
                        me.fireEvent("updateSections");
                    }
                }
            }
        },
        commands: {
            getsections: {
                execCommand: function (cmd, levels) {
                    var levelFn = levels || ["h1", "h2", "h3", "h4", "h5", "h6"];

                    for (var i = 0; i < levelFn.length; i++) {
                        if (typeof levelFn[i] == "string") {
                            levelFn[i] = (function (fn) {
                                return function (node) {
                                    return node.tagName == fn.toUpperCase();
                                };
                            })(levelFn[i]);
                        } else if (typeof levelFn[i] != "function") {
                            levelFn[i] = function (node) {
                                return null;
                            };
                        }
                    }

                    function getSectionLevel(node) {
                        for (var i = 0; i < levelFn.length; i++) {
                            if (levelFn[i](node)) return i;
                        }
                        return -1;
                    }

                    var me = this,
                        Directory = getSection({level: -1, title: "root"}),
                        previous = Directory;

                    function traversal(node, Directory) {
                        var level,
                            tmpSection = null,
                            parent,
                            child,
                            children = node.childNodes;
                        for (var i = 0, len = children.length; i < len; i++) {
                            child = children[i];
                            level = getSectionLevel(child);
                            if (level >= 0) {
                                var address = me.selection
                                        .getRange()
                                        .selectNode(child)
                                        .createAddress(true).startAddress,
                                    current = getSection({
                                        tag: child.tagName,
                                        title: child.innerText || child.textContent || "",
                                        level: level,
                                        dom: child,
                                        startAddress: utils.clone(address, []),
                                        endAddress: utils.clone(address, []),
                                        children: []
                                    });
                                previous.nextSection = current;
                                current.previousSection = previous;
                                parent = previous;
                                while (level <= parent.level) {
                                    parent = parent.parentSection;
                                }
                                current.parentSection = parent;
                                parent.children.push(current);
                                tmpSection = previous = current;
                            } else {
                                child.nodeType === 1 && traversal(child, Directory);
                                tmpSection &&
                                tmpSection.endAddress[tmpSection.endAddress.length - 1]++;
                            }
                        }
                    }

                    traversal(me.body, Directory);
                    return Directory;
                },
                notNeedUndo: true
            },
            movesection: {
                execCommand: function (cmd, sourceSection, targetSection, isAfter) {
                    var me = this,
                        targetAddress,
                        target;

                    if (!sourceSection || !targetSection || targetSection.level == -1)
                        return;

                    targetAddress = isAfter
                        ? targetSection.endAddress
                        : targetSection.startAddress;
                    target = getNodeFromAddress(targetAddress, me.body);

                    /* 判断目标地址是否被源章节包含 */
                    if (
                        !targetAddress ||
                        !target ||
                        isContainsAddress(
                            sourceSection.startAddress,
                            sourceSection.endAddress,
                            targetAddress
                        )
                    )
                        return;

                    var startNode = getNodeFromAddress(
                        sourceSection.startAddress,
                        me.body
                        ),
                        endNode = getNodeFromAddress(sourceSection.endAddress, me.body),
                        current,
                        nextNode;

                    if (isAfter) {
                        current = endNode;
                        while (
                            current &&
                            !(
                                domUtils.getPosition(startNode, current) &
                                domUtils.POSITION_FOLLOWING
                            )
                            ) {
                            nextNode = current.previousSibling;
                            domUtils.insertAfter(target, current);
                            if (current == startNode) break;
                            current = nextNode;
                        }
                    } else {
                        current = startNode;
                        while (
                            current &&
                            !(
                                domUtils.getPosition(current, endNode) &
                                domUtils.POSITION_FOLLOWING
                            )
                            ) {
                            nextNode = current.nextSibling;
                            target.parentNode.insertBefore(current, target);
                            if (current == endNode) break;
                            current = nextNode;
                        }
                    }

                    me.fireEvent("updateSections");

                    /* 获取地址的包含关系 */
                    function isContainsAddress(startAddress, endAddress, addressTarget) {
                        var isAfterStartAddress = false,
                            isBeforeEndAddress = false;
                        for (var i = 0; i < startAddress.length; i++) {
                            if (i >= addressTarget.length) break;
                            if (addressTarget[i] > startAddress[i]) {
                                isAfterStartAddress = true;
                                break;
                            } else if (addressTarget[i] < startAddress[i]) {
                                break;
                            }
                        }
                        for (var i = 0; i < endAddress.length; i++) {
                            if (i >= addressTarget.length) break;
                            if (addressTarget[i] < startAddress[i]) {
                                isBeforeEndAddress = true;
                                break;
                            } else if (addressTarget[i] > startAddress[i]) {
                                break;
                            }
                        }
                        return isAfterStartAddress && isBeforeEndAddress;
                    }
                }
            },
            deletesection: {
                execCommand: function (cmd, section, keepChildren) {
                    var me = this;

                    if (!section) return;

                    function getNodeFromAddress(startAddress) {
                        var current = me.body;
                        for (var i = 0; i < startAddress.length; i++) {
                            if (!current.childNodes) return null;
                            current = current.childNodes[startAddress[i]];
                        }
                        return current;
                    }

                    var startNode = getNodeFromAddress(section.startAddress),
                        endNode = getNodeFromAddress(section.endAddress),
                        current = startNode,
                        nextNode;

                    if (!keepChildren) {
                        while (
                            current &&
                            domUtils.inDoc(endNode, me.document) &&
                            !(
                                domUtils.getPosition(current, endNode) &
                                domUtils.POSITION_FOLLOWING
                            )
                            ) {
                            nextNode = current.nextSibling;
                            domUtils.remove(current);
                            current = nextNode;
                        }
                    } else {
                        domUtils.remove(current);
                    }

                    me.fireEvent("updateSections");
                }
            },
            selectsection: {
                execCommand: function (cmd, section) {
                    if (!section && !section.dom) return false;
                    var me = this,
                        range = me.selection.getRange(),
                        address = {
                            startAddress: utils.clone(section.startAddress, []),
                            endAddress: utils.clone(section.endAddress, [])
                        };
                    address.endAddress[address.endAddress.length - 1]++;
                    range.moveToAddress(address).select().scrollToView();
                    return true;
                },
                notNeedUndo: true
            },
            scrolltosection: {
                execCommand: function (cmd, section) {
                    if (!section && !section.dom) return false;
                    var me = this,
                        range = me.selection.getRange(),
                        address = {
                            startAddress: section.startAddress,
                            endAddress: section.endAddress
                        };
                    address.endAddress[address.endAddress.length - 1]++;
                    range.moveToAddress(address).scrollToView();
                    return true;
                },
                notNeedUndo: true
            }
        }
    };
});


// plugins/simpleupload.js
/**
 * @description
 * 简单上传:点击按钮,直接选择文件上传
 * @author Jinqn
 * @date 2014-03-31
 */
UE.plugin.register("simpleupload", function () {
    var me = this,
        isLoaded = false,
        containerBtn;

    function initUploadBtn() {
        var input = document.createElement("input");
        input.style.cssText = 'position:absolute;top:0;left:0;width:100%;height:100%;cursor:pointer;font-size:0;opacity:0;';
        input.type = 'file';
        input.accept = me.getOpt('imageAllowFiles').join(',');
        containerBtn.appendChild(input);
        domUtils.on(input, 'click', function (e) {
            var toolbarCallback = me.getOpt("toolbarCallback");
            if (toolbarCallback) {
                if (true === toolbarCallback('simpleupload', me)) {
                    e.stopPropagation();
                    e.preventDefault();
                }
            }
        });
        domUtils.on(input, 'change', function (e) {
            var state = me.queryCommandState("simpleupload");
            if (state === -1) {
                return;
            }
            if (!input.value) {
                return;
            }

            var loadingId = UE.dialog.loadingPlaceholder(me);

            if (!me.getOpt("imageActionName")) {
                UE.dialog.removeLoadingPlaceholder(me, loadingId);
                UE.dialog.tipError(me, me.getLang("autoupload.errorLoadConfig"));
                return;
            }

            var allowFiles = me.getOpt("imageAllowFiles");
            var filename = input.value, fileext = filename ? filename.substr(filename.lastIndexOf(".")) : "";
            if (
                !fileext ||
                (allowFiles &&
                    (allowFiles.join("") + ".").indexOf(fileext.toLowerCase() + ".") === -1)
            ) {
                UE.dialog.removeLoadingPlaceholder(me, loadingId);
                UE.dialog.tipError(me, me.getLang("autoupload.exceedTypeError"));
                return;
            }

            var upload = function (file) {
                const formData = new FormData();
                formData.append(me.getOpt('imageFieldName'), file, file.name);
                UE.api.requestAction(me, me.getOpt("imageActionName"), {
                    data: formData
                }).then(function (res) {
                    var resData = me.getOpt('serverResponsePrepare')( res.data )
                    if ('SUCCESS' === resData.state && resData.url) {
                        const loader = me.document.getElementById(loadingId);
                        domUtils.removeClasses(loader, "uep-loading");
                        const link = me.options.imageUrlPrefix + resData.url;
                        loader.setAttribute("src", link);
                        loader.setAttribute("_src", link);
                        loader.setAttribute("alt", resData.original || "");
                        loader.removeAttribute("id");
                        me.fireEvent("contentchange");
                        // 触发上传图片事件
                        me.fireEvent("uploadsuccess", {
                            res: resData,
                            type: 'image'
                        });
                    } else {
                        UE.dialog.removeLoadingPlaceholder(me, loadingId);
                        UE.dialog.tipError(me, resData.state);
                    }
                }).catch(function (err) {
                    UE.dialog.removeLoadingPlaceholder(me, loadingId);
                    UE.dialog.tipError(me, err);
                });
            };
            var file = input.files[0];
            // console.log('file',file);
            var imageCompressEnable = me.getOpt('imageCompressEnable'),
                imageMaxSize = me.getOpt('imageMaxSize'),
                imageCompressBorder = me.getOpt('imageCompressBorder');
            if (imageCompressEnable) {
                UE.image.compress(file, {
                    maxSizeMB: imageMaxSize / 1024 / 1024,
                    maxWidthOrHeight: imageCompressBorder
                }).then(function (compressedFile) {
                    if (me.options.debug) {
                        console.log('SimpleUpload.CompressImage', (compressedFile.size / file.size * 100).toFixed(2) + '%');
                    }
                    upload(compressedFile);
                }).catch(function (err) {
                    console.error('SimpleUpload.CompressImage.error', err);
                    upload(file);
                });
            } else {
                upload(file);
            }
        });

        var stateTimer;
        me.addListener("selectionchange", function () {
            clearTimeout(stateTimer);
            stateTimer = setTimeout(function () {
                var state = me.queryCommandState("simpleupload");
                if (state === -1) {
                    input.disabled = "disabled";
                } else {
                    input.disabled = false;
                }
            }, 400);
        });
        isLoaded = true;
    }

    return {
        bindEvents: {
            ready: function () {
                //设置loading的样式
                utils.cssRule(
                    "loading",
                    ".uep-loading{display:inline-block;cursor:default;background: url('" +
                    this.options.themePath +
                    this.options.theme +
                    "/images/loading.gif') no-repeat center center transparent;border-radius:3px;outline:1px solid #EEE;margin-right:1px;height:22px;width:22px;}\n" +
                    ".uep-loading-error{display:inline-block;cursor:default;background: url('" +
                    this.options.themePath +
                    this.options.theme +
                    "/images/loaderror.png') no-repeat center center transparent;border-radius:3px;outline:1px solid #EEE;margin-right:1px;height:22px;width:22px;" +
                    "}",
                    this.document
                );
            },
            /* 初始化简单上传按钮 */
            simpleuploadbtnready: function (type, container) {
                containerBtn = container;
                me.afterConfigReady(initUploadBtn);
            }
        },
        outputRule: function (root) {
            utils.each(root.getNodesByTagName("img"), function (n) {
                if (/\b(uep\-loading\-error)|(bloaderrorclass)\b/.test(n.getAttr("class"))) {
                    n.parentNode.removeChild(n);
                }
            });
        },
        commands: {
            simpleupload: {
                queryCommandState: function () {
                    return isLoaded ? 0 : -1;
                }
            }
        }
    };
});


// plugins/serverparam.js
/**
 * 服务器提交的额外参数列表设置插件
 * @file
 * @since 1.2.6.1
 */
UE.plugin.register("serverparam", function () {
    var me = this,
        serverParam = {};

    return {
        commands: {
            /**
             * 修改服务器提交的额外参数列表,清除所有项
             * @command serverparam
             * @method execCommand
             * @param { String } cmd 命令字符串
             * @example
             * ```javascript
             * editor.execCommand('serverparam');
             * editor.queryCommandValue('serverparam'); //返回空
             * ```
             */
            /**
             * 修改服务器提交的额外参数列表,删除指定项
             * @command serverparam
             * @method execCommand
             * @param { String } cmd 命令字符串
             * @param { String } key 要清除的属性
             * @example
             * ```javascript
             * editor.execCommand('serverparam', 'name'); //删除属性name
             * ```
             */
            /**
             * 修改服务器提交的额外参数列表,使用键值添加项
             * @command serverparam
             * @method execCommand
             * @param { String } cmd 命令字符串
             * @param { String } key 要添加的属性
             * @param { String } value 要添加属性的值
             * @example
             * ```javascript
             * editor.execCommand('serverparam', 'name', 'hello');
             * editor.queryCommandValue('serverparam'); //返回对象 {'name': 'hello'}
             * ```
             */
            /**
             * 修改服务器提交的额外参数列表,传入键值对对象添加多项
             * @command serverparam
             * @method execCommand
             * @param { String } cmd 命令字符串
             * @param { Object } key 传入的键值对对象
             * @example
             * ```javascript
             * editor.execCommand('serverparam', {'name': 'hello'});
             * editor.queryCommandValue('serverparam'); //返回对象 {'name': 'hello'}
             * ```
             */
            /**
             * 修改服务器提交的额外参数列表,使用自定义函数添加多项
             * @command serverparam
             * @method execCommand
             * @param { String } cmd 命令字符串
             * @param { Function } key 自定义获取参数的函数
             * @example
             * ```javascript
             * editor.execCommand('serverparam', function(editor){
             *     return {'key': 'value'};
             * });
             * editor.queryCommandValue('serverparam'); //返回对象 {'key': 'value'}
             * ```
             */

            /**
             * 获取服务器提交的额外参数列表
             * @command serverparam
             * @method queryCommandValue
             * @param { String } cmd 命令字符串
             * @example
             * ```javascript
             * editor.queryCommandValue( 'serverparam' ); //返回对象 {'key': 'value'}
             * ```
             */
            serverparam: {
                execCommand: function (cmd, key, value) {
                    if (key === undefined || key === null) {
                        //不传参数,清空列表
                        serverParam = {};
                    } else if (utils.isString(key)) {
                        //传入键值
                        if (value === undefined || value === null) {
                            delete serverParam[key];
                        } else {
                            serverParam[key] = value;
                        }
                    } else if (utils.isObject(key)) {
                        //传入对象,覆盖列表项
                        utils.extend(serverParam, key, false);
                    } else if (utils.isFunction(key)) {
                        //传入函数,添加列表项
                        utils.extend(serverParam, key(), false);
                    }
                },
                queryCommandValue: function () {
                    return serverParam || {};
                }
            }
        }
    };
});


// plugins/insertfile.js
/**
 * 插入附件
 */
UE.plugin.register("insertfile", function () {
    var me = this;

    function getFileIcon(url) {
        var ext = url.substr(url.lastIndexOf(".") + 1).toLowerCase(),
            maps = {
                "ai": "ai.svg",
                "apk": "apk.svg",
                "chm": "chm.svg",
                "css": "css.svg",
                "doc": "doc.svg",
                "docx": "docx.svg",
                "dwg": "dwg.svg",
                "gif": "gif.svg",
                "html": "html.svg",
                "jpeg": "jpeg.svg",
                "jpg": "jpg.svg",
                "log": "log.svg",
                "mp3": "mp3.svg",
                "mp4": "mp4.svg",
                "pdf": "pdf.svg",
                "png": "png.svg",
                "ppt": "ppt.svg",
                "pptx": "pptx.svg",
                "psd": "psd.svg",
                "rar": "rar.svg",
                "svg": "svg.svg",
                "torrent": "torrent.svg",
                "txt": "txt.svg",
                "unknown": "unknown.svg",
                "xls": "xls.svg",
                "xlsx": "xlsx.svg",
                "zip": "zip.svg",
            };
        return maps[ext] ? maps[ext] : maps["unknown"];
    }

    return {
        commands: {
            insertfile: {
                execCommand: function (command, filelist) {
                    filelist = utils.isArray(filelist) ? filelist : [filelist];

                    if (me.fireEvent("beforeinsertfile", filelist) === true) {
                        return;
                    }


                    //console.log('themePath',  );
                    var i,
                        item,
                        icon,
                        title,
                        html = "",
                        URL = me.getOpt("UEDITOR_HOME_URL"),
                        iconDir = me.options.themePath + me.options.theme + "/exts/";
                    for (i = 0; i < filelist.length; i++) {
                        item = filelist[i];
                        icon = iconDir + getFileIcon(item.url);
                        title =
                            item.title || item.url.substr(item.url.lastIndexOf("/") + 1);
                        html +=
                            '<p>' +
                            '<a style="background:#EEE;padding:10px;border-radius:5px;line-height:1.5em;display:inline-flex;align-items:center;" href="' +
                            item.url +
                            '" title="' +
                            title +
                            '" target="_blank">' +
                            '<img style="vertical-align:middle;margin-right:0.5em;height:1.5em;" src="' + icon + '" _src="' + icon + '" />' +
                            '<span style="color:#111111;line-height:1.5em;flex-grow:1;">' +
                            title +
                            "</span>" +
                            "</a>" +
                            "</p>";
                    }
                    me.execCommand("insertHtml", html);

                    me.fireEvent("afterinsertfile", filelist);
                }
            }
        }
    };
});


// plugins/markdown-shortcut.js
UE.plugins["markdown-shortcut"] = function () {

    if (!UE.browser.chrome) {
        return;
    }

    const me = this;

    const getCleanHtml = function (node) {
        let html = node.innerHTML
        html = html.replace(/[\u200b]*/g, '')
        return html
    }

    let shortCuts = [];
    // 注册 H1-H6 快捷键
    for (let i = 1; i <= 6; i++) {
        const regExp = new RegExp('^\\t?' + Array(i + 1).join('#') + '(\\s|&nbsp;)');
        (function (command) {
            shortCuts.push({
                name: 'Head' + i,
                tagName: ['P'],
                key: [' '],
                offset: [i + 1, i + 2],
                match: [regExp],
                callback: function (param) {
                    me.__hasEnterExecCommand = true;
                    me.execCommand('paragraph', command);
                    let range = me.selection.getRange();
                    let node = range.startContainer;
                    let html = getCleanHtml(node)
                    html = html.replace(regExp, '');
                    if (!html) {
                        html = domUtils.fillChar;
                    }
                    node.innerHTML = html;
                    me.__hasEnterExecCommand = false;
                }
            })
        })('h' + i);
    }

    me.on("ready", function () {

        domUtils.on(me.body, 'keyup', function (e) {
            let range = me.selection.getRange();
            if (range.endOffset !== range.startOffset) {
                return;
            }
            let key = e.key;
            let offset = range.startOffset;
            const node = range.startContainer.parentNode;
            let html = getCleanHtml(node);
            let tagName = node.tagName;
            // console.log('keyup', [node, range, tagName, key, offset, html]);
            for (let s of shortCuts) {
                if (!s.tagName.includes(tagName)) {
                    continue;
                }
                if (!s.key.includes(key)) {
                    continue;
                }
                if (!s.offset.includes(offset)) {
                    continue;
                }
                for (let m of s.match) {
                    let match = html.match(m);
                    // console.log('keyup', [html, m, match, s.name]);
                    if (match) {
                        s.callback({
                            node: node,
                        });
                        break;
                    }
                }
            }
        });

    });

};


// plugins/quick-operate.js
UE.plugins["quick-operate"] = function () {

    if (!UE.browser.chrome) {
        return;
    }
    return;

    let me = this;
    const uiUtils = UE.ui.uiUtils;

    me.on("ready", function () {
        let quickOperate = new UE.ui.QuickOperate({
            // items: contextItems,
            className: "edui-quick-operate",
            editor: me
        });
        quickOperate.render();

        let quickOperateNode = {
            root: null,
            target: null,
        }
        domUtils.on(quickOperate.el, 'mouseenter', function (evt) {
            quickOperateNode.root && quickOperateNode.root.classList && quickOperateNode.root.classList.add('edui-quick-operate-active');
        });
        domUtils.on(quickOperate.el, 'mouseleave', function (evt) {
            quickOperateNode.root && quickOperateNode.root.classList && quickOperateNode.root.classList.remove('edui-quick-operate-active');
        });
        domUtils.on(me.body, "mouseout", function (evt) {
            // quickOperate.hide();
        });
        domUtils.on(me.body, "mouseover", function (evt) {
            const node = evt.target
            let rootNode = node;
            for (; rootNode.parentNode && rootNode.parentNode.tagName !== 'BODY';) {
                rootNode = rootNode.parentNode;
            }
            quickOperateNode.root = rootNode
            quickOperateNode.target = node
            // me.body.querySelectorAll('& > *').forEach(item => {
            //   item.classList.remove('edui-quick-operate-active');
            // });
            // rootNode.classList.add('edui-quick-operate-active');
            const rect = node.getBoundingClientRect();
            const offset = uiUtils.getClientRect(node)
            offset.left = offset.left - 55
            // console.log('mouseover', rect, node, offset);
            // let offset = uiUtils.getViewportOffsetByEvent(evt);
            // console.log('quickOperate', quickOperate);
            quickOperate.showAt(offset);
        });

    });

};


// ui/ui.js
var baidu = baidu || {};
baidu.editor = baidu.editor || {};
UE.ui = baidu.editor.ui = {};


// ui/uiutils.js
(function () {
    var browser = baidu.editor.browser,
        domUtils = baidu.editor.dom.domUtils;

    var magic = "$EDITORUI";
    var root = (window[magic] = {});
    var uidMagic = "ID" + magic;
    var uidCount = 0;

    var uiUtils = (baidu.editor.ui.uiUtils = {
        uid: function (obj) {
            return obj ? obj[uidMagic] || (obj[uidMagic] = ++uidCount) : ++uidCount;
        },
        hook: function (fn, callback) {
            var dg;
            if (fn && fn._callbacks) {
                dg = fn;
            } else {
                dg = function () {
                    var q;
                    if (fn) {
                        q = fn.apply(this, arguments);
                    }
                    var callbacks = dg._callbacks;
                    var k = callbacks.length;
                    while (k--) {
                        var r = callbacks[k].apply(this, arguments);
                        if (q === undefined) {
                            q = r;
                        }
                    }
                    return q;
                };
                dg._callbacks = [];
            }
            dg._callbacks.push(callback);
            return dg;
        },
        createElementByHtml: function (html) {
            var el = document.createElement("div");
            el.innerHTML = html;
            el = el.firstChild;
            el.parentNode.removeChild(el);
            return el;
        },
        getViewportElement: function () {
            return browser.ie && browser.quirks
                ? document.body
                : document.documentElement;
        },
        getClientRect: function (element) {
            var bcr;
            //trace  IE6下在控制编辑器显隐时可能会报错,catch一下
            try {
                bcr = element.getBoundingClientRect();
            } catch (e) {
                bcr = {left: 0, top: 0, height: 0, width: 0};
            }
            var rect = {
                left: Math.round(bcr.left),
                top: Math.round(bcr.top),
                height: Math.round(bcr.bottom - bcr.top),
                width: Math.round(bcr.right - bcr.left)
            };
            var doc;
            while (
                (doc = element.ownerDocument) !== document &&
                (element = domUtils.getWindow(doc).frameElement)
                ) {
                bcr = element.getBoundingClientRect();
                rect.left += bcr.left;
                rect.top += bcr.top;
            }
            rect.bottom = rect.top + rect.height;
            rect.right = rect.left + rect.width;
            return rect;
        },
        getViewportRect: function () {
            var viewportEl = uiUtils.getViewportElement();
            var width = (window.innerWidth || viewportEl.clientWidth) | 0;
            var height = (window.innerHeight || viewportEl.clientHeight) | 0;
            return {
                left: 0,
                top: 0,
                height: height,
                width: width,
                bottom: height,
                right: width
            };
        },
        setViewportOffset: function (element, offset) {
            var rect;
            var fixedLayer = uiUtils.getFixedLayer();
            if (element.parentNode === fixedLayer) {
                element.style.left = offset.left + "px";
                element.style.top = offset.top + "px";
            } else {
                domUtils.setViewportOffset(element, offset);
            }
        },
        getEventOffset: function (evt) {
            var el = evt.target || evt.srcElement;
            var rect = uiUtils.getClientRect(el);
            var offset = uiUtils.getViewportOffsetByEvent(evt);
            return {
                left: offset.left - rect.left,
                top: offset.top - rect.top
            };
        },
        getViewportOffsetByEvent: function (evt) {
            var el = evt.target || evt.srcElement;
            var frameEl = domUtils.getWindow(el).frameElement;
            var offset = {
                left: evt.clientX,
                top: evt.clientY
            };
            if (frameEl && el.ownerDocument !== document) {
                var rect = uiUtils.getClientRect(frameEl);
                offset.left += rect.left;
                offset.top += rect.top;
            }
            return offset;
        },
        setGlobal: function (id, obj) {
            root[id] = obj;
            return magic + '["' + id + '"]';
        },
        unsetGlobal: function (id) {
            delete root[id];
        },
        copyAttributes: function (tgt, src) {
            var attributes = src.attributes;
            var k = attributes.length;
            while (k--) {
                var attrNode = attributes[k];
                if (
                    attrNode.nodeName != "style" &&
                    attrNode.nodeName != "class" &&
                    (!browser.ie || attrNode.specified)
                ) {
                    tgt.setAttribute(attrNode.nodeName, attrNode.nodeValue);
                }
            }
            if (src.className) {
                domUtils.addClass(tgt, src.className);
            }
            if (src.style.cssText) {
                tgt.style.cssText += ";" + src.style.cssText;
            }
        },
        removeStyle: function (el, styleName) {
            if (el.style.removeProperty) {
                el.style.removeProperty(styleName);
            } else if (el.style.removeAttribute) {
                el.style.removeAttribute(styleName);
            } else throw "";
        },
        contains: function (elA, elB) {
            return (
                elA &&
                elB &&
                (elA === elB
                    ? false
                    : elA.contains
                        ? elA.contains(elB)
                        : elA.compareDocumentPosition(elB) & 16)
            );
        },
        startDrag: function (evt, callbacks, doc) {
            var doc = doc || document;
            var startX = evt.clientX;
            var startY = evt.clientY;

            function handleMouseMove(evt) {
                var x = evt.clientX - startX;
                var y = evt.clientY - startY;
                callbacks.ondragmove(x, y, evt);
                if (evt.stopPropagation) {
                    evt.stopPropagation();
                } else {
                    evt.cancelBubble = true;
                }
            }

            if (doc.addEventListener) {
                function handleMouseUp(evt) {
                    doc.removeEventListener("mousemove", handleMouseMove, true);
                    doc.removeEventListener("mouseup", handleMouseUp, true);
                    window.removeEventListener("mouseup", handleMouseUp, true);
                    callbacks.ondragstop();
                }

                doc.addEventListener("mousemove", handleMouseMove, true);
                doc.addEventListener("mouseup", handleMouseUp, true);
                window.addEventListener("mouseup", handleMouseUp, true);

                evt.preventDefault();
            } else {
                var elm = evt.srcElement;
                elm.setCapture();

                function releaseCaptrue() {
                    elm.releaseCapture();
                    elm.detachEvent("onmousemove", handleMouseMove);
                    elm.detachEvent("onmouseup", releaseCaptrue);
                    elm.detachEvent("onlosecaptrue", releaseCaptrue);
                    callbacks.ondragstop();
                }

                elm.attachEvent("onmousemove", handleMouseMove);
                elm.attachEvent("onmouseup", releaseCaptrue);
                elm.attachEvent("onlosecaptrue", releaseCaptrue);
                evt.returnValue = false;
            }
            callbacks.ondragstart();
        },
        getFixedLayer: function () {
            var layer = document.getElementById("edui_fixedlayer");
            if (layer == null) {
                layer = document.createElement("div");
                layer.id = "edui_fixedlayer";
                document.body.appendChild(layer);
                if (browser.ie && browser.version <= 8) {
                    layer.style.position = "absolute";
                    bindFixedLayer();
                    setTimeout(updateFixedOffset);
                } else {
                    layer.style.position = "fixed";
                }
                layer.style.left = "0";
                layer.style.top = "0";
                layer.style.width = "0";
                layer.style.height = "0";
                layer.style.margin = "0";
            }
            return layer;
        },
        makeUnselectable: function (element) {
            if (browser.opera || (browser.ie && browser.version < 9)) {
                element.unselectable = "on";
                if (element.hasChildNodes()) {
                    for (var i = 0; i < element.childNodes.length; i++) {
                        if (element.childNodes[i].nodeType === 1) {
                            uiUtils.makeUnselectable(element.childNodes[i]);
                        }
                    }
                }
            } else {
                if (element.style.MozUserSelect !== undefined) {
                    element.style.MozUserSelect = "none";
                } else if (element.style.WebkitUserSelect !== undefined) {
                    element.style.WebkitUserSelect = "none";
                } else if (element.style.KhtmlUserSelect !== undefined) {
                    element.style.KhtmlUserSelect = "none";
                }
            }
        }
    });

    function updateFixedOffset() {
        var layer = document.getElementById("edui_fixedlayer");
        uiUtils.setViewportOffset(layer, {
            left: 0,
            top: 0
        });
        //        layer.style.display = 'none';
        //        layer.style.display = 'block';

        //#trace: 1354
        //        setTimeout(updateFixedOffset);
    }

    function bindFixedLayer(adjOffset) {
        domUtils.on(window, "scroll", updateFixedOffset);
        domUtils.on(
            window,
            "resize",
            baidu.editor.utils.defer(updateFixedOffset, 0, true)
        );
    }
})();


// ui/uibase.js
(function () {
    var utils = baidu.editor.utils,
        uiUtils = baidu.editor.ui.uiUtils,
        EventBase = baidu.editor.EventBase,
        UIBase = (baidu.editor.ui.UIBase = function () {
        });

    UIBase.prototype = {
        el: null,
        className: "",
        uiName: "",
        initOptions: function (options) {
            var me = this;
            for (var k in options) {
                me[k] = options[k];
            }
            this.id = this.id || "edui" + uiUtils.uid();
        },
        initUIBase: function () {
            this._globalKey = utils.unhtml(uiUtils.setGlobal(this.id, this));
        },
        render: function (holder) {
            var html = this.renderHtml();
            var el = uiUtils.createElementByHtml(html);

            //by xuheng 给每个node添加class
            var list = domUtils.getElementsByTagName(el, "*");
            var theme = "edui-" + (this.theme || this.editor.options.theme);
            var layer = document.getElementById("edui_fixedlayer");
            for (var i = 0, node; (node = list[i++]);) {
                domUtils.addClass(node, theme);
            }
            domUtils.addClass(el, theme);
            if (layer) {
                layer.className = "";
                domUtils.addClass(layer, theme);
            }

            var seatEl = this.getDom();
            if (seatEl != null) {
                seatEl.parentNode.replaceChild(el, seatEl);
                uiUtils.copyAttributes(el, seatEl);
            } else {
                if (typeof holder == "string") {
                    holder = document.getElementById(holder);
                }
                holder = holder || uiUtils.getFixedLayer();
                // console.log('Uibase.render',holder,el);
                domUtils.addClass(holder, theme);
                holder.appendChild(el);
            }
            this.el = el;
            this.postRender();
        },
        getDom: function (name) {
            if (!name) {
                return document.getElementById(this.id);
            } else {
                return document.getElementById(this.id + "_" + name);
            }
        },
        postRender: function () {
            this.fireEvent("postrender");
        },
        getHtmlTpl: function () {
            return "";
        },
        formatHtml: function (tpl) {
            var prefix = "edui-" + this.uiName;
            return tpl
                .replace(/##/g, this.id)
                .replace(/%%-/g, this.uiName ? prefix + "-" : "")
                .replace(/%%/g, (this.uiName ? prefix : "") + " " + this.className)
                .replace(/\$\$/g, this._globalKey);
        },
        renderHtml: function () {
            return this.formatHtml(this.getHtmlTpl());
        },
        dispose: function () {
            var box = this.getDom();
            if (box) baidu.editor.dom.domUtils.remove(box);
            uiUtils.unsetGlobal(this.id);
        },
        uiIsShow: true,
        uiShowStyleBackupValue: null,
        uiShow: function (enable) {
            if (enable) {
                if (this.uiIsShow) {
                    return;
                }
                this.getDom().style.display = this.uiShowStyleBackupValue;
                this.uiIsShow = true;
            } else {
                if (!this.uiIsShow) {
                    return;
                }
                this.uiShowStyleBackupValue = this.getDom().style.display;
                this.getDom().style.display = 'none';
                this.uiIsShow = false;
            }
        }
    };
    utils.inherits(UIBase, EventBase);
})();


// ui/separator.js
(function () {
    var utils = baidu.editor.utils,
        UIBase = baidu.editor.ui.UIBase,
        Separator = (baidu.editor.ui.Separator = function (options) {
            this.initOptions(options);
            this.initSeparator();
        });
    Separator.prototype = {
        uiName: "separator",
        initSeparator: function () {
            this.initUIBase();
        },
        getHtmlTpl: function () {
            return '<div id="##" class="edui-box %%"></div>';
        }
    };
    utils.inherits(Separator, UIBase);
})();


// ui/mask.js
///import core
///import uicore
(function () {
    var utils = baidu.editor.utils,
        domUtils = baidu.editor.dom.domUtils,
        UIBase = baidu.editor.ui.UIBase,
        uiUtils = baidu.editor.ui.uiUtils;

    var Mask = (baidu.editor.ui.Mask = function (options) {
        this.initOptions(options);
        this.initUIBase();
    });
    Mask.prototype = {
        getHtmlTpl: function () {
            return '<div id="##" class="edui-mask %%" onclick="return $$._onClick(event, this);" onmousedown="return $$._onMouseDown(event, this);"></div>';
        },
        postRender: function () {
            var me = this;
            domUtils.on(window, "resize", function () {
                setTimeout(function () {
                    if (!me.isHidden()) {
                        me._fill();
                    }
                });
            });
        },
        show: function (zIndex) {
            this._fill();
            this.getDom().style.display = "";
            this.getDom().style.zIndex = zIndex;
        },
        hide: function () {
            this.getDom().style.display = "none";
            this.getDom().style.zIndex = "";
        },
        isHidden: function () {
            return this.getDom().style.display == "none";
        },
        _onMouseDown: function () {
            return false;
        },
        _onClick: function (e, target) {
            this.fireEvent("click", e, target);
        },
        _fill: function () {
            var el = this.getDom();
            var vpRect = uiUtils.getViewportRect();
            el.style.width = vpRect.width + "px";
            el.style.height = vpRect.height + "px";
        }
    };
    utils.inherits(Mask, UIBase);
})();


// ui/popup.js
///import core
///import uicore
(function () {
    var utils = baidu.editor.utils,
        uiUtils = baidu.editor.ui.uiUtils,
        domUtils = baidu.editor.dom.domUtils,
        UIBase = baidu.editor.ui.UIBase,
        Popup = (baidu.editor.ui.Popup = function (options) {
            this.initOptions(options);
            this.initPopup();
        });

    var allPopups = [];

    function closeAllPopup(evt, el) {
        for (var i = 0; i < allPopups.length; i++) {
            var pop = allPopups[i];
            if (!pop.isHidden()) {
                if (pop.queryAutoHide(el) !== false) {
                    if (
                        evt &&
                        /scroll/gi.test(evt.type) &&
                        pop.className === "edui-wordpastepop"
                    )
                        return;
                    pop.hide();
                }
            }
        }

        if (allPopups.length) pop.editor.fireEvent("afterhidepop");
    }

    Popup.postHide = closeAllPopup;

    var ANCHOR_CLASSES = [
        "edui-anchor-topleft",
        "edui-anchor-topright",
        "edui-anchor-bottomleft",
        "edui-anchor-bottomright"
    ];
    Popup.prototype = {
        SHADOW_RADIUS: 5,
        content: null,
        _hidden: false,
        autoRender: true,
        canSideLeft: true,
        canSideUp: true,
        initPopup: function () {
            this.initUIBase();
            allPopups.push(this);
        },
        getHtmlTpl: function () {
            return (
                '<div id="##" class="edui-popup %%" onmousedown="return false;">' +
                ' <div id="##_body" class="edui-popup-body">' +
                ' <iframe style="position:absolute;z-index:-1;left:0;top:0;background-color: transparent;" frameborder="0" width="100%" height="100%" src="about:blank"></iframe>' +
                ' <div class="edui-shadow"></div>' +
                ' <div id="##_content" class="edui-popup-content">' +
                this.getContentHtmlTpl() +
                "  </div>" +
                " </div>" +
                "</div>"
            );
        },
        getContentHtmlTpl: function () {
            if (this.content) {
                if (typeof this.content == "string") {
                    return this.content;
                }
                return this.content.renderHtml();
            } else {
                return "";
            }
        },
        _UIBase_postRender: UIBase.prototype.postRender,
        postRender: function () {
            if (this.content instanceof UIBase) {
                this.content.postRender();
            }

            //捕获鼠标滚轮
            if (this.captureWheel && !this.captured) {
                this.captured = true;

                var winHeight =
                    (document.documentElement.clientHeight ||
                        document.body.clientHeight) - 80,
                    _height = this.getDom().offsetHeight,
                    _top = uiUtils.getClientRect(this.combox.getDom()).top,
                    content = this.getDom("content"),
                    ifr = this.getDom("body").getElementsByTagName("iframe"),
                    me = this;

                ifr.length && (ifr = ifr[0]);

                while (_top + _height > winHeight) {
                    _height -= 30;
                }
                content.style.height = _height + "px";
                //同步更改iframe高度
                ifr && (ifr.style.height = _height + "px");

                //阻止在combox上的鼠标滚轮事件, 防止用户的正常操作被误解
                domUtils.on(
                    content,
                    "onmousewheel" in document.body ? "mousewheel" : "DOMMouseScroll",
                    function (e) {
                        if (e.preventDefault) {
                            e.preventDefault();
                        } else {
                            e.returnValue = false;
                        }

                        if (e.wheelDelta) {
                            content.scrollTop -= e.wheelDelta / 120 * 60;
                        } else {
                            content.scrollTop -= e.detail / -3 * 60;
                        }
                    }
                );
            }
            this.fireEvent("postRenderAfter");
            this.hide(true);
            this._UIBase_postRender();
        },
        _doAutoRender: function () {
            if (!this.getDom() && this.autoRender) {
                this.render();
            }
        },
        mesureSize: function () {
            var box = this.getDom("content");
            return uiUtils.getClientRect(box);
        },
        fitSize: function () {
            // console.log('fitSize.popup')
            if (this.captureWheel && this.sized) {
                return this.__size;
            }
            this.sized = true;
            var popBodyEl = this.getDom("body");
            popBodyEl.style.width = "";
            popBodyEl.style.height = "";
            var size = this.mesureSize();
            if (this.captureWheel) {
                popBodyEl.style.width = -(-20 - size.width) + "px";
                var height = parseInt(this.getDom("content").style.height, 10);
                !window.isNaN(height) && (size.height = height);
            } else {
                popBodyEl.style.width = size.width + "px";
            }
            popBodyEl.style.height = size.height + "px";
            this.__size = size;
            this.captureWheel && (this.getDom("content").style.overflow = "auto");
            return size;
        },
        showAnchor: function (element, hoz) {
            this.showAnchorRect(uiUtils.getClientRect(element), hoz);
        },
        showAnchorRect: function (rect, hoz, adj) {
            this._doAutoRender();
            var vpRect = uiUtils.getViewportRect();
            this.getDom().style.visibility = "hidden";
            this._show();
            var popSize = this.fitSize();

            var sideLeft, sideUp, left, top;
            if (hoz) {
                sideLeft =
                    this.canSideLeft &&
                    (rect.right + popSize.width > vpRect.right &&
                        rect.left > popSize.width);
                sideUp =
                    this.canSideUp &&
                    (rect.top + popSize.height > vpRect.bottom &&
                        rect.bottom > popSize.height);
                left = sideLeft ? rect.left - popSize.width : rect.right;
                top = sideUp ? rect.bottom - popSize.height : rect.top;
            } else {
                sideLeft =
                    this.canSideLeft &&
                    (rect.right + popSize.width > vpRect.right &&
                        rect.left > popSize.width);
                sideUp =
                    this.canSideUp &&
                    (rect.top + popSize.height > vpRect.bottom &&
                        rect.bottom > popSize.height);
                left = sideLeft ? rect.right - popSize.width : rect.left;
                top = sideUp ? rect.top - popSize.height : rect.bottom;
            }
            if (!sideUp) {
                if (top + popSize.height > vpRect.bottom) {
                    top = vpRect.bottom - popSize.height
                }
            }
            // console.log('popup.showAnchorRect', vpRect, rect, hoz, sideUp, sideLeft, left, top);

            var popEl = this.getDom();
            uiUtils.setViewportOffset(popEl, {
                left: left,
                top: top
            });
            domUtils.removeClasses(popEl, ANCHOR_CLASSES);
            popEl.className +=
                " " + ANCHOR_CLASSES[(sideUp ? 1 : 0) * 2 + (sideLeft ? 1 : 0)];
            if (this.editor) {
                popEl.style.zIndex = this.editor.container.style.zIndex * 1 + 10;
                baidu.editor.ui.uiUtils.getFixedLayer().style.zIndex =
                    popEl.style.zIndex - 1;
            }
            this.getDom().style.visibility = "visible";
        },
        showAt: function (offset) {
            var left = offset.left;
            var top = offset.top;
            var rect = {
                left: left,
                top: top,
                right: left,
                bottom: top,
                height: 0,
                width: 0
            };
            this.showAnchorRect(rect, false, true);
        },
        _show: function () {
            if (this._hidden) {
                var box = this.getDom();
                box.style.display = "";
                this._hidden = false;
                //                if (box.setActive) {
                //                    box.setActive();
                //                }
                this.fireEvent("show");
            }
        },
        isHidden: function () {
            return this._hidden;
        },
        show: function () {
            this._doAutoRender();
            this._show();
        },
        hide: function (notNofity) {
            if (!this._hidden && this.getDom()) {
                this.getDom().style.display = "none";
                this._hidden = true;
                if (!notNofity) {
                    this.fireEvent("hide");
                }
            }
        },
        queryAutoHide: function (el) {
            return !el || !uiUtils.contains(this.getDom(), el);
        }
    };
    utils.inherits(Popup, UIBase);

    domUtils.on(document, "mousedown", function (evt) {
        var el = evt.target || evt.srcElement;
        closeAllPopup(evt, el);
    });
    domUtils.on(window, "scroll", function (evt, el) {
        closeAllPopup(evt, el);
    });
})();


// ui/colorpicker.js
///import core
///import uicore
(function () {
    var utils = baidu.editor.utils,
        UIBase = baidu.editor.ui.UIBase,
        ColorPicker = (baidu.editor.ui.ColorPicker = function (options) {
            this.initOptions(options);
            this.noColorText = this.noColorText || this.editor.getLang("clearColor");
            this.initUIBase();
        });

    ColorPicker.prototype = {
        getHtmlTpl: function () {
            return genColorPicker(this.noColorText, this.editor);
        },
        _onTableClick: function (evt) {
            var tgt = evt.target || evt.srcElement;
            var color = tgt.getAttribute("data-color");
            if (color) {
                this.fireEvent("pickcolor", color);
            }
        },
        _onTableOver: function (evt) {
            var tgt = evt.target || evt.srcElement;
            var color = tgt.getAttribute("data-color");
            if (color) {
                this.getDom("preview").style.backgroundColor = color;
            }
        },
        _onTableOut: function () {
            this.getDom("preview").style.backgroundColor = "";
        },
        _onPickNoColor: function () {
            this.fireEvent("picknocolor");
        },
        _onColorSelect: function (evt) {
            var input = evt.target || evt.srcElement;
            var color = input.value;
            if (color) {
                this.fireEvent("pickcolor", color);
            }
        }
    };
    utils.inherits(ColorPicker, UIBase);

    var COLORS = ("ffffff,000000,eeece1,1f497d,4f81bd,c0504d,9bbb59,8064a2,4bacc6,f79646," +
        "f2f2f2,7f7f7f,ddd9c3,c6d9f0,dbe5f1,f2dcdb,ebf1dd,e5e0ec,dbeef3,fdeada," +
        "d8d8d8,595959,c4bd97,8db3e2,b8cce4,e5b9b7,d7e3bc,ccc1d9,b7dde8,fbd5b5," +
        "bfbfbf,3f3f3f,938953,548dd4,95b3d7,d99694,c3d69b,b2a2c7,92cddc,fac08f," +
        "a5a5a5,262626,494429,17365d,366092,953734,76923c,5f497a,31859b,e36c09," +
        "7f7f7f,0c0c0c,1d1b10,0f243e,244061,632423,4f6128,3f3151,205867,974806," +
        "c00000,ff0000,ffc000,ffff00,92d050,00b050,00b0f0,0070c0,002060,7030a0,").split(
        ","
    );

    function genColorPicker(noColorText, editor) {
        var html =
            '<div id="##" class="edui-colorpicker %%">' +
            '<div class="edui-colorpicker-topbar edui-clearfix">' +
            // '<div unselectable="on" id="##_preview" class="edui-colorpicker-preview"></div>' +
            '<div id="##_preview" class="edui-colorpicker-preview"><input type="color" id="##_input" onchange="$$._onColorSelect(event,this);" /></div>' +
            '<div unselectable="on" class="edui-colorpicker-nocolor" onclick="$$._onPickNoColor(event, this);">' +
            noColorText +
            "</div>" +
            "</div>" +
            '<table  class="edui-box" style="border-collapse: collapse;" onmouseover="$$._onTableOver(event, this);" onmouseout="$$._onTableOut(event, this);" onclick="return $$._onTableClick(event, this);" cellspacing="0" cellpadding="0">' +
            '<tr style="border-bottom: 1px solid #ddd;font-size: 13px;line-height: 25px;color:#39C;padding-top: 2px"><td colspan="10">' +
            editor.getLang("themeColor") +
            "</td> </tr>" +
            '<tr class="edui-colorpicker-tablefirstrow" >';
        for (var i = 0; i < COLORS.length; i++) {
            if (i && i % 10 === 0) {
                html +=
                    "</tr>" +
                    (i == 60
                        ? '<tr style="border-bottom: 1px solid #ddd;font-size: 13px;line-height: 25px;color:#39C;"><td colspan="10">' +
                        editor.getLang("standardColor") +
                        "</td></tr>"
                        : "") +
                    "<tr" +
                    (i == 60 ? ' class="edui-colorpicker-tablefirstrow"' : "") +
                    ">";
            }
            html += i < 70
                ? '<td style="padding:2px 2px;"><a hidefocus title="' +
                COLORS[i] +
                '" onclick="return false;" href="javascript:" unselectable="on" class="edui-box edui-colorpicker-colorcell"' +
                ' data-color="#' +
                COLORS[i] +
                '"' +
                ' style="background-color:#' +
                COLORS[i] +
                ";border:solid #ccc 1px;" +
                '"' +
                "></a></td>"
                : "";
        }
        html += "</tr>";
        html += "</table></div>";
        return html;
    }
})();


// ui/tablepicker.js
///import core
///import uicore
(function () {
    var utils = baidu.editor.utils,
        uiUtils = baidu.editor.ui.uiUtils,
        UIBase = baidu.editor.ui.UIBase;

    var TablePicker = (baidu.editor.ui.TablePicker = function (options) {
        this.initOptions(options);
        this.initTablePicker();
    });
    TablePicker.prototype = {
        defaultNumRows: 10,
        defaultNumCols: 10,
        maxNumRows: 20,
        maxNumCols: 20,
        numRows: 10,
        numCols: 10,
        lengthOfCellSide: 22,
        initTablePicker: function () {
            this.initUIBase();
        },
        getHtmlTpl: function () {
            var me = this;
            return (
                '<div id="##" class="edui-tablepicker %%">' +
                '<div class="edui-tablepicker-body">' +
                '<div class="edui-infoarea">' +
                '<span id="##_label" class="edui-label"></span>' +
                "</div>" +
                '<div class="edui-pickarea"' +
                ' onmousemove="$$._onMouseMove(event, this);"' +
                ' onmouseover="$$._onMouseOver(event, this);"' +
                ' onmouseout="$$._onMouseOut(event, this);"' +
                ' onclick="$$._onClick(event, this);"' +
                ">" +
                '<div id="##_overlay" class="edui-overlay"></div>' +
                "</div>" +
                "</div>" +
                "</div>"
            );
        },
        _UIBase_render: UIBase.prototype.render,
        render: function (holder) {
            this._UIBase_render(holder);
            this.getDom("label").innerHTML =
                "0" +
                this.editor.getLang("t_row") +
                " x 0" +
                this.editor.getLang("t_col");
        },
        _track: function (numCols, numRows) {
            var style = this.getDom("overlay").style;
            var sideLen = this.lengthOfCellSide;
            style.width = numCols * sideLen + "px";
            style.height = numRows * sideLen + "px";
            var label = this.getDom("label");
            label.innerHTML =
                numCols +
                this.editor.getLang("t_col") +
                " x " +
                numRows +
                this.editor.getLang("t_row");
            this.numCols = numCols;
            this.numRows = numRows;
        },
        _onMouseOver: function (evt, el) {
            var rel = evt.relatedTarget || evt.fromElement;
            if (!uiUtils.contains(el, rel) && el !== rel) {
                this.getDom("label").innerHTML =
                    "0" +
                    this.editor.getLang("t_col") +
                    " x 0" +
                    this.editor.getLang("t_row");
                this.getDom("overlay").style.visibility = "";
            }
        },
        _onMouseOut: function (evt, el) {
            var rel = evt.relatedTarget || evt.toElement;
            if (!uiUtils.contains(el, rel) && el !== rel) {
                this.getDom("label").innerHTML =
                    "0" +
                    this.editor.getLang("t_col") +
                    " x 0" +
                    this.editor.getLang("t_row");
                this.getDom("overlay").style.visibility = "hidden";
            }
        },
        _onMouseMove: function (evt, el) {
            var style = this.getDom("overlay").style;
            var offset = uiUtils.getEventOffset(evt);
            var sideLen = this.lengthOfCellSide;
            var numCols = Math.ceil(offset.left / sideLen);
            var numRows = Math.ceil(offset.top / sideLen);
            this._track(numCols, numRows);
        },
        _onClick: function () {
            this.fireEvent("picktable", this.numCols, this.numRows);
        }
    };
    utils.inherits(TablePicker, UIBase);
})();


// ui/stateful.js
(function () {
    var browser = baidu.editor.browser,
        domUtils = baidu.editor.dom.domUtils,
        uiUtils = baidu.editor.ui.uiUtils;

    var TPL_STATEFUL =
        'onmousedown="$$.Stateful_onMouseDown(event, this);"' +
        ' onmouseup="$$.Stateful_onMouseUp(event, this);"' +
        (browser.ie
            ? ' onmouseenter="$$.Stateful_onMouseEnter(event, this);"' +
            ' onmouseleave="$$.Stateful_onMouseLeave(event, this);"'
            : ' onmouseover="$$.Stateful_onMouseOver(event, this);"' +
            ' onmouseout="$$.Stateful_onMouseOut(event, this);"');

    baidu.editor.ui.Stateful = {
        alwalysHoverable: false,
        target: null, //目标元素和this指向dom不一样
        Stateful_init: function () {
            this._Stateful_dGetHtmlTpl = this.getHtmlTpl;
            this.getHtmlTpl = this.Stateful_getHtmlTpl;
        },
        Stateful_getHtmlTpl: function () {
            var tpl = this._Stateful_dGetHtmlTpl();
            // 使用function避免$转义
            return tpl.replace(/stateful/g, function () {
                return TPL_STATEFUL;
            });
        },
        Stateful_onMouseEnter: function (evt, el) {
            this.target = el;
            if (!this.isDisabled() || this.alwalysHoverable) {
                this.addState("hover");
                this.fireEvent("over");
            }
        },
        Stateful_onMouseLeave: function (evt, el) {
            if (!this.isDisabled() || this.alwalysHoverable) {
                this.removeState("hover");
                this.removeState("active");
                this.fireEvent("out");
            }
        },
        Stateful_onMouseOver: function (evt, el) {
            var rel = evt.relatedTarget;
            if (!uiUtils.contains(el, rel) && el !== rel) {
                this.Stateful_onMouseEnter(evt, el);
            }
        },
        Stateful_onMouseOut: function (evt, el) {
            var rel = evt.relatedTarget;
            if (!uiUtils.contains(el, rel) && el !== rel) {
                this.Stateful_onMouseLeave(evt, el);
            }
        },
        Stateful_onMouseDown: function (evt, el) {
            if (!this.isDisabled()) {
                this.addState("active");
            }
        },
        Stateful_onMouseUp: function (evt, el) {
            if (!this.isDisabled()) {
                this.removeState("active");
            }
        },
        Stateful_postRender: function () {
            if (this.disabled && !this.hasState("disabled")) {
                this.addState("disabled");
            }
        },
        hasState: function (state) {
            return domUtils.hasClass(this.getStateDom(), "edui-state-" + state);
        },
        addState: function (state) {
            if (!this.hasState(state)) {
                this.getStateDom().className += " edui-state-" + state;
            }
        },
        removeState: function (state) {
            if (this.hasState(state)) {
                domUtils.removeClasses(this.getStateDom(), ["edui-state-" + state]);
            }
        },
        getStateDom: function () {
            return this.getDom("state");
        },
        isChecked: function () {
            return this.hasState("checked");
        },
        setChecked: function (checked) {
            if (!this.isDisabled() && checked) {
                this.addState("checked");
            } else {
                this.removeState("checked");
            }
        },
        isDisabled: function () {
            return this.hasState("disabled");
        },
        setDisabled: function (disabled) {
            if (disabled) {
                this.removeState("hover");
                this.removeState("checked");
                this.removeState("active");
                this.addState("disabled");
            } else {
                this.removeState("disabled");
            }
        }
    };
})();


// ui/button.js
///import core
///import uicore
///import ui/stateful.js
(function () {
    var utils = baidu.editor.utils,
        UIBase = baidu.editor.ui.UIBase,
        Stateful = baidu.editor.ui.Stateful,
        Button = (baidu.editor.ui.Button = function (options) {
            if (options.name) {
                var btnName = options.name;
                var cssRules = options.cssRules;
                if (!options.className) {
                    options.className = "edui-for-" + btnName;
                }
                options.cssRules =
                    ".edui-" +
                    (options.theme || "default") +
                    " .edui-toolbar .edui-button.edui-for-" +
                    btnName +
                    " .edui-icon {" +
                    cssRules +
                    "}";
            }
            this.initOptions(options);
            this.initButton();
        });
    Button.prototype = {
        uiName: "button",
        label: "",
        title: "",
        showIcon: true,
        showText: true,
        cssRules: "",
        initButton: function () {
            this.initUIBase();
            this.Stateful_init();
            if (this.cssRules) {
                utils.cssRule("edui-customize-" + this.name + "-style", this.cssRules);
            }
        },
        getHtmlTpl: function () {
            return (
                '<div id="##" class="edui-box %%">' +
                '<div id="##_state" stateful>' +
                '<div class="%%-wrap"><div id="##_body" unselectable="on" ' +
                (this.title ? 'title="' + this.title + '"' : "") +
                ' class="%%-body" onmousedown="return $$._onMouseDown(event, this);" onclick="return $$._onClick(event, this);">' +
                (this.showIcon ? '<div class="edui-box edui-icon"></div>' : "") +
                (this.showText
                    ? '<div class="edui-box edui-label">' + this.label + "</div>"
                    : "") +
                "</div>" +
                "</div>" +
                "</div></div>"
            );
        },
        postRender: function () {
            this.Stateful_postRender();
            this.setDisabled(this.disabled);
        },
        _onMouseDown: function (e) {
            var target = e.target || e.srcElement,
                tagName = target && target.tagName && target.tagName.toLowerCase();
            if (tagName == "input" || tagName == "object" || tagName == "object") {
                return false;
            }
        },
        _onClick: function () {
            if (!this.isDisabled()) {
                this.fireEvent("click");
            }
        },
        setTitle: function (text) {
            var label = this.getDom("label");
            label.innerHTML = text;
        }
    };
    utils.inherits(Button, UIBase);
    utils.extend(Button.prototype, Stateful);
})();


// ui/splitbutton.js
///import core
///import uicore
///import ui/stateful.js
(function () {
    var utils = baidu.editor.utils,
        uiUtils = baidu.editor.ui.uiUtils,
        domUtils = baidu.editor.dom.domUtils,
        UIBase = baidu.editor.ui.UIBase,
        Stateful = baidu.editor.ui.Stateful,
        SplitButton = (baidu.editor.ui.SplitButton = function (options) {
            this.initOptions(options);
            this.initSplitButton();
        });
    SplitButton.prototype = {
        popup: null,
        uiName: "splitbutton",
        title: "",
        initSplitButton: function () {
            this.initUIBase();
            this.Stateful_init();
            var me = this;
            if (this.popup != null) {
                var popup = this.popup;
                this.popup = null;
                this.setPopup(popup);
            }
        },
        _UIBase_postRender: UIBase.prototype.postRender,
        postRender: function () {
            this.Stateful_postRender();
            this._UIBase_postRender();
        },
        setPopup: function (popup) {
            if (this.popup === popup) return;
            if (this.popup != null) {
                this.popup.dispose();
            }
            popup.addListener("show", utils.bind(this._onPopupShow, this));
            popup.addListener("hide", utils.bind(this._onPopupHide, this));
            popup.addListener(
                "postrender",
                utils.bind(function () {
                    popup
                        .getDom("body")
                        .appendChild(
                            uiUtils.createElementByHtml(
                                '<div id="' +
                                this.popup.id +
                                '_bordereraser" class="edui-bordereraser edui-background" style="width:' +
                                (uiUtils.getClientRect(this.getDom()).width + 20) +
                                'px"></div>'
                            )
                        );
                    popup.getDom().className += " " + this.className;
                }, this)
            );
            this.popup = popup;
        },
        _onPopupShow: function () {
            this.addState("opened");
        },
        _onPopupHide: function () {
            this.removeState("opened");
        },
        getHtmlTpl: function () {
            return (
                '<div id="##" class="edui-box %%">' +
                "<div " +
                (this.title ? 'title="' + this.title + '"' : "") +
                ' id="##_state" stateful><div class="%%-body">' +
                '<div id="##_button_body" class="edui-box edui-button-body" onclick="$$._onButtonClick(event, this);">' +
                '<div class="edui-box edui-icon"></div>' +
                "</div>" +
                '<div class="edui-box edui-splitborder"></div>' +
                '<div class="edui-box edui-arrow" onclick="$$._onArrowClick();"></div>' +
                "</div></div></div>"
            );
        },
        showPopup: function () {
            // 当popup往上弹出的时候,做特殊处理
            var rect = uiUtils.getClientRect(this.getDom());
            rect.top -= this.popup.SHADOW_RADIUS;
            rect.height += this.popup.SHADOW_RADIUS;
            this.popup.showAnchorRect(rect);
        },
        _onArrowClick: function (event, el) {
            if (!this.isDisabled()) {
                this.showPopup();
            }
        },
        _onButtonClick: function () {
            if (!this.isDisabled()) {
                this.fireEvent("buttonclick");
            }
        }
    };
    utils.inherits(SplitButton, UIBase);
    utils.extend(SplitButton.prototype, Stateful, true);
})();


// ui/colorbutton.js
///import core
///import uicore
///import ui/colorpicker.js
///import ui/popup.js
///import ui/splitbutton.js
(function () {
    var utils = baidu.editor.utils,
        uiUtils = baidu.editor.ui.uiUtils,
        ColorPicker = baidu.editor.ui.ColorPicker,
        Popup = baidu.editor.ui.Popup,
        SplitButton = baidu.editor.ui.SplitButton,
        ColorButton = (baidu.editor.ui.ColorButton = function (options) {
            this.initOptions(options);
            this.initColorButton();
        });
    ColorButton.prototype = {
        initColorButton: function () {
            var me = this;
            this.popup = new Popup({
                content: new ColorPicker({
                    noColorText: me.editor.getLang("clearColor"),
                    editor: me.editor,
                    onpickcolor: function (t, color) {
                        me._onPickColor(color);
                    },
                    onpicknocolor: function (t, color) {
                        me._onPickNoColor(color);
                    }
                }),
                editor: me.editor
            });
            this.initSplitButton();
        },
        _SplitButton_postRender: SplitButton.prototype.postRender,
        postRender: function () {
            this._SplitButton_postRender();
            this.getDom("button_body").appendChild(
                uiUtils.createElementByHtml(
                    '<div id="' + this.id + '_colorlump" class="edui-colorlump"></div>'
                )
            );
            this.getDom().className += " edui-colorbutton";
        },
        setColor: function (color) {
            this.getDom("colorlump").style.backgroundColor = color;
            this.color = color;
        },
        _onPickColor: function (color) {
            if (this.fireEvent("pickcolor", color) !== false) {
                this.setColor(color);
                this.popup.hide();
            }
        },
        _onPickNoColor: function (color) {
            if (this.fireEvent("picknocolor") !== false) {
                this.popup.hide();
            }
        },
    };
    utils.inherits(ColorButton, SplitButton);
})();


// ui/tablebutton.js
///import core
///import uicore
///import ui/popup.js
///import ui/tablepicker.js
///import ui/splitbutton.js
(function () {
    var utils = baidu.editor.utils,
        Popup = baidu.editor.ui.Popup,
        TablePicker = baidu.editor.ui.TablePicker,
        SplitButton = baidu.editor.ui.SplitButton,
        TableButton = (baidu.editor.ui.TableButton = function (options) {
            this.initOptions(options);
            this.initTableButton();
        });
    TableButton.prototype = {
        initTableButton: function () {
            var me = this;
            this.popup = new Popup({
                content: new TablePicker({
                    editor: me.editor,
                    onpicktable: function (t, numCols, numRows) {
                        me._onPickTable(numCols, numRows);
                    }
                }),
                editor: me.editor
            });
            this.initSplitButton();
        },
        _onPickTable: function (numCols, numRows) {
            if (this.fireEvent("picktable", numCols, numRows) !== false) {
                this.popup.hide();
            }
        }
    };
    utils.inherits(TableButton, SplitButton);
})();


// ui/autotypesetpicker.js
///import core
///import uicore
(function () {
    var utils = baidu.editor.utils,
        UIBase = baidu.editor.ui.UIBase;

    var AutoTypeSetPicker = (baidu.editor.ui.AutoTypeSetPicker = function (
        options
    ) {
        this.initOptions(options);
        this.initAutoTypeSetPicker();
    });
    AutoTypeSetPicker.prototype = {
        initAutoTypeSetPicker: function () {
            this.initUIBase();
        },
        getHtmlTpl: function () {
            var me = this.editor,
                opt = me.options.autotypeset,
                lang = me.getLang("autoTypeSet");

            var textAlignInputName = "textAlignValue" + me.uid,
                imageBlockInputName = "imageBlockLineValue" + me.uid,
                symbolConverInputName = "symbolConverValue" + me.uid;

            return (
                '<div id="##" class="edui-autotypesetpicker %%">' +
                '<div class="edui-autotypesetpicker-body">' +
                "<table >" +
                '<tr><td nowrap><input type="checkbox" name="mergeEmptyline" ' +
                (opt["mergeEmptyline"] ? "checked" : "") +
                ">" +
                lang.mergeLine +
                '</td><td colspan="2"><input type="checkbox" name="removeEmptyline" ' +
                (opt["removeEmptyline"] ? "checked" : "") +
                ">" +
                lang.delLine +
                "</td></tr>" +
                '<tr><td nowrap><input type="checkbox" name="removeClass" ' +
                (opt["removeClass"] ? "checked" : "") +
                ">" +
                lang.removeFormat +
                '</td><td colspan="2"><input type="checkbox" name="indent" ' +
                (opt["indent"] ? "checked" : "") +
                ">" +
                lang.indent +
                "</td></tr>" +
                "<tr>" +
                '<td nowrap><input type="checkbox" name="textAlign" ' +
                (opt["textAlign"] ? "checked" : "") +
                ">" +
                lang.alignment +
                "</td>" +
                '<td colspan="2" id="' +
                textAlignInputName +
                '">' +
                '<input type="radio" name="' +
                textAlignInputName +
                '" value="left" ' +
                (opt["textAlign"] && opt["textAlign"] == "left" ? "checked" : "") +
                ">" +
                me.getLang("justifyleft") +
                '<input type="radio" name="' +
                textAlignInputName +
                '" value="center" ' +
                (opt["textAlign"] && opt["textAlign"] == "center" ? "checked" : "") +
                ">" +
                me.getLang("justifycenter") +
                '<input type="radio" name="' +
                textAlignInputName +
                '" value="right" ' +
                (opt["textAlign"] && opt["textAlign"] == "right" ? "checked" : "") +
                ">" +
                me.getLang("justifyright") +
                "</td>" +
                "</tr>" +
                "<tr>" +
                '<td nowrap><input type="checkbox" name="imageBlockLine" ' +
                (opt["imageBlockLine"] ? "checked" : "") +
                ">" +
                lang.imageFloat +
                "</td>" +
                '<td nowrap id="' +
                imageBlockInputName +
                '">' +
                '<input type="radio" name="' +
                imageBlockInputName +
                '" value="none" ' +
                (opt["imageBlockLine"] && opt["imageBlockLine"] == "none"
                    ? "checked"
                    : "") +
                ">" +
                me.getLang("default") +
                '<input type="radio" name="' +
                imageBlockInputName +
                '" value="left" ' +
                (opt["imageBlockLine"] && opt["imageBlockLine"] == "left"
                    ? "checked"
                    : "") +
                ">" +
                me.getLang("justifyleft") +
                '<input type="radio" name="' +
                imageBlockInputName +
                '" value="center" ' +
                (opt["imageBlockLine"] && opt["imageBlockLine"] == "center"
                    ? "checked"
                    : "") +
                ">" +
                me.getLang("justifycenter") +
                '<input type="radio" name="' +
                imageBlockInputName +
                '" value="right" ' +
                (opt["imageBlockLine"] && opt["imageBlockLine"] == "right"
                    ? "checked"
                    : "") +
                ">" +
                me.getLang("justifyright") +
                "</td>" +
                "</tr>" +
                '<tr><td nowrap><input type="checkbox" name="clearFontSize" ' +
                (opt["clearFontSize"] ? "checked" : "") +
                ">" +
                lang.removeFontsize +
                '</td><td colspan="2"><input type="checkbox" name="clearFontFamily" ' +
                (opt["clearFontFamily"] ? "checked" : "") +
                ">" +
                lang.removeFontFamily +
                "</td></tr>" +
                '<tr><td nowrap colspan="3"><input type="checkbox" name="removeEmptyNode" ' +
                (opt["removeEmptyNode"] ? "checked" : "") +
                ">" +
                lang.removeHtml +
                "</td></tr>" +
                '<tr><td nowrap colspan="3"><input type="checkbox" name="pasteFilter" ' +
                (opt["pasteFilter"] ? "checked" : "") +
                ">" +
                lang.pasteFilter +
                "</td></tr>" +
                "<tr>" +
                '<td nowrap><input type="checkbox" name="symbolConver" ' +
                (opt["bdc2sb"] || opt["tobdc"] ? "checked" : "") +
                ">" +
                lang.symbol +
                "</td>" +
                '<td id="' +
                symbolConverInputName +
                '">' +
                '<input type="radio" name="bdc" value="bdc2sb" ' +
                (opt["bdc2sb"] ? "checked" : "") +
                ">" +
                lang.bdc2sb +
                '<input type="radio" name="bdc" value="tobdc" ' +
                (opt["tobdc"] ? "checked" : "") +
                ">" +
                lang.tobdc +
                "" +
                "</td>" +
                '<td nowrap align="right"><button >' +
                lang.run +
                "</button></td>" +
                "</tr>" +
                "</table>" +
                "</div>" +
                "</div>"
            );
        },
        _UIBase_render: UIBase.prototype.render
    };
    utils.inherits(AutoTypeSetPicker, UIBase);
})();


// ui/autotypesetbutton.js
///import core
///import uicore
///import ui/popup.js
///import ui/autotypesetpicker.js
///import ui/splitbutton.js
(function () {
    var utils = baidu.editor.utils,
        Popup = baidu.editor.ui.Popup,
        AutoTypeSetPicker = baidu.editor.ui.AutoTypeSetPicker,
        SplitButton = baidu.editor.ui.SplitButton,
        AutoTypeSetButton = (baidu.editor.ui.AutoTypeSetButton = function (options) {
            this.initOptions(options);
            this.initAutoTypeSetButton();
        });

    function getPara(me) {
        var opt = {},
            cont = me.getDom(),
            editorId = me.editor.uid,
            inputType = null,
            attrName = null,
            ipts = domUtils.getElementsByTagName(cont, "input");
        for (var i = ipts.length - 1, ipt; (ipt = ipts[i--]);) {
            inputType = ipt.getAttribute("type");
            if (inputType == "checkbox") {
                attrName = ipt.getAttribute("name");
                opt[attrName] && delete opt[attrName];
                if (ipt.checked) {
                    var attrValue = document.getElementById(
                        attrName + "Value" + editorId
                    );
                    if (attrValue) {
                        if (/input/gi.test(attrValue.tagName)) {
                            opt[attrName] = attrValue.value;
                        } else {
                            var iptChilds = attrValue.getElementsByTagName("input");
                            for (
                                var j = iptChilds.length - 1, iptchild;
                                (iptchild = iptChilds[j--]);
                            ) {
                                if (iptchild.checked) {
                                    opt[attrName] = iptchild.value;
                                    break;
                                }
                            }
                        }
                    } else {
                        opt[attrName] = true;
                    }
                } else {
                    opt[attrName] = false;
                }
            } else {
                opt[ipt.getAttribute("value")] = ipt.checked;
            }
        }

        var selects = domUtils.getElementsByTagName(cont, "select");
        for (var i = 0, si; (si = selects[i++]);) {
            var attr = si.getAttribute("name");
            opt[attr] = opt[attr] ? si.value : "";
        }

        utils.extend(me.editor.options.autotypeset, opt);

        me.editor.setPreferences("autotypeset", opt);
    }

    AutoTypeSetButton.prototype = {
        initAutoTypeSetButton: function () {
            var me = this;
            this.popup = new Popup({
                //传入配置参数
                content: new AutoTypeSetPicker({editor: me.editor}),
                editor: me.editor,
                hide: function () {
                    if (!this._hidden && this.getDom()) {
                        getPara(this);
                        this.getDom().style.display = "none";
                        this._hidden = true;
                        this.fireEvent("hide");
                    }
                }
            });
            var flag = 0;
            this.popup.addListener("postRenderAfter", function () {
                var popupUI = this;
                if (flag) return;
                var cont = this.getDom(),
                    btn = cont.getElementsByTagName("button")[0];

                btn.onclick = function () {
                    getPara(popupUI);
                    me.editor.execCommand("autotypeset");
                    popupUI.hide();
                };

                domUtils.on(cont, "click", function (e) {
                    var target = e.target || e.srcElement,
                        editorId = me.editor.uid;
                    if (target && target.tagName == "INPUT") {
                        // 点击图片浮动的checkbox,去除对应的radio
                        if (
                            target.name == "imageBlockLine" ||
                            target.name == "textAlign" ||
                            target.name == "symbolConver"
                        ) {
                            var checked = target.checked,
                                radioTd = document.getElementById(
                                    target.name + "Value" + editorId
                                ),
                                radios = radioTd.getElementsByTagName("input"),
                                defalutSelect = {
                                    imageBlockLine: "none",
                                    textAlign: "left",
                                    symbolConver: "tobdc"
                                };

                            for (var i = 0; i < radios.length; i++) {
                                if (checked) {
                                    if (radios[i].value == defalutSelect[target.name]) {
                                        radios[i].checked = "checked";
                                    }
                                } else {
                                    radios[i].checked = false;
                                }
                            }
                        }
                        // 点击radio,选中对应的checkbox
                        if (
                            target.name == "imageBlockLineValue" + editorId ||
                            target.name == "textAlignValue" + editorId ||
                            target.name == "bdc"
                        ) {
                            var checkboxs = target.parentNode.previousSibling.getElementsByTagName(
                                "input"
                            );
                            checkboxs && (checkboxs[0].checked = true);
                        }

                        getPara(popupUI);
                    }
                });

                flag = 1;
            });
            this.initSplitButton();
        }
    };
    utils.inherits(AutoTypeSetButton, SplitButton);
})();


// ui/cellalignpicker.js
///import core
///import uicore
(function () {
    var utils = baidu.editor.utils,
        Popup = baidu.editor.ui.Popup,
        Stateful = baidu.editor.ui.Stateful,
        UIBase = baidu.editor.ui.UIBase;

    /**
     * 该参数将新增一个参数: selected, 参数类型为一个Object, 形如{ 'align': 'center', 'valign': 'top' }, 表示单元格的初始
     * 对齐状态为: 竖直居上,水平居中; 其中 align的取值为:'center', 'left', 'right'; valign的取值为: 'top', 'middle', 'bottom'
     * @update 2013/4/2 hancong03@baidu.com
     */
    var CellAlignPicker = (baidu.editor.ui.CellAlignPicker = function (options) {
        this.initOptions(options);
        this.initSelected();
        this.initCellAlignPicker();
    });
    CellAlignPicker.prototype = {
        //初始化选中状态, 该方法将根据传递进来的参数获取到应该选中的对齐方式图标的索引
        initSelected: function () {
            var status = {
                    valign: {
                        top: 0,
                        middle: 1,
                        bottom: 2
                    },
                    align: {
                        left: 0,
                        center: 1,
                        right: 2
                    },
                    count: 3
                },
                result = -1;

            if (this.selected) {
                this.selectedIndex =
                    status.valign[this.selected.valign] * status.count +
                    status.align[this.selected.align];
            }
        },
        initCellAlignPicker: function () {
            this.initUIBase();
            this.Stateful_init();
        },
        getHtmlTpl: function () {
            var alignType = ["left", "center", "right"],
                COUNT = 9,
                tempClassName = null,
                tempIndex = -1,
                tmpl = [];

            for (var i = 0; i < COUNT; i++) {
                tempClassName = this.selectedIndex === i
                    ? ' class="edui-cellalign-selected" '
                    : "";
                tempIndex = i % 3;

                tempIndex === 0 && tmpl.push("<tr>");

                tmpl.push(
                    '<td index="' +
                    i +
                    '" ' +
                    tempClassName +
                    ' stateful><div class="edui-icon edui-' +
                    alignType[tempIndex] +
                    '"></div></td>'
                );

                tempIndex === 2 && tmpl.push("</tr>");
            }

            return (
                '<div id="##" class="edui-cellalignpicker %%">' +
                '<div class="edui-cellalignpicker-body">' +
                '<table onclick="$$._onClick(event);">' +
                tmpl.join("") +
                "</table>" +
                "</div>" +
                "</div>"
            );
        },
        getStateDom: function () {
            return this.target;
        },
        _onClick: function (evt) {
            var target = evt.target || evt.srcElement;
            if (/icon/.test(target.className)) {
                this.items[target.parentNode.getAttribute("index")].onclick();
                Popup.postHide(evt);
            }
        },
        _UIBase_render: UIBase.prototype.render
    };
    utils.inherits(CellAlignPicker, UIBase);
    utils.extend(CellAlignPicker.prototype, Stateful, true);
})();


// ui/pastepicker.js
///import core
///import uicore
(function () {
    var utils = baidu.editor.utils,
        Stateful = baidu.editor.ui.Stateful,
        uiUtils = baidu.editor.ui.uiUtils,
        UIBase = baidu.editor.ui.UIBase;

    var PastePicker = (baidu.editor.ui.PastePicker = function (options) {
        this.initOptions(options);
        this.initPastePicker();
    });
    PastePicker.prototype = {
        initPastePicker: function () {
            this.initUIBase();
            this.Stateful_init();
        },
        getHtmlTpl: function () {
            return (
                '<div class="edui-pasteicon" onclick="$$._onClick(this)"></div>' +
                '<div class="edui-pastecontainer">' +
                '<div class="edui-title">' +
                this.editor.getLang("pasteOpt") +
                "</div>" +
                '<div class="edui-button">' +
                '<div title="' +
                this.editor.getLang("pasteSourceFormat") +
                '" onclick="$$.format(false)" stateful>' +
                '<div class="edui-richtxticon"></div></div>' +
                '<div title="' +
                this.editor.getLang("tagFormat") +
                '" onclick="$$.format(2)" stateful>' +
                '<div class="edui-tagicon"></div></div>' +
                '<div title="' +
                this.editor.getLang("pasteTextFormat") +
                '" onclick="$$.format(true)" stateful>' +
                '<div class="edui-plaintxticon"></div></div>' +
                "</div>" +
                "</div>" +
                "</div>"
            );
        },
        getStateDom: function () {
            return this.target;
        },
        format: function (param) {
            this.editor.ui._isTransfer = true;
            this.editor.fireEvent("pasteTransfer", param);
        },
        _onClick: function (cur) {
            var node = domUtils.getNextDomNode(cur),
                screenHt = uiUtils.getViewportRect().height,
                subPop = uiUtils.getClientRect(node);

            if (subPop.top + subPop.height > screenHt)
                node.style.top = -subPop.height - cur.offsetHeight + "px";
            else node.style.top = "";

            if (/hidden/gi.test(domUtils.getComputedStyle(node, "visibility"))) {
                node.style.visibility = "visible";
                domUtils.addClass(cur, "edui-state-opened");
            } else {
                node.style.visibility = "hidden";
                domUtils.removeClasses(cur, "edui-state-opened");
            }
        },
        _UIBase_render: UIBase.prototype.render
    };
    utils.inherits(PastePicker, UIBase);
    utils.extend(PastePicker.prototype, Stateful, true);
})();


// ui/toolbar.js
(function () {
    var utils = baidu.editor.utils,
        uiUtils = baidu.editor.ui.uiUtils,
        UIBase = baidu.editor.ui.UIBase,
        Toolbar = (baidu.editor.ui.Toolbar = function (options) {
            this.initOptions(options);
            this.initToolbar();
        });
    Toolbar.prototype = {
        items: null,
        initToolbar: function () {
            this.items = this.items || [];
            this.initUIBase();
        },
        add: function (item, index) {
            if (index === undefined) {
                this.items.push(item);
            } else {
                this.items.splice(index, 0, item);
            }
        },
        getHtmlTpl: function () {
            var buff = [];
            for (var i = 0; i < this.items.length; i++) {
                buff[i] = this.items[i].renderHtml();
            }
            return (
                '<div id="##" class="edui-toolbar %%" onselectstart="return false;" onmousedown="return $$._onMouseDown(event, this);">' +
                buff.join("") +
                "</div>"
            );
        },
        postRender: function () {
            var box = this.getDom();
            for (var i = 0; i < this.items.length; i++) {
                this.items[i].postRender();
            }
            uiUtils.makeUnselectable(box);
        },
        _onMouseDown: function (e) {
            var target = e.target || e.srcElement,
                tagName = target && target.tagName && target.tagName.toLowerCase();
            if (tagName == "input" || tagName == "object" || tagName == "object") {
                return false;
            }
        }
    };
    utils.inherits(Toolbar, UIBase);
})();


// ui/quick-operate.js
///import core
///import uicore
///import ui\popup.js
///import ui\stateful.js
(function () {
    var utils = baidu.editor.utils,
        domUtils = baidu.editor.dom.domUtils,
        uiUtils = baidu.editor.ui.uiUtils,
        UIBase = baidu.editor.ui.UIBase,
        Popup = baidu.editor.ui.Popup,
        Stateful = baidu.editor.ui.Stateful,
        CellAlignPicker = baidu.editor.ui.CellAlignPicker,
        QuickOperate = (baidu.editor.ui.QuickOperate = function (options) {
            this.initOptions(options);
            // this.initMenu();
        });

    // var menuSeparator = {
    //   renderHtml: function() {
    //     return '<div class="edui-menuitem edui-menuseparator"><div class="edui-menuseparator-inner"></div></div>';
    //   },
    //   postRender: function() {},
    //   queryAutoHide: function() {
    //     return true;
    //   }
    // };
    QuickOperate.prototype = {
        //   items: null,
        uiName: "quick-operate",
        //   initMenu: function() {
        //     this.items = this.items || [];
        //     this.initPopup();
        //     this.initItems();
        //   },
        //   initItems: function() {
        //     for (var i = 0; i < this.items.length; i++) {
        //       var item = this.items[i];
        //       if (item == "-") {
        //         this.items[i] = this.getSeparator();
        //       } else if (!(item instanceof MenuItem)) {
        //         item.editor = this.editor;
        //         item.theme = this.editor.options.theme;
        //         this.items[i] = this.createItem(item);
        //       }
        //     }
        //   },
        //   getSeparator: function() {
        //     return menuSeparator;
        //   },
        //   createItem: function(item) {
        //     //新增一个参数menu, 该参数存储了menuItem所对应的menu引用
        //     item.menu = this;
        //     return new MenuItem(item);
        //   },
        _Popup_getContentHtmlTpl: Popup.prototype.getContentHtmlTpl,
        getContentHtmlTpl: function () {
            //     if (this.items.length == 0) {
            //       return this._Popup_getContentHtmlTpl();
            //     }
            //     var buff = [];
            //     for (var i = 0; i < this.items.length; i++) {
            //       var item = this.items[i];
            //       buff[i] = item.renderHtml();
            //     }
            //     return '<div class="%%-body">' + buff.join("") + "</div>";
            return [
                '<div class="edui-quick-operate">',
                ' <div class="edui-quick-operate-status">',
                '   <div class="edui-quick-operate-icon"><i class="icon icon-image"></i></div>',
                '   <div class="edui-quick-operate-icon"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" data-icon="DragOutlined"><path d="M8.25 6.5a1.75 1.75 0 1 0 0-3.5 1.75 1.75 0 0 0 0 3.5Zm0 7.25a1.75 1.75 0 1 0 0-3.5 1.75 1.75 0 0 0 0 3.5Zm1.75 5.5a1.75 1.75 0 1 1-3.5 0 1.75 1.75 0 0 1 3.5 0ZM14.753 6.5a1.75 1.75 0 1 0 0-3.5 1.75 1.75 0 0 0 0 3.5ZM16.5 12a1.75 1.75 0 1 1-3.5 0 1.75 1.75 0 0 1 3.5 0Zm-1.747 9a1.75 1.75 0 1 0 0-3.5 1.75 1.75 0 0 0 0 3.5Z" fill="currentColor"></path></svg></div>',
                ' </div>',
                ' <div class="edui-quick-operate-menu">',
                '   <div class="item"><i class="icon icon-image"></i> 删除</div>',
                '   <div class="item"><i class="icon icon-image"></i> 左对齐</div>',
                '   <div class="item"><i class="icon icon-image"></i> 右对齐</div>',
                ' </div>',
                '</div>',
            ].join('')
        },
        //   _Popup_postRender: Popup.prototype.postRender,
        //   postRender: function() {
        //     var me = this;
        //     for (var i = 0; i < this.items.length; i++) {
        //       var item = this.items[i];
        //       item.ownerMenu = this;
        //       item.postRender();
        //     }
        //     domUtils.on(this.getDom(), "mouseover", function(evt) {
        //       evt = evt || event;
        //       var rel = evt.relatedTarget || evt.fromElement;
        //       var el = me.getDom();
        //       if (!uiUtils.contains(el, rel) && el !== rel) {
        //         me.fireEvent("over");
        //       }
        //     });
        //     this._Popup_postRender();
        //   },
        //   queryAutoHide: function(el) {
        //     if (el) {
        //       if (uiUtils.contains(this.getDom(), el)) {
        //         return false;
        //       }
        //       for (var i = 0; i < this.items.length; i++) {
        //         var item = this.items[i];
        //         if (item.queryAutoHide(el) === false) {
        //           return false;
        //         }
        //       }
        //     }
        //   },
        //   clearItems: function() {
        //     for (var i = 0; i < this.items.length; i++) {
        //       var item = this.items[i];
        //       clearTimeout(item._showingTimer);
        //       clearTimeout(item._closingTimer);
        //       if (item.subMenu) {
        //         item.subMenu.destroy();
        //       }
        //     }
        //     this.items = [];
        //   },
        destroy: function () {
            if (this.getDom()) {
                domUtils.remove(this.getDom());
            }
            //     this.clearItems();
        },
        dispose: function () {
            this.destroy();
        }
    };
    utils.inherits(QuickOperate, Popup);
    //
    // /**
    //    * @update 2013/04/03 hancong03 新增一个参数menu, 该参数存储了menuItem所对应的menu引用
    //    * @type {Function}
    //    */
    // var MenuItem = (baidu.editor.ui.MenuItem = function(options) {
    //   this.initOptions(options);
    //   this.initUIBase();
    //   this.Stateful_init();
    //   if (this.subMenu && !(this.subMenu instanceof QuickOperate)) {
    //     if (options.className && options.className.indexOf("aligntd") != -1) {
    //       var me = this;
    //
    //       //获取单元格对齐初始状态
    //       this.subMenu.selected = this.editor.queryCommandValue("cellalignment");
    //
    //       this.subMenu = new Popup({
    //         content: new CellAlignPicker(this.subMenu),
    //         parentMenu: me,
    //         editor: me.editor,
    //         destroy: function() {
    //           if (this.getDom()) {
    //             domUtils.remove(this.getDom());
    //           }
    //         }
    //       });
    //       this.subMenu.addListener("postRenderAfter", function() {
    //         domUtils.on(this.getDom(), "mouseover", function() {
    //           me.addState("opened");
    //         });
    //       });
    //     } else {
    //       this.subMenu = new QuickOperate(this.subMenu);
    //     }
    //   }
    // });
    // MenuItem.prototype = {
    //   label: "",
    //   subMenu: null,
    //   ownerMenu: null,
    //   uiName: "menuitem",
    //   alwalysHoverable: true,
    //   getHtmlTpl: function() {
    //     return (
    //       '<div id="##" class="%%" stateful onclick="$$._onClick(event, this);">' +
    //       '<div class="%%-body">' +
    //       this.renderLabelHtml() +
    //       "</div>" +
    //       "</div>"
    //     );
    //   },
    //   postRender: function() {
    //     var me = this;
    //     this.addListener("over", function() {
    //       me.ownerMenu.fireEvent("submenuover", me);
    //       if (me.subMenu) {
    //         me.delayShowSubMenu();
    //       }
    //     });
    //     if (this.subMenu) {
    //       this.getDom().className += " edui-hassubmenu";
    //       this.subMenu.render();
    //       this.addListener("out", function() {
    //         me.delayHideSubMenu();
    //       });
    //       this.subMenu.addListener("over", function() {
    //         clearTimeout(me._closingTimer);
    //         me._closingTimer = null;
    //         me.addState("opened");
    //       });
    //       this.ownerMenu.addListener("hide", function() {
    //         me.hideSubMenu();
    //       });
    //       this.ownerMenu.addListener("submenuover", function(t, subMenu) {
    //         if (subMenu !== me) {
    //           me.delayHideSubMenu();
    //         }
    //       });
    //       this.subMenu._bakQueryAutoHide = this.subMenu.queryAutoHide;
    //       this.subMenu.queryAutoHide = function(el) {
    //         if (el && uiUtils.contains(me.getDom(), el)) {
    //           return false;
    //         }
    //         return this._bakQueryAutoHide(el);
    //       };
    //     }
    //     this.getDom().style.tabIndex = "-1";
    //     uiUtils.makeUnselectable(this.getDom());
    //     this.Stateful_postRender();
    //   },
    //   delayShowSubMenu: function() {
    //     var me = this;
    //     if (!me.isDisabled()) {
    //       me.addState("opened");
    //       clearTimeout(me._showingTimer);
    //       clearTimeout(me._closingTimer);
    //       me._closingTimer = null;
    //       me._showingTimer = setTimeout(function() {
    //         me.showSubMenu();
    //       }, 250);
    //     }
    //   },
    //   delayHideSubMenu: function() {
    //     var me = this;
    //     if (!me.isDisabled()) {
    //       me.removeState("opened");
    //       clearTimeout(me._showingTimer);
    //       if (!me._closingTimer) {
    //         me._closingTimer = setTimeout(function() {
    //           if (!me.hasState("opened")) {
    //             me.hideSubMenu();
    //           }
    //           me._closingTimer = null;
    //         }, 400);
    //       }
    //     }
    //   },
    //   renderLabelHtml: function() {
    //     return (
    //       '<div class="edui-arrow"></div>' +
    //       '<div class="edui-box edui-icon"></div>' +
    //       '<div class="edui-box edui-label %%-label">' +
    //       (this.label || "") +
    //       "</div>"
    //     );
    //   },
    //   getStateDom: function() {
    //     return this.getDom();
    //   },
    //   queryAutoHide: function(el) {
    //     if (this.subMenu && this.hasState("opened")) {
    //       return this.subMenu.queryAutoHide(el);
    //     }
    //   },
    //   _onClick: function(event, this_) {
    //     if (this.hasState("disabled")) return;
    //     if (this.fireEvent("click", event, this_) !== false) {
    //       if (this.subMenu) {
    //         this.showSubMenu();
    //       } else {
    //         Popup.postHide(event);
    //       }
    //     }
    //   },
    //   showSubMenu: function() {
    //     var rect = uiUtils.getClientRect(this.getDom());
    //     rect.right -= 5;
    //     rect.left += 2;
    //     rect.width -= 7;
    //     rect.top -= 4;
    //     rect.bottom += 4;
    //     rect.height += 8;
    //     this.subMenu.showAnchorRect(rect, true, true);
    //   },
    //   hideSubMenu: function() {
    //     this.subMenu.hide();
    //   }
    // };
    // utils.inherits(MenuItem, UIBase);
    // utils.extend(MenuItem.prototype, Stateful, true);
})();


// ui/menu.js
///import core
///import uicore
///import ui\popup.js
///import ui\stateful.js
(function () {
    var utils = baidu.editor.utils,
        domUtils = baidu.editor.dom.domUtils,
        uiUtils = baidu.editor.ui.uiUtils,
        UIBase = baidu.editor.ui.UIBase,
        Popup = baidu.editor.ui.Popup,
        Stateful = baidu.editor.ui.Stateful,
        CellAlignPicker = baidu.editor.ui.CellAlignPicker,
        Menu = (baidu.editor.ui.Menu = function (options) {
            this.initOptions(options);
            this.initMenu();
        });

    var menuSeparator = {
        renderHtml: function () {
            return '<div class="edui-menuitem edui-menuseparator"><div class="edui-menuseparator-inner"></div></div>';
        },
        postRender: function () {
        },
        queryAutoHide: function () {
            return true;
        }
    };
    Menu.prototype = {
        items: null,
        uiName: "menu",
        initMenu: function () {
            this.items = this.items || [];
            this.initPopup();
            this.initItems();
        },
        initItems: function () {
            for (var i = 0; i < this.items.length; i++) {
                var item = this.items[i];
                if (item == "-") {
                    this.items[i] = this.getSeparator();
                } else if (!(item instanceof MenuItem)) {
                    item.editor = this.editor;
                    item.theme = this.editor.options.theme;
                    this.items[i] = this.createItem(item);
                }
            }
        },
        getSeparator: function () {
            return menuSeparator;
        },
        createItem: function (item) {
            //新增一个参数menu, 该参数存储了menuItem所对应的menu引用
            item.menu = this;
            return new MenuItem(item);
        },
        _Popup_getContentHtmlTpl: Popup.prototype.getContentHtmlTpl,
        getContentHtmlTpl: function () {
            if (this.items.length == 0) {
                return this._Popup_getContentHtmlTpl();
            }
            var buff = [];
            for (var i = 0; i < this.items.length; i++) {
                var item = this.items[i];
                buff[i] = item.renderHtml();
            }
            return '<div class="%%-body">' + buff.join("") + "</div>";
        },
        _Popup_postRender: Popup.prototype.postRender,
        postRender: function () {
            var me = this;
            for (var i = 0; i < this.items.length; i++) {
                var item = this.items[i];
                item.ownerMenu = this;
                item.postRender();
            }
            domUtils.on(this.getDom(), "mouseover", function (evt) {
                evt = evt || event;
                var rel = evt.relatedTarget || evt.fromElement;
                var el = me.getDom();
                if (!uiUtils.contains(el, rel) && el !== rel) {
                    me.fireEvent("over");
                }
            });
            this._Popup_postRender();
        },
        queryAutoHide: function (el) {
            if (el) {
                if (uiUtils.contains(this.getDom(), el)) {
                    return false;
                }
                for (var i = 0; i < this.items.length; i++) {
                    var item = this.items[i];
                    if (item.queryAutoHide(el) === false) {
                        return false;
                    }
                }
            }
        },
        clearItems: function () {
            for (var i = 0; i < this.items.length; i++) {
                var item = this.items[i];
                clearTimeout(item._showingTimer);
                clearTimeout(item._closingTimer);
                if (item.subMenu) {
                    item.subMenu.destroy();
                }
            }
            this.items = [];
        },
        destroy: function () {
            if (this.getDom()) {
                domUtils.remove(this.getDom());
            }
            this.clearItems();
        },
        dispose: function () {
            this.destroy();
        }
    };
    utils.inherits(Menu, Popup);

    /**
     * @update 2013/04/03 hancong03 新增一个参数menu, 该参数存储了menuItem所对应的menu引用
     * @type {Function}
     */
    var MenuItem = (baidu.editor.ui.MenuItem = function (options) {
        this.initOptions(options);
        this.initUIBase();
        this.Stateful_init();
        if (this.subMenu && !(this.subMenu instanceof Menu)) {
            if (options.className && options.className.indexOf("aligntd") != -1) {
                var me = this;

                //获取单元格对齐初始状态
                this.subMenu.selected = this.editor.queryCommandValue("cellalignment");

                this.subMenu = new Popup({
                    content: new CellAlignPicker(this.subMenu),
                    parentMenu: me,
                    editor: me.editor,
                    destroy: function () {
                        if (this.getDom()) {
                            domUtils.remove(this.getDom());
                        }
                    }
                });
                this.subMenu.addListener("postRenderAfter", function () {
                    domUtils.on(this.getDom(), "mouseover", function () {
                        me.addState("opened");
                    });
                });
            } else {
                this.subMenu = new Menu(this.subMenu);
            }
        }
    });
    MenuItem.prototype = {
        label: "",
        subMenu: null,
        ownerMenu: null,
        uiName: "menuitem",
        alwalysHoverable: true,
        getHtmlTpl: function () {
            return (
                '<div id="##" class="%%" stateful onclick="$$._onClick(event, this);">' +
                '<div class="%%-body">' +
                this.renderLabelHtml() +
                "</div>" +
                "</div>"
            );
        },
        postRender: function () {
            var me = this;
            this.addListener("over", function () {
                me.ownerMenu.fireEvent("submenuover", me);
                if (me.subMenu) {
                    me.delayShowSubMenu();
                }
            });
            if (this.subMenu) {
                this.getDom().className += " edui-hassubmenu";
                this.subMenu.render();
                this.addListener("out", function () {
                    me.delayHideSubMenu();
                });
                this.subMenu.addListener("over", function () {
                    clearTimeout(me._closingTimer);
                    me._closingTimer = null;
                    me.addState("opened");
                });
                this.ownerMenu.addListener("hide", function () {
                    me.hideSubMenu();
                });
                this.ownerMenu.addListener("submenuover", function (t, subMenu) {
                    if (subMenu !== me) {
                        me.delayHideSubMenu();
                    }
                });
                this.subMenu._bakQueryAutoHide = this.subMenu.queryAutoHide;
                this.subMenu.queryAutoHide = function (el) {
                    if (el && uiUtils.contains(me.getDom(), el)) {
                        return false;
                    }
                    return this._bakQueryAutoHide(el);
                };
            }
            this.getDom().style.tabIndex = "-1";
            uiUtils.makeUnselectable(this.getDom());
            this.Stateful_postRender();
        },
        delayShowSubMenu: function () {
            var me = this;
            if (!me.isDisabled()) {
                me.addState("opened");
                clearTimeout(me._showingTimer);
                clearTimeout(me._closingTimer);
                me._closingTimer = null;
                me._showingTimer = setTimeout(function () {
                    me.showSubMenu();
                }, 250);
            }
        },
        delayHideSubMenu: function () {
            var me = this;
            if (!me.isDisabled()) {
                me.removeState("opened");
                clearTimeout(me._showingTimer);
                if (!me._closingTimer) {
                    me._closingTimer = setTimeout(function () {
                        if (!me.hasState("opened")) {
                            me.hideSubMenu();
                        }
                        me._closingTimer = null;
                    }, 400);
                }
            }
        },
        renderLabelHtml: function () {
            return (
                '<div class="edui-arrow"></div>' +
                '<div class="edui-box edui-icon"></div>' +
                '<div class="edui-box edui-label %%-label">' +
                (this.label || "") +
                "</div>"
            );
        },
        getStateDom: function () {
            return this.getDom();
        },
        queryAutoHide: function (el) {
            if (this.subMenu && this.hasState("opened")) {
                return this.subMenu.queryAutoHide(el);
            }
        },
        _onClick: function (event, this_) {
            if (this.hasState("disabled")) return;
            if (this.fireEvent("click", event, this_) !== false) {
                if (this.subMenu) {
                    this.showSubMenu();
                } else {
                    Popup.postHide(event);
                }
            }
        },
        showSubMenu: function () {
            var rect = uiUtils.getClientRect(this.getDom());
            rect.right -= 5;
            rect.left += 2;
            rect.width -= 7;
            rect.top -= 4;
            rect.bottom += 4;
            rect.height += 8;
            this.subMenu.showAnchorRect(rect, true, true);
        },
        hideSubMenu: function () {
            this.subMenu.hide();
        }
    };
    utils.inherits(MenuItem, UIBase);
    utils.extend(MenuItem.prototype, Stateful, true);
})();


// ui/combox.js
///import core
///import uicore
///import ui/menu.js
///import ui/splitbutton.js
(function () {
    // todo: menu和item提成通用list
    var utils = baidu.editor.utils,
        uiUtils = baidu.editor.ui.uiUtils,
        Menu = baidu.editor.ui.Menu,
        SplitButton = baidu.editor.ui.SplitButton,
        Combox = (baidu.editor.ui.Combox = function (options) {
            this.initOptions(options);
            this.initCombox();
        });
    Combox.prototype = {
        uiName: "combox",
        onbuttonclick: function () {
            this.showPopup();
        },
        initCombox: function () {
            var me = this;
            this.items = this.items || [];
            for (var i = 0; i < this.items.length; i++) {
                var item = this.items[i];
                item.uiName = "listitem";
                item.index = i;
                item.onclick = function () {
                    me.selectByIndex(this.index);
                };
            }
            this.popup = new Menu({
                items: this.items,
                uiName: "list",
                editor: this.editor,
                captureWheel: true,
                combox: this
            });

            this.initSplitButton();
        },
        _SplitButton_postRender: SplitButton.prototype.postRender,
        postRender: function () {
            this._SplitButton_postRender();
            this.setLabel(this.label || "");
            this.setValue(this.initValue || "");
        },
        showPopup: function () {
            var rect = uiUtils.getClientRect(this.getDom());
            rect.top += 1;
            rect.bottom -= 1;
            rect.height -= 2;
            this.popup.showAnchorRect(rect);
        },
        getValue: function () {
            return this.value;
        },
        setValue: function (value) {
            var index = this.indexByValue(value);
            if (index != -1) {
                this.selectedIndex = index;
                this.setLabel(this.items[index].label);
                this.value = this.items[index].value;
            } else {
                this.selectedIndex = -1;
                this.setLabel(this.getLabelForUnknowValue(value));
                this.value = value;
            }
        },
        setLabel: function (label) {
            this.getDom("button_body").innerHTML = label;
            this.label = label;
        },
        getLabelForUnknowValue: function (value) {
            return value;
        },
        indexByValue: function (value) {
            for (var i = 0; i < this.items.length; i++) {
                if (value == this.items[i].value) {
                    return i;
                }
            }
            return -1;
        },
        getItem: function (index) {
            return this.items[index];
        },
        selectByIndex: function (index) {
            if (
                index < this.items.length &&
                this.fireEvent("select", index) !== false
            ) {
                this.selectedIndex = index;
                this.value = this.items[index].value;
                this.setLabel(this.items[index].label);
            }
        }
    };
    utils.inherits(Combox, SplitButton);
})();


// ui/dialog.js
///import core
///import uicore
///import ui/mask.js
///import ui/button.js
(function () {
    var utils = baidu.editor.utils,
        domUtils = baidu.editor.dom.domUtils,
        uiUtils = baidu.editor.ui.uiUtils,
        Mask = baidu.editor.ui.Mask,
        UIBase = baidu.editor.ui.UIBase,
        Button = baidu.editor.ui.Button,
        Dialog = (baidu.editor.ui.Dialog = function (options) {
            if (options.name) {
                var name = options.name;
                var cssRules = options.cssRules;
                if (!options.className) {
                    options.className = "edui-for-" + name;
                }
                if (cssRules) {
                    options.cssRules =
                        ".edui-for-" + name + " .edui-dialog-content  {" + cssRules + "}";
                }
            }
            this.initOptions(
                utils.extend(
                    {
                        autoReset: true,
                        draggable: true,
                        onok: function () {
                        },
                        oncancel: function () {
                        },
                        onclose: function (t, ok) {
                            return ok ? this.onok() : this.oncancel();
                        },
                        //是否控制dialog中的scroll事件, 默认为不阻止
                        holdScroll: false
                    },
                    options
                )
            );
            this.initDialog();
        });
    var modalMask;
    var dragMask;
    var activeDialog;
    Dialog.prototype = {
        draggable: false,
        uiName: "dialog",
        initDialog: function () {
            var me = this,
                theme = this.editor.options.theme;
            if (this.cssRules) {
                this.cssRules = ".edui-" + theme + " " + this.cssRules;
                utils.cssRule("edui-customize-" + this.name + "-style", this.cssRules);
            }
            this.initUIBase();
            this.modalMask =
                modalMask ||
                (modalMask = new Mask({
                    className: "edui-dialog-modalmask",
                    theme: theme,
                    onclick: function () {
                        activeDialog && activeDialog.close(false);
                    }
                }));
            this.dragMask =
                dragMask ||
                (dragMask = new Mask({
                    className: "edui-dialog-dragmask",
                    theme: theme
                }));
            this.closeButton = new Button({
                className: "edui-dialog-closebutton",
                title: me.closeDialog,
                theme: theme,
                onclick: function () {
                    me.close(false);
                }
            });

            this.fullscreen && this.initResizeEvent();

            if (this.buttons) {
                for (var i = 0; i < this.buttons.length; i++) {
                    if (!(this.buttons[i] instanceof Button)) {
                        this.buttons[i] = new Button(
                            utils.extend(
                                this.buttons[i],
                                {
                                    editor: this.editor
                                },
                                true
                            )
                        );
                    }
                }
            }
        },
        initResizeEvent: function () {
            var me = this;


            domUtils.on(window, "resize", function () {

                if (me._hidden || me._hidden === undefined) {
                    return;
                }

                if (me.__resizeTimer) {
                    window.clearTimeout(me.__resizeTimer);
                }

                me.__resizeTimer = window.setTimeout(function () {
                    me.__resizeTimer = null;


                    var dialogWrapNode = me.getDom(),
                        contentNode = me.getDom("content"),
                        wrapRect = UE.ui.uiUtils.getClientRect(dialogWrapNode),
                        contentRect = UE.ui.uiUtils.getClientRect(contentNode),
                        vpRect = uiUtils.getViewportRect();

                    contentNode.style.width =
                        vpRect.width - wrapRect.width + contentRect.width + "px";
                    contentNode.style.height =
                        vpRect.height - wrapRect.height + contentRect.height + "px";

                    dialogWrapNode.style.width = vpRect.width + "px";
                    dialogWrapNode.style.height = vpRect.height + "px";

                    me.fireEvent("resize");
                }, 100);
            });
        },
        fitSize: function () {
            // console.log('fitSize.dialog')
            var popBodyEl = this.getDom("body");
            var $foot = popBodyEl.querySelector('.edui-dialog-foot');
            var heightWithoutBody = 70;
            if (!$foot) {
                heightWithoutBody = 30;
            }
            var size = this.mesureSize();
            var winSize = uiUtils.getViewportRect();
            var width = size.width;
            var height = size.height - heightWithoutBody;
            var maxWidth = winSize.width - 2;
            var maxHeight = winSize.height - heightWithoutBody - 2;
            if (width > maxWidth) {
                height = height * maxWidth / width;
                width = maxWidth;
            }
            if (height > maxHeight) {
                width = width * maxHeight / height;
                height = maxHeight;
            }
            var scale = (width / size.width);
            // console.log('size', {sizeWidth: size.width, sizeHeight: size.height, width, height, scale});
            // console.log('popBodyEl',popBodyEl, popBodyEl.querySelector('.edui-dialog-foot'));
            // window._xxx = popBodyEl;
            var $content = popBodyEl.querySelector('.edui-dialog-content');
            if (!$content.dataset.dialogScaled) {
                $content.dataset.dialogScaled = true
                $content.style.width = (width) + 'px';
                $content.style.height = (height) + 'px';
                var $iframe = popBodyEl.querySelector('.edui-dialog-content iframe');
                $iframe.style.width = (size.width) + 'px';
                $iframe.style.height = (size.height - heightWithoutBody) + 'px';
                $iframe.style.transformOrigin = '0 0';
                $iframe.style.transform = 'scale(' + scale + ')';
                size.width = width
                size.height = height + heightWithoutBody
            }
            popBodyEl.style.width = size.width + "px";
            popBodyEl.style.height = size.height + "px";
            return size;
        },
        safeSetOffset: function (offset) {
            var me = this;
            var el = me.getDom();
            var vpRect = uiUtils.getViewportRect();
            var rect = uiUtils.getClientRect(el);
            var left = offset.left;
            if (left + rect.width > vpRect.right) {
                left = vpRect.right - rect.width;
            }
            var top = offset.top;
            if (top + rect.height > vpRect.bottom) {
                top = vpRect.bottom - rect.height;
            }
            el.style.left = Math.max(left, 0) + "px";
            el.style.top = Math.max(top, 0) + "px";
        },
        showAtCenter: function () {
            var vpRect = uiUtils.getViewportRect();

            if (!this.fullscreen) {
                this.getDom().style.display = "";
                var popSize = this.fitSize();
                var titleHeight = this.getDom("titlebar").offsetHeight | 0;
                var left = vpRect.width / 2 - popSize.width / 2;
                var top =
                    vpRect.height / 2 - (popSize.height - titleHeight) / 2 - titleHeight;
                var popEl = this.getDom();
                this.safeSetOffset({
                    left: Math.max(left | 0, 0),
                    top: Math.max(top | 0, 0)
                });
                if (!domUtils.hasClass(popEl, "edui-state-centered")) {
                    popEl.className += " edui-state-centered";
                }
            } else {
                var dialogWrapNode = this.getDom(),
                    contentNode = this.getDom("content");

                dialogWrapNode.style.display = "block";

                var wrapRect = UE.ui.uiUtils.getClientRect(dialogWrapNode),
                    contentRect = UE.ui.uiUtils.getClientRect(contentNode);
                dialogWrapNode.style.left = "-100000px";

                contentNode.style.width =
                    vpRect.width - wrapRect.width + contentRect.width + "px";
                contentNode.style.height =
                    vpRect.height - wrapRect.height + contentRect.height + "px";

                dialogWrapNode.style.width = vpRect.width + "px";
                dialogWrapNode.style.height = vpRect.height + "px";
                dialogWrapNode.style.left = 0;

                //保存环境的overflow值
                this._originalContext = {
                    html: {
                        overflowX: document.documentElement.style.overflowX,
                        overflowY: document.documentElement.style.overflowY
                    },
                    body: {
                        overflowX: document.body.style.overflowX,
                        overflowY: document.body.style.overflowY
                    }
                };

                document.documentElement.style.overflowX = "hidden";
                document.documentElement.style.overflowY = "hidden";
                document.body.style.overflowX = "hidden";
                document.body.style.overflowY = "hidden";
            }

            this._show();
        },
        getContentHtml: function () {
            var contentHtml = "";
            if (typeof this.content == "string") {
                contentHtml = this.content;
            } else if (this.iframeUrl) {
                contentHtml =
                    '<span id="' +
                    this.id +
                    '_contmask" class="dialogcontmask"></span><iframe id="' +
                    this.id +
                    '_iframe" class="%%-iframe" height="100%" width="100%" frameborder="0" src="' +
                    this.iframeUrl +
                    '"></iframe>';
            }
            return contentHtml;
        },
        getHtmlTpl: function () {
            var footHtml = "";

            if (this.buttons) {
                var buff = [];
                for (var i = 0; i < this.buttons.length; i++) {
                    buff[i] = this.buttons[i].renderHtml();
                }
                footHtml =
                    '<div class="%%-foot">' +
                    '<div id="##_buttons" class="%%-buttons">' +
                    buff.join("") +
                    "</div>" +
                    "</div>";
            }

            return (
                '<div id="##" class="%%"><div ' +
                (!this.fullscreen
                    ? 'class="%%"'
                    : 'class="%%-wrap edui-dialog-fullscreen-flag"') +
                '><div id="##_body" class="%%-body">' +
                '<div class="%%-shadow"></div>' +
                '<div id="##_titlebar" class="%%-titlebar">' +
                '<div class="%%-draghandle" onmousedown="$$._onTitlebarMouseDown(event, this);">' +
                '<span class="%%-caption">' +
                (this.title || "") +
                "</span>" +
                "</div>" +
                this.closeButton.renderHtml() +
                "</div>" +
                '<div id="##_content" class="%%-content">' +
                (this.autoReset ? "" : this.getContentHtml()) +
                "</div>" +
                footHtml +
                "</div></div></div>"
            );
        },
        postRender: function () {
            // todo: 保持居中/记住上次关闭位置选项
            if (!this.modalMask.getDom()) {
                this.modalMask.render();
                this.modalMask.hide();
            }
            if (!this.dragMask.getDom()) {
                this.dragMask.render();
                this.dragMask.hide();
            }
            var me = this;
            this.addListener("show", function () {
                me.modalMask.show(this.getDom().style.zIndex - 2);
            });
            this.addListener("hide", function () {
                me.modalMask.hide();
            });
            if (this.buttons) {
                for (var i = 0; i < this.buttons.length; i++) {
                    this.buttons[i].postRender();
                }
            }
            domUtils.on(window, "resize", function () {
                setTimeout(function () {
                    if (!me.isHidden()) {
                        me.safeSetOffset(uiUtils.getClientRect(me.getDom()));
                    }
                });
            });

            //hold住scroll事件,防止dialog的滚动影响页面
            //            if( this.holdScroll ) {
            //
            //                if( !me.iframeUrl ) {
            //                    domUtils.on( document.getElementById( me.id + "_iframe"), !browser.gecko ? "mousewheel" : "DOMMouseScroll", function(e){
            //                        domUtils.preventDefault(e);
            //                    } );
            //                } else {
            //                    me.addListener('dialogafterreset', function(){
            //                        window.setTimeout(function(){
            //                            var iframeWindow = document.getElementById( me.id + "_iframe").contentWindow;
            //
            //                            if( browser.ie ) {
            //
            //                                var timer = window.setInterval(function(){
            //
            //                                    if( iframeWindow.document && iframeWindow.document.body ) {
            //                                        window.clearInterval( timer );
            //                                        timer = null;
            //                                        domUtils.on( iframeWindow.document.body, !browser.gecko ? "mousewheel" : "DOMMouseScroll", function(e){
            //                                            domUtils.preventDefault(e);
            //                                        } );
            //                                    }
            //
            //                                }, 100);
            //
            //                            } else {
            //                                domUtils.on( iframeWindow, !browser.gecko ? "mousewheel" : "DOMMouseScroll", function(e){
            //                                    domUtils.preventDefault(e);
            //                                } );
            //                            }
            //
            //                        }, 1);
            //                    });
            //                }
            //
            //            }
            this._hide();
        },
        mesureSize: function () {
            var body = this.getDom("body");
            var width = uiUtils.getClientRect(this.getDom("content")).width;
            var dialogBodyStyle = body.style;
            dialogBodyStyle.width = width;
            // console.log('getClientRect', body)
            return uiUtils.getClientRect(body);
        },
        _onTitlebarMouseDown: function (evt, el) {
            if (this.draggable) {
                var rect;
                var vpRect = uiUtils.getViewportRect();
                var me = this;
                uiUtils.startDrag(evt, {
                    ondragstart: function () {
                        rect = uiUtils.getClientRect(me.getDom());
                        me.getDom("contmask").style.visibility = "visible";
                        me.dragMask.show(me.getDom().style.zIndex - 1);
                    },
                    ondragmove: function (x, y) {
                        var left = rect.left + x;
                        var top = rect.top + y;
                        me.safeSetOffset({
                            left: left,
                            top: top
                        });
                    },
                    ondragstop: function () {
                        me.getDom("contmask").style.visibility = "hidden";
                        domUtils.removeClasses(me.getDom(), ["edui-state-centered"]);
                        me.dragMask.hide();
                    }
                });
            }
        },
        reset: function () {
            this.getDom("content").innerHTML = this.getContentHtml();
            this.fireEvent("dialogafterreset");
        },
        _show: function () {
            if (this._hidden) {
                this.getDom().style.display = "";

                //要高过编辑器的zindxe
                this.editor.container.style.zIndex &&
                (this.getDom().style.zIndex =
                    this.editor.container.style.zIndex * 1 + 10);
                this._hidden = false;
                this.fireEvent("show");
                baidu.editor.ui.uiUtils.getFixedLayer().style.zIndex =
                    this.getDom().style.zIndex - 4;
            }
        },
        isHidden: function () {
            return this._hidden;
        },
        _hide: function () {
            if (!this._hidden) {
                var wrapNode = this.getDom();
                wrapNode.style.display = "none";
                wrapNode.style.zIndex = "";
                wrapNode.style.width = "";
                wrapNode.style.height = "";
                this._hidden = true;
                this.fireEvent("hide");
            }
        },
        open: function () {
            if (this.autoReset) {
                //有可能还没有渲染
                try {
                    this.reset();
                } catch (e) {
                    this.render();
                    this.open();
                }
            }
            this.showAtCenter();
            if (this.iframeUrl) {
                try {
                    this.getDom("iframe").focus();
                } catch (ex) {
                }
            }
            activeDialog = this;
        },
        _onCloseButtonClick: function (evt, el) {
            this.close(false);
        },
        close: function (ok) {
            if (this.fireEvent("close", ok) !== false) {
                //还原环境
                if (this.fullscreen) {
                    document.documentElement.style.overflowX = this._originalContext.html.overflowX;
                    document.documentElement.style.overflowY = this._originalContext.html.overflowY;
                    document.body.style.overflowX = this._originalContext.body.overflowX;
                    document.body.style.overflowY = this._originalContext.body.overflowY;
                    delete this._originalContext;
                }
                this._hide();

                //销毁content
                var content = this.getDom("content");
                var iframe = this.getDom("iframe");
                if (content && iframe) {
                    var doc = iframe.contentDocument || iframe.contentWindow.document;
                    doc && (doc.body.innerHTML = "");
                    domUtils.remove(content);
                }
            }
        }
    };
    utils.inherits(Dialog, UIBase);
})();


// ui/menubutton.js
///import core
///import uicore
///import ui/menu.js
///import ui/splitbutton.js
(function () {
    var utils = baidu.editor.utils,
        Menu = baidu.editor.ui.Menu,
        SplitButton = baidu.editor.ui.SplitButton,
        MenuButton = (baidu.editor.ui.MenuButton = function (options) {
            this.initOptions(options);
            this.initMenuButton();
        });
    MenuButton.prototype = {
        initMenuButton: function () {
            var me = this;
            this.uiName = "menubutton";
            this.popup = new Menu({
                items: me.items,
                className: me.className,
                editor: me.editor
            });
            this.popup.addListener("show", function () {
                var list = this;
                for (var i = 0; i < list.items.length; i++) {
                    list.items[i].removeState("checked");
                    if (list.items[i].value == me._value) {
                        list.items[i].addState("checked");
                        this.value = me._value;
                    }
                }
            });
            this.initSplitButton();
        },
        setValue: function (value) {
            this._value = value;
        }
    };
    utils.inherits(MenuButton, SplitButton);
})();


// ui/multiMenu.js
///import core
///import uicore
///commands 表情
(function () {
    var utils = baidu.editor.utils,
        Popup = baidu.editor.ui.Popup,
        SplitButton = baidu.editor.ui.SplitButton,
        MultiMenuPop = (baidu.editor.ui.MultiMenuPop = function (options) {
            this.initOptions(options);
            this.initMultiMenu();
        });

    MultiMenuPop.prototype = {
        initMultiMenu: function () {
            var me = this;
            this.popup = new Popup({
                content: "",
                editor: me.editor,
                iframe_rendered: false,
                onshow: function () {
                    if (!this.iframe_rendered) {
                        this.iframe_rendered = true;
                        this.getDom("content").innerHTML =
                            '<iframe id="' +
                            me.id +
                            '_iframe" src="' +
                            me.iframeUrl +
                            '" frameborder="0"></iframe>';
                        me.editor.container.style.zIndex &&
                        (this.getDom().style.zIndex =
                            me.editor.container.style.zIndex * 1 + 1);
                    }
                }
                // canSideUp:false,
                // canSideLeft:false
            });
            this.onbuttonclick = function () {
                this.showPopup();
            };
            this.initSplitButton();
        }
    };

    utils.inherits(MultiMenuPop, SplitButton);
})();


// ui/shortcutmenu.js
(function () {
    var UI = baidu.editor.ui,
        UIBase = UI.UIBase,
        uiUtils = UI.uiUtils,
        utils = baidu.editor.utils,
        domUtils = baidu.editor.dom.domUtils;

    var allMenus = [], //存储所有快捷菜单
        timeID,
        isSubMenuShow = false; //是否有子pop显示

    var ShortCutMenu = (UI.ShortCutMenu = function (options) {
        this.initOptions(options);
        this.initShortCutMenu();
    });

    ShortCutMenu.postHide = hideAllMenu;

    ShortCutMenu.prototype = {
        isHidden: true,
        SPACE: 5,
        initShortCutMenu: function () {
            this.items = this.items || [];
            this.initUIBase();
            this.initItems();
            this.initEvent();
            allMenus.push(this);
        },
        initEvent: function () {
            var me = this,
                doc = me.editor.document;

            /*
            domUtils.on(doc, "mousemove", function(e) {
              if (me.isHidden === false) {
                //有pop显示就不隐藏快捷菜单
                if (me.getSubMenuMark() || me.eventType == "contextmenu") return;

                var flag = true,
                  el = me.getDom(),
                  wt = el.offsetWidth,
                  ht = el.offsetHeight,
                  distanceX = wt / 2 + me.SPACE, //距离中心X标准
                  distanceY = ht / 2, //距离中心Y标准
                  x = Math.abs(e.screenX - me.left), //离中心距离横坐标
                  y = Math.abs(e.screenY - me.top); //离中心距离纵坐标

                clearTimeout(timeID);
                timeID = setTimeout(function() {
                  if (y > 0 && y < distanceY) {
                    me.setOpacity(el, "1");
                  } else if (y > distanceY && y < distanceY + 70) {
                    me.setOpacity(el, "0.5");
                    flag = false;
                  } else if (y > distanceY + 70 && y < distanceY + 140) {
                    me.hide();
                  }

                  if (flag && x > 0 && x < distanceX) {
                    me.setOpacity(el, "1");
                  } else if (x > distanceX && x < distanceX + 70) {
                    me.setOpacity(el, "0.5");
                  } else if (x > distanceX + 70 && x < distanceX + 140) {
                      console.log('hide')
                    me.hide();
                  }
                });
              }
            });
            */
            //ie\ff下 mouseout不准
            /*
            if (browser.chrome) {
              domUtils.on(doc, "mouseout", function(e) {
                var relatedTgt = e.relatedTarget || e.toElement;

                if (relatedTgt == null || relatedTgt.tagName == "HTML") {
                  me.hide();
                }
              });
            }
             */

            me.editor.addListener("afterhidepop", function () {
                if (!me.isHidden) {
                    isSubMenuShow = true;
                }
            });
        },
        initItems: function () {
            if (utils.isArray(this.items)) {
                for (var i = 0, len = this.items.length; i < len; i++) {
                    if ('string' !== typeof this.items[i]) {
                        continue;
                    }
                    var item = this.items[i].toLowerCase();

                    if (UI[item]) {
                        this.items[i] = new UI[item](this.editor);
                        this.items[i].className += " edui-short-cut-sub-menu ";
                    }
                }
            }
        },
        setOpacity: function (el, value) {
            if (browser.ie && browser.version < 9) {
                el.style.filter = "alpha(opacity = " + parseFloat(value) * 100 + ");";
            } else {
                el.style.opacity = value;
            }
        },
        getSubMenuMark: function () {
            isSubMenuShow = false;
            var layerEle = uiUtils.getFixedLayer();
            var list = domUtils.getElementsByTagName(layerEle, "div", function (node) {
                return domUtils.hasClass(node, "edui-short-cut-sub-menu edui-popup");
            });

            for (var i = 0, node; (node = list[i++]);) {
                if (node.style.display !== "none") {
                    isSubMenuShow = true;
                }
            }
            return isSubMenuShow;
        },
        show: function (e, hasContextmenu) {
            var me = this,
                offset = {},
                el = this.getDom(),
                fixedlayer = uiUtils.getFixedLayer();

            for (let item of this.items) {
                if ('shouldUiShow' in item) {
                    item.uiShow(item.shouldUiShow());
                }
            }

            function setPos(offset) {
                if (offset.left < 0) {
                    offset.left = 0;
                }
                if (offset.top < 0) {
                    offset.top = 0;
                }
                el.style.cssText =
                    "position:absolute;left:" +
                    offset.left +
                    "px;top:" +
                    offset.top +
                    "px;";
            }

            function setPosByCxtMenu(menu) {
                if (!menu.tagName) {
                    menu = menu.getDom();
                }
                offset.left = parseInt(menu.style.left);
                offset.top = parseInt(menu.style.top);
                offset.top -= el.offsetHeight + 15;
                setPos(offset);
            }

            me.eventType = e.type;
            el.style.cssText = "display:block;left:-9999px";

            // if (e.type === "contextmenu" && hasContextmenu) {
            //     var menu = domUtils.getElementsByTagName(
            //         fixedlayer,
            //         "div",
            //         "edui-contextmenu"
            //     )[0];
            //     if (menu) {
            //         setPosByCxtMenu(menu);
            //     } else {
            //         me.editor.addListener("aftershowcontextmenu", function (type, menu) {
            //             setPosByCxtMenu(menu);
            //         });
            //     }
            // } else {
            offset = uiUtils.getViewportOffsetByEvent(e);
            offset.top -= el.offsetHeight + me.SPACE;
            offset.left += me.SPACE + 20;
            setPos(offset);
            me.setOpacity(el, 1);
            // }

            me.isHidden = false;
            me.left = e.screenX + el.offsetWidth / 2 - me.SPACE;
            me.top = e.screenY - el.offsetHeight / 2 - me.SPACE;

            if (me.editor) {
                el.style.zIndex = me.editor.container.style.zIndex * 1 + 10;
                fixedlayer.style.zIndex = el.style.zIndex - 1;
            }
        },
        hide: function () {
            if (this.getDom()) {
                this.getDom().style.display = "none";
            }
            this.isHidden = true;
        },
        postRender: function () {
            if (utils.isArray(this.items)) {
                for (var i = 0, item; (item = this.items[i++]);) {
                    item.postRender();
                }
            }
        },
        getHtmlTpl: function () {
            var buff;
            if (utils.isArray(this.items)) {
                buff = [];
                for (var i = 0; i < this.items.length; i++) {
                    buff[i] = this.items[i].renderHtml();
                }
                buff = buff.join("");
            } else {
                buff = this.items;
            }

            return (
                '<div id="##" class="%% edui-toolbar" data-src="shortcutmenu" onmousedown="return false;" onselectstart="return false;" >' +
                buff +
                "</div>"
            );
        }
    };

    utils.inherits(ShortCutMenu, UIBase);

    function hideAllMenu(e) {
        var tgt = e.target || e.srcElement,
            cur = domUtils.findParent(
                tgt,
                function (node) {
                    return (
                        domUtils.hasClass(node, "edui-shortcutmenu") ||
                        domUtils.hasClass(node, "edui-popup")
                    );
                },
                true
            );

        if (!cur) {
            for (var i = 0, menu; (menu = allMenus[i++]);) {
                menu.hide();
            }
        }
    }

    domUtils.on(document, "mousedown", function (e) {
        hideAllMenu(e);
    });

    domUtils.on(window, "scroll", function (e) {
        hideAllMenu(e);
    });
})();


// ui/breakline.js
(function () {
    var utils = baidu.editor.utils,
        UIBase = baidu.editor.ui.UIBase,
        Breakline = (baidu.editor.ui.Breakline = function (options) {
            this.initOptions(options);
            this.initSeparator();
        });
    Breakline.prototype = {
        uiName: "Breakline",
        initSeparator: function () {
            this.initUIBase();
        },
        getHtmlTpl: function () {
            return "<br/>";
        }
    };
    utils.inherits(Breakline, UIBase);
})();


// ui/message.js
///import core
///import uicore
(function () {
    var utils = baidu.editor.utils,
        domUtils = baidu.editor.dom.domUtils,
        UIBase = baidu.editor.ui.UIBase,
        Message = (baidu.editor.ui.Message = function (options) {
            this.initOptions(options);
            this.initMessage();
        });

    Message.prototype = {
        initMessage: function () {
            this.initUIBase();
        },
        getHtmlTpl: function () {
            return (
                '<div id="##" class="edui-message %%">' +
                ' <div id="##_closer" class="edui-message-closer">×</div>' +
                ' <div id="##_body" class="edui-message-body edui-message-type-info">' +
                ' <iframe style="position:absolute;z-index:-1;left:0;top:0;background-color: transparent;" frameborder="0" width="100%" height="100%" src="about:blank"></iframe>' +
                ' <div class="edui-shadow"></div>' +
                ' <div id="##_content" class="edui-message-content">' +
                "  </div>" +
                " </div>" +
                "</div>"
            );
        },
        reset: function (opt) {
            var me = this;
            if (!opt.keepshow) {
                clearTimeout(this.timer);
                me.timer = setTimeout(function () {
                    me.hide();
                }, opt.timeout || 4000);
            }

            opt.content !== undefined && me.setContent(opt.content);
            opt.type !== undefined && me.setType(opt.type);

            me.show();
        },
        postRender: function () {
            var me = this,
                closer = this.getDom("closer");
            closer &&
            domUtils.on(closer, "click", function () {
                me.hide();
            });
        },
        setContent: function (content) {
            this.getDom("content").innerHTML = content;
        },
        setType: function (type) {
            type = type || "info";
            var body = this.getDom("body");
            body.className = body.className.replace(
                /edui-message-type-[\w-]+/,
                "edui-message-type-" + type
            );
        },
        getContent: function () {
            return this.getDom("content").innerHTML;
        },
        getType: function () {
            var arr = this.getDom("body").match(/edui-message-type-([\w-]+)/);
            return arr ? arr[1] : "";
        },
        show: function () {
            this.getDom().style.display = "block";
        },
        hide: function () {
            var dom = this.getDom();
            if (dom) {
                dom.style.display = "none";
                dom.parentNode && dom.parentNode.removeChild(dom);
            }
        }
    };

    utils.inherits(Message, UIBase);
})();


// adapter/editorui.js
//ui跟编辑器的适配層
//那个按钮弹出是dialog,是下拉筐等都是在这个js中配置
//自己写的ui也要在这里配置,放到baidu.editor.ui下边,当编辑器实例化的时候会根据ueditor.config中的toolbars找到相应的进行实例化
(function () {
    var utils = baidu.editor.utils;
    var editorui = baidu.editor.ui;
    var _Dialog = editorui.Dialog;
    editorui.buttons = {};

    editorui.Dialog = function (options) {
        var dialog = new _Dialog(options);
        dialog.addListener("hide", function () {
            if (dialog.editor) {
                var editor = dialog.editor;
                try {
                    if (browser.gecko) {
                        var y = editor.window.scrollY,
                            x = editor.window.scrollX;
                        editor.body.focus();
                        editor.window.scrollTo(x, y);
                    } else {
                        editor.focus();
                    }
                } catch (ex) {
                }
            }
        });
        return dialog;
    };

    //为工具栏添加按钮,以下都是统一的按钮触发命令,所以写在一起
    var btnCmds = [
        "undo",
        "redo",
        "formatmatch",
        "bold",
        "italic",
        "underline",
        "fontborder",
        "touppercase",
        "tolowercase",
        "strikethrough",
        "subscript",
        "superscript",
        "source",
        "indent",
        "outdent",
        "blockquote",
        "pasteplain",
        "pagebreak",
        "selectall",
        "print",
        "horizontal",
        "removeformat",
        "time",
        "date",
        "unlink",
        "insertparagraphbeforetable",
        "insertrow",
        "insertcol",
        "mergeright",
        "mergedown",
        "deleterow",
        "deletecol",
        "splittorows",
        "splittocols",
        "splittocells",
        "mergecells",
        "deletetable",
    ];

    for (var i = 0, ci; (ci = btnCmds[i++]);) {
        ci = ci.toLowerCase();
        editorui[ci] = (function (cmd) {
            return function (editor) {
                var ui = new editorui.Button({
                    className: "edui-for-" + cmd,
                    title:
                        editor.options.labelMap[cmd] ||
                        editor.getLang("labelMap." + cmd) ||
                        "",
                    onclick: function () {
                        editor.execCommand(cmd);
                    },
                    theme: editor.options.theme,
                    showText: false
                });
                switch (cmd) {
                    case 'bold':
                    case 'italic':
                    case 'underline':
                    case 'strikethrough':
                    case 'fontborder':
                        ui.shouldUiShow = (function (cmdInternal) {
                            return function () {
                                if (!editor.selection.getText()) {
                                    return false;
                                }
                                return editor.queryCommandState(cmdInternal) !== UE.constants.STATEFUL.DISABLED;
                            }
                        })(cmd);
                        break;
                }
                editorui.buttons[cmd] = ui;
                editor.addListener("selectionchange", function (
                    type,
                    causeByUi,
                    uiReady
                ) {
                    var state = editor.queryCommandState(cmd);
                    if (state === -1) {
                        ui.setDisabled(true);
                        ui.setChecked(false);
                    } else {
                        if (!uiReady) {
                            ui.setDisabled(false);
                            ui.setChecked(state);
                        }
                    }
                });
                return ui;
            };
        })(ci);
    }

    //清除文档
    editorui.cleardoc = function (editor) {
        var ui = new editorui.Button({
            className: "edui-for-cleardoc",
            title:
                editor.options.labelMap.cleardoc ||
                editor.getLang("labelMap.cleardoc") ||
                "",
            theme: editor.options.theme,
            onclick: function () {
                if (confirm(editor.getLang("confirmClear"))) {
                    editor.execCommand("cleardoc");
                }
            }
        });
        editorui.buttons["cleardoc"] = ui;
        editor.addListener("selectionchange", function () {
            ui.setDisabled(editor.queryCommandState("cleardoc") == -1);
        });
        return ui;
    };

    var imageTypeSet = [
        'none', 'left', 'center', 'right'
    ];
    for (let value of imageTypeSet) {
        (function (value) {
            editorui['image' + value] = function (editor) {
                var ui = new editorui.Button({
                    className: "edui-for-" + 'image' + value,
                    title:
                        editor.options.labelMap['image' + value] ||
                        editor.getLang(
                            "labelMap." + 'image' + value
                        ) ||
                        "",
                    theme: editor.options.theme,
                    onclick: function () {
                        editor.execCommand('imagefloat', value);
                    },
                    shouldUiShow: function () {
                        let closedNode = editor.selection.getRange().getClosedNode();
                        if (!closedNode || closedNode.tagName !== "IMG") {
                            return false;
                        }
                        if (domUtils.hasClass(closedNode, "uep-loading") || domUtils.hasClass(closedNode, "uep-loading-error")) {
                            return false;
                        }
                        return editor.queryCommandState('imagefloat') !== UE.constants.STATEFUL.DISABLED;
                    }
                });
                editorui.buttons['image' + value] = ui;
                editor.addListener("selectionchange", function (
                    type,
                    causeByUi,
                    uiReady
                ) {
                    ui.setDisabled(editor.queryCommandState('imagefloat') === UE.constants.STATEFUL.DISABLED);
                    ui.setChecked(editor.queryCommandValue('imagefloat') === value && !uiReady);
                });
                return ui;
            };
        })(value);
    }

    //排版,图片排版,文字方向
    var typeset = {
        justify: ["left", "right", "center", "justify"],
        directionality: ["ltr", "rtl"]
    };
    for (var p in typeset) {
        (function (cmd, val) {
            for (var i = 0, ci; (ci = val[i++]);) {
                (function (cmd2) {
                    editorui[cmd.replace("float", "") + cmd2] = function (editor) {
                        var ui = new editorui.Button({
                            className: "edui-for-" + cmd.replace("float", "") + cmd2,
                            title:
                                editor.options.labelMap[cmd.replace("float", "") + cmd2] ||
                                editor.getLang(
                                    "labelMap." + cmd.replace("float", "") + cmd2
                                ) ||
                                "",
                            theme: editor.options.theme,
                            onclick: function () {
                                editor.execCommand(cmd, cmd2);
                            }
                        });
                        editorui.buttons[cmd] = ui;
                        editor.addListener("selectionchange", function (
                            type,
                            causeByUi,
                            uiReady
                        ) {
                            ui.setDisabled(editor.queryCommandState(cmd) == -1);
                            ui.setChecked(editor.queryCommandValue(cmd) == cmd2 && !uiReady);
                        });
                        return ui;
                    };
                })(ci);
            }
        })(p, typeset[p]);
    }

    //字体颜色和背景颜色
    for (var i = 0, ci; (ci = ["backcolor", "forecolor"][i++]);) {
        editorui[ci] = (function (cmd) {
            return function (editor) {
                var ui = new editorui.ColorButton({
                    className: "edui-for-" + cmd,
                    color: "default",
                    title:
                        editor.options.labelMap[cmd] ||
                        editor.getLang("labelMap." + cmd) ||
                        "",
                    editor: editor,
                    onpickcolor: function (t, color) {
                        editor.execCommand(cmd, color);
                    },
                    onpicknocolor: function () {
                        editor.execCommand(cmd, "default");
                        this.setColor("transparent");
                        this.color = "default";
                    },
                    onbuttonclick: function () {
                        editor.execCommand(cmd, this.color);
                    },
                    shouldUiShow: function () {
                        if (!editor.selection.getText()) {
                            return false;
                        }
                        return editor.queryCommandState(cmd) !== UE.constants.STATEFUL.DISABLED;
                    }
                });

                editorui.buttons[cmd] = ui;
                editor.addListener("selectionchange", function () {
                    ui.setDisabled(editor.queryCommandState(cmd) == -1);
                });
                return ui;
            };
        })(ci);
    }

    var dialogIframeUrlMap = {
        anchor: "~/dialogs/anchor/anchor.html?2f10d082",
        insertimage: "~/dialogs/image/image.html?4da72874",
        link: "~/dialogs/link/link.html?ccbfcf18",
        spechars: "~/dialogs/spechars/spechars.html?3bbeb696",
        searchreplace: "~/dialogs/searchreplace/searchreplace.html?2cb782d2",
        insertvideo: "~/dialogs/video/video.html?aa46c3ba",
        insertaudio: "~/dialogs/audio/audio.html?0742e32d",
        help: "~/dialogs/help/help.html?05c0c8bf",
        preview: "~/dialogs/preview/preview.html?5d9a0847",
        emotion: "~/dialogs/emotion/emotion.html?a7bc0989",
        wordimage: "~/dialogs/wordimage/wordimage.html?30a3bf2b",
        formula: "~/dialogs/formula/formula.html?9a5a1511",
        attachment: "~/dialogs/attachment/attachment.html?2cf57519",
        insertframe: "~/dialogs/insertframe/insertframe.html?807119a5",
        edittip: "~/dialogs/table/edittip.html?fa0ea189",
        edittable: "~/dialogs/table/edittable.html?134e2f06",
        edittd: "~/dialogs/table/edittd.html?9fe1a06e",
        scrawl: "~/dialogs/scrawl/scrawl.html?81bccab9",
        template: "~/dialogs/template/template.html?3c8090b7",
        background: "~/dialogs/background/background.html?c2bb8b05",
        contentimport: "~/dialogs/contentimport/contentimport.html?e298f77b",
    };
    var dialogBtns = {
        noOk: ["searchreplace", "help", "spechars", "preview"],
        ok: [
            "attachment",
            "anchor",
            "link",
            "insertimage",
            "insertframe",
            "wordimage",
            "insertvideo",
            "insertaudio",
            "edittip",
            "edittable",
            "edittd",
            "scrawl",
            "template",
            "formula",
            "background",
            "contentimport",
        ]
    };
    for (var p in dialogBtns) {
        (function (type, vals) {
            for (var i = 0, ci; (ci = vals[i++]);) {
                //todo opera下存在问题
                if (browser.opera && ci === "searchreplace") {
                    continue;
                }
                (function (cmd) {
                    editorui[cmd] = function (editor, iframeUrl, title) {
                        iframeUrl =
                            iframeUrl ||
                            (editor.options.dialogIframeUrlMap || {})[cmd] ||
                            dialogIframeUrlMap[cmd];
                        title =
                            editor.options.labelMap[cmd] ||
                            editor.getLang("labelMap." + cmd) ||
                            "";

                        var dialog;
                        //没有iframeUrl不创建dialog
                        if (iframeUrl) {
                            dialog = new editorui.Dialog(
                                utils.extend(
                                    {
                                        iframeUrl: editor.ui.mapUrl(iframeUrl),
                                        editor: editor,
                                        className: "edui-for-" + cmd,
                                        title: title,
                                        holdScroll: cmd === "insertimage",
                                        fullscreen: /preview/.test(cmd),
                                        closeDialog: editor.getLang("closeDialog")
                                    },
                                    type === "ok"
                                        ? {
                                            buttons: [
                                                {
                                                    className: "edui-okbutton",
                                                    label: editor.getLang("ok"),
                                                    editor: editor,
                                                    onclick: function () {
                                                        dialog.close(true);
                                                    }
                                                },
                                                {
                                                    className: "edui-cancelbutton",
                                                    label: editor.getLang("cancel"),
                                                    editor: editor,
                                                    onclick: function () {
                                                        dialog.close(false);
                                                    }
                                                }
                                            ]
                                        }
                                        : {}
                                )
                            );

                            editor.ui._dialogs[cmd + "Dialog"] = dialog;
                        }

                        var ui = new editorui.Button({
                            className: "edui-for-" + cmd,
                            title: title,
                            onclick: function () {
                                if (editor.options.toolbarCallback) {
                                    if (true === editor.options.toolbarCallback(cmd, editor)) {
                                        return;
                                    }
                                }
                                if (dialog) {
                                    switch (cmd) {
                                        case "wordimage":
                                            var images = editor.execCommand("wordimage");
                                            if (images && images.length) {
                                                dialog.render();
                                                dialog.open();
                                            }
                                            break;
                                        case "scrawl":
                                            if (editor.queryCommandState("scrawl") !== -1) {
                                                dialog.render();
                                                dialog.open();
                                            }
                                            break;
                                        default:
                                            dialog.render();
                                            dialog.open();
                                    }
                                }
                            },
                            theme: editor.options.theme,
                            disabled: (cmd === "scrawl" && editor.queryCommandState("scrawl") === -1)
                        });
                        switch (cmd) {
                            case 'insertimage':
                            case 'formula':
                                ui.shouldUiShow = (function (cmd) {
                                    return function () {
                                        let closedNode = editor.selection.getRange().getClosedNode();
                                        if (!closedNode || closedNode.tagName !== "IMG") {
                                            return false;
                                        }
                                        if ('formula' === cmd && closedNode.getAttribute('data-formula-image') !== null) {
                                            return true;
                                        }
                                        if ('insertimage' === cmd) {
                                            return true;
                                        }
                                        return false;
                                    };
                                })(cmd);
                                break;
                        }
                        editorui.buttons[cmd] = ui;
                        editor.addListener("selectionchange", function () {
                            //只存在于右键菜单而无工具栏按钮的ui不需要检测状态
                            var unNeedCheckState = {edittable: 1};
                            if (cmd in unNeedCheckState) return;

                            var state = editor.queryCommandState(cmd);
                            if (ui.getDom()) {
                                ui.setDisabled(state === -1);
                                ui.setChecked(state);
                            }
                        });

                        return ui;
                    };
                })(ci.toLowerCase());
            }
        })(p, dialogBtns[p]);
    }

    editorui.insertcode = function (editor, list, title) {
        list = editor.options["insertcode"] || [];
        title =
            editor.options.labelMap["insertcode"] ||
            editor.getLang("labelMap.insertcode") ||
            "";
        // if (!list.length) return;
        var items = [];
        utils.each(list, function (key, val) {
            items.push({
                label: key,
                value: val,
                theme: editor.options.theme,
                renderLabelHtml: function () {
                    return (
                        '<div class="edui-label %%-label" >' + (this.label || "") + "</div>"
                    );
                }
            });
        });

        var ui = new editorui.Combox({
            editor: editor,
            items: items,
            onselect: function (t, index) {
                editor.execCommand("insertcode", this.items[index].value);
            },
            onbuttonclick: function () {
                this.showPopup();
            },
            title: title,
            initValue: title,
            className: "edui-for-insertcode",
            indexByValue: function (value) {
                if (value) {
                    for (var i = 0, ci; (ci = this.items[i]); i++) {
                        if (ci.value.indexOf(value) != -1) return i;
                    }
                }

                return -1;
            }
        });
        editorui.buttons["insertcode"] = ui;
        editor.addListener("selectionchange", function (type, causeByUi, uiReady) {
            if (!uiReady) {
                var state = editor.queryCommandState("insertcode");
                if (state == -1) {
                    ui.setDisabled(true);
                } else {
                    ui.setDisabled(false);
                    var value = editor.queryCommandValue("insertcode");
                    if (!value) {
                        ui.setValue(title);
                        return;
                    }
                    //trace:1871 ie下从源码模式切换回来时,字体会带单引号,而且会有逗号
                    value && (value = value.replace(/['"]/g, "").split(",")[0]);
                    ui.setValue(value);
                }
            }
        });
        return ui;
    };

    editorui.fontfamily = function (editor, list, title) {
        list = editor.options["fontfamily"] || [];
        title =
            editor.options.labelMap["fontfamily"] ||
            editor.getLang("labelMap.fontfamily") ||
            "";
        if (!list.length) return;
        for (var i = 0, ci, items = []; (ci = list[i]); i++) {
            var langLabel = editor.getLang("fontfamily")[ci.name] || "";
            (function (key, val) {
                items.push({
                    label: key,
                    value: val,
                    theme: editor.options.theme,
                    renderLabelHtml: function () {
                        return (
                            '<div class="edui-label %%-label" style="font-family:' +
                            utils.unhtml(this.value) +
                            '">' +
                            (this.label || "") +
                            "</div>"
                        );
                    }
                });
            })(ci.label || langLabel, ci.val);
        }
        var ui = new editorui.Combox({
            editor: editor,
            items: items,
            onselect: function (t, index) {
                editor.execCommand("FontFamily", this.items[index].value);
            },
            onbuttonclick: function () {
                this.showPopup();
            },
            title: title,
            initValue: title,
            className: "edui-for-fontfamily",
            indexByValue: function (value) {
                if (value) {
                    for (var i = 0, ci; (ci = this.items[i]); i++) {
                        if (ci.value.indexOf(value) != -1) return i;
                    }
                }
                return -1;
            }
        });
        editorui.buttons["fontfamily"] = ui;
        editor.addListener("selectionchange", function (type, causeByUi, uiReady) {
            if (!uiReady) {
                var state = editor.queryCommandState("FontFamily");
                if (state == -1) {
                    ui.setDisabled(true);
                } else {
                    ui.setDisabled(false);
                    var value = editor.queryCommandValue("FontFamily");
                    //trace:1871 ie下从源码模式切换回来时,字体会带单引号,而且会有逗号
                    value && (value = value.replace(/['"]/g, "").split(",")[0]);
                    ui.setValue(value);
                }
            }
        });
        return ui;
    };

    editorui.fontsize = function (editor, list, title) {
        title =
            editor.options.labelMap["fontsize"] ||
            editor.getLang("labelMap.fontsize") ||
            "";
        list = list || editor.options["fontsize"] || [];
        if (!list.length) return;
        var items = [];
        for (var i = 0; i < list.length; i++) {
            var size = list[i] + "px";
            items.push({
                label: size,
                value: size,
                theme: editor.options.theme,
                renderLabelHtml: function () {
                    return (
                        '<div class="edui-label %%-label" style="line-height:1;font-size:' +
                        this.value +
                        '">' +
                        (this.label || "") +
                        "</div>"
                    );
                }
            });
        }
        var ui = new editorui.Combox({
            editor: editor,
            items: items,
            title: title,
            initValue: title,
            onselect: function (t, index) {
                editor.execCommand("FontSize", this.items[index].value);
            },
            onbuttonclick: function () {
                this.showPopup();
            },
            className: "edui-for-fontsize"
        });
        editorui.buttons["fontsize"] = ui;
        editor.addListener("selectionchange", function (type, causeByUi, uiReady) {
            if (!uiReady) {
                var state = editor.queryCommandState("FontSize");
                if (state == -1) {
                    ui.setDisabled(true);
                } else {
                    ui.setDisabled(false);
                    ui.setValue(editor.queryCommandValue("FontSize"));
                }
            }
        });
        return ui;
    };

    editorui.paragraph = function (editor, list, title) {
        title =
            editor.options.labelMap["paragraph"] ||
            editor.getLang("labelMap.paragraph") ||
            "";
        list = editor.options["paragraph"] || [];
        if (utils.isEmptyObject(list)) return;
        var items = [];
        for (var i in list) {
            items.push({
                value: i,
                label: list[i] || editor.getLang("paragraph")[i],
                theme: editor.options.theme,
                renderLabelHtml: function () {
                    return (
                        '<div class="edui-label %%-label"><span class="edui-for-' +
                        this.value +
                        '">' +
                        (this.label || "") +
                        "</span></div>"
                    );
                }
            });
        }
        var ui = new editorui.Combox({
            editor: editor,
            items: items,
            title: title,
            initValue: title,
            className: "edui-for-paragraph",
            onselect: function (t, index) {
                editor.execCommand("Paragraph", this.items[index].value);
            },
            onbuttonclick: function () {
                this.showPopup();
            }
        });
        editorui.buttons["paragraph"] = ui;
        editor.addListener("selectionchange", function (type, causeByUi, uiReady) {
            if (!uiReady) {
                var state = editor.queryCommandState("Paragraph");
                if (state == -1) {
                    ui.setDisabled(true);
                } else {
                    ui.setDisabled(false);
                    var value = editor.queryCommandValue("Paragraph");
                    var index = ui.indexByValue(value);
                    if (index != -1) {
                        ui.setValue(value);
                    } else {
                        ui.setValue(ui.initValue);
                    }
                }
            }
        });
        return ui;
    };

    //自定义标题
    editorui.customstyle = function (editor) {
        var list = editor.options["customstyle"] || [],
            title =
                editor.options.labelMap["customstyle"] ||
                editor.getLang("labelMap.customstyle") ||
                "";
        if (!list.length) return;
        var langCs = editor.getLang("customstyle");
        for (var i = 0, items = [], t; (t = list[i++]);) {
            (function (t) {
                var ck = {};
                ck.label = t.label ? t.label : langCs[t.name];
                ck.style = t.style;
                ck.className = t.className;
                ck.tag = t.tag;
                items.push({
                    label: ck.label,
                    value: ck,
                    theme: editor.options.theme,
                    renderLabelHtml: function () {
                        return (
                            '<div class="edui-label %%-label">' +
                            "<" +
                            ck.tag +
                            " " +
                            (ck.className ? ' class="' + ck.className + '"' : "") +
                            (ck.style ? ' style="' + ck.style + '"' : "") +
                            ">" +
                            ck.label +
                            "</" +
                            ck.tag +
                            ">" +
                            "</div>"
                        );
                    }
                });
            })(t);
        }

        var ui = new editorui.Combox({
            editor: editor,
            items: items,
            title: title,
            initValue: title,
            className: "edui-for-customstyle",
            onselect: function (t, index) {
                editor.execCommand("customstyle", this.items[index].value);
            },
            onbuttonclick: function () {
                this.showPopup();
            },
            indexByValue: function (value) {
                for (var i = 0, ti; (ti = this.items[i++]);) {
                    if (ti.label == value) {
                        return i - 1;
                    }
                }
                return -1;
            }
        });
        editorui.buttons["customstyle"] = ui;
        editor.addListener("selectionchange", function (type, causeByUi, uiReady) {
            if (!uiReady) {
                var state = editor.queryCommandState("customstyle");
                if (state == -1) {
                    ui.setDisabled(true);
                } else {
                    ui.setDisabled(false);
                    var value = editor.queryCommandValue("customstyle");
                    var index = ui.indexByValue(value);
                    if (index != -1) {
                        ui.setValue(value);
                    } else {
                        ui.setValue(ui.initValue);
                    }
                }
            }
        });
        return ui;
    };

    editorui.inserttable = function (editor, iframeUrl, title) {
        title =
            editor.options.labelMap["inserttable"] ||
            editor.getLang("labelMap.inserttable") ||
            "";
        var ui = new editorui.TableButton({
            editor: editor,
            title: title,
            className: "edui-for-inserttable",
            onpicktable: function (t, numCols, numRows) {
                editor.execCommand("InsertTable", {
                    numRows: numRows,
                    numCols: numCols,
                    border: 1
                });
            },
            onbuttonclick: function () {
                this.showPopup();
            }
        });
        editorui.buttons["inserttable"] = ui;
        editor.addListener("selectionchange", function () {
            ui.setDisabled(editor.queryCommandState("inserttable") == -1);
        });
        return ui;
    };

    editorui.lineheight = function (editor) {
        var val = editor.options.lineheight || [];
        if (!val.length) return;
        for (var i = 0, ci, items = []; (ci = val[i++]);) {
            items.push({
                //todo:写死了
                label: ci,
                value: ci,
                theme: editor.options.theme,
                onclick: function () {
                    editor.execCommand("lineheight", this.value);
                }
            });
        }
        var ui = new editorui.MenuButton({
            editor: editor,
            className: "edui-for-lineheight",
            title:
                editor.options.labelMap["lineheight"] ||
                editor.getLang("labelMap.lineheight") ||
                "",
            items: items,
            onbuttonclick: function () {
                var value = editor.queryCommandValue("LineHeight") || this.value;
                editor.execCommand("LineHeight", value);
            }
        });
        editorui.buttons["lineheight"] = ui;
        editor.addListener("selectionchange", function () {
            var state = editor.queryCommandState("LineHeight");
            if (state == -1) {
                ui.setDisabled(true);
            } else {
                ui.setDisabled(false);
                var value = editor.queryCommandValue("LineHeight");
                value && ui.setValue((value + "").replace(/cm/, ""));
                ui.setChecked(state);
            }
        });
        return ui;
    };

    var rowspacings = ["top", "bottom"];
    for (var r = 0, ri; (ri = rowspacings[r++]);) {
        (function (cmd) {
            editorui["rowspacing" + cmd] = function (editor) {
                var val = editor.options["rowspacing" + cmd] || [];
                if (!val.length) return null;
                for (var i = 0, ci, items = []; (ci = val[i++]);) {
                    items.push({
                        label: ci,
                        value: ci,
                        theme: editor.options.theme,
                        onclick: function () {
                            editor.execCommand("rowspacing", this.value, cmd);
                        }
                    });
                }
                var ui = new editorui.MenuButton({
                    editor: editor,
                    className: "edui-for-rowspacing" + cmd,
                    title:
                        editor.options.labelMap["rowspacing" + cmd] ||
                        editor.getLang("labelMap.rowspacing" + cmd) ||
                        "",
                    items: items,
                    onbuttonclick: function () {
                        var value =
                            editor.queryCommandValue("rowspacing", cmd) || this.value;
                        editor.execCommand("rowspacing", value, cmd);
                    }
                });
                editorui.buttons[cmd] = ui;
                editor.addListener("selectionchange", function () {
                    var state = editor.queryCommandState("rowspacing", cmd);
                    if (state == -1) {
                        ui.setDisabled(true);
                    } else {
                        ui.setDisabled(false);
                        var value = editor.queryCommandValue("rowspacing", cmd);
                        value && ui.setValue((value + "").replace(/%/, ""));
                        ui.setChecked(state);
                    }
                });
                return ui;
            };
        })(ri);
    }

    //有序,无序列表
    var lists = ["insertorderedlist", "insertunorderedlist"];
    for (var l = 0, cl; (cl = lists[l++]);) {
        (function (cmd) {
            editorui[cmd] = function (editor) {
                var vals = editor.options[cmd],
                    _onMenuClick = function () {
                        editor.execCommand(cmd, this.value);
                    },
                    items = [];
                for (var i in vals) {
                    items.push({
                        label: vals[i] || editor.getLang()[cmd][i] || "",
                        value: i,
                        theme: editor.options.theme,
                        onclick: _onMenuClick
                    });
                }
                var ui = new editorui.MenuButton({
                    editor: editor,
                    className: "edui-for-" + cmd,
                    title: editor.getLang("labelMap." + cmd) || "",
                    items: items,
                    onbuttonclick: function () {
                        var value = editor.queryCommandValue(cmd) || this.value;
                        editor.execCommand(cmd, value);
                    }
                });
                editorui.buttons[cmd] = ui;
                editor.addListener("selectionchange", function () {
                    var state = editor.queryCommandState(cmd);
                    if (state == -1) {
                        ui.setDisabled(true);
                    } else {
                        ui.setDisabled(false);
                        var value = editor.queryCommandValue(cmd);
                        ui.setValue(value);
                        ui.setChecked(state);
                    }
                });
                return ui;
            };
        })(cl);
    }

    editorui.fullscreen = function (editor, title) {
        title =
            editor.options.labelMap["fullscreen"] ||
            editor.getLang("labelMap.fullscreen") ||
            "";
        var ui = new editorui.Button({
            className: "edui-for-fullscreen",
            title: title,
            theme: editor.options.theme,
            onclick: function () {
                if (editor.ui) {
                    editor.ui.setFullScreen(!editor.ui.isFullScreen());
                }
                this.setChecked(editor.ui.isFullScreen());
            }
        });
        editorui.buttons["fullscreen"] = ui;
        editor.addListener("selectionchange", function () {
            var state = editor.queryCommandState("fullscreen");
            ui.setDisabled(state == -1);
            ui.setChecked(editor.ui.isFullScreen());
        });
        return ui;
    };

    // 表情
    editorui['emotion'] = function (editor, iframeUrl) {
        var cmd = "emotion";
        var ui = new editorui.MultiMenuPop({
            title:
                editor.options.labelMap[cmd] ||
                editor.getLang("labelMap." + cmd + "") ||
                "",
            editor: editor,
            className: "edui-for-" + cmd,
            iframeUrl: editor.ui.mapUrl(
                iframeUrl ||
                (editor.options.dialogIframeUrlMap || {})[cmd] ||
                dialogIframeUrlMap[cmd]
            )
        });
        editorui.buttons[cmd] = ui;

        editor.addListener("selectionchange", function () {
            ui.setDisabled(editor.queryCommandState(cmd) == -1);
        });
        return ui;
    };

    editorui['autotypeset'] = function (editor) {
        var ui = new editorui.AutoTypeSetButton({
            editor: editor,
            title:
                editor.options.labelMap["autotypeset"] ||
                editor.getLang("labelMap.autotypeset") ||
                "",
            className: "edui-for-autotypeset",
            onbuttonclick: function () {
                editor.execCommand("autotypeset");
            }
        });
        editorui.buttons["autotypeset"] = ui;
        editor.addListener("selectionchange", function () {
            ui.setDisabled(editor.queryCommandState("autotypeset") == -1);
        });
        return ui;
    };

    /* 简单上传插件 */
    editorui['simpleupload'] = function (editor) {
        var name = "simpleupload",
            ui = new editorui.Button({
                className: "edui-for-" + name,
                title:
                    editor.options.labelMap[name] ||
                    editor.getLang("labelMap." + name) ||
                    "",
                onclick: function () {
                },
                theme: editor.options.theme,
                showText: false
            });
        editorui.buttons[name] = ui;
        editor.addListener("ready", function () {
            var b = ui.getDom("body"),
                iconSpan = b.children[0];
            editor.fireEvent("simpleuploadbtnready", iconSpan);
        });
        editor.addListener("selectionchange", function (type, causeByUi, uiReady) {
            var state = editor.queryCommandState(name);
            if (state == -1) {
                ui.setDisabled(true);
                ui.setChecked(false);
            } else {
                if (!uiReady) {
                    ui.setDisabled(false);
                    ui.setChecked(state);
                }
            }
        });
        return ui;
    };

})();


// adapter/editor.js
///import core
///commands 全屏
///commandsName FullScreen
///commandsTitle  全屏
(function () {
    var utils = baidu.editor.utils,
        uiUtils = baidu.editor.ui.uiUtils,
        UIBase = baidu.editor.ui.UIBase,
        domUtils = baidu.editor.dom.domUtils;
    var nodeStack = [];

    function EditorUI(options) {
        this.initOptions(options);
        this.initEditorUI();
    }

    EditorUI.prototype = {
        uiName: "editor",
        initEditorUI: function () {
            this.editor.ui = this;
            this._dialogs = {};
            this.initUIBase();
            this._initToolbars();
            var editor = this.editor,
                me = this;

            editor.addListener("ready", function () {
                //提供getDialog方法
                editor.getDialog = function (name) {
                    return editor.ui._dialogs[name + "Dialog"];
                };
                domUtils.on(editor.window, "scroll", function (evt) {
                    baidu.editor.ui.Popup.postHide(evt);
                });
                //提供编辑器实时宽高(全屏时宽高不变化)
                editor.ui._actualFrameWidth = editor.options.initialFrameWidth;

                UE.browser.ie &&
                UE.browser.version === 6 &&
                editor.container.ownerDocument.execCommand(
                    "BackgroundImageCache",
                    false,
                    true
                );

                //display bottom-bar label based on config
                if (editor.options.elementPathEnabled) {
                    editor.ui.getDom("elementpath").innerHTML =
                        '<div class="edui-editor-breadcrumb">' +
                        editor.getLang("elementPathTip") +
                        ":</div>";
                }
                if (editor.options.wordCount) {
                    function countFn() {
                        setCount(editor, me);
                        domUtils.un(editor.document, "click", arguments.callee);
                    }

                    domUtils.on(editor.document, "click", countFn);
                    editor.ui.getDom("wordcount").innerHTML = editor.getLang(
                        "wordCountTip"
                    );
                }
                editor.ui._scale();
                if (editor.options.scaleEnabled) {
                    if (editor.autoHeightEnabled) {
                        editor.disableAutoHeight();
                    }
                    me.enableScale();
                } else {
                    me.disableScale();
                }
                if (
                    !editor.options.elementPathEnabled &&
                    !editor.options.wordCount &&
                    !editor.options.scaleEnabled
                ) {
                    editor.ui.getDom("elementpath").style.display = "none";
                    editor.ui.getDom("wordcount").style.display = "none";
                    editor.ui.getDom("scale").style.display = "none";
                }

                if (!editor.selection.isFocus()) return;
                editor.fireEvent("selectionchange", false, true);
            });

            editor.addListener("mousedown", function (t, evt) {
                var el = evt.target || evt.srcElement;
                baidu.editor.ui.Popup.postHide(evt, el);
                baidu.editor.ui.ShortCutMenu.postHide(evt);
            });

            editor.addListener("delcells", function () {
                if (UE.ui["edittip"]) {
                    new UE.ui["edittip"](editor);
                }
                editor.getDialog("edittip").open();
            });

            var pastePop,
                isPaste = false,
                timer;
            editor.addListener("afterpaste", function () {
                if (editor.queryCommandState("pasteplain")) return;
                if (baidu.editor.ui.PastePicker) {
                    pastePop = new baidu.editor.ui.Popup({
                        content: new baidu.editor.ui.PastePicker({editor: editor}),
                        editor: editor,
                        className: "edui-wordpastepop"
                    });
                    pastePop.render();
                }
                isPaste = true;
            });

            editor.addListener("afterinserthtml", function () {
                clearTimeout(timer);
                timer = setTimeout(function () {
                    if (pastePop && (isPaste || editor.ui._isTransfer)) {
                        if (pastePop.isHidden()) {
                            var span = domUtils.createElement(editor.document, "span", {
                                    style: "line-height:0px;",
                                    innerHTML: "\ufeff"
                                }),
                                range = editor.selection.getRange();
                            range.insertNode(span);
                            var tmp = getDomNode(span, "firstChild", "previousSibling");
                            tmp &&
                            pastePop.showAnchor(tmp.nodeType == 3 ? tmp.parentNode : tmp);
                            domUtils.remove(span);
                        } else {
                            pastePop.show();
                        }
                        delete editor.ui._isTransfer;
                        isPaste = false;
                    }
                }, 200);
            });
            editor.addListener("contextmenu", function (t, evt) {
                baidu.editor.ui.Popup.postHide(evt);
            });
            editor.addListener("keydown", function (t, evt) {
                if (pastePop) pastePop.dispose(evt);
                var keyCode = evt.keyCode || evt.which;
                if (evt.altKey && keyCode == 90) {
                    UE.ui.buttons["fullscreen"].onclick();
                }
            });
            editor.addListener("wordcount", function (type) {
                setCount(this, me);
            });

            function setCount(editor, ui) {
                editor.setOpt({
                    wordCount: true,
                    maximumWords: 10000,
                    wordCountMsg:
                        editor.options.wordCountMsg || editor.getLang("wordCountMsg"),
                    wordOverFlowMsg:
                        editor.options.wordOverFlowMsg || editor.getLang("wordOverFlowMsg")
                });
                var opt = editor.options,
                    max = opt.maximumWords,
                    msg = opt.wordCountMsg,
                    errMsg = opt.wordOverFlowMsg,
                    countDom = ui.getDom("wordcount");
                if (!opt.wordCount) {
                    return;
                }
                var count = editor.getContentLength(true);
                if (count > max) {
                    countDom.innerHTML = errMsg;
                    editor.fireEvent("wordcountoverflow");
                } else {
                    countDom.innerHTML = msg
                        .replace("{#leave}", max - count)
                        .replace("{#count}", count);
                }
            }

            editor.addListener("selectionchange", function () {
                if (editor.options.elementPathEnabled) {
                    me[
                    (editor.queryCommandState("elementpath") == -1 ? "dis" : "en") +
                    "ableElementPath"
                        ]();
                }
                if (editor.options.scaleEnabled) {
                    me[
                    (editor.queryCommandState("scale") == -1 ? "dis" : "en") +
                    "ableScale"
                        ]();
                }
            });
            var popup = new baidu.editor.ui.Popup({
                editor: editor,
                content: "",
                className: "edui-bubble",
                _onEditButtonClick: function () {
                    this.hide();
                    editor.ui._dialogs.linkDialog.open();
                },
                _onImgEditButtonClick: function (name) {
                    this.hide();
                    editor.ui._dialogs[name] && editor.ui._dialogs[name].open();
                },
                _onImgSetFloat: function (value) {
                    this.hide();
                    editor.execCommand("imagefloat", value);
                },
                _setIframeAlign: function (value) {
                    var frame = popup.anchorEl;
                    var newFrame = frame.cloneNode(true);
                    switch (value) {
                        case -2:
                            newFrame.setAttribute("align", "");
                            break;
                        case -1:
                            newFrame.setAttribute("align", "left");
                            break;
                        case 1:
                            newFrame.setAttribute("align", "right");
                            break;
                    }
                    frame.parentNode.insertBefore(newFrame, frame);
                    domUtils.remove(frame);
                    popup.anchorEl = newFrame;
                    popup.showAnchor(popup.anchorEl);
                },
                _updateIframe: function () {
                    var frame = (editor._iframe = popup.anchorEl);
                    if (domUtils.hasClass(frame, "ueditor_baidumap")) {
                        editor.selection.getRange().selectNode(frame).select();
                        editor.ui._dialogs.mapDialog.open();
                        popup.hide();
                    } else {
                        editor.ui._dialogs.insertframeDialog.open();
                        popup.hide();
                    }
                },
                _onRemoveButtonClick: function (cmdName) {
                    editor.execCommand(cmdName);
                    this.hide();
                },
                queryAutoHide: function (el) {
                    if (el && el.ownerDocument == editor.document) {
                        if (
                            el.tagName.toLowerCase() == "img" ||
                            domUtils.findParentByTagName(el, "a", true)
                        ) {
                            return el !== popup.anchorEl;
                        }
                    }
                    return baidu.editor.ui.Popup.prototype.queryAutoHide.call(this, el);
                }
            });
            popup.render();
            if (editor.options.imagePopup) {
                editor.addListener("mouseover", function (t, evt) {
                    evt = evt || window.event;
                    var el = evt.target || evt.srcElement;
                    if (
                        editor.ui._dialogs.insertframeDialog &&
                        /iframe/gi.test(el.tagName)
                    ) {
                        var html = popup.formatHtml(
                            "<nobr>" +
                            '<span onclick=$$._setIframeAlign(-2) class="edui-clickable">' +
                            editor.getLang("default") +
                            '</span>&nbsp;&nbsp;<span onclick=$$._setIframeAlign(-1) class="edui-clickable">' +
                            editor.getLang("justifyleft") +
                            '</span>&nbsp;&nbsp;<span onclick=$$._setIframeAlign(1) class="edui-clickable">' +
                            editor.getLang("justifyright") +
                            "</span>&nbsp;&nbsp;" +
                            ' <span onclick="$$._updateIframe( this);" class="edui-clickable">' +
                            editor.getLang("modify") +
                            "</span></nobr>"
                        );
                        if (html) {
                            popup.getDom("content").innerHTML = html;
                            popup.anchorEl = el;
                            popup.showAnchor(popup.anchorEl);
                        } else {
                            popup.hide();
                        }
                    }
                });
                editor.addListener("selectionchange", function (t, causeByUi) {
                    if (!causeByUi) {
                        return;
                    }
                    var html = "",
                        str = "",
                        closedNode = editor.selection.getRange().getClosedNode(),
                        dialogs = editor.ui._dialogs;
                    // 图片选中处理
                    if (closedNode && closedNode.tagName === "IMG") {
                        var dialogName = "insertimageDialog";
                        if (
                            closedNode.className.indexOf("edui-faked-video") !== -1 ||
                            closedNode.className.indexOf("edui-upload-video") !== -1
                        ) {
                            dialogName = "insertvideoDialog";
                        }
                        if (
                            closedNode.className.indexOf("edui-faked-audio") !== -1 ||
                            closedNode.className.indexOf("edui-upload-audio") !== -1
                        ) {
                            dialogName = "insertaudioDialog";
                        }
                        if (closedNode.getAttribute("anchorname")) {
                            dialogName = "anchorDialog";
                            html = popup.formatHtml(
                                "<nobr>" +
                                '<span onclick=$$._onImgEditButtonClick("anchorDialog") class="edui-clickable">' +
                                editor.getLang("modify") +
                                "</span>&nbsp;&nbsp;" +
                                "<span onclick=$$._onRemoveButtonClick('anchor') class=\"edui-clickable\">" +
                                editor.getLang("delete") +
                                "</span></nobr>"
                            );
                        }
                        // if (img.getAttribute("data-word-image")) {
                        //   //todo 放到dialog去做查询
                        //   editor['data-word-image'] = [img.getAttribute("data-word-image")];
                        //   dialogName = "wordimageDialog";
                        // }
                        if (
                            domUtils.hasClass(closedNode, "uep-loading") ||
                            domUtils.hasClass(closedNode, "uep-loading-error")
                        ) {
                            dialogName = "";
                        }
                        if (!dialogs[dialogName]) {
                            return;
                        }

                        var actions = [];
                        if (closedNode.getAttribute("data-word-image")) {
                            actions.push("<span onclick=\"$$._onImgEditButtonClick('wordimageDialog');\" class='edui-clickable edui-popup-action-item'>" +
                                editor.getLang("save") +
                                "</span>");
                        } else {
                            // actions.push("<span onclick=\"$$._onImgEditButtonClick('" + dialogName + '\');" class="edui-clickable edui-popup-action-item">' +
                            //     editor.getLang("modify") +
                            //     "</span>");
                        }

                        if (actions.length > 0) {
                            // wrap with <nobr> </nobr>
                            actions.unshift('<nobr>');
                            actions.push('</nobr>');
                        }

                        !html && (html = popup.formatHtml(actions.join("")));
                    }
                    // 链接选中处理
                    if (editor.ui._dialogs.linkDialog) {
                        var link = editor.queryCommandValue("link");
                        var url;
                        if (
                            link &&
                            (url = link.getAttribute("_href") || link.getAttribute("href", 2))
                        ) {
                            var txt = url;
                            if (url.length > 30) {
                                txt = url.substring(0, 20) + "...";
                            }
                            if (html) {
                                html += '<div style="height:5px;"></div>';
                            }
                            html += popup.formatHtml(
                                "<nobr>" +
                                editor.getLang("anchorMsg") +
                                ': <a target="_blank" href="' +
                                url +
                                '" title="' +
                                url +
                                '" >' +
                                txt +
                                "</a>" +
                                ' <span class="edui-clickable" onclick="$$._onEditButtonClick();">' +
                                editor.getLang("modify") +
                                "</span>" +
                                ' <span class="edui-clickable" onclick="$$._onRemoveButtonClick(\'unlink\');"> ' +
                                editor.getLang("clear") +
                                "</span></nobr>"
                            );
                            popup.showAnchor(link);
                        }
                    }

                    if (html) {
                        popup.getDom("content").innerHTML = html;
                        popup.anchorEl = closedNode || link;
                        popup.showAnchor(popup.anchorEl);
                    } else {
                        popup.hide();
                    }
                });
            }
        },
        _initToolbars: function () {
            var editor = this.editor;
            var toolbars = this.toolbars || [];
            if (toolbars[0]) {
                toolbars[0].unshift(
                    'message'
                );
            }
            var toolbarUis = [];
            var extraUIs = [];
            for (var i = 0; i < toolbars.length; i++) {
                var toolbar = toolbars[i];
                var toolbarUi = new baidu.editor.ui.Toolbar({
                    theme: editor.options.theme
                });
                for (var j = 0; j < toolbar.length; j++) {
                    var toolbarItem = toolbar[j];
                    var toolbarItemUi = null;
                    if (typeof toolbarItem == "string") {
                        toolbarItem = toolbarItem.toLowerCase();
                        if (toolbarItem === "|") {
                            toolbarItem = "Separator";
                        }
                        if (toolbarItem === "||") {
                            toolbarItem = "Breakline";
                        }
                        var ui = baidu.editor.ui[toolbarItem];
                        if (ui) {
                            if (utils.isFunction(ui)) {
                                toolbarItemUi = new baidu.editor.ui[toolbarItem](editor);
                            } else {
                                if (ui.id && ui.id !== editor.key) {
                                    continue;
                                }
                                var itemUI = ui.execFn.call(editor, editor, toolbarItem);
                                if (itemUI) {
                                    if (ui.index === undefined) {
                                        toolbarUi.add(itemUI);
                                        continue;
                                    } else {
                                        extraUIs.push({
                                            index: ui.index,
                                            itemUI: itemUI
                                        });
                                    }
                                }
                            }
                        }
                        //fullscreen这里单独处理一下,放到首行去
                        if (toolbarItem === "fullscreen") {
                            if (toolbarUis && toolbarUis[0]) {
                                toolbarUis[0].items.splice(0, 0, toolbarItemUi);
                            } else {
                                toolbarItemUi && toolbarUi.items.splice(0, 0, toolbarItemUi);
                            }
                            continue;
                        }
                    } else {
                        toolbarItemUi = toolbarItem;
                    }
                    if (toolbarItemUi && toolbarItemUi.id) {
                        toolbarUi.add(toolbarItemUi);
                    }
                }
                toolbarUis[i] = toolbarUi;
            }

            //接受外部定制的UI

            utils.each(extraUIs, function (obj) {
                toolbarUi.add(obj.itemUI, obj.index);
            });
            this.toolbars = toolbarUis;
        },
        getHtmlTpl: function () {
            return (
                '<div id="##" class="%%">' +
                '<div id="##_toolbarbox" class="%%-toolbarbox">' +
                (this.toolbars.length
                    ? '<div id="##_toolbarboxouter" class="%%-toolbarboxouter"><div class="%%-toolbarboxinner">' +
                    this.renderToolbarBoxHtml() +
                    "</div></div>"
                    : "") +
                '<div id="##_toolbarmsg" class="%%-toolbarmsg" style="display:none;">' +
                '<div id = "##_upload_dialog" class="%%-toolbarmsg-upload" onclick="$$.showWordImageDialog();">' +
                this.editor.getLang("clickToUpload") +
                "</div>" +
                '<div class="%%-toolbarmsg-close" onclick="$$.hideToolbarMsg();">x</div>' +
                '<div id="##_toolbarmsg_label" class="%%-toolbarmsg-label"></div>' +
                '<div style="height:0;overflow:hidden;clear:both;"></div>' +
                "</div>" +
                '<div id="##_message_holder" class="%%-messageholder"></div>' +
                "</div>" +
                '<div id="##_iframeholder" class="%%-iframeholder">' +
                "</div>" +
                //modify wdcount by matao
                '<div id="##_bottombar" class="%%-bottomContainer"><table><tr>' +
                '<td id="##_elementpath" class="%%-bottombar"></td>' +
                '<td id="##_wordcount" class="%%-wordcount"></td>' +
                '<td id="##_scale" class="%%-scale"><div class="%%-icon"></div></td>' +
                "</tr></table></div>" +
                '<div id="##_scalelayer"></div>' +
                "</div>"
            );
        },
        showWordImageDialog: function () {
            this._dialogs["wordimageDialog"].open();
        },
        renderToolbarBoxHtml: function () {
            var buff = [];
            for (var i = 0; i < this.toolbars.length; i++) {
                buff.push(this.toolbars[i].renderHtml());
            }
            return buff.join("");
        },
        setFullScreen: function (fullscreen) {
            var editor = this.editor,
                container = editor.container.parentNode.parentNode;
            if (this._fullscreen != fullscreen) {
                this._fullscreen = fullscreen;
                this.editor.fireEvent("beforefullscreenchange", fullscreen);
                if (baidu.editor.browser.gecko) {
                    var bk = editor.selection.getRange().createBookmark();
                }
                if (fullscreen) {

                    // add https://gitee.com/modstart-lib/ueditor-plus/issues/I85R7X
                    this._bakEditorContaninerWidth = editor.iframe.parentNode.style.width;

                    while (container.tagName !== "BODY") {
                        var position = baidu.editor.dom.domUtils.getComputedStyle(
                            container,
                            "position"
                        );
                        nodeStack.push(position);
                        container.style.position = "static";
                        container = container.parentNode;
                    }
                    this._bakHtmlOverflow = document.documentElement.style.overflow;
                    this._bakBodyOverflow = document.body.style.overflow;
                    this._bakAutoHeight = this.editor.autoHeightEnabled;
                    this._bakScrollTop = Math.max(
                        document.documentElement.scrollTop,
                        document.body.scrollTop
                    );

                    // delete https://gitee.com/modstart-lib/ueditor-plus/issues/I85R7X
                    // this._bakEditorContaninerWidth = editor.iframe.parentNode.offsetWidth;

                    if (this._bakAutoHeight) {
                        //当全屏时不能执行自动长高
                        editor.autoHeightEnabled = false;
                        this.editor.disableAutoHeight();
                    }

                    document.documentElement.style.overflow = "hidden";
                    //修复,滚动条不收起的问题

                    window.scrollTo(0, window.scrollY);
                    this._bakCssText = this.getDom().style.cssText;
                    this._bakCssText1 = this.getDom("iframeholder").style.cssText;
                    editor.iframe.parentNode.style.width = "";
                    this._updateFullScreen();
                } else {
                    while (container.tagName !== "BODY") {
                        container.style.position = nodeStack.shift();
                        container = container.parentNode;
                    }
                    this.getDom().style.cssText = this._bakCssText;
                    this.getDom("iframeholder").style.cssText = this._bakCssText1;
                    if (this._bakAutoHeight) {
                        editor.autoHeightEnabled = true;
                        this.editor.enableAutoHeight();
                    }

                    document.documentElement.style.overflow = this._bakHtmlOverflow;
                    document.body.style.overflow = this._bakBodyOverflow;
                    // modify https://gitee.com/modstart-lib/ueditor-plus/issues/I85R7X
                    editor.iframe.parentNode.style.width = this._bakEditorContaninerWidth
                    // editor.iframe.parentNode.style.width = this._bakEditorContaninerWidth + "px";
                    window.scrollTo(0, this._bakScrollTop);
                }
                if (browser.gecko && editor.body.contentEditable === "true") {
                    var input = document.createElement("input");
                    document.body.appendChild(input);
                    editor.body.contentEditable = false;
                    setTimeout(function () {
                        input.focus();
                        setTimeout(function () {
                            editor.body.contentEditable = true;
                            editor.fireEvent("fullscreenchanged", fullscreen);
                            editor.selection.getRange().moveToBookmark(bk).select(true);
                            baidu.editor.dom.domUtils.remove(input);
                            fullscreen && window.scroll(0, 0);
                        }, 0);
                    }, 0);
                }

                if (editor.body.contentEditable === "true") {
                    this.editor.fireEvent("fullscreenchanged", fullscreen);
                    this.triggerLayout();
                }
            }
        },
        _updateFullScreen: function () {
            if (this._fullscreen) {
                var vpRect = uiUtils.getViewportRect();
                this.getDom().style.cssText =
                    "border:0;position:absolute;left:0;top:var(--ueditor-top-offset," +
                    (this.editor.options.topOffset || 0) +
                    "px);width:" +
                    vpRect.width +
                    "px;height:" +
                    vpRect.height +
                    "px;z-index:" +
                    (this.getDom().style.zIndex * 1 + 100);
                uiUtils.setViewportOffset(this.getDom(), {
                    left: 0,
                    // top: this.editor.options.topOffset || 0
                });
                this.editor.setHeight(
                    vpRect.height -
                    this.getDom("toolbarbox").offsetHeight -
                    this.getDom("bottombar").offsetHeight -
                    (this.editor.options.topOffset || 0),
                    true
                );
                //不手动调一下,会导致全屏失效
                if (browser.gecko) {
                    try {
                        window.onresize();
                    } catch (e) {
                    }
                }
            }
        },
        _updateElementPath: function () {
            var bottom = this.getDom("elementpath"),
                list;
            if (
                this.elementPathEnabled &&
                (list = this.editor.queryCommandValue("elementpath"))
            ) {
                var buff = [];
                for (var i = 0, ci; (ci = list[i]); i++) {
                    buff[i] = this.formatHtml(
                        '<span unselectable="on" onclick="$$.editor.execCommand(&quot;elementpath&quot;, &quot;' +
                        i +
                        '&quot;);">' +
                        ci +
                        "</span>"
                    );
                }
                bottom.innerHTML =
                    '<div class="edui-editor-breadcrumb" onmousedown="return false;">' +
                    this.editor.getLang("elementPathTip") +
                    ": " +
                    buff.join(" &gt; ") +
                    "</div>";
            } else {
                bottom.style.display = "none";
            }
        },
        disableElementPath: function () {
            var bottom = this.getDom("elementpath");
            bottom.innerHTML = "";
            bottom.style.display = "none";
            this.elementPathEnabled = false;
        },
        enableElementPath: function () {
            var bottom = this.getDom("elementpath");
            bottom.style.display = "";
            this.elementPathEnabled = true;
            this._updateElementPath();
        },
        _scale: function () {
            var doc = document,
                editor = this.editor,
                editorHolder = editor.container,
                editorDocument = editor.document,
                toolbarBox = this.getDom("toolbarbox"),
                bottombar = this.getDom("bottombar"),
                scale = this.getDom("scale"),
                scalelayer = this.getDom("scalelayer");

            var isMouseMove = false,
                position = null,
                minEditorHeight = 0,
                minEditorWidth = editor.options.minFrameWidth,
                pageX = 0,
                pageY = 0,
                scaleWidth = 0,
                scaleHeight = 0;

            function down() {
                position = domUtils.getXY(editorHolder);

                if (!minEditorHeight) {
                    minEditorHeight =
                        editor.options.minFrameHeight +
                        toolbarBox.offsetHeight +
                        bottombar.offsetHeight;
                }

                scalelayer.style.cssText =
                    "position:absolute;left:0;display:;top:0;background-color:#41ABFF;opacity:0.4;filter: Alpha(opacity=40);width:" +
                    editorHolder.offsetWidth +
                    "px;height:" +
                    editorHolder.offsetHeight +
                    "px;z-index:" +
                    (editor.options.zIndex + 1);

                domUtils.on(doc, "mousemove", move);
                domUtils.on(editorDocument, "mouseup", up);
                domUtils.on(doc, "mouseup", up);
            }

            var me = this;
            //by xuheng 全屏时关掉缩放
            this.editor.addListener("fullscreenchanged", function (e, fullScreen) {
                if (fullScreen) {
                    me.disableScale();
                } else {
                    if (me.editor.options.scaleEnabled) {
                        me.enableScale();
                        var tmpNode = me.editor.document.createElement("span");
                        me.editor.body.appendChild(tmpNode);
                        me.editor.body.style.height =
                            Math.max(
                                domUtils.getXY(tmpNode).y,
                                me.editor.iframe.offsetHeight - 20
                            ) + "px";
                        domUtils.remove(tmpNode);
                    }
                }
            });

            function move(event) {
                clearSelection();
                var e = event || window.event;
                pageX = e.pageX || doc.documentElement.scrollLeft + e.clientX;
                pageY = e.pageY || doc.documentElement.scrollTop + e.clientY;
                scaleWidth = pageX - position.x;
                scaleHeight = pageY - position.y;

                if (scaleWidth >= minEditorWidth) {
                    isMouseMove = true;
                    scalelayer.style.width = scaleWidth + "px";
                }
                if (scaleHeight >= minEditorHeight) {
                    isMouseMove = true;
                    scalelayer.style.height = scaleHeight + "px";
                }
            }

            function up() {
                if (isMouseMove) {
                    isMouseMove = false;
                    editor.ui._actualFrameWidth = scalelayer.offsetWidth - 2;
                    editorHolder.style.width = editor.ui._actualFrameWidth + "px";

                    editor.setHeight(
                        scalelayer.offsetHeight -
                        bottombar.offsetHeight -
                        toolbarBox.offsetHeight -
                        2,
                        true
                    );
                }
                if (scalelayer) {
                    scalelayer.style.display = "none";
                }
                clearSelection();
                domUtils.un(doc, "mousemove", move);
                domUtils.un(editorDocument, "mouseup", up);
                domUtils.un(doc, "mouseup", up);
            }

            function clearSelection() {
                if (browser.ie) doc.selection.clear();
                else window.getSelection().removeAllRanges();
            }

            this.enableScale = function () {
                //trace:2868
                if (editor.queryCommandState("source") == 1) return;
                scale.style.display = "";
                this.scaleEnabled = true;
                domUtils.on(scale, "mousedown", down);
            };
            this.disableScale = function () {
                scale.style.display = "none";
                this.scaleEnabled = false;
                domUtils.un(scale, "mousedown", down);
            };
        },
        isFullScreen: function () {
            return this._fullscreen;
        },
        postRender: function () {
            UIBase.prototype.postRender.call(this);
            for (var i = 0; i < this.toolbars.length; i++) {
                this.toolbars[i].postRender();
            }
            var me = this;
            var timerId,
                domUtils = baidu.editor.dom.domUtils,
                updateFullScreenTime = function () {
                    clearTimeout(timerId);
                    timerId = setTimeout(function () {
                        me._updateFullScreen();
                    });
                };
            domUtils.on(window, "resize", updateFullScreenTime);

            me.addListener("destroy", function () {
                domUtils.un(window, "resize", updateFullScreenTime);
                clearTimeout(timerId);
            });
        },
        showToolbarMsg: function (msg, flag) {
            this.getDom("toolbarmsg_label").innerHTML = msg;
            this.getDom("toolbarmsg").style.display = "";
            //
            if (!flag) {
                var w = this.getDom("upload_dialog");
                w.style.display = "none";
            }
        },
        hideToolbarMsg: function () {
            this.getDom("toolbarmsg").style.display = "none";
        },
        mapUrl: function (url) {
            return url
                ? url.replace("~/", this.editor.options.UEDITOR_CORS_URL || "")
                : "";
        },
        triggerLayout: function () {
            var dom = this.getDom();
            if (dom.style.zoom == "1") {
                dom.style.zoom = "100%";
            } else {
                dom.style.zoom = "1";
            }
        }
    };
    utils.inherits(EditorUI, baidu.editor.ui.UIBase);

    var instances = {};

    UE.ui.Editor = function (options) {
        var editor = new UE.Editor(options);
        editor.options.editor = editor;
        utils.loadFile(document, {
            href:
                editor.options.themePath + editor.options.theme + "/css/ueditor.css?98125a73",
            tag: "link",
            type: "text/css",
            rel: "stylesheet"
        });

        var oldRender = editor.render;
        editor.render = function (holder) {
            if (holder.constructor === String) {
                editor.key = holder;
                instances[holder] = editor;
            }
            utils.domReady(function () {
                editor.langIsReady
                    ? renderUI()
                    : editor.addListener("langReady", renderUI);

                function renderUI() {
                    editor.setOpt({
                        labelMap: editor.options.labelMap || editor.getLang("labelMap")
                    });
                    new EditorUI(editor.options);
                    if (holder) {
                        if (holder.constructor === String) {
                            holder = document.getElementById(holder);
                        }
                        holder &&
                        holder.getAttribute("name") &&
                        (editor.options.textarea = holder.getAttribute("name"));
                        if (holder && /script|textarea/gi.test(holder.tagName)) {
                            var newDiv = document.createElement("div");
                            holder.parentNode.insertBefore(newDiv, holder);
                            var cont = holder.value || holder.innerHTML;
                            editor.options.initialContent = /^[\t\r\n ]*$/.test(cont)
                                ? editor.options.initialContent
                                : cont
                                    .replace(/>[\n\r\t]+([ ]{4})+/g, ">")
                                    .replace(/[\n\r\t]+([ ]{4})+</g, "<")
                                    .replace(/>[\n\r\t]+</g, "><");
                            holder.className && (newDiv.className = holder.className);
                            holder.style.cssText &&
                            (newDiv.style.cssText = holder.style.cssText);
                            if (/textarea/i.test(holder.tagName)) {
                                editor.textarea = holder;
                                editor.textarea.style.display = "none";
                            } else {
                                holder.parentNode.removeChild(holder);
                            }
                            if (holder.id) {
                                newDiv.id = holder.id;
                                domUtils.removeAttributes(holder, "id");
                            }
                            holder = newDiv;
                            holder.innerHTML = "";
                        }
                    }
                    domUtils.addClass(holder, "edui-" + editor.options.theme);
                    editor.ui.render(holder);
                    var opt = editor.options;
                    //给实例添加一个编辑器的容器引用
                    editor.container = editor.ui.getDom();
                    var parents = domUtils.findParents(holder, true);
                    var displays = [];
                    for (var i = 0, ci; (ci = parents[i]); i++) {
                        displays[i] = ci.style.display;
                        ci.style.display = "block";
                    }
                    if (opt.initialFrameWidth) {
                        opt.minFrameWidth = opt.initialFrameWidth;
                    } else {
                        opt.minFrameWidth = opt.initialFrameWidth = holder.offsetWidth;
                        var styleWidth = holder.style.width;
                        if (/%$/.test(styleWidth)) {
                            opt.initialFrameWidth = styleWidth;
                        }
                    }
                    if (opt.initialFrameHeight) {
                        opt.minFrameHeight = opt.initialFrameHeight;
                    } else {
                        opt.initialFrameHeight = opt.minFrameHeight = holder.offsetHeight;
                    }
                    for (var i = 0, ci; (ci = parents[i]); i++) {
                        ci.style.display = displays[i];
                    }
                    //编辑器最外容器设置了高度,会导致,编辑器不占位
                    //todo 先去掉,没有找到原因
                    if (holder.style.height) {
                        holder.style.height = "";
                    }
                    editor.container.style.width =
                        opt.initialFrameWidth +
                        (/%$/.test(opt.initialFrameWidth) ? "" : "px");
                    editor.container.style.zIndex = opt.zIndex;
                    oldRender.call(editor, editor.ui.getDom("iframeholder"));
                    editor.fireEvent("afteruiready");
                }
            });
        };
        return editor;
    };

    /**
     * @file
     * @name UE
     * @short UE
     * @desc UEditor的顶部命名空间
     */
    /**
     * @name getEditor
     * @since 1.2.4+
     * @grammar UE.getEditor(id,[opt])  =>  Editor实例
     * @desc 提供一个全局的方法得到编辑器实例
     *
     * * ''id''  放置编辑器的容器id, 如果容器下的编辑器已经存在,就直接返回
     * * ''opt'' 编辑器的可选参数
     * @example
     *  UE.getEditor('containerId',{onready:function(){//创建一个编辑器实例
     *      this.setContent('hello')
     *  }});
     *  UE.getEditor('containerId'); //返回刚创建的实例
     *
     */
    UE.getEditor = function (id, opt) {
        var editor = instances[id];
        if (!editor) {
            editor = instances[id] = new UE.ui.Editor(opt);
            editor.render(id);
        }
        return editor;
    };

    UE.delEditor = function (id) {
        var editor;
        if ((editor = instances[id])) {
            editor.key && editor.destroy();
            delete instances[id];
        }
    };

    UE.registerUI = function (uiName, fn, index, editorId) {
        utils.each(uiName.split(/\s+/), function (name) {
            baidu.editor.ui[name] = {
                id: editorId,
                execFn: fn,
                index: index
            };
        });
    };
})();


// adapter/message.js
UE.registerUI("message", function (editor) {
    var editorui = baidu.editor.ui;
    var Message = editorui.Message;
    var holder;
    var _messageItems = [];
    var me = editor;

    me.setOpt("enableMessageShow", true);
    if (me.getOpt("enableMessageShow") === false) {
        return;
    }

    me.addListener("ready", function () {
        holder = document.getElementById(me.ui.id + "_message_holder");
        updateHolderPos();
        setTimeout(function () {
            updateHolderPos();
        }, 500);
    });

    me.addListener("showmessage", function (type, opt) {
        opt = utils.isString(opt)
            ? {
                content: opt
            }
            : opt;
        var message = new Message({
                timeout: opt.timeout,
                type: opt.type,
                content: opt.content,
                keepshow: opt.keepshow,
                editor: me
            }),
            mid = opt.id || "msg_" + (+new Date()).toString(36);
        message.render(holder);
        _messageItems[mid] = message;
        message.reset(opt);
        updateHolderPos();
        return mid;
    });

    me.addListener("updatemessage", function (type, id, opt) {
        opt = utils.isString(opt)
            ? {
                content: opt
            }
            : opt;
        var message = _messageItems[id];
        message.render(holder);
        message && message.reset(opt);
    });

    me.addListener("hidemessage", function (type, id) {
        var message = _messageItems[id];
        message && message.hide();
    });

    function updateHolderPos() {
        if (!holder || !me.ui) return;
        var toolbarbox = me.ui.getDom("toolbarbox");
        if (toolbarbox) {
            holder.style.top = toolbarbox.offsetHeight + 3 + "px";
        }
        holder.style.zIndex =
            Math.max(me.options.zIndex, me.iframe.style.zIndex) + 1;
    }
});



})();