/*!


 * UEditor Mini版本


 * version: 1.2.2


 * build: Wed Mar 19 2014 17:14:14 GMT+0800 (中国标准时间)


 */





(function($){





UMEDITOR_CONFIG = window.UMEDITOR_CONFIG || {};





window.UM = {


    plugins : {},





    commands : {},





    I18N : {},





    version : "1.2.2"


};





var dom = UM.dom = {};


/**


 * 浏览器判断模块


 * @file


 * @module UE.browser


 * @since 1.2.6.1


 */





/**


 * 提供浏览器检测的模块


 * @unfile


 * @module UE.browser


 */


var browser = UM.browser = function(){


    var agent = navigator.userAgent.toLowerCase(),


        opera = window.opera,


        browser = {


            /**


             * @property {boolean} ie 检测当前浏览器是否为IE


             * @example


             * ```javascript


             * if ( UE.browser.ie ) {


         *     console.log( '当前浏览器是IE' );


         * }


             * ```


             */


            ie		:  /(msie\s|trident.*rv:)([\w.]+)/.test(agent),





            /**


             * @property {boolean} opera 检测当前浏览器是否为Opera


             * @example


             * ```javascript


             * if ( UE.browser.opera ) {


         *     console.log( '当前浏览器是Opera' );


         * }


             * ```


             */


            opera	: ( !!opera && opera.version ),





            /**


             * @property {boolean} webkit 检测当前浏览器是否是webkit内核的浏览器


             * @example


             * ```javascript


             * if ( UE.browser.webkit ) {


         *     console.log( '当前浏览器是webkit内核浏览器' );


         * }


             * ```


             */


            webkit	: ( agent.indexOf( ' applewebkit/' ) > -1 ),





            /**


             * @property {boolean} mac 检测当前浏览器是否是运行在mac平台下


             * @example


             * ```javascript


             * if ( UE.browser.mac ) {


         *     console.log( '当前浏览器运行在mac平台下' );


         * }


             * ```


             */


            mac	: ( agent.indexOf( 'macintosh' ) > -1 ),





            /**


             * @property {boolean} quirks 检测当前浏览器是否处于“怪异模式”下


             * @example


             * ```javascript


             * if ( UE.browser.quirks ) {


         *     console.log( '当前浏览器运行处于“怪异模式”' );


         * }


             * ```


             */


            quirks : ( document.compatMode == 'BackCompat' )


        };





    /**


     * @property {boolean} gecko 检测当前浏览器内核是否是gecko内核


     * @example


     * ```javascript


     * if ( UE.browser.gecko ) {


    *     console.log( '当前浏览器内核是gecko内核' );


    * }


     * ```


     */


    browser.gecko =( navigator.product == 'Gecko' && !browser.webkit && !browser.opera && !browser.ie);





    var version = 0;





    // Internet Explorer 6.0+


    if ( browser.ie ){








        var v1 =  agent.match(/(?:msie\s([\w.]+))/);


        var v2 = agent.match(/(?:trident.*rv:([\w.]+))/);


        if(v1 && v2 && v1[1] && v2[1]){


            version = Math.max(v1[1]*1,v2[1]*1);


        }else if(v1 && v1[1]){


            version = v1[1]*1;


        }else if(v2 && v2[1]){


            version = v2[1]*1;


        }else{


            version = 0;


        }





        browser.ie11Compat = document.documentMode == 11;


        /**


         * @property { boolean } ie9Compat 检测浏览器模式是否为 IE9 兼容模式


         * @warning 如果浏览器不是IE， 则该值为undefined


         * @example


         * ```javascript


         * if ( UE.browser.ie9Compat ) {


         *     console.log( '当前浏览器运行在IE9兼容模式下' );


         * }


         * ```


         */


        browser.ie9Compat = document.documentMode == 9;





        /**


         * @property { boolean } ie8 检测浏览器是否是IE8浏览器


         * @warning 如果浏览器不是IE， 则该值为undefined


         * @example


         * ```javascript


         * if ( UE.browser.ie8 ) {


         *     console.log( '当前浏览器是IE8浏览器' );


         * }


         * ```


         */


        browser.ie8 = !!document.documentMode;





        /**


         * @property { boolean } ie8Compat 检测浏览器模式是否为 IE8 兼容模式


         * @warning 如果浏览器不是IE， 则该值为undefined


         * @example


         * ```javascript


         * if ( UE.browser.ie8Compat ) {


         *     console.log( '当前浏览器运行在IE8兼容模式下' );


         * }


         * ```


         */


        browser.ie8Compat = document.documentMode == 8;





        /**


         * @property { boolean } ie7Compat 检测浏览器模式是否为 IE7 兼容模式


         * @warning 如果浏览器不是IE， 则该值为undefined


         * @example


         * ```javascript


         * if ( UE.browser.ie7Compat ) {


         *     console.log( '当前浏览器运行在IE7兼容模式下' );


         * }


         * ```


         */


        browser.ie7Compat = ( ( version == 7 && !document.documentMode )


            || document.documentMode == 7 );





        /**


         * @property { boolean } ie6Compat 检测浏览器模式是否为 IE6 模式 或者怪异模式


         * @warning 如果浏览器不是IE， 则该值为undefined


         * @example


         * ```javascript


         * if ( UE.browser.ie6Compat ) {


         *     console.log( '当前浏览器运行在IE6模式或者怪异模式下' );


         * }


         * ```


         */


        browser.ie6Compat = ( version < 7 || browser.quirks );





        browser.ie9above = version > 8;





        browser.ie9below = version < 9;





    }





    // 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;


/**


 * @file


 * @name UM.Utils


 * @short Utils


 * @desc UEditor封装使用的静态工具函数


 * @import editor.js


 */


var utils = UM.utils = {


    /**


     * 遍历数组，对象，nodeList


     * @name each


     * @grammar UM.utils.each(obj,iterator,[context])


     * @since 1.2.4+


     * @desc


     * * obj 要遍历的对象


     * * iterator 遍历的方法,方法的第一个是遍历的值，第二个是索引，第三个是obj


     * * context  iterator的上下文


     * @example


     * UM.utils.each([1,2],function(v,i){


     *     console.log(v)//值


     *     console.log(i)//索引


     * })


     * UM.utils.each(document.getElementsByTagName('*'),function(n){


     *     console.log(n.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;


                }


            }


        }


    },





    makeInstance:function (obj) {


        var noop = new Function();


        noop.prototype = obj;


        obj = new noop;


        noop.prototype = null;


        return obj;


    },


    /**


     * 将source对象中的属性扩展到target对象上


     * @name extend


     * @grammar UM.utils.extend(target,source)  => Object  //覆盖扩展


     * @grammar UM.utils.extend(target,source,true)  ==> Object  //保留扩展


     */


    extend:function (t, s, b) {


        if (s) {


            for (var k in s) {


                if (!b || !t.hasOwnProperty(k)) {


                    t[k] = s[k];


                }


            }


        }


        return t;


    },


    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


     * @name inherits


     * @grammar UM.utils.inherits(subClass,superClass) => subClass


     * @example


     * function SuperClass(){


     *     this.name = "小李";


     * }


     * SuperClass.prototype = {


     *     hello:function(str){


     *         console.log(this.name + str);


     *     }


     * }


     * function SubClass(){


     *     this.name = "小张";


     * }


     * UM.utils.inherits(SubClass,SuperClass);


     * var sub = new SubClass();


     * 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上下文，也就是this


     * @name bind


     * @grammar UM.utils.bind(fn,context)  =>  fn


     */


    bind:function (fn, context) {


        return function () {


            return fn.apply(context, arguments);


        };


    },





    /**


     * 创建延迟delay执行的函数fn


     * @name defer


     * @grammar UM.utils.defer(fn,delay)  =>fn   //延迟delay毫秒执行fn，返回fn


     * @grammar UM.utils.defer(fn,delay,exclusion)  =>fn   //延迟delay毫秒执行fn，若exclusion为真，则互斥执行fn


     * @example


     * function test(){


     *     console.log("延迟输出！");


     * }


     * //非互斥延迟执行


     * var testDefer = UM.utils.defer(test,1000);


     * testDefer();   =>  "延迟输出！";


     * testDefer();   =>  "延迟输出！";


     * //互斥延迟执行


     * var testDefer1 = UM.utils.defer(test,1000,true);


     * testDefer1();   =>  //本次不执行


     * testDefer1();   =>  "延迟输出！";


     */


    defer:function (fn, delay, exclusion) {


        var timerID;


        return function () {


            if (exclusion) {


                clearTimeout(timerID);


            }


            timerID = setTimeout(fn, delay);


        };


    },





    /**


     * 查找元素item在数组array中的索引, 若找不到返回-1


     * @name indexOf


     * @grammar UM.utils.indexOf(array,item)  => index|-1  //默认从数组开头部开始搜索


     * @grammar UM.utils.indexOf(array,item,start)  => index|-1  //start指定开始查找的位置


     */


    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


     * @name removeItem


     * @grammar UM.utils.removeItem(array,item)


     */


    removeItem:function (array, item) {


        for (var i = 0, l = array.length; i < l; i++) {


            if (array[i] === item) {


                array.splice(i, 1);


                i--;


            }


        }


    },





    /**


     * 删除字符串str的首尾空格


     * @name trim


     * @grammar UM.utils.trim(str) => String


     */


    trim:function (str) {


        return str.replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, '');


    },





    /**


     * 将字符串list(以','分隔)或者数组list转成哈希对象


     * @name listToMap


     * @grammar UM.utils.listToMap(list)  => Object  //Object形如{test:1,br:1,textarea:1}


     */


    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符号转义,默认将转义''&<">''四个字符，可自定义reg来确定需要转义的字符


     * @name unhtml


     * @grammar UM.utils.unhtml(str);  => String


     * @grammar UM.utils.unhtml(str,reg)  => String


     * @example


     * var html = '<body>You say:"你好！Baidu & UEditor!"</body>';


     * UM.utils.unhtml(html);   ==>  &lt;body&gt;You say:&quot;你好！Baidu &amp; UEditor!&quot;&lt;/body&gt;


     * UM.utils.unhtml(html,/[<>]/g)  ==>  &lt;body&gt;You say:"你好！Baidu & UEditor!"&lt;/body&gt;


     */


    unhtml:function (str, reg) {


        return str ? str.replace(reg || /[&<">'](?:(amp|lt|quot|gt|#39|nbsp);)?/g, function (a, b) {


            if (b) {


                return a;


            } else {


                return {


                    '<':'&lt;',


                    '&':'&amp;',


                    '"':'&quot;',


                    '>':'&gt;',


                    "'":'&#39;'


                }[a]


            }





        }) : '';


    },


    /**


     * 将str中的转义字符还原成html字符


     * @name html


     * @grammar UM.utils.html(str)  => String   //详细参见<code><a href = '#unhtml'>unhtml</a></code>


     */


    html:function (str) {


        return str ? str.replace(/&((g|l|quo)t|amp|#39);/g, function (m) {


            return {


                '&lt;':'<',


                '&amp;':'&',


                '&quot;':'"',


                '&gt;':'>',


                '&#39;':"'"


            }[m]


        }) : '';


    },


    /**


     * 将css样式转换为驼峰的形式。如font-size => fontSize


     * @name cssStyleToDomStyle


     * @grammar UM.utils.cssStyleToDomStyle(cssName)  => String


     */


    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中，并依据obj来设置属性，加载成功后执行回调函数fn


     * @name loadFile


     * @grammar UM.utils.loadFile(doc,obj)


     * @grammar UM.utils.loadFile(doc,obj,fn)


     * @example


     * //指定加载到当前document中一个script文件，加载成功后执行function


     * 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 umeditor.config.js ')


            };


            doc.getElementsByTagName("head")[0].appendChild(element);


        }


    }(),


    /**


     * 判断obj对象是否为空


     * @name isEmptyObject


     * @grammar UM.utils.isEmptyObject(obj)  => true|false


     * @example


     * UM.utils.isEmptyObject({}) ==>true


     * UM.utils.isEmptyObject([]) ==>true


     * UM.utils.isEmptyObject("") ==>true


     */


    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;


    },





    /**


     * 统一将颜色值使用16进制形式表示


     * @name fixColor


     * @grammar UM.utils.fixColor(name,value) => value


     * @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;


    },





    /**


     * 深度克隆对象，从source到target


     * @name clone


     * @grammar UM.utils.clone(source) => anthorObj 新的对象是完整的source的副本


     * @grammar UM.utils.clone(source,target) => target包含了source的所有内容，重名会覆盖


     */


    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


     * @name transUnitToPx


     * @grammar UM.utils.transUnitToPx('20pt') => '27px'


     * @grammar UM.utils.transUnitToPx('0pt') => '0'


     */


    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' : '');


    },


    /**


     * 动态添加css样式


     * @name cssRule


     * @grammar UM.utils.cssRule('添加的样式的节点名称',['样式'，'放到哪个document上'])


     * @grammar UM.utils.cssRule('body','body{background:#ccc}') => null  //给body添加背景颜色


     * @grammar UM.utils.cssRule('body') =>样式的字符串  //取得key值为body的样式的内容,如果没有找到key值先关的样式将返回空，例如刚才那个背景颜色，将返回 body{background:#ccc}


     * @grammar UM.utils.cssRule('body','') =>null //清空给定的key值的背景颜色


     */


    cssRule:browser.ie && browser.version != 11 ? function (key, style, doc) {


        var indexList, index;


        doc = doc || document;


        if (doc.indexList) {


            indexList = doc.indexList;


        } else {


            indexList = doc.indexList = {};


        }


        var sheetStyle;


        if (!indexList[key]) {


            if (style === undefined) {


                return ''


            }


            sheetStyle = doc.createStyleSheet('', index = doc.styleSheets.length);


            indexList[key] = index;


        } else {


            sheetStyle = doc.styleSheets[indexList[key]];


        }


        if (style === undefined) {


            return sheetStyle.cssText


        }


        sheetStyle.cssText = style || ''


    } : function (key, style, doc) {


        doc = doc || document;


        var head = doc.getElementsByTagName('head')[0], node;


        if (!(node = doc.getElementById(key))) {


            if (style === undefined) {


                return ''


            }


            node = doc.createElement('style');


            node.id = key;


            head.appendChild(node)


        }


        if (style === undefined) {


            return node.innerHTML


        }


        if (style !== '') {


            node.innerHTML = style;


        } else {


            head.removeChild(node)


        }


    }





};


/**


 * 判断str是否为字符串


 * @name isString


 * @grammar UM.utils.isString(str) => true|false


 */


/**


 * 判断array是否为数组


 * @name isArray


 * @grammar UM.utils.isArray(obj) => true|false


 */


/**


 * 判断obj对象是否为方法


 * @name isFunction


 * @grammar UM.utils.isFunction(obj)  => true|false


 */


/**


 * 判断obj对象是否为数字


 * @name isNumber


 * @grammar UM.utils.isNumber(obj)  => true|false


 */


utils.each(['String', 'Function', 'Array', 'Number', 'RegExp', 'Object'], function (v) {


    UM.utils['is' + v] = function (obj) {


        return Object.prototype.toString.apply(obj) == '[object ' + v + ']';


    }


});


/**


 * @file


 * @name UM.EventBase


 * @short EventBase


 * @import editor.js,core/utils.js


 * @desc UE采用的事件基类，继承此类的对应类将获取addListener,removeListener,fireEvent方法。


 * 在UE中，Editor以及所有ui实例都继承了该类，故可以在对应的ui对象以及editor对象上使用上述方法。


 */


var EventBase = UM.EventBase = function () {};





EventBase.prototype = {


    /**


     * 注册事件监听器


     * @name addListener


     * @grammar editor.addListener(types,fn)  //types为事件名称，多个可用空格分隔


     * @example


     * 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是注册的事件的编辑器实例


     * })


     */


    addListener:function (types, listener) {


        types = utils.trim(types).split(' ');


        for (var i = 0, ti; ti = types[i++];) {


            getListener(this, ti, true).push(listener);


        }


    },


    /**


     * 移除事件监听器


     * @name removeListener


     * @grammar editor.removeListener(types,fn)  //types为事件名称，多个可用空格分隔


     * @example


     * //changeCallback为方法体


     * editor.removeListener("selectionchange",changeCallback);


     */


    removeListener:function (types, listener) {


        types = utils.trim(types).split(' ');


        for (var i = 0, ti; ti = types[i++];) {


            utils.removeItem(getListener(this, ti) || [], listener);


        }


    },


    /**


     * 触发事件


     * @name fireEvent


     * @grammar editor.fireEvent(types)  //types为事件名称，多个可用空格分隔


     * @example


     * editor.fireEvent("selectionchange");


     */


    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;


    }


};


/**


 * 获得对象所拥有监听类型的所有监听器


 * @public


 * @function


 * @param {Object} obj  查询监听器的对象


 * @param {String} type 事件类型


 * @param {Boolean} force  为true且当前所有type类型的侦听器不存在时，创建一个空监听器数组


 * @returns {Array} 监听器数组


 */


function getListener(obj, type, force) {


    var allListeners;


    type = type.toLowerCase();


    return ( ( allListeners = ( obj.__allListeners || force && ( obj.__allListeners = {} ) ) )


        && ( allListeners[type] || force && ( allListeners[type] = [] ) ) );


}








///import editor.js


///import core/dom/dom.js


///import core/utils.js


/**


 * dtd html语义化的体现类


 * @constructor


 * @namespace dtd


 */


var dtd = dom.dtd = (function() {


    function _( s ) {


        for (var k in s) {


            s[k.toUpperCase()] = s[k];


        }


        return s;


    }


    var X = utils.extend2;


    var A = _({isindex:1,fieldset:1}),


        B = _({input:1,button:1,select:1,textarea:1,label:1}),


        C = X( _({a:1}), B ),


        D = X( {iframe:1}, C ),


        E = _({hr:1,ul:1,menu:1,div:1,blockquote:1,noscript:1,table:1,center:1,address:1,dir:1,pre:1,h5:1,dl:1,h4:1,noframes:1,h6:1,ol:1,h1:1,h3:1,h2:1}),


        F = _({ins:1,del:1,script:1,style:1}),


        G = X( _({b:1,acronym:1,bdo:1,'var':1,'#':1,abbr:1,code:1,br:1,i:1,cite:1,kbd:1,u:1,strike:1,s:1,tt:1,strong:1,q:1,samp:1,em:1,dfn:1,span:1}), F ),


        H = X( _({sub:1,img:1,embed:1,object:1,sup:1,basefont:1,map:1,applet:1,font:1,big:1,small:1}), G ),


        I = X( _({p:1}), H ),


        J = X( _({iframe:1}), H, B ),


        K = _({img:1,embed:1,noscript:1,br:1,kbd:1,center:1,button:1,basefont:1,h5:1,h4:1,samp:1,h6:1,ol:1,h1:1,h3:1,h2:1,form:1,font:1,'#':1,select:1,menu:1,ins:1,abbr:1,label:1,code:1,table:1,script:1,cite:1,input:1,iframe:1,strong:1,textarea:1,noframes:1,big:1,small:1,span:1,hr:1,sub:1,bdo:1,'var':1,div:1,object:1,sup:1,strike:1,dir:1,map:1,dl:1,applet:1,del:1,isindex:1,fieldset:1,ul:1,b:1,acronym:1,a:1,blockquote:1,i:1,u:1,s:1,tt:1,address:1,q:1,pre:1,p:1,em:1,dfn:1}),





        L = X( _({a:0}), J ),//a不能被切开，所以把他


        M = _({tr:1}),


        N = _({'#':1}),


        O = X( _({param:1}), K ),


        P = X( _({form:1}), A, D, E, I ),


        Q = _({li:1,ol:1,ul:1}),


        R = _({style:1,script:1}),


        S = _({base:1,link:1,meta:1,title:1}),


        T = X( S, R ),


        U = _({head:1,body:1}),


        V = _({html:1});





    var block = _({address:1,blockquote:1,center:1,dir:1,div:1,dl:1,fieldset:1,form:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,hr:1,isindex:1,menu:1,noframes:1,ol:1,p:1,pre:1,table:1,ul:1}),





        empty =  _({area:1,base:1,basefont:1,br:1,col:1,command:1,dialog:1,embed:1,hr:1,img:1,input:1,isindex:1,keygen:1,link:1,meta:1,param:1,source:1,track:1,wbr:1});





    return  _({





        // $ 表示自定的属性





        // body外的元素列表.


        $nonBodyContent: X( V, U, S ),





        //块结构元素列表


        $block : block,





        //内联元素列表


        $inline : L,





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





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





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





        //自闭和元素


        $empty : empty,





        //不是自闭合，但不能让range选中里边


        $nonChild : _({iframe:1,textarea:1}),


        //列表元素列表


        $listItem : _({dd:1,dt:1,li:1}),





        //列表根元素列表


        $list: _({ul:1,ol:1,dl:1}),





        //不能认为是空的元素


        $isNotEmpty : _({table:1,ul:1,ol:1,dl:1,iframe:1,area:1,base:1,col:1,hr:1,img:1,embed:1,input:1,link:1,meta:1,param:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1}),





        //如果没有子节点就可以删除的元素列表，像span,a


        $removeEmpty : _({a:1,abbr:1,acronym:1,address:1,b:1,bdo:1,big:1,cite:1,code:1,del:1,dfn:1,em:1,font:1,i:1,ins:1,label:1,kbd:1,q:1,s:1,samp:1,small:1,span:1,strike:1,strong:1,sub:1,sup:1,tt:1,u:1,'var':1}),





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





        //在table元素里的元素列表


        $tableContent : _({caption:1,col:1,colgroup:1,tbody:1,td:1,tfoot:1,th:1,thead:1,tr:1,table:1}),


        //不转换的标签


        $notTransContent : _({pre:1,script:1,style:1,textarea:1}),


        html: U,


        head: T,


        style: N,


        script: N,


        body: P,


        base: {},


        link: {},


        meta: {},


        title: N,


        col : {},


        tr : _({td:1,th:1}),


        img : {},


        embed: {},


        colgroup : _({thead:1,col:1,tbody:1,tr:1,tfoot:1}),


        noscript : P,


        td : P,


        br : {},


        th : P,


        center : P,


        kbd : L,


        button : X( I, E ),


        basefont : {},


        h5 : L,


        h4 : L,


        samp : L,


        h6 : L,


        ol : Q,


        h1 : L,


        h3 : L,


        option : N,


        h2 : L,


        form : X( A, D, E, I ),


        select : _({optgroup:1,option:1}),


        font : L,


        ins : L,


        menu : Q,


        abbr : L,


        label : L,


        table : _({thead:1,col:1,tbody:1,tr:1,colgroup:1,caption:1,tfoot:1}),


        code : L,


        tfoot : M,


        cite : L,


        li : P,


        input : {},


        iframe : P,


        strong : L,


        textarea : N,


        noframes : P,


        big : L,


        small : L,


        //trace:


        span :_({'#':1,br:1,b:1,strong:1,u:1,i:1,em:1,sub:1,sup:1,strike:1,span:1}),


        hr : L,


        dt : L,


        sub : L,


        optgroup : _({option:1}),


        param : {},


        bdo : L,


        'var' : L,


        div : P,


        object : O,


        sup : L,


        dd : P,


        strike : L,


        area : {},


        dir : Q,


        map : X( _({area:1,form:1,p:1}), A, F, E ),


        applet : O,


        dl : _({dt:1,dd:1}),


        del : L,


        isindex : {},


        fieldset : X( _({legend:1}), K ),


        thead : M,


        ul : Q,


        acronym : L,


        b : L,


        a : X( _({a:1}), J ),


        blockquote :X(_({td:1,tr:1,tbody:1,li:1}),P),


        caption : L,


        i : L,


        u : L,


        tbody : M,


        s : L,


        address : X( D, I ),


        tt : L,


        legend : L,


        q : L,


        pre : X( G, C ),


        p : X(_({'a':1}),L),


        em :L,


        dfn : L


    });


})();





/**


 * @file


 * @name UM.dom.domUtils


 * @short DomUtils


 * @import editor.js, core/utils.js,core/browser.js,core/dom/dtd.js


 * @desc UEditor封装的底层dom操作库


 */





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*/


    },


    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;


    },


    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');


    },


    /**


     * 获取节点A相对于节点B的位置关系


     * @name getPosition


     * @grammar UM.dom.domUtils.getPosition(nodeA,nodeB)  =>  Number


     * @example


     *  switch (returnValue) {


     *      case 0: //相等，同一节点


     *      case 1: //无关，节点不相连


     *      case 2: //跟随，即节点A头部位于节点B头部的后面


     *      case 4: //前置，即节点A头部位于节点B头部的前面


     *      case 8: //被包含，即节点A被节点B包含


     *      case 10://组合类型，即节点A满足跟随节点B且被节点B包含。实际上，如果被包含，必定跟随，所以returnValue事实上不会存在8的情况。


     *      case 16://包含，即节点A包含节点B


     *      case 20://组合类型，即节点A满足前置节点A且包含节点B。同样，如果包含，必定前置，所以returnValue事实上也不会存在16的情况


     *  }


     */


    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在父节点中的索引位置


     * @name getNodeIndex


     * @grammar UM.dom.domUtils.getNodeIndex(node)  => Number  //索引值从0开始


     */


    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是否在节点doc的树上，实质上是检测是否被doc包含


     * @name inDoc


     * @grammar UM.dom.domUtils.inDoc(node,doc)   =>  true|false


     */


    inDoc: function (node, doc) {


        return domUtils.getPosition(node, doc) == 10;


    },


    /**


     * 查找node节点的祖先节点


     * @name findParent


     * @grammar UM.dom.domUtils.findParent(node)  => Element  // 直接返回node节点的父节点


     * @grammar UM.dom.domUtils.findParent(node,filterFn)  => Element  //filterFn为过滤函数，node作为参数，返回true时才会将node作为符合要求的节点返回


     * @grammar UM.dom.domUtils.findParent(node,filterFn,includeSelf)  => Element  //includeSelf指定是否包含自身


     */


    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;


    },


    /**


     * 通过tagName查找node节点的祖先节点


     * @name findParentByTagName


     * @grammar UM.dom.domUtils.findParentByTagName(node,tagNames)   =>  Element  //tagNames支持数组，区分大小写


     * @grammar UM.dom.domUtils.findParentByTagName(node,tagNames,includeSelf)   =>  Element  //includeSelf指定是否包含自身


     * @grammar UM.dom.domUtils.findParentByTagName(node,tagNames,includeSelf,excludeFn)   =>  Element  //excludeFn指定例外过滤条件，返回true时忽略该节点


     */


    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的祖先节点集合


     * @name findParents


     * @grammar UM.dom.domUtils.findParents(node)  => Array  //返回一个祖先节点数组集合，不包含自身


     * @grammar UM.dom.domUtils.findParents(node,includeSelf)  => Array  //返回一个祖先节点数组集合，includeSelf指定是否包含自身


     * @grammar UM.dom.domUtils.findParents(node,includeSelf,filterFn)  => Array  //返回一个祖先节点数组集合，filterFn指定过滤条件，返回true的node将被选取


     * @grammar UM.dom.domUtils.findParents(node,includeSelf,filterFn,closerFirst)  => Array  //返回一个祖先节点数组集合，closerFirst为true的话，node的直接父亲节点是数组的第0个


     */


    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


     * @name insertAfter


     * @grammar UM.dom.domUtils.insertAfter(node,newNode)  => newNode


     */


    insertAfter: function (node, newNode) {


        return node.parentNode.insertBefore(newNode, node.nextSibling);


    },





    /**


     * 删除节点node，并根据keepChildren指定是否保留子节点


     * @name remove


     * @grammar UM.dom.domUtils.remove(node)  =>  node


     * @grammar UM.dom.domUtils.remove(node,keepChildren)  =>  node


     */


    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是否属于bookmark节点


     * @name isBookmarkNode


     * @grammar UM.dom.domUtils.isBookmarkNode(node)  => true|false


     */


    isBookmarkNode: function (node) {


        return node.nodeType == 1 && node.id && /^_baidu_bookmark_/i.test(node.id);


    },


    /**


     * 获取节点node所在的window对象


     * @name  getWindow


     * @grammar UM.dom.domUtils.getWindow(node)  => 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');


    },





    /**


     * 将一个文本节点node拆分成两个文本节点，offset指定拆分位置


     * @name split


     * @grammar UM.dom.domUtils.split(node,offset)  =>  TextNode  //返回从切分位置开始的后一个文本节点


     */


    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;


    },





    /**


     * 检测节点node是否为空节点（包括空格、换行、占位符等字符）


     * @name  isWhitespace


     * @grammar  UM.dom.domUtils.isWhitespace(node)  => true|false


     */


    isWhitespace: function (node) {


        return !new RegExp('[^ \t\n\r' + domUtils.fillChar + ']').test(node.nodeValue);


    },


    /**


     * 获取元素element相对于viewport的位置坐标


     * @name getXY


     * @grammar UM.dom.domUtils.getXY(element)  => Object //返回坐标对象{x:left,y:top}


     */


    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};


    },


    /**


     * 检查节点node是否是空inline节点


     * @name  isEmptyInlineElement


     * @grammar   UM.dom.domUtils.isEmptyInlineElement(node)  => 1|0


     * @example


     * <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是否为块元素


     * @name isBlockElm


     * @grammar UM.dom.domUtils.isBlockElm(node)  => true|false


     */


    isBlockElm: function (node) {


        return node.nodeType == 1 && (dtd.$block[node.tagName] || styleBlock[domUtils.getComputedStyle(node, 'display')]) && !dtd.$nonChild[node.tagName];


    },








    /**


     * 原生方法getElementsByTagName的封装


     * @name getElementsByTagName


     * @grammar UM.dom.domUtils.getElementsByTagName(node,tagName)  => Array  //节点集合数组


     */


    getElementsByTagName: function (node, name, filter) {


        if (filter && utils.isString(filter)) {


            var className = filter;


            filter = function (node) {


                var result = false;


                $.each(utils.trim(className).replace(/[ ]{2,}/g, ' ').split(' '), function (i, v) {


                    if ($(node).hasClass(v)) {


                        result = true;


                        return false;


                    }


                })


                return result;


            }


        }


        name = utils.trim(name).replace(/[ ]{2,}/g, ' ').split(' ');


        var arr = [];


        for (var n = 0, ni; ni = name[n++];) {


            var list = node.getElementsByTagName(ni);


            for (var i = 0, ci; ci = list[i++];) {


                if (!filter || filter(ci))


                    arr.push(ci);


            }


        }


        return arr;


    },








    /**


     * 设置节点node及其子节点不会被选中


     * @name unSelectable


     * @grammar UM.dom.domUtils.unSelectable(node)


     */


    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上的属性attrNames，attrNames为属性名称数组


     * @name  removeAttributes


     * @grammar UM.dom.domUtils.removeAttributes(node,attrNames)


     * @example


     * //Before remove


     * <span style="font-size:14px;" id="test" name="followMe">xxxxx</span>


     * //Remove


     * UM.dom.domUtils.removeAttributes(node,["id","name"]);


     * //After remove


     * <span style="font-size:14px;">xxxxx</span>


     */


    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 = '';


                    !browser.ie && node.removeAttributeNode(node.getAttributeNode('style'))


            }


            node.removeAttribute(ci);


        }


    },


    /**


     * 在doc下创建一个标签名为tag，属性为attrs的元素


     * @name createElement


     * @grammar UM.dom.domUtils.createElement(doc,tag,attrs)  =>  Node  //返回创建的节点


     */


    createElement: function (doc, tag, attrs) {


        return domUtils.setAttributes(doc.createElement(tag), attrs)


    },


    /**


     * 为节点node添加属性attrs，attrs为属性键值对


     * @name setAttributes


     * @grammar UM.dom.domUtils.setAttributes(node,attrs)  => node


     */


    setAttributes: function (node, attrs) {


        for (var attr in attrs) {


            if (attrs.hasOwnProperty(attr)) {


                var value = attrs[attr];


                switch (attr) {


                    case 'class':


                        //ie下要这样赋值，setAttribute不起作用


                        node.className = value;


                        break;


                    case 'style' :


                        node.style.cssText = node.style.cssText + ";" + value;


                        break;


                    case 'innerHTML':


                        node[attr] = value;


                        break;


                    case 'value':


                        node.value = value;


                        break;


                    default:


                        node.setAttribute(attrFix[attr] || attr, value);


                }


            }


        }


        return node;


    },





    /**


     * 获取元素element的计算样式


     * @name getComputedStyle


     * @grammar UM.dom.domUtils.getComputedStyle(element,styleName)  => String //返回对应样式名称的样式值


     * @example


     * getComputedStyle(document.body,"font-size")  =>  "15px"


     * getComputedStyle(form,"color")  =>  "#ffccdd"


     */


    getComputedStyle: function (element, styleName) {


        return utils.transUnitToPx(utils.fixColor(styleName, $(element).css(styleName)));


    },





    /**


     * 阻止事件默认行为


     * @param {Event} 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的某个样式值


     * @name getStyle


     * @grammar UM.dom.domUtils.getStyle(element,name)  => String


     */


    getStyle: function (element, name) {


        var value = element.style[ utils.cssStyleToDomStyle(name) ];


        return utils.fixColor(name, value);


    },


    /**


     * 为元素element设置样式属性值


     * @name setStyle


     * @grammar UM.dom.domUtils.setStyle(element,name,value)


     */


    setStyle: function (element, name, value) {


        element.style[utils.cssStyleToDomStyle(name)] = value;


        if (!utils.trim(element.style.cssText)) {


            this.removeAttributes(element, 'style')


        }


    },





    /**


     * 删除_moz_dirty属性


     * @function


     */


    removeDirtyAttr: function (node) {


        for (var i = 0, ci, nodes = node.getElementsByTagName('*'); ci = nodes[i++];) {


            ci.removeAttribute('_moz_dirty');


        }


        node.removeAttribute('_moz_dirty');


    },


    /**


     * 返回子节点的数量


     * @function


     * @param {Node}    node    父节点


     * @param  {Function}    fn    过滤子节点的规则，若为空，则得到所有子节点的数量


     * @return {Number}    符合条件子节点的数量


     */


    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;


    },





    /**


     * 判断是否为空节点


     * @function


     * @param {Node}    node    节点


     * @return {Boolean}    是否为空节点


     */


    isEmptyNode: function (node) {


        return !node.firstChild || domUtils.getChildCount(node, function (node) {


            return  !domUtils.isBr(node) && !domUtils.isBookmarkNode(node) && !domUtils.isWhitespace(node)


        }) == 0


    },





    /**


     * 判断节点是否为br


     * @function


     * @param {Node}    node   节点


     */


    isBr: function (node) {


        return node.nodeType == 1 && node.tagName == 'BR';


    },


    isFillChar: function (node, isInStart) {


        return node.nodeType == 3 && !node.nodeValue.replace(new RegExp((isInStart ? '^' : '' ) + domUtils.fillChar), '').length


    },





    isEmptyBlock: function (node, reg) {


        if (node.nodeType != 1)


            return 0;


        reg = reg || new RegExp('[ \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;


    },





    //判断是否是编辑器自定义的参数


    isCustomeNode: function (node) {


        return node.nodeType == 1 && node.getAttribute('_ue_custom_node_');


    },


    fillNode: function (doc, node) {


        var tmpNode = browser.ie ? doc.createTextNode(domUtils.fillChar) : doc.createElement('br');


        node.innerHTML = '';


        node.appendChild(tmpNode);


    },


    isBoundaryNode: function (node, dir) {


        var tmp;


        while (!domUtils.isBody(node)) {


            tmp = node;


            node = node.parentNode;


            if (tmp !== node[dir]) {


                return false;


            }


        }


        return true;


    },


    isFillChar: function (node, isInStart) {


        return node.nodeType == 3 && !node.nodeValue.replace(new RegExp((isInStart ? '^' : '' ) + domUtils.fillChar), '').length


    },


    isBody: function(node){


        return $(node).hasClass('edui-body-container');


    }


};


var fillCharReg = new RegExp(domUtils.fillChar, 'g');


///import editor.js


///import core/utils.js


///import core/browser.js


///import core/dom/dom.js


///import core/dom/dtd.js


///import core/dom/domUtils.js


/**


 * @file


 * @name UM.dom.Range


 * @anthor zhanyi


 * @short Range


 * @import editor.js,core/utils.js,core/browser.js,core/dom/domUtils.js,core/dom/dtd.js


 * @desc Range范围实现类，本类是UEditor底层核心类，统一w3cRange和ieRange之间的差异，包括接口和属性


 */


(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;


    }








    /**


     * @name Range


     * @grammar new UM.dom.Range(document)  => Range 实例


     * @desc 创建一个跟document绑定的空的Range实例


     * - ***startContainer*** 开始边界的容器节点,可以是elementNode或者是textNode


     * - ***startOffset*** 容器节点中的偏移量，如果是elementNode就是childNodes中的第几个，如果是textNode就是nodeValue的第几个字符


     * - ***endContainer*** 结束边界的容器节点,可以是elementNode或者是textNode


     * - ***endOffset*** 容器节点中的偏移量，如果是elementNode就是childNodes中的第几个，如果是textNode就是nodeValue的第几个字符


     * - ***document*** 跟range关联的document对象


     * - ***collapsed*** 是否是闭合状态


     */


    var Range = dom.Range = function (document,body) {


        var me = this;


        me.startContainer =


            me.startOffset =


                me.endContainer =


                    me.endOffset = null;


        me.document = document;


        me.collapsed = true;


        me.body = body;


    };





    /**


     * 删除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;


        }


    }





    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;


    }


    Range.prototype = {


        /**


         * @name deleteContents


         * @grammar range.deleteContents()  => Range


         * @desc 删除当前选区范围中的所有内容并返回range实例，这时的range已经变成了闭合状态


         * @example


         * DOM Element :


         * <b>x<i>x[x<i>xx]x</b>


         * //执行方法后


         * <b>x<i>x<i>|x</b>


         * 注意range改变了


         * range.startContainer => b


         * range.startOffset  => 2


         * range.endContainer => b


         * range.endOffset => 2


         * range.collapsed => true


         */


        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;


        },


        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;


        },


        /**


         * @name  setStart


         * @grammar range.setStart(node,offset)  => Range


         * @desc    设置range的开始位置位于node节点内，偏移量为offset


         * 如果node是elementNode那offset指的是childNodes中的第几个，如果是textNode那offset指的是nodeValue的第几个字符


         */


        setStart:function (node, offset) {


            return setEndPoint(true, node, offset, this);


        },


        /**


         * 设置range的结束位置位于node节点，偏移量为offset


         * 如果node是elementNode那offset指的是childNodes中的第几个，如果是textNode那offset指的是nodeValue的第几个字符


         * @name  setEnd


         * @grammar range.setEnd(node,offset)  => Range


         */


        setEnd:function (node, offset) {


            return setEndPoint(false, node, offset, this);


        },


        /**


         * 将Range开始位置设置到node节点之后


         * @name  setStartAfter


         * @grammar range.setStartAfter(node)  => Range


         * @example


         * <b>xx<i>x|x</i>x</b>


         * 执行setStartAfter(i)后


         * range.startContainer =>b


         * range.startOffset =>2


         */


        setStartAfter:function (node) {


            return this.setStart(node.parentNode, domUtils.getNodeIndex(node) + 1);


        },


        /**


         * 将Range开始位置设置到node节点之前


         * @name  setStartBefore


         * @grammar range.setStartBefore(node)  => Range


         * @example


         * <b>xx<i>x|x</i>x</b>


         * 执行setStartBefore(i)后


         * range.startContainer =>b


         * range.startOffset =>1


         */


        setStartBefore:function (node) {


            return this.setStart(node.parentNode, domUtils.getNodeIndex(node));


        },


        /**


         * 将Range结束位置设置到node节点之后


         * @name  setEndAfter


         * @grammar range.setEndAfter(node)  => Range


         * @example


         * <b>xx<i>x|x</i>x</b>


         * setEndAfter(i)后


         * range.endContainer =>b


         * range.endtOffset =>2


         */


        setEndAfter:function (node) {


            return this.setEnd(node.parentNode, domUtils.getNodeIndex(node) + 1);


        },


        /**


         * 将Range结束位置设置到node节点之前


         * @name  setEndBefore


         * @grammar range.setEndBefore(node)  => Range


         * @example


         * <b>xx<i>x|x</i>x</b>


         * 执行setEndBefore(i)后


         * range.endContainer =>b


         * range.endtOffset =>1


         */


        setEndBefore:function (node) {


            return this.setEnd(node.parentNode, domUtils.getNodeIndex(node));


        },


        /**


         * 将Range开始位置设置到node节点内的开始位置


         * @name  setStartAtFirst


         * @grammar range.setStartAtFirst(node)  => Range


         */


        setStartAtFirst:function (node) {


            return this.setStart(node, 0);


        },


        /**


         * 将Range开始位置设置到node节点内的结束位置


         * @name  setStartAtLast


         * @grammar range.setStartAtLast(node)  => Range


         */


        setStartAtLast:function (node) {


            return this.setStart(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length);


        },


        /**


         * 将Range结束位置设置到node节点内的开始位置


         * @name  setEndAtFirst


         * @grammar range.setEndAtFirst(node)  => Range


         */


        setEndAtFirst:function (node) {


            return this.setEnd(node, 0);


        },


        /**


         * 将Range结束位置设置到node节点内的结束位置


         * @name  setEndAtLast


         * @grammar range.setEndAtLast(node)  => Range


         */


        setEndAtLast:function (node) {


            return this.setEnd(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length);


        },





        /**


         * 选中完整的指定节点,并返回包含该节点的range


         * @name  selectNode


         * @grammar range.selectNode(node)  => Range


         */


        selectNode:function (node) {


            return this.setStartBefore(node).setEndAfter(node);


        },


        /**


         * 选中node内部的所有节点，并返回对应的range


         * @name selectNodeContents


         * @grammar range.selectNodeContents(node)  => Range


         * @example


         * <b>xx[x<i>xxx</i>]xxx</b>


         * 执行后


         * <b>[xxx<i>xxx</i>xxx]</b>


         * range.startContainer =>b


         * range.startOffset =>0


         * range.endContainer =>b


         * range.endOffset =>3


         */


        selectNodeContents:function (node) {


            return this.setStart(node, 0).setEndAtLast(node);


        },





        /**


         * 克隆一个新的range对象


         * @name  cloneRange


         * @grammar range.cloneRange() => Range


         */


        cloneRange:function () {


            var me = this;


            return new Range(me.document).setStart(me.startContainer, me.startOffset).setEnd(me.endContainer, me.endOffset);





        },





        /**


         * 让选区闭合到尾部，若toStart为真，则闭合到头部


         * @name  collapse


         * @grammar range.collapse() => Range


         * @grammar range.collapse(true) => Range   //闭合选区到头部


         */


        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的边界，使其"收缩"到最小的位置


         * @name  shrinkBoundary


         * @grammar range.shrinkBoundary()  => Range  //range开始位置和结束位置都调整，参见<code><a href="#adjustmentboundary">adjustmentBoundary</a></code>


         * @grammar range.shrinkBoundary(true)  => Range  //仅调整开始位置，忽略结束位置


         * @example


         * <b>xx[</b>xxxxx] ==> <b>xx</b>[xxxxx]


         * <b>x[xx</b><i>]xxx</i> ==> <b>x[xx]</b><i>xxx</i>


         * [<b><i>xxxx</i>xxxxxxx</b>] ==> <b><i>[xxxx</i>xxxxxxx]</b>


         */


        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;


        },





        /**


         * 调整边界容器，如果是textNode,就调整到elementNode上


         * @name trimBoundary


         * @grammar range.trimBoundary([ignoreEnd])  => Range //true忽略结束边界


         * @example


         * DOM Element :


         * <b>|xxx</b>


         * startContainer = xxx; startOffset = 0


         * //执行后本方法后


         * startContainer = <b>;  startOffset = 0


         * @example


         * Dom Element :


         * <b>xx|x</b>


         * startContainer = xxx;  startOffset = 2


         * //执行本方法后，xxx被实实在在地切分成两个TextNode


         * startContainer = <b>; startOffset = 1


         */


        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;


        },


        /**


         * 如果选区在文本的边界上，就扩展选区到文本的父节点上


         * @name  txtToElmBoundary


         * @example


         * Dom Element :


         * <b> |xxx</b>


         * startContainer = xxx;  startOffset = 0


         * //本方法执行后


         * startContainer = <b>; startOffset = 0


         * @example


         * Dom Element :


         * <b> xxx| </b>


         * startContainer = xxx; startOffset = 3


         * //本方法执行后


         * startContainer = <b>; startOffset = 1


         */


        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;


        },





        /**


         * 在当前选区的开始位置前插入一个节点或者fragment，range的开始位置会在插入节点的前边


         * @name  insertNode


         * @grammar range.insertNode(node)  => Range //node可以是textNode,elementNode,fragment


         * @example


         * Range :


         * xxx[x<p>xxxx</p>xxxx]x<p>sdfsdf</p>


         * 待插入Node :


         * <p>ssss</p>


         * 执行本方法后的Range :


         * xxx[<p>ssss</p>x<p>xxxx</p>xxxx]x<p>sdfsdf</p>


         */


        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);


        },


        /**


         * 设置光标闭合位置,toEnd设置为true时光标将闭合到选区的结尾


         * @name  setCursor


         * @grammar range.setCursor([toEnd])  =>  Range   //toEnd为true时，光标闭合到选区的末尾


         */


        setCursor:function (toEnd, noFillData) {


            return this.collapse(!toEnd).select(noFillData);


        },


        /**


         * 创建当前range的一个书签，记录下当前range的位置，方便当dom树改变时，还能找回原来的选区位置


         * @name createBookmark


         * @grammar range.createBookmark([serialize])  => Object  //{start:开始标记,end:结束标记,id:serialize} serialize为真时，开始结束标记是插入节点的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


            }


        },


        /**


         *  移动边界到书签位置，并删除插入的书签节点


         *  @name  moveToBookmark


         *  @grammar range.moveToBookmark(bookmark)  => Range //让当前的range选到给定bookmark的位置,bookmark对象是由range.createBookmark创建的


         */


        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的边界，使其"缩小"到最合适的位置


         * @name adjustmentBoundary


         * @grammar range.adjustmentBoundary() => Range   //参见<code><a href="#shrinkboundary">shrinkBoundary</a></code>


         * @example


         * <b>xx[</b>xxxxx] ==> <b>xx</b>[xxxxx]


         * <b>x[xx</b><i>]xxx</i> ==> <b>x[xx</b>]<i>xxx</i>


         */


        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;


        },





        /**


         * 得到一个自闭合的节点,常用于获取自闭和的节点，例如图片节点


         * @name  getClosedNode


         * @grammar range.getClosedNode()  => node|null


         * @example


         * <b>xxxx[<img />]xxx</b>


         */


        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选中内容节点（在页面上表现为反白显示）


         * @name select


         * @grammar range.select();  => Range


         */


        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.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;


        },


      





        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;


        },


        moveToAddress : function(addr,ignoreEnd){


            var me = this;


            function getNode(address,isStart){


                var tmpNode = me.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;


        },


        equals : function(rng){


            for(var p in this){


                if(this.hasOwnProperty(p)){


                    if(this[p] !== rng[p])


                        return false


                }


            }


            return true;





        },


        scrollIntoView : function(){


            var $span = $('<span style="padding:0;margin:0;display:block;border:0">&nbsp;</span>');


            this.cloneRange().insertNode($span.get(0));


            var winScrollTop = $(window).scrollTop(),


                winHeight = $(window).height(),


                spanTop = $span.offset().top;


            if(spanTop < winScrollTop-winHeight || spanTop > winScrollTop + winHeight ){


                if(spanTop > winScrollTop + winHeight){


                    window.scrollTo(0,spanTop - winHeight + $span.height())


                }else{


                    window.scrollTo(0,winScrollTop - spanTop)


                }





            }


            $span.remove();


        },


        getOffset : function(){


            var bk = this.createBookmark();


            var offset = $(bk.start).css('display','inline-block').offset();


            this.moveToBookmark(bk);


            return offset


        }


    };


})();


///import editor.js


///import core/browser.js


///import core/dom/dom.js


///import core/dom/dtd.js


///import core/dom/domUtils.js


///import core/dom/Range.js


/**


 * @class UM.dom.Selection    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,txtRange ) {


        var ieRange;


        //ie下有可能报错


        try {


            ieRange = sel.getNative(txtRange).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,body ) {


        var me = this;


        me.document = doc;


        me.body = body;


        if ( browser.ie9below ) {


            $( body).on('beforedeactivate', function () {


                me._bakIERange = me.getIERange();


            } ).on('activate', function () {


                try {


                    var ieNativRng =  _getIERange( me );


                    if ( (!ieNativRng || !me.rangeInBody(ieNativRng)) && me._bakIERange ) {


                        me._bakIERange.select();


                    }


                } catch ( ex ) {


                }


                me._bakIERange = null;


            } );


        }


    };





    Selection.prototype = {


        hasNativeRange : function(){


            var rng;


            if(!browser.ie || browser.ie9above){


                var nativeSel = this.getNative();


                if(!nativeSel.rangeCount){


                    return false;


                }


                rng = nativeSel.getRangeAt(0);


            }else{


                rng = _getIERange(this);


            }


            return this.rangeInBody(rng);


        },


        /**


         * 获取原生seleciton对象


         * @public


         * @function


         * @name    UM.dom.Selection.getNative


         * @return {Selection}    获得selection对象


         */


        getNative:function (txtRange) {


            var doc = this.document;


            try {


                return !doc ? null : browser.ie9below || txtRange? doc.selection : domUtils.getWindow( doc ).getSelection();


            } catch ( e ) {


                return null;


            }


        },


        /**


         * 获得ieRange


         * @public


         * @function


         * @name    UM.dom.Selection.getIERange


         * @return {ieRange}    返回ie原生的Range


         */


        getIERange:function (txtRange) {


            var ieRange = _getIERange( this,txtRange );


            if ( !ieRange  || !this.rangeInBody(ieRange,txtRange)) {


                if ( this._bakIERange ) {


                    return this._bakIERange;


                }


            }


            return ieRange;


        },


        rangeInBody : function(rng,txtRange){


            var node = browser.ie9below || txtRange ? rng.item ? rng.item() : rng.parentElement() : rng.startContainer;





            return node === this.body || domUtils.inDoc(node,this.body);


        },


        /**


         * 缓存当前选区的range和选区的开始节点


         * @public


         * @function


         * @name    UM.dom.Selection.cache


         */


        cache:function () {


            this.clear();


            this._cachedRange = this.getRange();


            this._cachedStartElement = this.getStart();


            this._cachedStartElementPath = this.getStartElementPath();


        },





        getStartElementPath:function () {


            if ( this._cachedStartElementPath ) {


                return this._cachedStartElementPath;


            }


            var start = this.getStart();


            if ( start ) {


                return domUtils.findParents( start, true, null, true )


            }


            return [];


        },


        /**


         * 清空缓存


         * @public


         * @function


         * @name    UM.dom.Selection.clear


         */


        clear:function () {


            this._cachedStartElementPath = this._cachedRange = this._cachedStartElement = null;


        },


        /**


         * 编辑器是否得到了选区


         */


        isFocus:function () {


            return this.hasNativeRange()





        },


        /**


         * 获取选区对应的Range


         * @public


         * @function


         * @name    UM.dom.Selection.getRange


         * @returns {UM.dom.Range}    得到Range对象


         */


        getRange:function () {


            var me = this;


            function optimze( range ) {


                var child = me.body.firstChild,


                    collapsed = range.collapsed;


                while ( child && child.firstChild ) {


                    range.setStart( child, 0 );


                    child = child.firstChild;


                }


                if ( !range.startContainer ) {


                    range.setStart( me.body, 0 )


                }


                if ( collapsed ) {


                    range.collapse( true );


                }


            }





            if ( me._cachedRange != null ) {


                return this._cachedRange;


            }


            var range = new dom.Range( me.document,me.body );


            if ( browser.ie9below ) {


                var nativeRange = me.getIERange();


                if ( nativeRange  && this.rangeInBody(nativeRange)) {





                    try{


                        transformIERangeToRange( nativeRange, range );


                    }catch(e){


                        optimze( range );


                    }





                } else {


                    optimze( range );


                }


            } else {


                var sel = me.getNative();


                if ( sel && sel.rangeCount && me.rangeInBody(sel.getRangeAt( 0 ))) {


                    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 && (this._bakRange.startContainer === this.body || domUtils.inDoc( this._bakRange.startContainer, this.body )) ){


                        return this._bakRange;


                    }


                    optimze( range );


                }


            }





            return this._bakRange = range;


        },





        /**


         * 获取开始元素，用于状态反射


         * @public


         * @function


         * @name    UM.dom.Selection.getStart


         * @return {Element}     获得开始元素


         */


        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 {


                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;


        },


        /**


         * 得到选区中的文本


         * @public


         * @function


         * @name    UM.dom.Selection.getText


         * @return  {String}    选区中包含的文本


         */


        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 '';


        }


    };


})();


/**


 * @file


 * @name UM.Editor


 * @short Editor


 * @import editor.js,core/utils.js,core/EventBase.js,core/browser.js,core/dom/dtd.js,core/dom/domUtils.js,core/dom/Range.js,core/dom/Selection.js,plugins/serialize.js


 * @desc 编辑器主类，包含编辑器提供的大部分公用接口


 */


(function () {


    var uid = 0, _selectionChangeTimer;





    /**


     * @private


     * @ignore


     * @param form  编辑器所在的form元素


     * @param editor  编辑器实例对象


     */


    function setValue(form, editor) {


        var textarea;


        if (editor.textarea) {


            if (utils.isString(editor.textarea)) {


                for (var i = 0, ti, tis = domUtils.getElementsByTagName(form, 'textarea'); ti = tis[i++];) {


                    if (ti.id == 'umeditor_textarea_' + editor.options.textarea) {


                        textarea = ti;


                        break;


                    }


                }


            } else {


                textarea = editor.textarea;


            }


        }


        if (!textarea) {


            form.appendChild(textarea = domUtils.createElement(document, 'textarea', {


                'name': editor.options.textarea,


                'id': 'umeditor_textarea_' + editor.options.textarea,


                'style': "display:none"


            }));


            //不要产生多个textarea


            editor.textarea = textarea;


        }


        textarea.value = editor.hasContents() ?


            (editor.options.allHtmlEnabled ? editor.getAllHtml() : editor.getContent(null, null, true)) :


            ''


    }


    function loadPlugins(me){


        //初始化插件


        for (var pi in UM.plugins) {


            if(me.options.excludePlugins.indexOf(pi) == -1){


                UM.plugins[pi].call(me);


                me.plugins[pi] = 1;


            }


        }


        me.langIsReady = true;





        me.fireEvent("langReady");


    }


    function checkCurLang(I18N){


        for(var lang in I18N){


            return lang


        }


    }


    /**


     * UEditor编辑器类


     * @name Editor


     * @desc 创建一个跟编辑器实例


     * - ***container*** 编辑器容器对象


     * - ***iframe*** 编辑区域所在的iframe对象


     * - ***window*** 编辑区域所在的window


     * - ***document*** 编辑区域所在的document对象


     * - ***body*** 编辑区域所在的body对象


     * - ***selection*** 编辑区域的选区对象


     */


    var Editor = UM.Editor = function (options) {


        var me = this;


        me.uid = uid++;


        EventBase.call(me);


        me.commands = {};


        me.options = utils.extend(utils.clone(options || {}), UMEDITOR_CONFIG, true);


        me.shortcutkeys = {};


        me.inputRules = [];


        me.outputRules = [];


        //设置默认的常用属性


        me.setOpt({


            isShow: true,


            initialContent: '',


            initialStyle:'',


            autoClearinitialContent: false,


            textarea: 'editorValue',


            focus: false,


            focusInEnd: true,


            autoClearEmptyNode: true,


            fullscreen: false,


            readonly: false,


            zIndex: 999,


            enterTag: 'p',


            lang: 'zh-cn',


            langPath: me.options.UMEDITOR_HOME_URL + 'lang/',


            theme: 'default',


            themePath: me.options.UMEDITOR_HOME_URL + 'themes/',


            allHtmlEnabled: false,


            autoSyncData : true,


            autoHeightEnabled : true,


            excludePlugins:''


        });


        me.plugins = {};


        if(!utils.isEmptyObject(UM.I18N)){


            //修改默认的语言类型


            me.options.lang = checkCurLang(UM.I18N);


            loadPlugins(me)


        }else{


            utils.loadFile(document, {


                src: me.options.langPath + me.options.lang + "/" + me.options.lang + ".js",


                tag: "script",


                type: "text/javascript",


                defer: "defer"


            }, function () {


                loadPlugins(me)


            });


        }





    };


    Editor.prototype = {


        /**


         * 当编辑器ready后执行传入的fn,如果编辑器已经完成ready，就马上执行fn，fn的中的this是编辑器实例。


         * 大部分的实例接口都需要放在该方法内部执行，否则在IE下可能会报错。


         * @name ready


         * @grammar editor.ready(fn) fn是当编辑器渲染好后执行的function


         * @example


         * var editor = new UM.ui.Editor();


         * editor.render("myEditor");


         * editor.ready(function(){


         *     editor.setContent("欢迎使用UEditor！");


         * })


         */


        ready: function (fn) {


            var me = this;


            if (fn) {


                me.isReady ? fn.apply(me) : me.addListener('ready', fn);


            }


        },


        /**


         * 为编辑器设置默认参数值。若用户配置为空，则以默认配置为准


         * @grammar editor.setOpt(key,value);      //传入一个键、值对


         * @grammar editor.setOpt({ key:value});   //传入一个json对象


         */


        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] || ''


        },


        /**


         * 销毁编辑器实例对象


         * @name destroy


         * @grammar editor.destroy();


         */


        destroy: function () {





            var me = this;


            me.fireEvent('destroy');


            var container = me.container.parentNode;


            if(container === document.body){


                container = me.container;


            }


            var textarea = me.textarea;


            if (!textarea) {


                textarea = document.createElement('textarea');


                container.parentNode.insertBefore(textarea, container);


            } else {


                textarea.style.display = ''


            }





            textarea.style.width = me.body.offsetWidth + 'px';


            textarea.style.height = me.body.offsetHeight + 'px';


            textarea.value = me.getContent();


            textarea.id = me.key;


            if(container.contains(textarea)){


                $(textarea).insertBefore(container);


            }


            container.innerHTML = '';





            domUtils.remove(container);


            UM.clearCache(me.id);


            //trace:2004


            for (var p in me) {


                if (me.hasOwnProperty(p)) {


                    delete this[p];


                }


            }





        },


        initialCont : function(holder){





            if(holder){


                holder.getAttribute('name') && ( this.options.textarea = holder.getAttribute('name'));


                if (holder && /script|textarea/ig.test(holder.tagName)) {


                    var newDiv = document.createElement('div');


                    holder.parentNode.insertBefore(newDiv, holder);


                    this.options.initialContent = UM.htmlparser(holder.value || holder.innerHTML|| this.options.initialContent).toHtml();


                    holder.className && (newDiv.className = holder.className);


                    holder.style.cssText && (newDiv.style.cssText = holder.style.cssText);





                    if (/textarea/i.test(holder.tagName)) {


                        this.textarea = holder;


                        this.textarea.style.display = 'none';





                    } else {


                        holder.parentNode.removeChild(holder);


                        holder.id && (newDiv.id = holder.id);


                    }


                    holder = newDiv;


                    holder.innerHTML = '';


                }


                return holder;


            }else{


                return null;


            }





        },


        /**


         * 渲染编辑器的DOM到指定容器，必须且只能调用一次


         * @name render


         * @grammar editor.render(containerId);    //可以指定一个容器ID


         * @grammar editor.render(containerDom);   //也可以直接指定容器对象


         */


        render: function (container) {


            var me = this,


                options = me.options,


                getStyleValue=function(attr){


                    return parseInt($(container).css(attr));


                };





            if (utils.isString(container)) {


                container = document.getElementById(container);


            }


            if (container) {


                this.id = container.getAttribute('id');


                UM.setEditor(this);


                utils.cssRule('edui-style-body',me.options.initialStyle,document);





                container = this.initialCont(container);





                container.className += ' edui-body-container';





                if(options.initialFrameWidth){


                    options.minFrameWidth = options.initialFrameWidth


                }else{


                    //都没给值，先写死了


                    options.minFrameWidth = options.initialFrameWidth = $(container).width() || UM.defaultWidth;


                }


                if(options.initialFrameHeight){


                    options.minFrameHeight = options.initialFrameHeight


                }else{





                    options.initialFrameHeight = options.minFrameHeight = $(container).height() || UM.defaultHeight;


                }





                container.style.width = /%$/.test(options.initialFrameWidth) ?  '100%' : options.initialFrameWidth -


                    getStyleValue("padding-left")-


                    getStyleValue("padding-right")  +'px';





                var height = /%$/.test(options.initialFrameHeight) ?  '100%' : (options.initialFrameHeight - getStyleValue("padding-top")- getStyleValue("padding-bottom") );


                if(this.options.autoHeightEnabled){


                    container.style.minHeight = height +'px';


                    container.style.height = '';


                    if(browser.ie && browser.version <= 6){


                        container.style.height = height ;


                        container.style.setExpression('height', 'this.scrollHeight <= ' + height + ' ? "' + height + 'px" : "auto"');


                    }


                }else{


                    $(container).height(height)


                }


                container.style.zIndex = options.zIndex;


                this._setup(container);





            }


        },


        /**


         * 编辑器初始化


         * @private


         * @ignore


         * @param {Element} doc 编辑器Iframe中的文档对象


         */


        _setup: function (cont) {


            var me = this,


                options = me.options;





            cont.contentEditable = true;


            document.body.spellcheck = false;





            me.document = document;


            me.window = document.defaultView || document.parentWindow;


            me.body = cont;


            me.$body = $(cont);


            me.selection = new dom.Selection(document,me.body);


            me._isEnabled = false;


            //gecko初始化就能得到range,无法判断isFocus了


            var geckoSel;


            if (browser.gecko && (geckoSel = this.selection.getNative())) {


                geckoSel.removeAllRanges();


            }


            this._initEvents();


            //为form提交提供一个隐藏的textarea


            for (var form = cont.parentNode; form && !domUtils.isBody(form); form = form.parentNode) {


                if (form.tagName == 'FORM') {


                    me.form = form;


                    if(me.options.autoSyncData){


                        $(cont).on('blur',function(){


                            setValue(form,me);


                        })


                    }else{


                        $(form).on('submit', function () {


                            setValue(this, me);


                        })


                    }


                    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 = cont.parentNode;


            }





            me._bindshortcutKeys();


            me.isReady = 1;


            me.fireEvent('ready');


            options.onready && options.onready.call(me);


            if(!browser.ie || browser.ie9above){





                $(me.body).on( 'blur focus', function (e) {


                    var nSel = me.selection.getNative();


                    //chrome下会出现alt+tab切换时，导致选区位置不对


                    if (e.type == 'blur') {


                        if(nSel.rangeCount > 0 ){


                            me._bakRange = nSel.getRangeAt(0);


                        }


                    } else {


                        try {


                            me._bakRange && nSel.addRange(me._bakRange)


                        } catch (e) {


                        }


                        me._bakRange = null;


                    }


                });


            }





            !options.isShow && me.setHide();


            options.readonly && me.setDisabled();


        },


        /**


         * 同步编辑器的数据，为提交数据做准备，主要用于你是手动提交的情况


         * @name sync


         * @grammar editor.sync(); //从编辑器的容器向上查找，如果找到就同步数据


         * @grammar editor.sync(formID); //formID制定一个要同步数据的form的id,编辑器的数据会同步到你指定form下


         * @desc


         * 后台取得数据得键值使用你容器上得''name''属性，如果没有就使用参数传入的''textarea''


         * @example


         * editor.sync();


         * form.sumbit(); //form变量已经指向了form元素


         *


         */


        sync: function (formId) {


            var me = this,


                form = formId ? document.getElementById(formId) :


                    domUtils.findParent(me.body.parentNode, function (node) {


                        return node.tagName == 'FORM'


                    }, true);


            form && setValue(form, me);


        },


        /**


         * 设置编辑器高度


         * @name setHeight


         * @grammar editor.setHeight(number);  //纯数值，不带单位


         */


        setHeight: function (height,notSetHeight) {


            !notSetHeight && (this.options.initialFrameHeight = height);


            if(this.options.autoHeightEnabled){


                $(this.body).css({


                    'min-height':height + 'px'


                });


                if(browser.ie && browser.version <= 6 && this.container){


                    this.container.style.height = height ;


                    this.container.style.setExpression('height', 'this.scrollHeight <= ' + height + ' ? "' + height + 'px" : "auto"');


                }


            }else{


                $(this.body).height(height)


            }


            this.fireEvent('resize');


        },


        /**


         * 设置编辑器宽度


         * @name setWidth


         * @grammar editor.setWidth(number);  //纯数值，不带单位


         */


        setWidth:function(width){


            this.$container && this.$container.width(width);


            $(this.body).width(width - $(this.body).css('padding-left').replace('px','') * 1 - $(this.body).css('padding-right').replace('px','') * 1);


            this.fireEvent('resize');


        },


        addshortcutkey: function (cmd, keys) {


            var obj = {};


            if (keys) {


                obj[cmd] = keys


            } else {


                obj = cmd;


            }


            utils.extend(this.shortcutkeys, obj)


        },


        _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);


                            }


                        }


                    }





                }


            });


        },


        /**


         * 获取编辑器内容


         * @name getContent


         * @grammar editor.getContent()  => String //若编辑器中只包含字符"&lt;p&gt;&lt;br /&gt;&lt;/p/&gt;"会返回空。


         * @grammar editor.getContent(fn)  => String


         * @example


         * getContent默认是会现调用hasContents来判断编辑器是否为空，如果是，就直接返回空字符串


         * 你也可以传入一个fn来接替hasContents的工作，定制判断的规则


         * editor.getContent(function(){


         *     return false //编辑器没有内容 ，getContent直接返回空


         * })


         */


        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 = UM.htmlparser(me.body.innerHTML,ignoreBlank);


            me.filterOutputRule(root);


            me.fireEvent('aftergetcontent',root);


            return  root.toHtml(formatter);


        },


        /**


         * 取得完整的html代码，可以直接显示成完整的html文档


         * @name getAllHtml


         * @grammar editor.getAllHtml()  => String


         */


        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>';


        },


        /**


         * 得到编辑器的纯文本内容，但会保留段落格式


         * @name getPlainTxt


         * @grammar editor.getPlainTxt()  => String


         */


        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, ' ');


        },





        /**


         * 获取编辑器中的纯文本内容,没有段落格式


         * @name getContentTxt


         * @grammar editor.getContentTxt()  => String


         */


        getContentTxt: function () {


            var reg = new RegExp(domUtils.fillChar, 'g');


            //取出来的空格会有c2a0会变成乱码，处理这种情况\u00a0


            return this.body[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').replace(/\u00a0/g, ' ');


        },





        /**


         * 将html设置到编辑器中, 如果是用于初始化时给编辑器赋初值，则必须放在ready方法内部执行


         * @name setContent


         * @grammar editor.setContent(html)


         * @example


         * var editor = new UM.ui.Editor()


         * editor.ready(function(){


         *     //需要ready后执行，否则可能报错


         *     editor.setContent("欢迎使用UEditor！");


         * })


         */


        setContent: function (html, isAppendTo, notFireSelectionchange) {


            var me = this;





            me.fireEvent('beforesetcontent', html);


            var root = UM.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);


            }


        },





        /**


         * 让编辑器获得焦点，toEnd确定focus位置


         * @name focus


         * @grammar editor.focus([toEnd])   //默认focus到编辑器头部，toEnd为true时focus到内容尾部


         */


        focus: function (toEnd) {


            try {


                var me = this,


                    rng = me.selection.getRange();


                if (toEnd) {


                    rng.setStartAtLast(me.body.lastChild).setCursor(false, true);


                } else {


                    rng.select(true);


                }


                this.fireEvent('focus');


            } catch (e) {


            }


        },


        /**


         * 使编辑区域失去焦点


         */


        blur:function(){


            var sel = this.selection.getNative();


            sel.empty ? sel.empty() : sel.removeAllRanges();


            this.fireEvent('blur')


        },


        /**


         * 判断编辑器当前是否获得了焦点


         */


        isFocus : function(){


            if(this.fireEvent('isfocus')===true){


                return true;


            }


            return this.selection.isFocus();


        },





        /**


         * 初始化UE事件及部分事件代理


         * @private


         * @ignore


         */


        _initEvents: function () {


            var me = this,


                cont = me.body,


                _proxyDomEvent = function(){


                    me._proxyDomEvent.apply(me, arguments);


                };





            $(cont)


                .on( 'click contextmenu mousedown keydown keyup keypress mouseup mouseover mouseout selectstart', _proxyDomEvent)


                .on( 'focus blur', _proxyDomEvent)


                .on('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);


                });


        },


        /**


         * 触发事件代理


         * @private


         * @ignore


         */


        _proxyDomEvent: function (evt) {


            return this.fireEvent(evt.type.replace(/^on/, ''), evt);


        },


        /**


         * 变化选区


         * @private


         * @ignore


         */


        _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.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);


        },


        _callCmdFn: function (fnName, args) {


            args = Array.prototype.slice.call(args,0);


            var cmdName = args.shift().toLowerCase(),


                cmd, cmdFn;


            cmd = this.commands[cmdName] || UM.commands[cmdName];


            cmdFn = cmd && cmd[fnName];


            //没有querycommandstate或者没有command的都默认返回0


            if ((!cmd || !cmdFn) && fnName == 'queryCommandState') {


                return 0;


            } else if (cmdFn) {


                return cmdFn.apply(this, [cmdName].concat(args));


            }


        },





        /**


         * 执行编辑命令cmdName，完成富文本编辑效果


         * @name execCommand


         * @grammar editor.execCommand(cmdName)   => {*}


         */


        execCommand: function (cmdName) {


            if(!this.isFocus()){


                var bakRange = this.selection._bakRange;


                if(bakRange){


                    bakRange.select()


                }else{


                    this.focus(true)


                }





            }


            cmdName = cmdName.toLowerCase();


            var me = this,


                result,


                cmd = me.commands[cmdName] || UM.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('beforeexeccommand', cmdName);


                    result = this._callCmdFn('execCommand', arguments);


                    (!cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange');


                    me.fireEvent('afterexeccommand', cmdName);


                    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命令，查选编辑器当前的选区，返回命令的状态


         * @name  queryCommandState


         * @grammar editor.queryCommandState(cmdName)  => (-1|0|1)


         * @desc


         * * ''-1'' 当前命令不可用


         * * ''0'' 当前命令可用


         * * ''1'' 当前命令已经执行过了


         */


        queryCommandState: function (cmdName) {


            try{


                return this._callCmdFn('queryCommandState', arguments);


            }catch(e){


                return 0


            }





        },





        /**


         * 根据传入的command命令，查选编辑器当前的选区，根据命令返回相关的值


         * @name  queryCommandValue


         * @grammar editor.queryCommandValue(cmdName)  =>  {*}


         */


        queryCommandValue: function (cmdName) {


            try{


                return this._callCmdFn('queryCommandValue', arguments);


            }catch(e){


                return null


            }


        },


        /**


         * 检查编辑区域中是否有内容，若包含tags中的节点类型，直接返回true


         * @name  hasContents


         * @desc


         * 默认有文本内容，或者有以下节点都不认为是空


         * <code>{table:1,ul:1,ol:1,dl:1,iframe:1,area:1,base:1,col:1,hr:1,img:1,embed:1,input:1,link:1,meta:1,param:1}</code>


         * @grammar editor.hasContents()  => (true|false)


         * @grammar editor.hasContents(tags)  =>  (true|false)  //若文档中包含tags数组里对应的tag，直接返回true


         * @example


         * editor.hasContents(['span']) //如果编辑器里有这些，不认为是空


         */


        hasContents: function (tags) {


            if (tags) {


                for (var i = 0, ci; ci = tags[i++];) {


                    if (this.body.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.body, ci);


                for (var n = 0, cn; cn = nodes[n++];) {


                    if (domUtils.isCustomeNode(cn)) {


                        return true;


                    }


                }


            }


            return false;


        },


        /**


         * 重置编辑器，可用来做多个tab使用同一个编辑器实例


         * @name  reset


         * @desc


         * * 清空编辑器内容


         * * 清空回退列表


         * @grammar editor.reset()


         */


        reset: function () {


            this.fireEvent('reset');


        },


        isEnabled: function(){


            return this._isEnabled != true;


        },





        setEnabled: function () {


            var me = this, range;





            me.body.contentEditable = true;





            /* 恢复选区 */


            if (me.lastBk) {


                range = me.selection.getRange();


                try {


                    range.moveToBookmark(me.lastBk);


                    delete me.lastBk


                } catch (e) {


                    range.setStartAtFirst(me.body).collapse(true)


                }


                range.select(true);


            }





            /* 恢复query函数 */


            if (me.bkqueryCommandState) {


                me.queryCommandState = me.bkqueryCommandState;


                delete me.bkqueryCommandState;


            }





            /* 恢复原生事件 */


            if (me._bkproxyDomEvent) {


                me._proxyDomEvent = me._bkproxyDomEvent;


                delete me._bkproxyDomEvent;


            }





            /* 触发事件 */


            me.fireEvent('setEnabled');


        },


        /**


         * 设置当前编辑区域可以编辑


         * @name enable


         * @grammar editor.enable()


         */


        enable: function () {


            return this.setEnabled();


        },


        setDisabled: function (except, keepDomEvent) {


            var me = this;





            me.body.contentEditable = false;


            me._except = except ? utils.isArray(except) ? except : [except] : [];





            /* 备份最后的选区 */


            if (!me.lastBk) {


                me.lastBk = me.selection.getRange().createBookmark(true);


            }





            /* 备份并重置query函数 */


            if(!me.bkqueryCommandState) {


                me.bkqueryCommandState = me.queryCommandState;


                me.queryCommandState = function (type) {


                    if (utils.indexOf(me._except, type) != -1) {


                        return me.bkqueryCommandState.apply(me, arguments);


                    }


                    return -1;


                };


            }





            /* 备份并墙原生事件 */


            if(!keepDomEvent && !me._bkproxyDomEvent) {


                me._bkproxyDomEvent = me._proxyDomEvent;


                me._proxyDomEvent = function () {


                    return false;


                };


            }





            /* 触发事件 */


            me.fireEvent('selectionchange');


            me.fireEvent('setDisabled', me._except);


        },


        /** 设置当前编辑区域不可编辑,except中的命令除外


         * @name disable


         * @grammar editor.disable()


         * @grammar editor.disable(except)  //例外的命令，也即即使设置了disable，此处配置的命令仍然可以执行


         * @example


         * //禁用工具栏中除加粗和插入图片之外的所有功能


         * editor.disable(['bold','insertimage']);//可以是单一的String,也可以是Array


         */


        disable: function (except) {


            return this.setDisabled(except);


        },


        /**


         * 设置默认内容


         * @ignore


         * @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);


            }


        }(),


        /**


         * show方法的兼容版本


         * @private


         * @ignore


         */


        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 = '';


            }





        },


        /**


         * 显示编辑器


         * @name show


         * @grammar editor.show()


         */


        show: function () {


            return this.setShow();


        },


        /**


         * hide方法的兼容版本


         * @private


         * @ignore


         */


        setHide: function () {


            var me = this;


            if (!me.lastBk) {


                me.lastBk = me.selection.getRange().createBookmark(true);


            }


            me.container.style.display = 'none'


        },


        /**


         * 隐藏编辑器


         * @name hide


         * @grammar editor.hide()


         */


        hide: function () {


            return this.setHide();


        },


        /**


         * 根据制定的路径，获取对应的语言资源


         * @name  getLang


         * @grammar editor.getLang(path)  =>  （JSON|String) 路径根据的是lang目录下的语言文件的路径结构


         * @example


         * editor.getLang('contextMenu.delete') //如果当前是中文，那返回是的是删除


         */


        getLang: function (path) {


            var lang = UM.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;


        },


        /**


         * 计算编辑器当前内容的长度


         * @name  getContentLength


         * @grammar editor.getContentLength(ingoneHtml,tagNames)  =>


         * @example


         * editor.getLang(true)


         */


        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.body.getElementsByTagName(ci).length;


                }


            }


            return count;


        },


        addInputRule: function (rule,ignoreUndo) {


            rule.ignoreUndo = ignoreUndo;


            this.inputRules.push(rule);


        },


        filterInputRule: function (root,isUndoLoad) {


            for (var i = 0, ci; ci = this.inputRules[i++];) {


                if(isUndoLoad && ci.ignoreUndo){


                    continue;


                }


                ci.call(this, root)


            }


        },


        addOutputRule: function (rule,ignoreUndo) {


            rule.ignoreUndo = ignoreUndo;


            this.outputRules.push(rule)


        },


        filterOutputRule: function (root,isUndoLoad) {


            for (var i = 0, ci; ci = this.outputRules[i++];) {


                if(isUndoLoad && ci.ignoreUndo){


                    continue;


                }


                ci.call(this, root)


            }


        }


    };


    utils.inherits(Editor, EventBase);


})();





/**


 * @file


 * @name UM.filterWord


 * @short filterWord


 * @desc 用来过滤word粘贴过来的字符串


 * @import editor.js,core/utils.js


 * @anthor zhanyi


 */


var filterWord = UM.filterWord = function () {





    //是否是word过来的内容


    function isWordDocument( str ) {


        return /(class="?Mso|style="[^"]*\bmso\-|w:WordDocument|<(v|o):|lang=)/ig.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]*?-->/ig, "" )


            //转换图片


            .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/ig, 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, "'" )


                        .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,';') + '"' : '');


            })


            .replace(/[\d.]+(cm|pt)/g,function(str){


                return utils.transUnitToPx(str)


            })





    }





    return function ( html ) {


        return (isWordDocument( html ) ? filterPasteWord( html ) : html);


    };


}();


///import editor.js


///import core/utils.js


///import core/dom/dom.js


///import core/dom/dtd.js


///import core/htmlparser.js


//模拟的节点类


//by zhanyi


(function () {


    var uNode = UM.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 UM.htmlparser(html).children[0]


        } else {


            return new uNode({


                type:'element',


                children:[],


                tagName:html


            })


        }


    };


    uNode.createText = function (data,noTrans) {


        return new UM.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 { UM.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 = UM.htmlparser(htmlstr);


                for (var i = 0, ci; ci = tmpRoot.children[i++];) {


                    this.children.push(ci);


                    ci.parentNode = this;


                }


                return this;


            } else {


                var tmpRoot = new UM.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 { UM.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 { UM.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 { UM.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 { UM.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 { UM.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 { UM.uNode } target 要替换成该节点参数


         * @param { UM.uNode } source 要被替换掉的节点


         * @return { UM.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 { UM.uNode } node 要插入的节点


         * @return { UM.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 { UM.uNode } target 要插入的节点


         * @param { UM.uNode } source 在该参数节点前面插入


         * @return { UM.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 { UM.uNode } target 要插入的节点


         * @param { UM.uNode } source 在该参数节点后面插入


         * @return { UM.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 { UM.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;


                }





            }


        },


        hasAttr: function( attrName ){


            var attrVal = this.getAttr( attrName );


            return ( attrVal !== null ) && ( attrVal !== undefined );


        },


        /**


         * 获取当前节点在父节点下的位置索引


         * @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 { UM.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))


        },


        hasClass: function( className ){


            if( this.hasAttr('class') ) {


                var classNames = this.getAttr('class').split(/\s+/),


                    hasClass = false;


                $.each(classNames, function(key, item){


                    if( item === className ) {


                        hasClass = true;


                    }


                });


                return hasClass;


            } else {


                return false;


            }


        },


        addClass: function( className ){





            var classes = null,


                hasClass = false;





            if( this.hasAttr('class') ) {





                classes = this.getAttr('class');


                classes = classes.split(/\s+/);





                classes.forEach( function( item ){





                    if( item===className ) {


                        hasClass = true;


                        return;


                    }





                } );





                !hasClass && classes.push( className );





                this.setAttr('class', classes.join(" "));





            } else {


                this.setAttr('class', className);


            }





        },


        removeClass: function( className ){


            if( this.hasAttr('class') ) {


                var cl = this.getAttr('class');


                cl = cl.replace(new RegExp('\\b' + className + '\\b', 'g'),'');


                this.setAttr('class', utils.trim(cl).replace(/[ ]{2,}/g,' '));


            }


        },


        /**


         * 传入一个函数，递归遍历当前节点下的所有节点


         * @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;


        }


    }


})();





//html字符串转换成uNode节点


//by zhanyi


var htmlparser = UM.htmlparser = function (htmlstr,ignoreBlank) {


    //todo 原来的方式  [^"'<>\/] 有\/就不能配对上 <TD vAlign=top background=../AAA.JPG> 这样的标签了


    //先去掉了，加上的原因忘了，这里先记录


    var re_tag = /<(?:(?:\/([^>]+)>)|(?:!--([\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 = UM.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;


        }





        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;


};


/**


 * @file


 * @name UM.filterNode


 * @short filterNode


 * @desc 根据给定的规则过滤节点


 * @import editor.js,core/utils.js


 * @anthor zhanyi


 */


var filterNode = UM.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;


    }


}();


///import core


/**


 * @description 插入内容


 * @name baidu.editor.execCommand


 * @param   {String}   cmdName     inserthtml插入内容的命令


 * @param   {String}   html                要插入的内容


 * @author zhanyi


 */


UM.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 = UM.htmlparser(html);


            //如果给了过滤规则就先进行过滤


            if(me.options.filterRules){


                UM.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)


            }


        }


        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{


            if(browser.ie9below && range.startContainer.nodeType == 1 && !range.startContainer.childNodes[range.startOffset]){


                var start = range.startContainer,pre = start.childNodes[range.startOffset-1];


                if(pre && pre.nodeType == 1 && dtd.$empty[pre.tagName]){


                    var txt = this.document.createTextNode(domUtils.fillChar);


                    range.insertNode(txt).setStart(txt,0).collapse(true);


                }


            }


            setTimeout(function(){


                range.select(true);


            })





        }catch(e){}








        setTimeout(function(){


            range = me.selection.getRange();


            range.scrollIntoView();


            me.fireEvent('afterinserthtml');


        },200);


    }


};





///import core


///import plugins\inserthtml.js


///commands 插入图片，操作图片的对齐方式


///commandsName  InsertImage,ImageNone,ImageLeft,ImageRight,ImageCenter


///commandsTitle  图片,默认,居左,居右,居中


///commandsDialog  dialogs\image


/**


 * Created by .


 * User: zhanyi


 * for image


 */


UM.commands['insertimage'] = {


    execCommand:function (cmd, opt) {


        opt = utils.isArray(opt) ? opt : [opt];


        if (!opt.length) {


            return;


        }


        var me = this;


        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(''), true);


    }


};


///import core


///commands 段落格式,居左,居右,居中,两端对齐


///commandsName  JustifyLeft,JustifyCenter,JustifyRight,JustifyJustify


///commandsTitle  居左对齐,居中对齐,居右对齐,两端对齐


/**


 * @description 居左右中


 * @name UM.execCommand


 * @param   {String}   cmdName     justify执行对齐方式的命令


 * @param   {String}   align               对齐方式：left居左，right居右，center居中，justify两端对齐


 * @author zhanyi


 */


UM.plugins['justify']=function(){


    var me = this;


    $.each('justifyleft justifyright justifycenter justifyfull'.split(' '),function(i,cmdName){


        me.commands[cmdName] = {


            execCommand:function (cmdName) {


                return this.document.execCommand(cmdName);


            },


            queryCommandValue: function (cmdName) {


                var val = this.document.queryCommandValue(cmdName);


                return   val === true || val === 'true' ? cmdName.replace(/justify/,'') : '';


            },


            queryCommandState: function (cmdName) {


                return this.document.queryCommandState(cmdName) ? 1 : 0


            }


        };


    })


};





///import core


///import plugins\removeformat.js


///commands 字体颜色,背景色,字号,字体,下划线,删除线


///commandsName  ForeColor,BackColor,FontSize,FontFamily,Underline,StrikeThrough


///commandsTitle  字体颜色,背景色,字号,字体,下划线,删除线


/**


 * @description 字体


 * @name UM.execCommand


 * @param {String}     cmdName    执行的功能名称


 * @param {String}    value             传入的值


 */


UM.plugins['font'] = function () {


    var me = this,


        fonts = {


            'forecolor': 'forecolor',


            'backcolor': 'backcolor',


            'fontsize': 'fontsize',


            'fontfamily': 'fontname'


        },


        cmdNameToStyle = {


            'forecolor': 'color',


            'backcolor': 'background-color',


            'fontsize': 'font-size',


            'fontfamily': 'font-family'


        },


        cmdNameToAttr = {


            'forecolor': 'color',


            'fontsize': 'size',


            'fontfamily': 'face'


        };


    me.setOpt({


        'fontfamily': [


            { 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'},


            { name: 'sans-serif',val:'sans-serif'}


        ],


        'fontsize': [10, 12,  16, 18,24, 32,48]


    });





    me.addOutputRule(function (root) {


        utils.each(root.getNodesByTagName('font'), function (node) {


            if (node.tagName == 'font') {


                var cssStyle = [];


                for (var p in node.attrs) {


                    switch (p) {


                        case 'size':


                            var val =  node.attrs[p];


                            $.each({


                                '10':'1',


                                '12':'2',


                                '16':'3',


                                '18':'4',


                                '24':'5',


                                '32':'6',


                                '48':'7'


                            },function(k,v){


                                if(v == val){


                                    val = k;


                                    return false;


                                }


                            });


                            cssStyle.push('font-size:' + val + '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(';')


                };


            }


            node.tagName = 'span';


            if(node.parentNode.tagName == 'span' && node.parentNode.children.length == 1){


                $.each(node.attrs,function(k,v){





                    node.parentNode.attrs[k] = k == 'style' ? node.parentNode.attrs[k] + v : v;


                })


                node.parentNode.removeChild(node,true);


            }


        });


    });


    for(var p in fonts){


        (function (cmd) {


            me.commands[cmd] = {


                execCommand: function (cmdName,value) {


                    if(value == 'transparent'){


                        return;


                    }


                    var rng = this.selection.getRange();


                    if(rng.collapsed){


                        var span = $('<span></span>').css(cmdNameToStyle[cmdName],value)[0];


                        rng.insertNode(span).setStart(span,0).setCursor();


                    }else{


                        if(cmdName == 'fontsize'){


                            value  = {


                                '10':'1',


                                '12':'2',


                                '16':'3',


                                '18':'4',


                                '24':'5',


                                '32':'6',


                                '48':'7'


                            }[(value+"").replace(/px/,'')]


                        }


                        this.document.execCommand(fonts[cmdName],false, value);


                        if(browser.gecko){


                            $.each(this.$body.find('a'),function(i,a){


                                var parent = a.parentNode;


                                if(parent.lastChild === parent.firstChild && /FONT|SPAN/.test(parent.tagName)){


                                    var cloneNode = parent.cloneNode(false);


                                    cloneNode.innerHTML = a.innerHTML;


                                    $(a).html('').append(cloneNode).insertBefore(parent);





                                    $(parent).remove();


                                }


                            });


                        }


                        if(!browser.ie){


                            var nativeRange = this.selection.getNative().getRangeAt(0);


                            var common = nativeRange.commonAncestorContainer;


                            var rng = this.selection.getRange(),


                                bk = rng.createBookmark(true);





                            $(common).find('a').each(function(i,n){


                                var parent = n.parentNode;


                                if(parent.nodeName == 'FONT'){


                                    var font = parent.cloneNode(false);


                                    font.innerHTML = n.innerHTML;


                                    $(n).html('').append(font);


                                }


                            });


                            rng.moveToBookmark(bk).select()


                        }


                        return true


                    }





                },


                queryCommandValue: function (cmdName) {


                    var start = me.selection.getStart();


                    var val = $(start).css(cmdNameToStyle[cmdName]);


                    if(val === undefined){


                        val = $(start).attr(cmdNameToAttr[cmdName])


                    }


                    return val ? utils.fixColor(cmdName,val).replace(/px/,'') : '';


                },


                queryCommandState: function (cmdName) {


                    return this.queryCommandValue(cmdName)


                }


            };


        })(p);


    }


};


///import core


///commands 超链接,取消链接


///commandsName  Link,Unlink


///commandsTitle  超链接,取消链接


///commandsDialog  dialogs\link


/**


 * 超链接


 * @function


 * @name UM.execCommand


 * @param   {String}   cmdName     link插入超链接


 * @param   {Object}  options         url地址，title标题，target是否打开新页


 * @author zhanyi


 */


/**


 * 取消链接


 * @function


 * @name UM.execCommand


 * @param   {String}   cmdName     unlink取消链接


 * @author zhanyi


 */





UM.plugins['link'] = function(){


    var me = this;





    me.setOpt('autourldetectinie',false);


    //在ie下禁用autolink


    if(browser.ie && this.options.autourldetectinie === false){


        this.addListener('keyup',function(cmd,evt){


            var me = this,keyCode = evt.keyCode;


            if(keyCode == 13 || keyCode == 32){


                var rng = me.selection.getRange();


                var start = rng.startContainer;


                if(keyCode == 13){


                    if(start.nodeName == 'P'){


                        var pre = start.previousSibling;


                        if(pre && pre.nodeType == 1){


                            var pre = pre.lastChild;


                            if(pre && pre.nodeName == 'A' && !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);


                       }


                   }


                }





            }








        });


    }





    this.addOutputRule(function(root){


        $.each(root.getNodesByTagName('a'),function(i,a){


            var _href = utils.html(a.getAttr('_href'));


            if(!/^(ftp|https?|\/|file)/.test(_href)){


                _href = 'http://' + _href;


            }


            a.setAttr('href', _href);


            a.setAttr('_href')


            if(a.getAttr('title')==''){


                a.setAttr('title')


            }


        })


    });


    this.addInputRule(function(root){


        $.each(root.getNodesByTagName('a'),function(i,a){


            a.setAttr('_href', utils.html(a.getAttr('href')));


        })


    });


    me.commands['link'] = {


        execCommand : function( cmdName, opt ) {





            var me = this;


            var rng = me.selection.getRange();


            if(rng.collapsed){


                var start = rng.startContainer;


                if(start = domUtils.findParentByTagName(start,'a',true)){


                    $(start).attr(opt);


                    rng.selectNode(start).select()


                }else{


                    rng.insertNode($('<a>' +opt.href+'</a>').attr(opt)[0]).select()





                }





            }else{


                me.document.execCommand('createlink',false,'_umeditor_link');


                utils.each(domUtils.getElementsByTagName(me.body,'a',function(n){





                    return n.getAttribute('href') == '_umeditor_link'


                }),function(l){


                    if($(l).text() == '_umeditor_link'){


                        $(l).text(opt.href);


                    }


                    domUtils.setAttributes(l,opt);


                    rng.selectNode(l).select()


                })


            }





        },


        queryCommandState:function(){


            return this.queryCommandValue('link') ? 1 : 0;


        },


        queryCommandValue:function(){


            var path = this.selection.getStartElementPath();


            var result;


            $.each(path,function(i,n){


                if(n.nodeName == "A"){


                    result = n;


                    return false;


                }


            })


            return result;


        }


    };


    me.commands['unlink'] = {


        execCommand : function() {


            this.document.execCommand('unlink');


        }


    };


};


///import core


///commands 打印


///commandsName  Print


///commandsTitle  打印


/**


 * @description 打印


 * @name baidu.editor.execCommand


 * @param   {String}   cmdName     print打印编辑器内容


 * @author zhanyi


 */


UM.commands['print'] = {


    execCommand : function(){


        var me = this,


            id = 'editor_print_' + +new Date();





        $('<iframe src="" id="' + id + '" name="' + id + '" frameborder="0"></iframe>').attr('id', id)


            .css({


                width:'0px',


                height:'0px',


                'overflow':'hidden',


                'float':'left',


                'position':'absolute',


                top:'-10000px',


                left:'-10000px'


            })


            .appendTo(me.$container.find('.edui-dialog-container'));





        var w = window.open('', id, ''),


            d = w.document;


        d.open();


        d.write('<html><head></head><body><div>'+this.getContent(null,null,true)+'</div><script>' +


            "setTimeout(function(){" +


            "window.print();" +


            "setTimeout(function(){" +


            "window.parent.$('#" + id + "').remove();" +


            "},100);" +


            "},200);" +


            '</script></body></html>');


        d.close();


    },


    notNeedUndo : 1


};


///import core


///commands 格式


///commandsName  Paragraph


///commandsTitle  段落格式


/**


 * 段落样式


 * @function


 * @name UM.execCommand


 * @param   {String}   cmdName     paragraph插入段落执行命令


 * @param   {String}   style               标签值为：'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'


 * @param   {String}   attrs               标签的属性


 * @author zhanyi


 */


UM.plugins['paragraph'] = function() {


    var me = this;


    me.setOpt('paragraph',{'p':'', 'h1':'', 'h2':'', 'h3':'', 'h4':'', 'h5':'', 'h6':''});


    me.commands['paragraph'] = {


        execCommand : function( cmdName, style ) {


            return this.document.execCommand('formatBlock',false,'<' + style + '>');


        },


        queryCommandValue : function() {


            try{


                var  val = this.document.queryCommandValue('formatBlock')


            }catch(e){


            }


            return val ;


        }


    };


};





///import core


///import plugins\inserthtml.js


///commands 分割线


///commandsName  Horizontal


///commandsTitle  分隔线


/**


 * 分割线


 * @function


 * @name UM.execCommand


 * @param {String}     cmdName    horizontal插入分割线


 */


UM.plugins['horizontal'] = function(){


    var me = this;


    me.commands['horizontal'] = {


        execCommand : function(  ) {


            this.document.execCommand('insertHorizontalRule');


            var rng = me.selection.getRange().txtToElmBoundary(true),


                start = rng.startContainer;


            if(domUtils.isBody(rng.startContainer)){


                var next = rng.startContainer.childNodes[rng.startOffset];


                if(!next){


                    next = $('<p></p>').appendTo(rng.startContainer).html(browser.ie ? '&nbsp;' : '<br/>')[0]


                }


                rng.setStart(next,0).setCursor()


            }else{





                while(dtd.$inline[start.tagName] && start.lastChild === start.firstChild){





                    var parent = start.parentNode;


                    parent.appendChild(start.firstChild);


                    parent.removeChild(start);


                    start = parent;


                }


                while(dtd.$inline[start.tagName]){


                    start = start.parentNode;


                }


                if(start.childNodes.length == 1 && start.lastChild.nodeName == 'HR'){


                    var hr = start.lastChild;


                    $(hr).insertBefore(start);


                    rng.setStart(start,0).setCursor();


                }else{


                    hr = $('hr',start)[0];


                    domUtils.breakParent(hr,start);


                    var pre = hr.previousSibling;


                    if(pre && domUtils.isEmptyBlock(pre)){


                        $(pre).remove()


                    }


                    rng.setStart(hr.nextSibling,0).setCursor();


                }





            }


        }


    };





};








///import core


///commands 清空文档


///commandsName  ClearDoc


///commandsTitle  清空文档


/**


 *


 * 清空文档


 * @function


 * @name UM.execCommand


 * @param   {String}   cmdName     cleardoc清空文档


 */





UM.commands['cleardoc'] = {


    execCommand : function() {


        var me = this,


            range = me.selection.getRange();


        me.body.innerHTML = "<p>"+(ie ? "" : "<br/>")+"</p>";


        range.setStart(me.body.firstChild,0).setCursor(false,true);


        setTimeout(function(){


            me.fireEvent("clearDoc");


        },0);





    }


};








///import core


///commands 撤销和重做


///commandsName  Undo,Redo


///commandsTitle  撤销,重做


/**


 * @description 回退


 * @author zhanyi


 */





UM.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 = UM.htmlparser(scene.content.replace(fillchar, ''));


            me.options.autoClearEmptyNode = false;


            me.filterInputRule(root,true);


            me.options.autoClearEmptyNode = orgState;


            //trace:873


            //去掉展位符


            me.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,me.body).moveToAddress(scene.address);


                if(browser.ie && rng.collapsed && rng.startContainer.nodeType == 1){


                    var tmpNode = rng.startContainer.childNodes[rng.startOffset];


                    if( !tmpNode || tmpNode.nodeType == 1 && dtd.$empty[tmpNode]){


                        rng.insertNode(me.document.createTextNode(' ')).collapse(true);


                    }


                }


                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 = UM.htmlparser(me.body.innerHTML,true);


            me.options.autoClearEmptyNode = false;


            me.filterOutputRule(root,true);


            me.options.autoClearEmptyNode = orgState;


            var cont = root.toHtml();


            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.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 () {


        $(this.body).on('compositionstart', function () {


            inputType = true;


        }).on('compositionend', function () {


            inputType = false;


        })


    });


    //快捷键


    me.addshortcutkey({


        "Undo":"ctrl+90", //undo


        "Redo":"ctrl+89,shift+ctrl+z" //redo





    });


    var isCollapsed = true;


    me.addListener('keydown', 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){





                if (cont.selection.getRange().collapsed)


                    cont.fireEvent('contentchange');


                cont.undoManger.save(false,true);


                cont.fireEvent('selectionchange');


            }


            saveSceneTimer = setTimeout(function(){


                if(inputType){


                    var interalTimer = setInterval(function(){


                        if(!inputType){


                            save(me);


                            clearInterval(interalTimer)


                        }


                    },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;


            }


        }


    });





};





///import core


///import plugins/inserthtml.js


///import plugins/undo.js


///import plugins/serialize.js


///commands 粘贴


///commandsName  PastePlain


///commandsTitle  纯文本粘贴模式


/**


 * @description 粘贴


 * @author zhanyi


 */


UM.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));


        this.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:" +


        //要在现在光标平行的位置加入，否则会出现跳动的问题


        $(bk.start).position().top  + '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;








    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 = UM.filterWord(html);


            //取消了忽略空白的第二个参数，粘贴过来的有些是有空白的，会被套上相关的标签


            var root = UM.htmlparser(html);


            //如果给了过滤规则就先进行过滤


            if (me.options.filterRules) {


                UM.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)


                    }


                })


            }


            html = {'html': root.toHtml()};


            me.fireEvent('beforepaste', html, root);


            //抢了默认的粘贴，那后边的内容就不执行了，比如表格粘贴


            if(!html.html){


                return;


            }





            me.execCommand('insertHtml', html.html, true);


            me.fireEvent("afterpaste", html);


        }


    }








    me.addListener('ready', function () {


        $(me.body).on( 'cut', function () {


            var range = me.selection.getRange();


            if (!range.collapsed && me.undoManger) {


                me.undoManger.save();


            }


        }).on(browser.ie || browser.opera ? 'keydown' : 'paste', function (e) {


            //ie下beforepaste在点击右键时也会触发，所以用监控键盘才处理


            if ((browser.ie || browser.opera) && ((!e.ctrlKey && !e.metaKey) || e.keyCode != '86')) {


                return;


            }


            getClipboardData.call(me, function (div) {


                filter(div);


            });


        });





    });


};








///import core


///commands 有序列表,无序列表


///commandsName  InsertOrderedList,InsertUnorderedList


///commandsTitle  有序列表,无序列表


/**


 * 有序列表


 * @function


 * @name UM.execCommand


 * @param   {String}   cmdName     insertorderlist插入有序列表


 * @param   {String}   style               值为：decimal,lower-alpha,lower-roman,upper-alpha,upper-roman


 * @author zhanyi


 */


/**


 * 无序链接


 * @function


 * @name UM.execCommand


 * @param   {String}   cmdName     insertunorderlist插入无序列表


 * * @param   {String}   style            值为：circle,disc,square


 * @author zhanyi


 */





UM.plugins['list'] = function () {


    var me = this;





    me.setOpt( {


        'insertorderedlist':{


            'decimal':'',


            'lower-alpha':'',


            'lower-roman':'',


            'upper-alpha':'',


            'upper-roman':''


        },


        'insertunorderedlist':{


            'circle':'',


            'disc':'',


            'square':''


        }


    } );





    this.addInputRule(function(root){


        utils.each(root.getNodesByTagName('li'), function (node) {


            if(node.children.length == 0){


                node.parentNode.removeChild(node);


            }


        })


    });


    me.commands['insertorderedlist'] =


    me.commands['insertunorderedlist'] = {


            execCommand:function (cmdName) {


                this.document.execCommand(cmdName);


                var rng = this.selection.getRange(),


                    bk = rng.createBookmark(true);





                this.$body.find('ol,ul').each(function(i,n){


                    var parent = n.parentNode;


                    if(parent.tagName == 'P' && parent.lastChild === parent.firstChild){


                        $(n).children().each(function(j,li){


                            var p = parent.cloneNode(false);


                            $(p).append(li.innerHTML);


                            $(li).html('').append(p);


                        });


                        $(n).insertBefore(parent);


                        $(parent).remove();


                    }





                    if(dtd.$inline[parent.tagName]){


                        if(parent.tagName == 'SPAN'){





                            $(n).children().each(function(k,li){


                                var span = parent.cloneNode(false);


                                if(li.firstChild.nodeName != 'P'){





                                    while(li.firstChild){


                                        span.appendChild(li.firstChild)


                                    };


                                    $('<p></p>').appendTo(li).append(span);


                                }else{


                                    while(li.firstChild){


                                        span.appendChild(li.firstChild)


                                    };


                                    $(li.firstChild).append(span);


                                }


                            })





                        }


                        domUtils.remove(parent,true)


                    }


                });














                rng.moveToBookmark(bk).select();


                return true;


            },


            queryCommandState:function (cmdName) {


                return this.document.queryCommandState(cmdName);


            }


        };


};








///import core


///import plugins/serialize.js


///import plugins/undo.js


///commands 查看源码


///commandsName  Source


///commandsTitle  查看源码


(function (){


    var sourceEditors = {


        textarea: function (editor, holder){


            var textarea = holder.ownerDocument.createElement('textarea');


            textarea.style.cssText = 'resize:none;border:0;padding:0;margin:0;overflow-y:auto;outline:0';


            // 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 {


                container : textarea,


                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;


                }


            };


        }


    };





    UM.plugins['source'] = function (){


        var me = this;


        var opt = this.options;


        var sourceMode = false;


        var sourceEditor;





        opt.sourceEditor = 'textarea';





        me.setOpt({


            sourceEditorFirst:false


        });


        function createSourceEditor(holder){


            return sourceEditors.textarea(me, holder);


        }





        var bakCssText;


        //解决在源码模式下getContent不能得到最新的内容问题


        var oldGetContent = me.getContent,


            bakAddress;





        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.body.style.cssText;


                    me.body.style.cssText += ';position:absolute;left:-32768px;top:-32768px;';








                    me.fireEvent('beforegetcontent');


                    var root = UM.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.body.parentNode);





                    sourceEditor.setContent(content);





                    var getStyleValue=function(attr){


                        return parseInt($(me.body).css(attr));


                    };


                    $(sourceEditor.container).width($(me.body).width()+getStyleValue("padding-left")+getStyleValue("padding-right"))


                        .height($(me.body).height());


                    setTimeout(function (){


                        sourceEditor.select();


                    });


                    //重置getContent，源码模式下取值也能是最新的数据


                    me.getContent = function (){


                        return sourceEditor.getContent() || '<p>' + (browser.ie ? '' : '<br/>')+'</p>';


                    };


                } else {


                    me.$body.css({


                        'position':'',


                        'left':'',


                        'top':''


                    });


//                    me.body.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(cont);


                    sourceEditor.dispose();


                    sourceEditor = null;


                    //还原getContent方法


                    me.getContent = oldGetContent;


                    var first = me.body.firstChild;


                    //trace:1106 都删除空了，下边会报错，所以补充一个p占位


                    if(!first){


                        me.body.innerHTML = '<p>'+(browser.ie?'':'<br/>')+'</p>';


                    }


                    //要在ifm为显示时ff才能取到selection,否则报错


                    //这里不能比较位置了


                    me.undoManger && me.undoManger.save(true);


                    if(browser.gecko){


                        me.body.contentEditable = true;


                    }


                    try{


                        me.selection.getRange().moveToAddress(bakAddress).select();


                    }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


                } ? oldQueryCommandState.apply(this, arguments)  : -1


            }


            return oldQueryCommandState.apply(this, arguments);


        };





    };





})();


///import core


///import plugins/undo.js


///commands 设置回车标签p或br


///commandsName  EnterKey


///commandsTitle  设置回车标签p或br


/**


 * @description 处理回车


 * @author zhanyi


 */


UM.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);


                    }





                }





            }





        }


    });





    browser.ie && me.addListener('setDisabled',function(){


        $(me.body).find('p').each(function(i,p){


            if(domUtils.isEmptyBlock(p)){


                p.innerHTML = '&nbsp;'


            }


        })


    })


};





///import core


///commands 预览


///commandsName  Preview


///commandsTitle  预览


/**


 * 预览


 * @function


 * @name UM.execCommand


 * @param   {String}   cmdName     preview预览编辑器内容


 */


UM.commands['preview'] = {


    execCommand : function(){


        var w = window.open('', '_blank', ''),


            d = w.document,


            c = this.getContent(null,null,true),


            path = this.getOpt('UMEDITOR_HOME_URL'),


            formula = c.indexOf('mathquill-embedded-latex')!=-1 ?


                '<link rel="stylesheet" href="' + path + 'third-party/mathquill/mathquill.css"/>' +


                '<script src="' + path + 'third-party/jquery.min.js"></script>' +


                '<script src="' + path + 'third-party/mathquill/mathquill.min.js"></script>':'';


        d.open();


        d.write('<html><head>' + formula + '</head><body><div>'+c+'</div></body></html>');


        d.close();


    },


    notNeedUndo : 1


};





///import core


///commands 加粗,斜体,上标,下标


///commandsName  Bold,Italic,Subscript,Superscript


///commandsTitle  加粗,加斜,下标,上标


/**


 * b u i等基础功能实现


 * @function


 * @name UM.execCommands


 * @param    {String}    cmdName    bold加粗。italic斜体。subscript上标。superscript下标。


*/


UM.plugins['basestyle'] = function(){


    var basestyles = ['bold','underline','superscript','subscript','italic','strikethrough'],


        me = this;


    //添加快捷键


    me.addshortcutkey({


        "Bold" : "ctrl+66",//^B


        "Italic" : "ctrl+73", //^I


        "Underline" : "ctrl+shift+85",//^U


        "strikeThrough" : 'ctrl+shift+83' //^s


    });


    //过滤最后的产出数据


    me.addOutputRule(function(root){


        $.each(root.getNodesByTagName('b i u strike s'),function(i,node){


            switch (node.tagName){


                case 'b':


                    node.tagName = 'strong';


                    break;


                case 'i':


                    node.tagName = 'em';


                    break;


                case 'u':


                    node.tagName = 'span';


                    node.setStyle('text-decoration','underline');


                    break;


                case 's':


                case 'strike':


                    node.tagName = 'span';


                    node.setStyle('text-decoration','line-through')


            }


        });


    });


    $.each(basestyles,function(i,cmd){


        me.commands[cmd] = {


            execCommand : function( cmdName ) {


                var rng = this.selection.getRange();


                if(rng.collapsed && this.queryCommandState(cmdName) != 1){


                    var node = this.document.createElement({


                        'bold':'strong',


                        'underline':'u',


                        'superscript':'sup',


                        'subscript':'sub',


                        'italic':'em',


                        'strikethrough':'strike'


                    }[cmdName]);


                    rng.insertNode(node).setStart(node,0).setCursor(false);


                    return true;


                }else{


                    return this.document.execCommand(cmdName)


                }





            },


            queryCommandState : function(cmdName) {


                if(browser.gecko){


                    return this.document.queryCommandState(cmdName)


                }


                var path = this.selection.getStartElementPath(),result = false;


                $.each(path,function(i,n){


                    switch (cmdName){


                        case 'bold':


                            if(n.nodeName == 'STRONG' || n.nodeName == 'B'){


                                result = 1;


                                return false;


                            }


                            break;


                        case 'underline':


                            if(n.nodeName == 'U' || n.nodeName == 'SPAN' && $(n).css('text-decoration') == 'underline'){


                                result = 1;


                                return false;


                            }


                            break;


                        case 'superscript':


                            if(n.nodeName == 'SUP'){


                                result = 1;


                                return false;


                            }


                            break;


                        case 'subscript':


                            if(n.nodeName == 'SUB'){


                                result = 1;


                                return false;


                            }


                            break;


                        case 'italic':


                            if(n.nodeName == 'EM' || n.nodeName == 'I'){


                                result = 1;


                                return false;


                            }


                            break;


                        case 'strikethrough':


                            if(n.nodeName == 'S' || n.nodeName == 'STRIKE' || n.nodeName == 'SPAN' && $(n).css('text-decoration') == 'line-through'){


                                result = 1;


                                return false;


                            }


                            break;


                    }


                })


                return result


            }


        };


    })


};








///import core


///import plugins/inserthtml.js


///commands 视频


///commandsName InsertVideo


///commandsTitle  插入视频


///commandsDialog  dialogs\video


UM.plugins['video'] = function (){


    var me =this,


        div;





    /**


     * 创建插入视频字符窜


     * @param url 视频地址


     * @param width 视频宽度


     * @param height 视频高度


     * @param align 视频对齐


     * @param toEmbed 是否以flash代替显示


     * @param addParagraph  是否需要添加P 标签


     */


    function creatInsertStr(url,width,height,id,align,toEmbed){


        return  !toEmbed ?





                '<img ' + (id ? 'id="' + id+'"' : '') + ' width="'+ width +'" height="' + height + '" _url="'+url+'" class="edui-faked-video"'  +


                ' src="' + me.options.UMEDITOR_HOME_URL+'themes/default/images/spacer.gif" style="background:url('+me.options.UMEDITOR_HOME_URL+'themes/default/images/videologo.gif) no-repeat center center; border:1px solid gray;'+(align ? 'float:' + align + ';': '')+'" />'





                :


                '<embed type="application/x-shockwave-flash" class="edui-faked-video" pluginspage="http://www.macromedia.com/go/getflashplayer"' +


                ' src="' + url + '" width="' + width  + '" height="' + height  + '"'  + (align ? ' style="float:' + align + '"': '') +


                ' wmode="transparent" play="true" loop="false" menu="false" allowscriptaccess="never" allowfullscreen="true" >';


    }





    function switchImgAndEmbed(root,img2embed){


        utils.each(root.getNodesByTagName(img2embed ? 'img' : 'embed'),function(node){


            if(node.getAttr('class') == 'edui-faked-video'){





                var html = creatInsertStr( img2embed ? node.getAttr('_url') : node.getAttr('src'),node.getAttr('width'),node.getAttr('height'),null,node.getStyle('float') || '',img2embed);


                node.parentNode.replaceChild(UM.uNode.createElement(html),node)


            }


        })


    }





    me.addOutputRule(function(root){


        switchImgAndEmbed(root,true)


    });


    me.addInputRule(function(root){


        switchImgAndEmbed(root)


    });





    me.commands["insertvideo"] = {


        execCommand: function (cmd, videoObjs){


            videoObjs = utils.isArray(videoObjs)?videoObjs:[videoObjs];


            var html = [],id = 'tmpVedio';


            for(var i=0,vi,len = videoObjs.length;i<len;i++){


                 vi = videoObjs[i];


                 html.push(creatInsertStr( vi.url, vi.width || 420,  vi.height || 280, id + i,vi.align,false));


            }


            me.execCommand("inserthtml",html.join(""),true);





        },


        queryCommandState : function(){


            var img = me.selection.getRange().getClosedNode(),


                flag = img && (img.className == "edui-faked-video");


            return flag ? 1 : 0;


        }


    };


};


///import core


///commands 全选


///commandsName  SelectAll


///commandsTitle  全选


/**


 * 选中所有


 * @function


 * @name UM.execCommand


 * @param   {String}   cmdName    selectall选中编辑器里的所有内容


 * @author zhanyi


*/


UM.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"


    });


};





//UM.plugins['removeformat'] = function () {


//    var me = this;


//    me.commands['removeformat'] = {


//        execCommand: function () {


//            me.document.execCommand('removeformat');


//


//            /* 处理ie8和firefox选区有链接时,清除格式的bug */


//            if (browser.gecko || browser.ie8 || browser.webkit) {


//                var nativeRange = this.selection.getNative().getRangeAt(0),


//                    common = nativeRange.commonAncestorContainer,


//                    rng = me.selection.getRange(),


//                    bk = rng.createBookmark();


//


//                function isEleInBookmark(node, bk){


//                    if ( (domUtils.getPosition(node, bk.start) & domUtils.POSITION_FOLLOWING) &&


//                        (domUtils.getPosition(bk.end, node) & domUtils.POSITION_FOLLOWING) ) {


//                        return true;


//                    } else if ( (domUtils.getPosition(node, bk.start) & domUtils.POSITION_CONTAINS) ||


//                        (domUtils.getPosition(node, bk.end) & domUtils.POSITION_CONTAINS) ) {


//                        return true;


//                    }


//                    return false;


//                }


//


//                $(common).find('a').each(function (k, a) {


//                    if ( isEleInBookmark(a, bk) ) {


//                        a.removeAttribute('style');


//                    }


//                });


//


//            }


//        }


//    };


//


//};


//








UM.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上的样式去掉


                                if(!dtd.$tableContent[current.tagName] && !dtd.$list[current.tagName]){


                                    domUtils.removeAttributes( current, removeFormatAttributes );


                                    if ( isRedundantSpan( current ) ){


                                        domUtils.remove( current, true );


                                    }


                                }





                            }


                        }


                        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();


            if(!range.collapsed) {


                doRemove( range );


                range.select();


            }





        }





    };





};


/*


 *   处理特殊键的兼容性问题


 */


UM.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 == 8) {


            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;


                }


            }


            start = rng.startContainer;


            if(rng.collapsed && start.nodeType == 1){


                var currentNode = start.childNodes[rng.startOffset-1];


                if(currentNode && currentNode.nodeType == 1 && currentNode.tagName == 'BR'){


                    me.fireEvent('saveScene');


                    rng.setStartBefore(currentNode).collapse(true);


                    domUtils.remove(currentNode);


                    rng.select();


                    me.fireEvent('saveScene');


                }


            }





            //trace:3613


            if(browser.chrome){


                if(rng.collapsed){





                    while(rng.startOffset == 0 && !domUtils.isEmptyBlock(rng.startContainer)){


                        rng.setStartBefore(rng.startContainer)


                    }


                    var pre = rng.startContainer.childNodes[rng.startOffset-1];


                    if(pre && pre.nodeName == 'BR'){


                        rng.setStartBefore(pre);


                        me.fireEvent('saveScene');


                        $(pre).remove();


                        rng.setCursor();


                        me.fireEvent('saveScene');


                    }





                }


            }


        }


        //trace:1634


        //ff的del键在容器空的时候，也会删除


        if(browser.gecko && keyCode == 46){


            var 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;


                }


            }


        }


    });


    me.addListener('keyup', function(type, evt) {


        var keyCode = evt.keyCode || evt.which,


            rng,me = this;


        if(keyCode == 8){


            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()


                }





            }


        }





    })


};


/**


 * 自动保存草稿


 */


UM.plugins['autosave'] = function() {








    var me = this,


        //无限循环保护


        lastSaveTime = new Date(),


        //最小保存间隔时间


        MIN_TIME = 20,


        //auto save key


        saveKey = null;








    //默认间隔时间


    me.setOpt('saveInterval', 500);





    //存储媒介封装


    var LocalStorage = UM.LocalStorage = ( function () {





        var storage = window.localStorage || getUserData() || null,


            LOCAL_FILE = "localStorage";





        return {





            saveLocalData: function ( key, data ) {





                if ( storage && data) {


                    storage.setItem( key, data  );


                    return true;


                }





                return false;





            },





            getLocalData: function ( key ) {





                if ( storage ) {


                    return storage.getItem( key );


                }





                return null;





            },





            removeItem: function ( key ) {





                storage && storage.removeItem( key );





            }





        };





        function getUserData () {





            var container = document.createElement( "div" );


            container.style.display = "none";





            if( !container.addBehavior ) {


                return null;


            }





            container.addBehavior("#default#userdata");





            return {





                getItem: function ( key ) {





                    var result = null;





                    try {


                        document.body.appendChild( container );


                        container.load( LOCAL_FILE );


                        result = container.getAttribute( key );


                        document.body.removeChild( container );


                    } catch ( e ) {


                    }





                    return result;





                },





                setItem: function ( key, value ) {





                    document.body.appendChild( container );


                    container.setAttribute( key, value );


                    container.save( LOCAL_FILE );


                    document.body.removeChild( container );





                },


//               暂时没有用到


//                clear: function () {


//


//                    var expiresTime = new Date();


//                    expiresTime.setFullYear( expiresTime.getFullYear() - 1 );


//                    document.body.appendChild( container );


//                    container.expires = expiresTime.toUTCString();


//                    container.save( LOCAL_FILE );


//                    document.body.removeChild( container );


//


//                },





                removeItem: function ( key ) {





                    document.body.appendChild( container );


                    container.removeAttribute( key );


                    container.save( LOCAL_FILE );


                    document.body.removeChild( container );





                }





            };





        }





    } )();





    function save ( editor ) {





        var saveData = null;





        if ( new Date() - lastSaveTime < MIN_TIME ) {


            return;


        }





        if ( !editor.hasContents() ) {


            //这里不能调用命令来删除， 会造成事件死循环


            saveKey && LocalStorage.removeItem( saveKey );


            return;


        }





        lastSaveTime = new Date();





        editor._saveFlag = null;





        saveData = me.body.innerHTML;





        if ( editor.fireEvent( "beforeautosave", {


            content: saveData


        } ) === false ) {


            return;


        }





        LocalStorage.saveLocalData( saveKey, saveData );





        editor.fireEvent( "afterautosave", {


            content: saveData


        } );





    }





    me.addListener('ready', function(){


        var _suffix = "-drafts-data",


            key = null;





        if ( me.key ) {


            key = me.key + _suffix;


        } else {


            key = ( me.container.parentNode.id || 'ue-common' ) + _suffix;


        }





        //页面地址+编辑器ID 保持唯一


        saveKey = ( location.protocol + location.host + location.pathname ).replace( /[.:\/]/g, '_' ) + key;


    });





    me.addListener('contentchange', function(){





        if ( !saveKey ) {


            return;


        }





        if ( me._saveFlag ) {


            window.clearTimeout( me._saveFlag );


        }





        if ( me.options.saveInterval > 0 ) {





            me._saveFlag = window.setTimeout( function () {





                save( me );





            }, me.options.saveInterval );





        } else {





            save(me);





        }





    })








    me.commands['clearlocaldata'] = {


        execCommand:function (cmd, name) {


            if ( saveKey && LocalStorage.getLocalData( saveKey ) ) {


                LocalStorage.removeItem( saveKey )


            }


        },


        notNeedUndo: true,


        ignoreContentChange:true


    };





    me.commands['getlocaldata'] = {


        execCommand:function (cmd, name) {


            return saveKey ? LocalStorage.getLocalData( saveKey ) || '' : '';


        },


        notNeedUndo: true,


        ignoreContentChange:true


    };





    me.commands['drafts'] = {


        execCommand:function (cmd, name) {


            if ( saveKey ) {


                me.body.innerHTML = LocalStorage.getLocalData( saveKey ) || '<p>'+(browser.ie ? '&nbsp;' : '<br/>')+'</p>';


                me.focus(true);


            }


        },


        queryCommandState: function () {


            return saveKey ? ( LocalStorage.getLocalData( saveKey ) === null ? -1 : 0 ) : -1;


        },


        notNeedUndo: true,


        ignoreContentChange:true


    }





};





/**


 * @description


 * 1.拖放文件到编辑区域，自动上传并插入到选区


 * 2.插入粘贴板的图片，自动上传并插入到选区


 * @author Jinqn


 * @date 2013-10-14


 */


UM.plugins['autoupload'] = function () {





    var me = this;





    me.setOpt('pasteImageEnabled', true);


    me.setOpt('dropFileEnabled', true);


    var sendAndInsertImage = function (file, editor) {


        //模拟数据


        var fd = new FormData();


        fd.append(editor.options.imageFieldName || 'upfile', file, file.name || ('blob.' + file.type.substr('image/'.length)));


        fd.append('type', 'ajax');


        var xhr = new XMLHttpRequest();


        xhr.open("post", me.options.imageUrl, true);


        xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");


        xhr.addEventListener('load', function (e) {


            try {


                var json = eval('('+e.target.response+')'),


                    link = json.url,


                    picLink = me.options.imagePath + link;


                editor.execCommand('insertimage', {


                    src: picLink,


                    _src: picLink


                });


            } catch (er) {


            }


        });


        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;


    }





    me.addListener('ready', function () {


        if (window.FormData && window.FileReader) {


            var autoUploadHandler = function (e) {


                var hasImg = false,


                    items;


                //获取粘贴板文件列表或者拖放文件列表


                items = e.type == 'paste' ? getPasteImage(e.originalEvent) : getDropImage(e.originalEvent);


                if (items) {


                    var len = items.length,


                        file;


                    while (len--) {


                        file = items[len];


                        if (file.getAsFile) file = file.getAsFile();


                        if (file && file.size > 0 && /image\/\w+/i.test(file.type)) {


                            sendAndInsertImage(file, me);


                            hasImg = true;


                        }


                    }


                    if (hasImg) return false;


                }





            };


            me.getOpt('pasteImageEnabled') && me.$body.on('paste', autoUploadHandler);


            me.getOpt('dropFileEnabled') && me.$body.on('drop', autoUploadHandler);





            //取消拖放图片时出现的文字光标位置提示


            me.$body.on('dragover', function (e) {


                if (e.originalEvent.dataTransfer.types[0] == 'Files') {


                    return false;


                }


            });


        }


    });





};


/**


 * 公式插件


 */


UM.plugins['formula'] = function () {


    var me = this;





    function getActiveIframe() {


        return me.$body.find('iframe.edui-formula-active')[0] || null;


    }





    function blurActiveIframe(){


        var iframe = getActiveIframe();


        iframe && iframe.contentWindow.formula.blur();


    }





    me.addInputRule(function (root) {


        $.each(root.getNodesByTagName('span'), function (i, node) {


            if (node.hasClass('mathquill-embedded-latex')) {


                var firstChild, latex = '';


                while(firstChild = node.firstChild()){


                    latex += firstChild.data;


                    node.removeChild(firstChild);


                }


                node.tagName = 'iframe';


                node.setAttr({


                    'frameborder': '0',


                    'src': me.getOpt('UMEDITOR_HOME_URL') + 'dialogs/formula/formula.html',


                    'data-latex': utils.unhtml(latex)


                });


            }


        });


    });


    me.addOutputRule(function (root) {


        $.each(root.getNodesByTagName('iframe'), function (i, node) {


            if (node.hasClass('mathquill-embedded-latex')) {


                node.tagName = 'span';


                node.appendChild(UM.uNode.createText(node.getAttr('data-latex')));


                node.setAttr({


                    'frameborder': '',


                    'src': '',


                    'data-latex': ''


                });


            }


        });


    });


    me.addListener('click', function(){


        blurActiveIframe();


    });


    me.addListener('afterexeccommand', function(type, cmd){


        if(cmd != 'formula') {


            blurActiveIframe();


        }


    });





    me.commands['formula'] = {


        execCommand: function (cmd, latex) {


            var iframe = getActiveIframe();


            if (iframe) {


                iframe.contentWindow.formula.insertLatex(latex);


            } else {


                me.execCommand('inserthtml', '<span class="mathquill-embedded-latex">' + latex + '</span>');


                browser.ie && browser.ie9below && setTimeout(function(){


                    var rng = me.selection.getRange(),


                        startContainer = rng.startContainer;


                    if(startContainer.nodeType == 1 && !startContainer.childNodes[rng.startOffset]){


                        rng.insertNode(me.document.createTextNode(' '));


                        rng.setCursor()


                    }


                },100)


            }


        },


        queryCommandState: function (cmd) {


            return 0;


        },


        queryCommandValue: function (cmd) {


            var iframe = getActiveIframe();


            return iframe && iframe.contentWindow.formula.getLatex();


        }


    }





};





(function ($) {


    //对jquery的扩展


    $.parseTmpl = function parse(str, data) {


        var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' + 'with(obj||{}){__p.push(\'' + str.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/<%=([\s\S]+?)%>/g,function (match, code) {


            return "'," + code.replace(/\\'/g, "'") + ",'";


        }).replace(/<%([\s\S]+?)%>/g,function (match, code) {


                return "');" + code.replace(/\\'/g, "'").replace(/[\r\n\t]/g, ' ') + "__p.push('";


            }).replace(/\r/g, '\\r').replace(/\n/g, '\\n').replace(/\t/g, '\\t') + "');}return __p.join('');";


        var func = new Function('obj', tmpl);


        return data ? func(data) : func;


    };


    $.extend2 = function (t, s) {


        var a = arguments,


            notCover = $.type(a[a.length - 1]) == 'boolean' ? a[a.length - 1] : false,


            len = $.type(a[a.length - 1]) == 'boolean' ? a.length - 1 : a.length;


        for (var i = 1; i < len; i++) {


            var x = a[i];


            for (var k in x) {


                if (!notCover || !t.hasOwnProperty(k)) {


                    t[k] = x[k];


                }


            }


        }


        return t;


    };





    $.IE6 = !!window.ActiveXObject && parseFloat(navigator.userAgent.match(/msie (\d+)/i)[1]) == 6;





    //所有ui的基类


    var _eventHandler = [];


    var _widget = function () {


    };


    var _prefix = 'edui';


    _widget.prototype = {


        on: function (ev, cb) {


            this.root().on(ev, $.proxy(cb, this));


            return this;


        },


        off: function (ev, cb) {


            this.root().off(ev, $.proxy(cb, this));


            return this;


        },


        trigger: function (ev, data) {


            return  this.root().trigger(ev, data) === false ? false : this;


        },


        root: function ($el) {


            return this._$el || (this._$el = $el);


        },


        destroy: function () {





        },


        data: function (key, val) {


            if (val !== undefined) {


                this.root().data(_prefix + key, val);


                return this;


            } else {


                return this.root().data(_prefix + key)


            }


        },


        register: function (eventName, $el, fn) {


            _eventHandler.push({


                'evtname': eventName,


                '$els': $.isArray($el) ? $el : [$el],


                handler: $.proxy(fn, $el)


            })


        }


    };





    //从jq实例上拿到绑定的widget实例


    $.fn.edui = function (obj) {


        return obj ? this.data('eduiwidget', obj) : this.data('eduiwidget');


    };





    function _createClass(ClassObj, properties, supperClass) {


        ClassObj.prototype = $.extend2(


            $.extend({}, properties),


            (UM.ui[supperClass] || _widget).prototype,


            true


        );


        ClassObj.prototype.supper = (UM.ui[supperClass] || _widget).prototype;


        //父class的defaultOpt 合并


        if( UM.ui[supperClass] && UM.ui[supperClass].prototype.defaultOpt ) {





            var parentDefaultOptions = UM.ui[supperClass].prototype.defaultOpt,


                subDefaultOptions = ClassObj.prototype.defaultOpt;





            ClassObj.prototype.defaultOpt = $.extend( {}, parentDefaultOptions, subDefaultOptions || {} );





        }


        return ClassObj


    }





    var _guid = 1;





    function mergeToJQ(ClassObj, className) {


        $[_prefix + className] = ClassObj;


        $.fn[_prefix + className] = function (opt) {


            var result, args = Array.prototype.slice.call(arguments, 1);





            this.each(function (i, el) {


                var $this = $(el);


                var obj = $this.edui();


                if (!obj) {


                    ClassObj(!opt || !$.isPlainObject(opt) ? {} : opt, $this);


                    $this.edui(obj)


                }


                if ($.type(opt) == 'string') {


                    if (opt == 'this') {


                        result = obj;


                    } else {


                        result = obj[opt].apply(obj, args);


                        if (result !== obj && result !== undefined) {


                            return false;


                        }


                        result = null;


                    }





                }


            });





            return result !== null ? result : this;


        }


    }





    UM.ui = {


        define: function (className, properties, supperClass) {


            var ClassObj = UM.ui[className] = _createClass(function (options, $el) {


                    var _obj = function () {


                    };


                    $.extend(_obj.prototype, ClassObj.prototype, {


                            guid: className + _guid++,


                            widgetName: className


                        }


                    );


                    var obj = new _obj;


                    if ($.type(options) == 'string') {


                        obj.init && obj.init({});


                        obj.root().edui(obj);


                        obj.root().find('a').click(function (evt) {


                            evt.preventDefault()


                        });


                        return obj.root()[_prefix + className].apply(obj.root(), arguments)


                    } else {


                        $el && obj.root($el);


                        obj.init && obj.init(!options || $.isPlainObject(options) ? $.extend2(options || {}, obj.defaultOpt || {}, true) : options);


                        try{


                            obj.root().find('a').click(function (evt) {


                                evt.preventDefault()


                            });


                        }catch(e){


                        }





                        return obj.root().edui(obj);


                    }





                },properties, supperClass);





            mergeToJQ(ClassObj, className);


        }


    };





    $(function () {


        $(document).on('click mouseup mousedown dblclick mouseover', function (evt) {


            $.each(_eventHandler, function (i, obj) {


                if (obj.evtname == evt.type) {


                    $.each(obj.$els, function (i, $el) {


                        if ($el[0] !== evt.target && !$.contains($el[0], evt.target)) {


                            obj.handler(evt);


                        }


                    })


                }


            })


        })


    })


})(jQuery);


//button 类


UM.ui.define('button', {


    tpl: '<<%if(!texttype){%>div class="edui-btn edui-btn-<%=icon%> <%if(name){%>edui-btn-name-<%=name%><%}%>" unselectable="on" onmousedown="return false" <%}else{%>a class="edui-text-btn"<%}%><% if(title) {%> data-original-title="<%=title%>" <%};%>> ' +


        '<% if(icon) {%><div unselectable="on" class="edui-icon-<%=icon%> edui-icon"></div><% }; %><%if(text) {%><span unselectable="on" onmousedown="return false" class="edui-button-label"><%=text%></span><%}%>' +


        '<%if(caret && text){%><span class="edui-button-spacing"></span><%}%>' +


        '<% if(caret) {%><span unselectable="on" onmousedown="return false" class="edui-caret"></span><% };%></<%if(!texttype){%>div<%}else{%>a<%}%>>',


    defaultOpt: {


        text: '',


        title: '',


        icon: '',


        width: '',


        caret: false,


        texttype: false,


        click: function () {


        }


    },


    init: function (options) {


        var me = this;





        me.root($($.parseTmpl(me.tpl, options)))


            .click(function (evt) {


                me.wrapclick(options.click, evt)


            });





        me.root().hover(function () {


            if(!me.root().hasClass("edui-disabled")){


                me.root().toggleClass('edui-hover')


            }


        })





        return me;


    },


    wrapclick: function (fn, evt) {


        if (!this.disabled()) {


            this.root().trigger('wrapclick');


            $.proxy(fn, this, evt)()


        }


        return this;


    },


    label: function (text) {


        if (text === undefined) {


            return this.root().find('.edui-button-label').text();


        } else {


            this.root().find('.edui-button-label').text(text);


            return this;


        }


    },


    disabled: function (state) {


        if (state === undefined) {


            return this.root().hasClass('edui-disabled')


        }


        this.root().toggleClass('edui-disabled', state);


        if(this.root().hasClass('edui-disabled')){


            this.root().removeClass('edui-hover')


        }


        return this;


    },


    active: function (state) {


        if (state === undefined) {


            return this.root().hasClass('edui-active')


        }


        this.root().toggleClass('edui-active', state)





        return this;


    },


    mergeWith: function ($obj) {


        var me = this;


        me.data('$mergeObj', $obj);


        $obj.edui().data('$mergeObj', me.root());


        if (!$.contains(document.body, $obj[0])) {


            $obj.appendTo(me.root());


        }


        me.on('click',function () {


            me.wrapclick(function () {


                $obj.edui().show();


            })


        }).register('click', me.root(), function (evt) {


                $obj.hide()


            });


    }


});


//toolbar 类


(function () {


    UM.ui.define('toolbar', {


        tpl: '<div class="edui-toolbar"  ><div class="edui-btn-toolbar" unselectable="on" onmousedown="return false"  ></div></div>'


          ,


        init: function () {


            var $root = this.root($(this.tpl));


            this.data('$btnToolbar', $root.find('.edui-btn-toolbar'))


        },


        appendToBtnmenu : function(data){


            var $cont = this.data('$btnToolbar');


            data = $.isArray(data) ? data : [data];


            $.each(data,function(i,$item){


                $cont.append($item)


            })


        }


    });


})();





//menu 类


UM.ui.define('menu',{


    show : function($obj,dir,fnname,topOffset,leftOffset){





        fnname = fnname || 'position';


        if(this.trigger('beforeshow') === false){


            return;


        }else{


            this.root().css($.extend({display:'block'},$obj ? {


                top : $obj[fnname]().top + ( dir == 'right' ? 0 : $obj.outerHeight()) - (topOffset || 0),


                left : $obj[fnname]().left + (dir == 'right' ?  $obj.outerWidth() : 0) -  (leftOffset || 0)


            }:{}))


            this.trigger('aftershow');


        }


    },


    hide : function(all){


        var $parentmenu;


        if(this.trigger('beforehide') === false){


            return;


        } else {





            if($parentmenu = this.root().data('parentmenu')){


                if($parentmenu.data('parentmenu')|| all)


                    $parentmenu.edui().hide();


            }


            this.root().css('display','none');


            this.trigger('afterhide');


        }


    },


    attachTo : function($obj){


        var me = this;


        if(!$obj.data('$mergeObj')){


            $obj.data('$mergeObj',me.root());


            $obj.on('wrapclick',function(evt){


                me.show()


            });


            me.register('click',$obj,function(evt){


               me.hide()


            });


            me.data('$mergeObj',$obj)


        }


    }


});


//dropmenu 类


UM.ui.define('dropmenu', {


    tmpl: '<ul class="edui-dropdown-menu" aria-labelledby="dropdownMenu" >' +


        '<%for(var i=0,ci;ci=data[i++];){%>' +


        '<%if(ci.divider){%><li class="edui-divider"></li><%}else{%>' +


        '<li <%if(ci.active||ci.disabled){%>class="<%= ci.active|| \'\' %> <%=ci.disabled||\'\' %>" <%}%> data-value="<%= ci.value%>">' +


        '<a href="#" tabindex="-1"><em class="edui-dropmenu-checkbox"><i class="edui-icon-ok"></i></em><%= ci.label%></a>' +


        '</li><%}%>' +


        '<%}%>' +


        '</ul>',


    defaultOpt: {


        data: [],


        click: function () {





        }


    },


    init: function (options) {


        var me = this;


        var eventName = {


            click: 1,


            mouseover: 1,


            mouseout: 1


        };





        this.root($($.parseTmpl(this.tmpl, options))).on('click', 'li[class!="edui-disabled edui-divider edui-dropdown-submenu"]',function (evt) {


            $.proxy(options.click, me, evt, $(this).data('value'), $(this))()


        }).find('li').each(function (i, el) {


                var $this = $(this);


                if (!$this.hasClass("edui-disabled edui-divider edui-dropdown-submenu")) {


                    var data = options.data[i];


                    $.each(eventName, function (k) {


                        data[k] && $this[k](function (evt) {


                            $.proxy(data[k], el)(evt, data, me.root)


                        })


                    })


                }


            })





    },


    disabled: function (cb) {


        $('li[class!=edui-divider]', this.root()).each(function () {


            var $el = $(this);


            if (cb === true) {


                $el.addClass('edui-disabled')


            } else if ($.isFunction(cb)) {


                $el.toggleClass('edui-disabled', cb(li))


            } else {


                $el.removeClass('edui-disabled')


            }





        });


    },


    val: function (val) {


        var currentVal;


        $('li[class!="edui-divider edui-disabled edui-dropdown-submenu"]', this.root()).each(function () {


            var $el = $(this);


            if (val === undefined) {


                if ($el.find('em.edui-dropmenu-checked').length) {


                    currentVal = $el.data('value');


                    return false


                }


            } else {


                $el.find('em').toggleClass('edui-dropmenu-checked', $el.data('value') == val)


            }


        });


        if (val === undefined) {


            return currentVal


        }


    },


    addSubmenu: function (label, menu, index) {


        index = index || 0;





        var $list = $('li[class!=edui-divider]', this.root());


        var $node = $('<li class="edui-dropdown-submenu"><a tabindex="-1" href="#">' + label + '</a></li>').append(menu);





        if (index >= 0 && index < $list.length) {


            $node.insertBefore($list[index]);


        } else if (index < 0) {


            $node.insertBefore($list[0]);


        } else if (index >= $list.length) {


            $node.appendTo($list);


        }


    }


}, 'menu');


//splitbutton 类


///import button


UM.ui.define('splitbutton',{


    tpl :'<div class="edui-splitbutton <%if (name){%>edui-splitbutton-<%= name %><%}%>"  unselectable="on" <%if(title){%>data-original-title="<%=title%>"<%}%>><div class="edui-btn"  unselectable="on" ><%if(icon){%><div  unselectable="on" class="edui-icon-<%=icon%> edui-icon"></div><%}%><%if(text){%><%=text%><%}%></div>'+


            '<div  unselectable="on" class="edui-btn edui-dropdown-toggle" >'+


                '<div  unselectable="on" class="edui-caret"><\/div>'+


            '</div>'+


        '</div>',


    defaultOpt:{


        text:'',


        title:'',


        click:function(){}


    },


    init : function(options){


        var me = this;


        me.root( $($.parseTmpl(me.tpl,options)));


        me.root().find('.edui-btn:first').click(function(evt){


            if(!me.disabled()){


                $.proxy(options.click,me)();


            }


        });


        me.root().find('.edui-dropdown-toggle').click(function(){


            if(!me.disabled()){


                me.trigger('arrowclick')


            }


        });


        me.root().hover(function () {


            if(!me.root().hasClass("edui-disabled")){


                me.root().toggleClass('edui-hover')


            }


        });





        return me;


    },


    wrapclick:function(fn,evt){


        if(!this.disabled()){


            $.proxy(fn,this,evt)()


        }


        return this;


    },


    disabled : function(state){


        if(state === undefined){


            return this.root().hasClass('edui-disabled')


        }


        this.root().toggleClass('edui-disabled',state).find('.edui-btn').toggleClass('edui-disabled',state);


        return this;


    },


    active:function(state){


        if(state === undefined){


            return this.root().hasClass('edui-active')


        }


        this.root().toggleClass('edui-active',state).find('.edui-btn:first').toggleClass('edui-active',state);


        return this;


    },


    mergeWith:function($obj){


        var me = this;


        me.data('$mergeObj',$obj);


        $obj.edui().data('$mergeObj',me.root());


        if(!$.contains(document.body,$obj[0])){


            $obj.appendTo(me.root());


        }


        me.root().delegate('.edui-dropdown-toggle','click',function(){


            me.wrapclick(function(){


                $obj.edui().show();


            })


        });


        me.register('click',me.root().find('.edui-dropdown-toggle'),function(evt){


            $obj.hide()


        });


    }


});


/**


 * Created with JetBrains PhpStorm.


 * User: hn


 * Date: 13-7-10


 * Time: 下午3:07


 * To change this template use File | Settings | File Templates.


 */


UM.ui.define('colorsplitbutton',{





    tpl : '<div class="edui-splitbutton <%if (name){%>edui-splitbutton-<%= name %><%}%>"  unselectable="on" <%if(title){%>data-original-title="<%=title%>"<%}%>><div class="edui-btn"  unselectable="on" ><%if(icon){%><div  unselectable="on" class="edui-icon-<%=icon%> edui-icon"></div><%}%><div class="edui-splitbutton-color-label" <%if (color) {%>style="background: <%=color%>"<%}%>></div><%if(text){%><%=text%><%}%></div>'+


            '<div  unselectable="on" class="edui-btn edui-dropdown-toggle" >'+


            '<div  unselectable="on" class="edui-caret"><\/div>'+


            '</div>'+


            '</div>',


    defaultOpt: {


        color: ''


    },


    init: function( options ){





        var me = this;





        me.supper.init.call( me, options );





    },


    colorLabel: function(){


        return this.root().find('.edui-splitbutton-color-label');


    }





}, 'splitbutton');


//popup 类


UM.ui.define('popup', {


    tpl: '<div class="edui-dropdown-menu edui-popup"'+


        '<%if(!<%=stopprop%>){%>onmousedown="return false"<%}%>'+


        '><div class="edui-popup-body" unselectable="on" onmousedown="return false"><%=subtpl%></div>' +


        '<div class="edui-popup-caret"></div>' +


        '</div>',


    defaultOpt: {


        stopprop:false,


        subtpl: '',


        width: '',


        height: ''


    },


    init: function (options) {


        this.root($($.parseTmpl(this.tpl, options)));


        return this;


    },


    mergeTpl: function (data) {


        return $.parseTmpl(this.tpl, {subtpl: data});


    },


    show: function ($obj, posObj) {


        if (!posObj) posObj = {};





        var fnname = posObj.fnname || 'position';


        if (this.trigger('beforeshow') === false) {


            return;


        } else {


            this.root().css($.extend({display: 'block'}, $obj ? {


                top: $obj[fnname]().top + ( posObj.dir == 'right' ? 0 : $obj.outerHeight()) - (posObj.offsetTop || 0),


                left: $obj[fnname]().left + (posObj.dir == 'right' ? $obj.outerWidth() : 0) - (posObj.offsetLeft || 0),


                position: 'absolute'


            } : {}));





            this.root().find('.edui-popup-caret').css({


                top: posObj.caretTop || 0,


                left: posObj.caretLeft || 0,


                position: 'absolute'


            }).addClass(posObj.caretDir || "up")





        }


        this.trigger("aftershow");


    },


    hide: function () {


        this.root().css('display', 'none');


        this.trigger('afterhide')


    },


    attachTo: function ($obj, posObj) {


        var me = this


        if (!$obj.data('$mergeObj')) {


            $obj.data('$mergeObj', me.root());


            $obj.on('wrapclick', function (evt) {


                me.show($obj, posObj)


            });


            me.register('click', $obj, function (evt) {


                me.hide()


            });


            me.data('$mergeObj', $obj)


        }


    },


    getBodyContainer: function () {


        return this.root().find(".edui-popup-body");


    }


});


//scale 类


UM.ui.define('scale', {


    tpl: '<div class="edui-scale" unselectable="on">' +


        '<span class="edui-scale-hand0"></span>' +


        '<span class="edui-scale-hand1"></span>' +


        '<span class="edui-scale-hand2"></span>' +


        '<span class="edui-scale-hand3"></span>' +


        '<span class="edui-scale-hand4"></span>' +


        '<span class="edui-scale-hand5"></span>' +


        '<span class="edui-scale-hand6"></span>' +


        '<span class="edui-scale-hand7"></span>' +


        '</div>',


    defaultOpt: {


        $doc: $(document),


        $wrap: $(document)


    },


    init: function (options) {


        if(options.$doc) this.defaultOpt.$doc = options.$doc;


        if(options.$wrap) this.defaultOpt.$wrap = options.$wrap;


        this.root($($.parseTmpl(this.tpl, options)));


        this.initStyle();


        this.startPos = this.prePos = {x: 0, y: 0};


        this.dragId = -1;


        return this;


    },


    initStyle: function () {


        utils.cssRule('edui-style-scale', '.edui-scale{display:none;position:absolute;border:1px solid #38B2CE;cursor:hand;}' +


            '.edui-scale span{position:absolute;left:0;top:0;width:7px;height:7px;overflow:hidden;font-size:0px;display:block;background-color:#3C9DD0;}'


            + '.edui-scale .edui-scale-hand0{cursor:nw-resize;top:0;margin-top:-4px;left:0;margin-left:-4px;}'


            + '.edui-scale .edui-scale-hand1{cursor:n-resize;top:0;margin-top:-4px;left:50%;margin-left:-4px;}'


            + '.edui-scale .edui-scale-hand2{cursor:ne-resize;top:0;margin-top:-4px;left:100%;margin-left:-3px;}'


            + '.edui-scale .edui-scale-hand3{cursor:w-resize;top:50%;margin-top:-4px;left:0;margin-left:-4px;}'


            + '.edui-scale .edui-scale-hand4{cursor:e-resize;top:50%;margin-top:-4px;left:100%;margin-left:-3px;}'


            + '.edui-scale .edui-scale-hand5{cursor:sw-resize;top:100%;margin-top:-3px;left:0;margin-left:-4px;}'


            + '.edui-scale .edui-scale-hand6{cursor:s-resize;top:100%;margin-top:-3px;left:50%;margin-left:-4px;}'


            + '.edui-scale .edui-scale-hand7{cursor:se-resize;top:100%;margin-top:-3px;left:100%;margin-left:-3px;}');


    },


    _eventHandler: function (e) {


        var me = this,


            $doc = me.defaultOpt.$doc;


        switch (e.type) {


            case 'mousedown':


                var hand = e.target || e.srcElement, hand;


                if (hand.className.indexOf('edui-scale-hand') != -1) {


                    me.dragId = hand.className.slice(-1);


                    me.startPos.x = me.prePos.x = e.clientX;


                    me.startPos.y = me.prePos.y = e.clientY;


                    $doc.bind('mousemove', $.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;


                    me.updateTargetElement();


                }


                break;


            case 'mouseup':


                if (me.dragId != -1) {


                    me.dragId = -1;


                    me.updateTargetElement();


                    var $target = me.data('$scaleTarget');


                    if ($target.parent()) me.attachTo(me.data('$scaleTarget'));


                }


                $doc.unbind('mousemove', $.proxy(me._eventHandler, me));


                break;


            default:


                break;


        }


    },


    updateTargetElement: function () {


        var me = this,


            $root = me.root(),


            $target = me.data('$scaleTarget');


        $target.css({width: $root.width(), height: $root.height()});


        me.attachTo($target);


    },


    updateContainerStyle: function (dir, offset) {


        var me = this,


            $dom = me.root(),


            tmp,


            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]


            ];





        if (rect[dir][0] != 0) {


            tmp = parseInt($dom.offset().left) + offset.x;


            $dom.css('left', me._validScaledProp('left', tmp));


        }


        if (rect[dir][1] != 0) {


            tmp = parseInt($dom.offset().top) + offset.y;


            $dom.css('top', me._validScaledProp('top', tmp));


        }


        if (rect[dir][2] != 0) {


            tmp = $dom.width() + rect[dir][2] * offset.x;


            $dom.css('width', me._validScaledProp('width', tmp));


        }


        if (rect[dir][3] != 0) {


            tmp = $dom.height() + rect[dir][3] * offset.y;


            $dom.css('height', me._validScaledProp('height', tmp));


        }


    },


    _validScaledProp: function (prop, value) {


        var $ele = this.root(),


            $wrap = this.defaultOpt.$doc,


            calc = function(val, a, b){


                return (val + a) > b ? b - a : value;


            };





        value = isNaN(value) ? 0 : value;


        switch (prop) {


            case 'left':


                return value < 0 ? 0 : calc(value, $ele.width(), $wrap.width());


            case 'top':


                return value < 0 ? 0 : calc(value, $ele.height(),$wrap.height());


            case 'width':


                return value <= 0 ? 1 : calc(value, $ele.offset().left, $wrap.width());


            case 'height':


                return value <= 0 ? 1 : calc(value, $ele.offset().top, $wrap.height());


        }


    },


    show: function ($obj) {


        var me = this;


        if ($obj) me.attachTo($obj);


        me.root().bind('mousedown', $.proxy(me._eventHandler, me));


        me.defaultOpt.$doc.bind('mouseup', $.proxy(me._eventHandler, me));


        me.root().show();


        me.trigger("aftershow");


    },


    hide: function () {


        var me = this;


        me.root().unbind('mousedown', $.proxy(me._eventHandler, me));


        me.defaultOpt.$doc.unbind('mouseup', $.proxy(me._eventHandler, me));


        me.root().hide();


        me.trigger('afterhide')


    },


    attachTo: function ($obj) {


        var me = this,


            imgPos = $obj.offset(),


            $root = me.root(),


            $wrap = me.defaultOpt.$wrap,


            posObj = $wrap.offset();





        me.data('$scaleTarget', $obj);


        me.root().css({


            position: 'absolute',


            width: $obj.width(),


            height: $obj.height(),


            left: imgPos.left - posObj.left - parseInt($wrap.css('border-left-width')) - parseInt($root.css('border-left-width')),


            top: imgPos.top - posObj.top - parseInt($wrap.css('border-top-width')) - parseInt($root.css('border-top-width'))


        });


    },


    getScaleTarget: function () {


        return this.data('$scaleTarget')[0];


    }


});


//colorpicker 类


UM.ui.define('colorpicker', {


    tpl: function (opt) {


        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(',');





        var html = '<div unselectable="on" onmousedown="return false" class="edui-colorpicker<%if (name){%> edui-colorpicker-<%=name%><%}%>" >' +


            '<table unselectable="on" onmousedown="return false">' +


            '<tr><td colspan="10">'+opt.lang_themeColor+'</td> </tr>' +


            '<tr class="edui-colorpicker-firstrow" >';





        for (var i = 0; i < COLORS.length; i++) {


            if (i && i % 10 === 0) {


                html += '</tr>' + (i == 60 ? '<tr><td colspan="10">'+opt.lang_standardColor+'</td></tr>' : '') + '<tr' + (i == 60 ? ' class="edui-colorpicker-firstrow"' : '') + '>';


            }


            html += i < 70 ? '<td><a unselectable="on" onmousedown="return false" title="' + COLORS[i] + '" class="edui-colorpicker-colorcell"' +


                ' data-color="#' + COLORS[i] + '"' +


                ' style="background-color:#' + COLORS[i] + ';border:solid #ccc;' +


                (i < 10 || i >= 60 ? 'border-width:1px;' :


                    i >= 10 && i < 20 ? 'border-width:1px 1px 0 1px;' :


                        'border-width:0 1px 0 1px;') +


                '"' +


                '></a></td>' : '';


        }


        html += '</tr></table></div>';


        return html;


    },


    init: function (options) {


        var me = this;


        me.root($($.parseTmpl(me.supper.mergeTpl(me.tpl(options)),options)));





        me.root().on("click",function (e) {


            me.trigger('pickcolor',  $(e.target).data('color'));


        });


    }


}, 'popup');


/**


 * Created with JetBrains PhpStorm.


 * User: hn


 * Date: 13-5-29


 * Time: 下午8:01


 * To change this template use File | Settings | File Templates.


 */





(function(){





    var widgetName = 'combobox',


        itemClassName = 'edui-combobox-item',


        HOVER_CLASS = 'edui-combobox-item-hover',


        ICON_CLASS = 'edui-combobox-checked-icon',


        labelClassName = 'edui-combobox-item-label';





    UM.ui.define( widgetName, ( function(){





        return {


            tpl: "<ul class=\"dropdown-menu edui-combobox-menu<%if (comboboxName!=='') {%> edui-combobox-<%=comboboxName%><%}%>\" unselectable=\"on\" onmousedown=\"return false\" role=\"menu\" aria-labelledby=\"dropdownMenu\">" +


                "<%if(autoRecord) {%>" +


                "<%for( var i=0, len = recordStack.length; i<len; i++ ) {%>" +


                "<%var index = recordStack[i];%>" +


                "<li class=\"<%=itemClassName%><%if( selected == index ) {%> edui-combobox-checked<%}%>\" data-item-index=\"<%=index%>\" unselectable=\"on\" onmousedown=\"return false\">" +


                "<span class=\"edui-combobox-icon\" unselectable=\"on\" onmousedown=\"return false\"></span>" +


                "<label class=\"<%=labelClassName%>\" style=\"<%=itemStyles[ index ]%>\" unselectable=\"on\" onmousedown=\"return false\"><%=items[index]%></label>" +


                "</li>" +


                "<%}%>" +


                "<%if( i ) {%>" +


                "<li class=\"edui-combobox-item-separator\"></li>" +


                "<%}%>" +


                "<%}%>" +


                "<%for( var i=0, label; label = items[i]; i++ ) {%>" +


                "<li class=\"<%=itemClassName%><%if( selected == i ) {%> edui-combobox-checked<%}%> edui-combobox-item-<%=i%>\" data-item-index=\"<%=i%>\" unselectable=\"on\" onmousedown=\"return false\">" +


                "<span class=\"edui-combobox-icon\" unselectable=\"on\" onmousedown=\"return false\"></span>" +


                "<label class=\"<%=labelClassName%>\" style=\"<%=itemStyles[ i ]%>\" unselectable=\"on\" onmousedown=\"return false\"><%=label%></label>" +


                "</li>" +


                "<%}%>" +


                "</ul>",


            defaultOpt: {


                //记录栈初始列表


                recordStack: [],


                //可用项列表


                items: [],


		        //item对应的值列表


                value: [],


                comboboxName: '',


                selected: '',


                //自动记录


                autoRecord: true,


                //最多记录条数


                recordCount: 5


            },


            init: function( options ){





                var me = this;





                $.extend( me._optionAdaptation( options ), me._createItemMapping( options.recordStack, options.items ), {


                    itemClassName: itemClassName,


                    iconClass: ICON_CLASS,


                    labelClassName: labelClassName


                } );





                this._transStack( options );





                me.root( $( $.parseTmpl( me.tpl, options ) ) );





                this.data( 'options', options ).initEvent();





            },


            initEvent: function(){





                var me = this;





                me.initSelectItem();





                this.initItemActive();





            },


            /**


             * 初始化选择项


             */


            initSelectItem: function(){





                var me = this,


                    labelClass = "."+labelClassName;





                me.root().delegate('.' + itemClassName, 'click', function(){





                    var $li = $(this),


                        index = $li.attr('data-item-index');





                    me.trigger('comboboxselect', {


                        index: index,


                        label: $li.find(labelClass).text(),


                        value: me.data('options').value[ index ]


                    }).select( index );





                    me.hide();





                    return false;





                });





            },


            initItemActive: function(){


                var fn = {


                    mouseenter: 'addClass',


                    mouseleave: 'removeClass'


                };


                if ($.IE6) {


                    this.root().delegate( '.'+itemClassName,  'mouseenter mouseleave', function( evt ){


                        $(this)[ fn[ evt.type ] ]( HOVER_CLASS );


                    }).one('afterhide', function(){


                    });


                }


            },


            /**


             * 选择给定索引的项


             * @param index 项索引


             * @returns {*} 如果存在对应索引的项，则返回该项；否则返回null


             */


            select: function( index ){





                var itemCount = this.data('options').itemCount,


                    items = this.data('options').autowidthitem;





                if ( items && !items.length ) {


                    items = this.data('options').items;


                }





                if( itemCount == 0 ) {


                    return null;


                }





                if( index < 0 ) {





                    index = itemCount + index % itemCount;





                } else if ( index >= itemCount ) {





                    index = itemCount-1;





                }





                this.trigger( 'changebefore', items[ index ] );





                this._update( index );





                this.trigger( 'changeafter', items[ index ] );





                return null;





            },


            selectItemByLabel: function( label ){





                var itemMapping = this.data('options').itemMapping,


                    me = this,


                    index = null;





                !$.isArray( label ) && ( label = [ label ] );





                $.each( label, function( i, item ){





                    index = itemMapping[ item ];





                    if( index !== undefined ) {





                        me.select( index );


                        return false;





                    }





                } );





            },


            /**


             * 转换记录栈


             */


            _transStack: function( options ) {





                var temp = [],


                    itemIndex = -1,


                    selected = -1;





                $.each( options.recordStack, function( index, item ){





                    itemIndex = options.itemMapping[ item ];





                    if( $.isNumeric( itemIndex ) ) {





                        temp.push( itemIndex );





                        //selected的合法性检测


                        if( item == options.selected ) {


                            selected = itemIndex;


                        }





                    }





                } );





                options.recordStack = temp;


                options.selected = selected;


                temp = null;





            },


            _optionAdaptation: function( options ) {





                if( !( 'itemStyles' in options ) ) {





                    options.itemStyles = [];





                    for( var i = 0, len = options.items.length; i < len; i++ ) {


                        options.itemStyles.push('');


                    }





                }





                options.autowidthitem = options.autowidthitem || options.items;


                options.itemCount = options.items.length;





                return options;





            },


            _createItemMapping: function( stackItem, items ){





                var temp = {},


                    result = {


                        recordStack: [],


                        mapping: {}


                    };





                $.each( items, function( index, item ){


                    temp[ item ] = index;


                } );





                result.itemMapping = temp;





                $.each( stackItem, function( index, item ){





                    if( temp[ item ] !== undefined ) {


                        result.recordStack.push( temp[ item ] );


                        result.mapping[ item ] = temp[ item ];


                    }





                } );





                return result;





            },


            _update: function ( index ) {





                var options = this.data("options"),


                    newStack = [],


                    newChilds = null;





                $.each( options.recordStack, function( i, item ){





                    if( item != index ) {


                        newStack.push( item );


                    }





                } );





                //压入最新的记录


                newStack.unshift( index );





                if( newStack.length > options.recordCount ) {


                    newStack.length = options.recordCount;


                }





                options.recordStack = newStack;


                options.selected = index;





                newChilds = $( $.parseTmpl( this.tpl, options ) );





                //重新渲染


                this.root().html( newChilds.html() );





                newChilds = null;


                newStack = null;





            }


        };





    } )(), 'menu' );





})();





/**


 * Combox 抽象基类


 * User: hn


 * Date: 13-5-29


 * Time: 下午8:01


 * To change this template use File | Settings | File Templates.


 */





(function(){





    var widgetName = 'buttoncombobox';





    UM.ui.define( widgetName, ( function(){





        return {


            defaultOpt: {


                //按钮初始文字


                label: '',


                title: ''


            },


            init: function( options ) {





                var me = this;





                var btnWidget = $.eduibutton({


                    caret: true,


                    name: options.comboboxName,


                    title: options.title,


                    text: options.label,


                    click: function(){


                        me.show( this.root() );


                    }


                });





                me.supper.init.call( me, options );





                //监听change， 改变button显示内容


                me.on('changebefore', function( e, label ){


                    btnWidget.eduibutton('label', label );


                });





                me.data( 'button', btnWidget );





                me.attachTo(btnWidget)





            },


            button: function(){


                return this.data( 'button' );


            }


        }





    } )(), 'combobox' );





})();





/*modal 类*/


UM.ui.define('modal', {


    tpl: '<div class="edui-modal" tabindex="-1" >' +


        '<div class="edui-modal-header">' +


        '<div class="edui-close" data-hide="modal"></div>' +


        '<h3 class="edui-title"><%=title%></h3>' +


        '</div>' +


        '<div class="edui-modal-body"  style="<%if(width){%>width:<%=width%>px;<%}%>' +


        '<%if(height){%>height:<%=height%>px;<%}%>">' +


        ' </div>' +


        '<% if(cancellabel || oklabel) {%>' +


        '<div class="edui-modal-footer">' +


        '<div class="edui-modal-tip"></div>' +


        '<%if(oklabel){%><div class="edui-btn edui-btn-primary" data-ok="modal"><%=oklabel%></div><%}%>' +


        '<%if(cancellabel){%><div class="edui-btn" data-hide="modal"><%=cancellabel%></div><%}%>' +


        '</div>' +


        '<%}%></div>',


    defaultOpt: {


        title: "",


        cancellabel: "",


        oklabel: "",


        width: '',


        height: '',


        backdrop: true,


        keyboard: true


    },


    init: function (options) {


        var me = this;





        me.root($($.parseTmpl(me.tpl, options || {})));





        me.data("options", options);


        if (options.okFn) {


            me.on('ok', $.proxy(options.okFn, me))


        }


        if (options.cancelFn) {


            me.on('beforehide', $.proxy(options.cancelFn, me))


        }





        me.root().delegate('[data-hide="modal"]', 'click', $.proxy(me.hide, me))


            .delegate('[data-ok="modal"]', 'click', $.proxy(me.ok, me));





        $('[data-hide="modal"],[data-ok="modal"]',me.root()).hover(function(){


            $(this).toggleClass('edui-hover')


        });


    },


    toggle: function () {


        var me = this;


        return me[!me.data("isShown") ? 'show' : 'hide']();


    },


    show: function () {





        var me = this;





        me.trigger("beforeshow");





        if (me.data("isShown")) return;





        me.data("isShown", true);





        me.escape();





        me.backdrop(function () {


            me.autoCenter();


            me.root()


                .show()


                .focus()


                .trigger('aftershow');


        })


    },


    showTip: function ( text ) {


        $( '.edui-modal-tip', this.root() ).html( text ).fadeIn();


    },


    hideTip: function ( text ) {


        $( '.edui-modal-tip', this.root() ).fadeOut( function (){


            $(this).html('');


        } );


    },


    autoCenter: function () {


        //ie6下不用处理了


        !$.IE6 && this.root().css("margin-left", -(this.root().width() / 2));


    },


    hide: function () {


        var me = this;





        me.trigger("beforehide");





        if (!me.data("isShown")) return;





        me.data("isShown", false);





        me.escape();





        me.hideModal();


    },


    escape: function () {


        var me = this;


        if (me.data("isShown") && me.data("options").keyboard) {


            me.root().on('keyup', function (e) {


                e.which == 27 && me.hide();


            })


        }


        else if (!me.data("isShown")) {


            me.root().off('keyup');


        }


    },


    hideModal: function () {


        var me = this;


        me.root().hide();


        me.backdrop(function () {


            me.removeBackdrop();


            me.trigger('afterhide');


        })


    },


    removeBackdrop: function () {


        this.$backdrop && this.$backdrop.remove();


        this.$backdrop = null;


    },


    backdrop: function (callback) {


        var me = this;


        if (me.data("isShown") && me.data("options").backdrop) {


            me.$backdrop = $('<div class="edui-modal-backdrop" />').click(


                me.data("options").backdrop == 'static' ?


                    $.proxy(me.root()[0].focus, me.root()[0])


                    : $.proxy(me.hide, me)


            )


        }


        me.trigger('afterbackdrop');


        callback && callback();





    },


    attachTo: function ($obj) {


        var me = this


        if (!$obj.data('$mergeObj')) {





            $obj.data('$mergeObj', me.root());


            $obj.on('click', function () {


                me.toggle($obj)


            });


            me.data('$mergeObj', $obj)


        }


    },


    ok: function () {


        var me = this;


        me.trigger('beforeok');


        if (me.trigger("ok", me) === false) {


            return;


        }


        me.hide();


    },


    getBodyContainer: function () {


        return this.root().find('.edui-modal-body')


    }


});








/*tooltip 类*/


UM.ui.define('tooltip', {


    tpl: '<div class="edui-tooltip" unselectable="on" onmousedown="return false">' +


        '<div class="edui-tooltip-arrow" unselectable="on" onmousedown="return false"></div>' +


        '<div class="edui-tooltip-inner" unselectable="on" onmousedown="return false"></div>' +


        '</div>',


    init: function (options) {


        var me = this;


        me.root($($.parseTmpl(me.tpl, options || {})));


    },


    content: function (e) {


        var me = this,


            title = $(e.currentTarget).attr("data-original-title");





        me.root().find('.edui-tooltip-inner')['text'](title);


    },


    position: function (e) {


        var me = this,


            $obj = $(e.currentTarget);





        me.root().css($.extend({display: 'block'}, $obj ? {


            top: $obj.outerHeight(),


            left: (($obj.outerWidth() - me.root().outerWidth()) / 2)


        } : {}))


    },


    show: function (e) {


        if ($(e.currentTarget).hasClass('edui-disabled')) return;





        var me = this;


        me.content(e);


        me.root().appendTo($(e.currentTarget));


        me.position(e);


        me.root().css('display', 'block');


    },


    hide: function () {


        var me = this;


        me.root().css('display', 'none')


    },


    attachTo: function ($obj) {


        var me = this;





        function tmp($obj) {


            var me = this;





            if (!$.contains(document.body, me.root()[0])) {


                me.root().appendTo($obj);


            }





            me.data('tooltip', me.root());





            $obj.each(function () {


                if ($(this).attr("data-original-title")) {


                    $(this).on('mouseenter', $.proxy(me.show, me))


                        .on('mouseleave click', $.proxy(me.hide, me))





                }


            });





        }





        if ($.type($obj) === "undefined") {


            $("[data-original-title]").each(function (i, el) {


                tmp.call(me, $(el));


            })





        } else {


            if (!$obj.data('tooltip')) {


                tmp.call(me, $obj);


            }


        }


    }


});





/*tab 类*/


UM.ui.define('tab', {


    init: function (options) {


        var me = this,


            slr = options.selector;





        if ($.type(slr)) {


            me.root($(slr, options.context));


            me.data("context", options.context);





            $(slr, me.data("context")).on('click', function (e) {


                me.show(e);


            });


        }


    },


    show: function (e) {





        var me = this,


            $cur = $(e.target),


            $ul = $cur.closest('ul'),


            selector,


            previous,


            $target,


            e;





        selector = $cur.attr('data-context');


        selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '');





        var $tmp = $cur.parent('li');





        if (!$tmp.length || $tmp.hasClass('edui-active')) return;





        previous = $ul.find('.edui-active:last a')[0];





        e = $.Event('beforeshow', {


            target: $cur[0],


            relatedTarget: previous


        });





        me.trigger(e);





        if (e.isDefaultPrevented()) return;





        $target = $(selector, me.data("context"));





        me.activate($cur.parent('li'), $ul);


        me.activate($target, $target.parent(), function () {


            me.trigger({


                type: 'aftershow', relatedTarget: previous


            })


        });


    },


    activate: function (element, container, callback) {


        if (element === undefined) {


            return $(".edui-tab-item.edui-active",this.root()).index();


        }





        var $active = container.find('> .edui-active');





        $active.removeClass('edui-active');





        element.addClass('edui-active');





        callback && callback();


    }


});








//button 类


UM.ui.define('separator', {


    tpl: '<div class="edui-separator" unselectable="on" onmousedown="return false" ></div>',


    init: function (options) {


        var me = this;


        me.root($($.parseTmpl(me.tpl, options)));


        return me;


    }


});


/**


 * @file adapter.js


 * @desc adapt ui to editor


 * @import core/Editor.js, core/utils.js


 */





(function () {


    var _editorUI = {},


        _editors = {},


        _readyFn = [],


        _activeWidget = null,


        _widgetData = {},


        _widgetCallBack = {},


        _cacheUI = {},


        _maxZIndex = null;





    utils.extend(UM, {


        defaultWidth : 500,


        defaultHeight : 500,


        registerUI: function (name, fn) {


            utils.each(name.split(/\s+/), function (uiname) {


                _editorUI[uiname] = fn;


            })


        },





        setEditor : function(editor){


            !_editors[editor.id] && (_editors[editor.id] = editor);


        },


        registerWidget : function(name,pro,cb){


            _widgetData[name] = $.extend2(pro,{


                $root : '',


                _preventDefault:false,


                root:function($el){


                    return this.$root || (this.$root = $el);


                },


                preventDefault:function(){


                    this._preventDefault = true;


                },


                clear:false


            });


            if(cb){


                _widgetCallBack[name] = cb;


            }


        },


        getWidgetData : function(name){


            return _widgetData[name]


        },


        setWidgetBody : function(name,$widget,editor){


            if(!editor._widgetData){





                utils.extend(editor,{


                    _widgetData : {},


                    getWidgetData : function(name){


                        return this._widgetData[name];


                    },


                    getWidgetCallback : function(widgetName){


                        var me = this;


                        return function(){


                            return  _widgetCallBack[widgetName].apply(me,[me,$widget].concat(Array.prototype.slice.call(arguments,0)))


                        }


                    }


                })





            }


            var pro = _widgetData[name];


            if(!pro){


                return null;


            }


            pro = editor._widgetData[name];


            if(!pro){


                pro = _widgetData[name];


                pro = editor._widgetData[name] = $.type(pro) == 'function' ? pro : utils.clone(pro);


            }





            pro.root($widget.edui().getBodyContainer());





            pro.initContent(editor,$widget);


            if(!pro._preventDefault){


                pro.initEvent(editor,$widget);


            }





            pro.width &&  $widget.width(pro.width);








        },


        setActiveWidget : function($widget){


            _activeWidget = $widget;


        },


        getEditor: function (id, options) {


            var editor = _editors[id] || (_editors[id] = this.createEditor(id, options));


            _maxZIndex = _maxZIndex ? Math.max(editor.getOpt('zIndex'), _maxZIndex):editor.getOpt('zIndex');


            return editor;


        },


        setTopEditor: function(editor){


            $.each(_editors, function(i, o){


                if(editor == o) {


                    editor.$container && editor.$container.css('zIndex', _maxZIndex + 1);


                } else {


                    o.$container && o.$container.css('zIndex', o.getOpt('zIndex'));


                }


            });


        },


        clearCache : function(id){


            if ( _editors[id]) {


                delete  _editors[id]


            }


        },


        delEditor: function (id) {


            var editor;


            if (editor = _editors[id]) {


                editor.destroy();


            }


        },


        ready: function( fn ){


            _readyFn.push( fn );


        },


        createEditor: function (id, opt) {


            var editor = new UM.Editor(opt);


            var T = this;





            editor.langIsReady ? $.proxy(renderUI,T)() : editor.addListener("langReady", $.proxy(renderUI,T));


            function renderUI(){








                var $container = this.createUI('#' + id, editor);


                editor.key=id;


                editor.ready(function(){


                    $.each( _readyFn, function( index, fn ){


                        $.proxy( fn, editor )();


                    } );


                });


                var options = editor.options;


                if(options.initialFrameWidth){


                    options.minFrameWidth = options.initialFrameWidth


                }else{


                    options.minFrameWidth = options.initialFrameWidth = editor.$body.width() || UM.defaultWidth;


                }





                $container.css({


                    width: options.initialFrameWidth,


                    zIndex:editor.getOpt('zIndex')


                });





                //ie6下缓存图片


                UM.browser.ie && UM.browser.version === 6 && document.execCommand("BackgroundImageCache", false, true);





                editor.render(id);








                //添加tooltip;


                $.eduitooltip && $.eduitooltip('attachTo', $("[data-original-title]",$container)).css('z-index',editor.getOpt('zIndex')+1);





                $container.find('a').click(function(evt){


                    evt.preventDefault()


                });





                editor.fireEvent("afteruiready");


            }





            return editor;





        },


        createUI: function (id, editor) {


            var $editorCont = $(id),


                $container = $('<div class="edui-container"><div class="edui-editor-body"></div></div>').insertBefore($editorCont);


            editor.$container = $container;


            editor.container = $container[0];





            editor.$body = $editorCont;





            //修正在ie9+以上的版本中，自动长高收起时的，残影问题


            if(browser.ie && browser.ie9above){


                var $span = $('<span style="padding:0;margin:0;height:0;width:0"></span>');


                $span.insertAfter($container);


            }


            //初始化注册的ui组件


            $.each(_editorUI,function(n,v){


                var widget = v.call(editor,n);


                if(widget){


                    _cacheUI[n] = widget;


                }





            });





            $container.find('.edui-editor-body').append($editorCont).before(this.createToolbar(editor.options, editor));





            $container.find('.edui-toolbar').append($('<div class="edui-dialog-container"></div>'));








            return $container;


        },


        createToolbar: function (options, editor) {


            var $toolbar = $.eduitoolbar(), toolbar = $toolbar.edui();


            //创建下来菜单列表





            if (options.toolbar && options.toolbar.length) {


                var btns = [];


                $.each(options.toolbar,function(i,uiNames){


                    $.each(uiNames.split(/\s+/),function(index,name){


                        if(name == '|'){


                                $.eduiseparator && btns.push($.eduiseparator());


                        }else{


                            var ui = _cacheUI[name];


                            if(name=="fullscreen"){


                                ui&&btns.unshift(ui);


                            }else{


                                ui && btns.push(ui);


                            }


                        }





                    });


                    btns.length && toolbar.appendToBtnmenu(btns);


                });


            } else {


                $toolbar.find('.edui-btn-toolbar').remove()


            }


            return $toolbar;


        }





    })








})();











UM.registerUI('bold italic redo undo underline strikethrough superscript subscript insertorderedlist insertunorderedlist ' +


    'cleardoc selectall link unlink print preview justifyleft justifycenter justifyright justifyfull removeformat horizontal drafts',


    function(name) {


        var me = this;


        var $btn = $.eduibutton({


            icon : name,


            click : function(){


                me.execCommand(name);


            },


            title: this.getLang('labelMap')[name] || ''


        });





        this.addListener('selectionchange',function(){


            var state = this.queryCommandState(name);


            $btn.edui().disabled(state == -1).active(state == 1)


        });


        return $btn;


    }


);








/**


 * 全屏组件


 */





(function(){





    //状态缓存


    var STATUS_CACHE = {},


    //状态值列表


        STATUS_LIST = [ 'width', 'height', 'position', 'top', 'left', 'margin', 'padding', 'overflowX', 'overflowY' ],


        CONTENT_AREA_STATUS = {},


    //页面状态


        DOCUMENT_STATUS = {},


        DOCUMENT_ELEMENT_STATUS = {},





        FULLSCREENS = {};








    UM.registerUI('fullscreen', function( name ){





        var me = this,


            $button = $.eduibutton({


                'icon': 'fullscreen',


                'title': (me.options.labelMap && me.options.labelMap[name]) || me.getLang("labelMap." + name),


                'click': function(){


                    //切换


                    me.execCommand( name );


                    UM.setTopEditor(me);


                }


            });





        me.addListener( "selectionchange", function () {





            var state = this.queryCommandState( name );


            $button.edui().disabled( state == -1 ).active( state == 1 );





        } );





        //切换至全屏


        me.addListener('ready', function(){





            me.options.fullscreen && Fullscreen.getInstance( me ).toggle();





        });





        return $button;





    });





    UM.commands[ 'fullscreen' ] = {





        execCommand: function (cmdName) {





            Fullscreen.getInstance( this ).toggle();





        },


        queryCommandState: function (cmdName) {





            return this._edui_fullscreen_status;


        },


        notNeedUndo: 1





    };





    function Fullscreen( editor ) {





        var me = this;





        if( !editor ) {


            throw new Error('invalid params, notfound editor');


        }





        me.editor = editor;





        //记录初始化的全屏组件


        FULLSCREENS[ editor.uid ] = this;





        editor.addListener('destroy', function(){


            delete FULLSCREENS[ editor.uid ];


            me.editor = null;


        });





    }





    Fullscreen.prototype = {





        /**


         * 全屏状态切换


         */


        toggle: function(){





            var editor = this.editor,


            //当前编辑器的缩放状态


                _edui_fullscreen_status = this.isFullState();


            editor.fireEvent('beforefullscreenchange', !_edui_fullscreen_status );





            //更新状态


            this.update( !_edui_fullscreen_status );





            !_edui_fullscreen_status ? this.enlarge() : this.revert();





            editor.fireEvent('afterfullscreenchange', !_edui_fullscreen_status );


            if(editor.body.contentEditable === 'true'){


                editor.fireEvent( 'fullscreenchanged', !_edui_fullscreen_status );


            }





            editor.fireEvent( 'selectionchange' );





        },


        /**


         * 执行放大


         */


        enlarge: function(){





            this.saveSataus();





            this.setDocumentStatus();





            this.resize();





        },


        /**


         * 全屏还原


         */


        revert: function(){





            //还原CSS表达式


            var options = this.editor.options,


                height = /%$/.test(options.initialFrameHeight) ?  '100%' : (options.initialFrameHeight - this.getStyleValue("padding-top")- this.getStyleValue("padding-bottom") - this.getStyleValue('border-width'));





            $.IE6 && this.getEditorHolder().style.setExpression('height', 'this.scrollHeight <= ' + height + ' ? "' + height + 'px" : "auto"');





            //还原容器状态


            this.revertContainerStatus();





            this.revertContentAreaStatus();





            this.revertDocumentStatus();





        },


        /**


         * 更新状态


         * @param isFull 当前状态是否是全屏状态


         */


        update: function( isFull ) {


            this.editor._edui_fullscreen_status = isFull;


        },


        /**


         * 调整当前编辑器的大小, 如果当前编辑器不处于全屏状态， 则不做调整


         */


        resize: function(){





            var $win = null,


                height = 0,


                width = 0,


                borderWidth = 0,


                paddingWidth = 0,


                editor = this.editor,


                me = this,


                bound = null,


                editorBody = null;





            if( !this.isFullState() ) {


                return;


            }





            $win = $( window );


            width = $win.width();


            height = $win.height();


            editorBody = this.getEditorHolder();


            //文本编辑区border宽度


            borderWidth = parseInt( domUtils.getComputedStyle( editorBody, 'border-width' ), 10 ) || 0;


            //容器border宽度


            borderWidth += parseInt( domUtils.getComputedStyle( editor.container, 'border-width' ), 10 ) || 0;


            //容器padding


            paddingWidth += parseInt( domUtils.getComputedStyle( editorBody, 'padding-left' ), 10 ) + parseInt( domUtils.getComputedStyle( editorBody, 'padding-right' ), 10 ) || 0;





            //干掉css表达式


            $.IE6 && editorBody.style.setExpression( 'height', null );





            bound = this.getBound();





            $( editor.container ).css( {


                width: width + 'px',


                height: height + 'px',


                position: !$.IE6 ? 'fixed' : 'absolute',


                top: bound.top,


                left: bound.left,


                margin: 0,


                padding: 0,


                overflowX: 'hidden',


                overflowY: 'hidden'


            } );





            $( editorBody ).css({


                width: width - 2*borderWidth - paddingWidth + 'px',


                height: height - 2*borderWidth - ( editor.options.withoutToolbar ? 0 : $( '.edui-toolbar', editor.container ).outerHeight() ) - $( '.edui-bottombar', editor.container).outerHeight() + 'px',


                overflowX: 'hidden',


                overflowY: 'auto'


            });





        },


        /**


         * 保存状态


         */


        saveSataus: function(){





            var styles = this.editor.container.style,


                tmp = null,


                cache = {};





            for( var i= 0, len = STATUS_LIST.length; i<len; i++ ) {





                tmp = STATUS_LIST[ i ];


                cache[ tmp ] = styles[ tmp ];





            }





            STATUS_CACHE[ this.editor.uid ] = cache;





            this.saveContentAreaStatus();


            this.saveDocumentStatus();





        },


        saveContentAreaStatus: function(){





            var $holder = $(this.getEditorHolder());





            CONTENT_AREA_STATUS[ this.editor.uid ] = {


                width: $holder.css("width"),


                overflowX: $holder.css("overflowX"),


                overflowY: $holder.css("overflowY"),


                height: $holder.css("height")


            };





        },


        /**


         * 保存与指定editor相关的页面的状态


         */


        saveDocumentStatus: function(){





            var $doc = $( this.getEditorDocumentBody() );





            DOCUMENT_STATUS[ this.editor.uid ] = {


                overflowX: $doc.css( 'overflowX' ),


                overflowY: $doc.css( 'overflowY' )


            };


            DOCUMENT_ELEMENT_STATUS[ this.editor.uid ] = {


                overflowX: $( this.getEditorDocumentElement() ).css( 'overflowX'),


                overflowY: $( this.getEditorDocumentElement() ).css( 'overflowY' )


            };





        },


        /**


         * 恢复容器状态


         */


        revertContainerStatus: function(){


            $( this.editor.container ).css( this.getEditorStatus() );


        },


        /**


         * 恢复编辑区状态


         */


        revertContentAreaStatus: function(){


            var holder = this.getEditorHolder(),


                state = this.getContentAreaStatus();





            if ( this.supportMin() ) {


                delete state.height;


                holder.style.height = null;


            }





            $( holder ).css( state );


        },


        /**


         * 恢复页面状态


         */


        revertDocumentStatus: function() {





            var status = this.getDocumentStatus();


            $( this.getEditorDocumentBody() ).css( 'overflowX', status.body.overflowX );


            $( this.getEditorDocumentElement() ).css( 'overflowY', status.html.overflowY );





        },


        setDocumentStatus: function(){


            $(this.getEditorDocumentBody()).css( {


                overflowX: 'hidden',


                overflowY: 'hidden'


            } );


            $(this.getEditorDocumentElement()).css( {


                overflowX: 'hidden',


                overflowY: 'hidden'


            } );


        },


        /**


         * 检测当前编辑器是否处于全屏状态全屏状态


         * @returns {boolean} 是否处于全屏状态


         */


        isFullState: function(){


            return !!this.editor._edui_fullscreen_status;


        },


        /**


         * 获取编辑器状态


         */


        getEditorStatus: function(){


            return STATUS_CACHE[ this.editor.uid ];


        },


        getContentAreaStatus: function(){


            return CONTENT_AREA_STATUS[ this.editor.uid ];


        },


        getEditorDocumentElement: function(){


            return this.editor.container.ownerDocument.documentElement;


        },


        getEditorDocumentBody: function(){


            return this.editor.container.ownerDocument.body;


        },


        /**


         * 获取编辑区包裹对象


         */


        getEditorHolder: function(){


            return this.editor.body;


        },


        /**


         * 获取编辑器状态


         * @returns {*}


         */


        getDocumentStatus: function(){


            return {


                'body': DOCUMENT_STATUS[ this.editor.uid ],


                'html': DOCUMENT_ELEMENT_STATUS[ this.editor.uid ]


            };


        },


        supportMin: function () {





            var node = null;





            if ( !this._support ) {





                node = document.createElement("div");





                this._support = "minWidth" in node.style;





                node = null;





            }





            return this._support;





        },


        getBound: function () {





            var tags = {


                    html: true,


                    body: true


                },


                result = {


                    top: 0,


                    left: 0


                },


                offsetParent = null;





            if ( !$.IE6 ) {


                return result;


            }





            offsetParent = this.editor.container.offsetParent;





            if( offsetParent && !tags[ offsetParent.nodeName.toLowerCase() ] ) {


                tags = offsetParent.getBoundingClientRect();


                result.top = -tags.top;


                result.left = -tags.left;


            }





            return result;





        },


        getStyleValue: function (attr) {


            return parseInt(domUtils.getComputedStyle( this.getEditorHolder() ,attr));


        }


    };








    $.extend( Fullscreen, {


        /**


         * 监听resize


         */


        listen: function(){





            var timer = null;





            if( Fullscreen._hasFullscreenListener ) {


                return;


            }





            Fullscreen._hasFullscreenListener = true;





            $( window ).on( 'resize', function(){





                if( timer !== null ) {


                    window.clearTimeout( timer );


                    timer = null;


                }





                timer = window.setTimeout(function(){





                    for( var key in FULLSCREENS ) {


                        FULLSCREENS[ key ].resize();


                    }





                    timer = null;





                }, 50);





            } );





        },





        getInstance: function ( editor ) {





            if ( !FULLSCREENS[editor.uid  ] ) {


                new Fullscreen( editor );


            }





            return FULLSCREENS[editor.uid  ];





        }





    });





    //开始监听


    Fullscreen.listen();





})();


UM.registerUI('link image video map formula',function(name){





    var me = this, currentRange, $dialog,


        opt = {


            title: (me.options.labelMap && me.options.labelMap[name]) || me.getLang("labelMap." + name),


            url: me.options.UMEDITOR_HOME_URL + 'dialogs/' + name + '/' + name + '.js'


        };





    var $btn = $.eduibutton({


        icon: name,


        title: this.getLang('labelMap')[name] || ''


    });


    //加载模版数据


    utils.loadFile(document,{


        src: opt.url,


        tag: "script",


        type: "text/javascript",


        defer: "defer"


    },function(){


        //调整数据


        var data = UM.getWidgetData(name);


        if(!data) return;


        if(data.buttons){


            var ok = data.buttons.ok;


            if(ok){


                opt.oklabel = ok.label || me.getLang('ok');


                if(ok.exec){


                    opt.okFn = function(){


                        return $.proxy(ok.exec,null,me,$dialog)()


                    }


                }


            }


            var cancel = data.buttons.cancel;


            if(cancel){


                opt.cancellabel = cancel.label || me.getLang('cancel');


                if(cancel.exec){


                    opt.cancelFn = function(){


                        return $.proxy(cancel.exec,null,me,$dialog)()


                    }


                }


            }


        }


        data.width && (opt.width = data.width);


        data.height && (opt.height = data.height);





        $dialog = $.eduimodal(opt);





        $dialog.attr('id', 'edui-dialog-' + name).addClass('edui-dialog-' + name)


            .find('.edui-modal-body').addClass('edui-dialog-' + name + '-body');





        $dialog.edui().on('beforehide',function () {


            var rng = me.selection.getRange();


            if (rng.equals(currentRange)) {


                rng.select()


            }


        }).on('beforeshow', function () {


                var $root = this.root(),


                    win = null,


                    offset = null;


                currentRange = me.selection.getRange();


                if (!$root.parent()[0]) {


                    me.$container.find('.edui-dialog-container').append($root);


                }





                //IE6下 特殊处理, 通过计算进行定位


                if( $.IE6 ) {





                    win = {


                        width: $( window ).width(),


                        height: $( window ).height()


                    };


                    offset = $root.parents(".edui-toolbar")[0].getBoundingClientRect();


                    $root.css({


                        position: 'absolute',


                        margin: 0,


                        left: ( win.width - $root.width() ) / 2 - offset.left,


                        top: 100 - offset.top


                    });





                }


                UM.setWidgetBody(name,$dialog,me);


                UM.setTopEditor(me);


        }).on('afterbackdrop',function(){


            this.$backdrop.css('zIndex',me.getOpt('zIndex')+1).appendTo(me.$container.find('.edui-dialog-container'))


            $dialog.css('zIndex',me.getOpt('zIndex')+2)


        }).on('beforeok',function(){


            try{


                currentRange.select()


            }catch(e){}


        }).attachTo($btn)


    });














    me.addListener('selectionchange', function () {


        var state = this.queryCommandState(name);


        $btn.edui().disabled(state == -1).active(state == 1)


    });


    return $btn;


});


UM.registerUI( 'emotion formula', function( name ){


    var me = this,


        url  = me.options.UMEDITOR_HOME_URL + 'dialogs/' +name+ '/'+name+'.js';





    var $btn = $.eduibutton({


        icon: name,


        title: this.getLang('labelMap')[name] || ''


    });





    //加载模版数据


    utils.loadFile(document,{


        src: url,


        tag: "script",


        type: "text/javascript",


        defer: "defer"


    },function(){


        var opt = {


            url : url


        };


        //调整数据


        var data = UM.getWidgetData(name);





        data.width && (opt.width = data.width);


        data.height && (opt.height = data.height);





        $.eduipopup(opt).css('zIndex',me.options.zIndex + 1)


            .addClass('edui-popup-' + name)


            .edui()


            .on('beforeshow',function(){


                var $root = this.root();


                if(!$root.parent().length){


                    me.$container.find('.edui-dialog-container').append($root);


                }


                UM.setWidgetBody(name,$root,me);


                UM.setTopEditor(me);


            }).attachTo($btn,{


                offsetTop:-5,


                offsetLeft:10,


                caretLeft:11,


                caretTop:-8


            });


        me.addListener('selectionchange', function () {


            var state = this.queryCommandState(name);


            $btn.edui().disabled(state == -1).active(state == 1);


        });


    });


    return $btn;





} );


UM.registerUI('imagescale',function () {


    var me = this,


        $imagescale;





    me.setOpt('imageScaleEnabled', true);





    if (browser.webkit && me.getOpt('imageScaleEnabled')) {





        me.addListener('click', function (type, e) {


            var range = me.selection.getRange(),


                img = range.getClosedNode(),


                target = e.target;





            /* 点击第一个图片的后面,八个角不消失 fix:3652 */


            if (img && img.tagName == 'IMG' && target == img) {





                if (!$imagescale) {


                    $imagescale = $.eduiscale({'$wrap':me.$container}).css('zIndex', me.options.zIndex);


                    me.$container.append($imagescale);





                    var _keyDownHandler = function () {


                        $imagescale.edui().hide();


                    }, _mouseDownHandler = function (e) {


                        var ele = e.target || e.srcElement;


                        if (ele && ele.className.indexOf('edui-scale') == -1) {


                            _keyDownHandler(e);


                        }


                    }, timer;





                    $imagescale.edui()


                        .on('aftershow', function () {


                            $(document).bind('keydown', _keyDownHandler);


                            $(document).bind('mousedown', _mouseDownHandler);


                            me.selection.getNative().removeAllRanges();


                        })


                        .on('afterhide', function () {


                            $(document).unbind('keydown', _keyDownHandler);


                            $(document).unbind('mousedown', _mouseDownHandler);


                            var target = $imagescale.edui().getScaleTarget();


                            if (target.parentNode) {


                                me.selection.getRange().selectNode(target).select();


                            }


                        })


                        .on('mousedown', function (e) {


                            me.selection.getNative().removeAllRanges();


                            var ele = e.target || e.srcElement;


                            if (ele && ele.className.indexOf('edui-scale-hand') == -1) {


                                timer = setTimeout(function() {


                                    $imagescale.edui().hide();


                                }, 200);


                            }


                        })


                        .on('mouseup', function (e) {


                            var ele = e.target || e.srcElement;


                            if (ele && ele.className.indexOf('edui-scale-hand') == -1) {


                                clearTimeout(timer);


                            }


                        });


                }


                $imagescale.edui().show($(img));





            } else {


                if ($imagescale && $imagescale.css('display') != 'none') $imagescale.edui().hide();





            }


        });





        me.addListener('click', function (type, e) {


            if (e.target.tagName == 'IMG') {


                var range = new dom.Range(me.document, me.body);


                range.selectNode(e.target).select();


            }


        });





    }


});


UM.registerUI('autofloat',function(){


    var me = this,


        lang = me.getLang();


    me.setOpt({


        autoFloatEnabled: true,


        topOffset: 0


    });


    var optsAutoFloatEnabled = me.options.autoFloatEnabled !== false,


        topOffset = me.options.topOffset;








    //如果不固定toolbar的位置，则直接退出


    if(!optsAutoFloatEnabled){


        return;


    }


    me.ready(function(){


        var LteIE6 = browser.ie && browser.version <= 6,


            quirks = browser.quirks;





        function checkHasUI(){


            if(!UM.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=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;


            };


        var isFullScreening = false;


        function setFloating(){


            if(isFullScreening){


                return;


            }


            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(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(){





            if(placeHolder.parentNode){


                placeHolder.parentNode.removeChild(placeHolder);


            }


            toolbarBox.style.cssText = bakCssText;


        }





        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(){


            $(window).off('scroll resize',updateFloating);


            me.removeListener('keydown', defer_updateFloating);


        });





        if(checkHasUI(me)){


            toolbarBox = $('.edui-toolbar',me.container)[0];


            me.addListener("afteruiready",function(){


                setTimeout(function(){


                    orgTop = $(toolbarBox).offset().top;


                },100);


            });


            bakCssText = toolbarBox.style.cssText;


            placeHolder.style.height = toolbarBox.offsetHeight + 'px';


            if(LteIE6){


                fixIE6FixedPos();


            }





            $(window).on('scroll resize',updateFloating);


            me.addListener('keydown', defer_updateFloating);


            me.addListener('resize', function(){


                unsetFloating();


                placeHolder.style.height = toolbarBox.offsetHeight + 'px';


                updateFloating();


            });





            me.addListener('beforefullscreenchange', function (t, enabled){


                if (enabled) {


                    unsetFloating();


                    isFullScreening = enabled;


                }


            });


            me.addListener('fullscreenchanged', function (t, enabled){


                if (!enabled) {


                    updateFloating();


                }


                isFullScreening = enabled;


            });


            me.addListener('sourcemodechanged', function (t, enabled){


                setTimeout(function (){


                    updateFloating();


                },0);


            });


            me.addListener("clearDoc",function(){


                setTimeout(function(){


                    updateFloating();


                },0);





            })


        }


    })








});


UM.registerUI('source',function(name){


    var me = this;


    me.addListener('fullscreenchanged',function(){


        me.$container.find('textarea').width(me.$body.width() - 10).height(me.$body.height())





    });


    var $btn = $.eduibutton({


        icon : name,


        click : function(){


            me.execCommand(name);


            UM.setTopEditor(me);


        },


        title: this.getLang('labelMap')[name] || ''


    });





    this.addListener('selectionchange',function(){


        var state = this.queryCommandState(name);


        $btn.edui().disabled(state == -1).active(state == 1)


    });


    return $btn;


});





UM.registerUI('paragraph fontfamily fontsize', function( name ) {





    var me = this,


        label = (me.options.labelMap && me.options.labelMap[name]) || me.getLang("labelMap." + name),


        options = {


            label: label,


            title: label,


            comboboxName: name,


            items: me.options[ name ] || [],


            itemStyles: [],


            value: [],


            autowidthitem: []


        },


        $combox = null,


        comboboxWidget = null;


    if(options.items.length == 0){


        return null;


    }


    switch ( name ) {





        case 'paragraph':


            options = transForParagraph( options );


            break;





        case 'fontfamily':


            options = transForFontfamily( options );


            break;





        case 'fontsize':


            options = transForFontsize( options );


            break;





    }





    //实例化


    $combox =  $.eduibuttoncombobox(options).css('zIndex',me.getOpt('zIndex') + 1);


    comboboxWidget =  $combox.edui();





    comboboxWidget.on('comboboxselect', function( evt, res ){


                        me.execCommand( name, res.value );


                    }).on("beforeshow", function(){


                        if( $combox.parent().length === 0 ) {


                            $combox.appendTo(  me.$container.find('.edui-dialog-container') );


                        }


                        UM.setTopEditor(me);


                    });








    //状态反射


    this.addListener('selectionchange',function( evt ){





        var state  = this.queryCommandState( name ),


            value = this.queryCommandValue( name );





        //设置按钮状态


        comboboxWidget.button().edui().disabled( state == -1 ).active( state == 1 );


        if(value){


            //设置label


            value = value.replace(/['"]/g, '').toLowerCase().split(/['|"]?\s*,\s*[\1]?/);





            comboboxWidget.selectItemByLabel( value );


        }








    });





    return comboboxWidget.button().addClass('edui-combobox');





    /**


     * 宽度自适应工具函数


     * @param word 单词内容


     * @param hasSuffix 是否含有后缀


     */


    function wordCountAdaptive ( word, hasSuffix ) {





        var $tmpNode = $('<span>' ).html( word ).css( {


                display: 'inline',


                position: 'absolute',


                top: -10000000,


                left: -100000


            } ).appendTo( document.body),


            width = $tmpNode.width();





        $tmpNode.remove();


        $tmpNode = null;





        if( width < 50 ) {





            return word;





        } else {





            word = word.slice( 0, hasSuffix ? -4 : -1 );





            if( !word.length ) {


                return '...';


            }





            return wordCountAdaptive( word + '...', true );





        }





    }








    //段落参数转换


    function transForParagraph ( options ) {





        var tempItems = [];





        for( var key in options.items ) {





            options.value.push( key );


            tempItems.push( key );


            options.autowidthitem.push( wordCountAdaptive( key ) );





        }





        options.items = tempItems;


        options.autoRecord = false;





        return options;





    }





    //字体参数转换


    function transForFontfamily ( options ) {





        var temp = null,


            tempItems = [];





        for( var i = 0, len = options.items.length; i < len; i++ ) {





            temp = options.items[ i ].val;


            tempItems.push( temp.split(/\s*,\s*/)[0] );


            options.itemStyles.push('font-family: ' + temp);


            options.value.push( temp );


            options.autowidthitem.push( wordCountAdaptive( tempItems[ i ] ) );





        }





        options.items = tempItems;





        return options;





    }





    //字体大小参数转换


    function transForFontsize ( options ) {





        var temp = null,


            tempItems = [];





        options.itemStyles = [];


        options.value = [];





        for( var i = 0, len = options.items.length; i < len; i++ ) {





            temp = options.items[ i ];


            tempItems.push( temp );


            options.itemStyles.push('font-size: ' + temp +'px');





        }





        options.value = options.items;


        options.items = tempItems;


        options.autoRecord = false;





        return options;





    }





});








UM.registerUI('forecolor backcolor', function( name ) {


    function getCurrentColor() {


        return domUtils.getComputedStyle( $colorLabel[0], 'background-color' );


    }





    var me = this,


        $colorPickerWidget = null,


        $colorLabel = null,


        $btn = null;





    //querycommand


    this.addListener('selectionchange', function(){





        var state = this.queryCommandState( name );


        $btn.edui().disabled( state == -1 ).active( state == 1 );





    });





    $btn = $.eduicolorsplitbutton({


        icon: name,


        caret: true,


        name: name,


        title: me.getLang("labelMap")[name],


        click: function() {


            me.execCommand( name, getCurrentColor() );


        }


    });





    $colorLabel = $btn.edui().colorLabel();





    $colorPickerWidget = $.eduicolorpicker({


        name: name,


        lang_clearColor: me.getLang('clearColor') || '',


        lang_themeColor: me.getLang('themeColor') || '',


        lang_standardColor: me.getLang('standardColor') || ''


    })


        .on('pickcolor', function( evt, color ){


            window.setTimeout( function(){


                $colorLabel.css("backgroundColor", color);


                me.execCommand( name, color );


            }, 0 );


        })


        .on('show',function(){


            UM.setActiveWidget( colorPickerWidget.root() );


        }).css('zIndex',me.getOpt('zIndex') + 1);





    $btn.edui().on('arrowclick',function(){


        if(!$colorPickerWidget.parent().length){


            me.$container.find('.edui-dialog-container').append($colorPickerWidget);


        }


        $colorPickerWidget.edui().show($btn,{


            caretDir:"down",


            offsetTop:-5,


            offsetLeft:8,


            caretLeft:11,


            caretTop:-8


        });


        UM.setTopEditor(me);


    }).register('click', $btn, function () {


            $colorPickerWidget.edui().hide()


        });





    return $btn;





});





})(jQuery)