/*! * UEditor Mini版本 * version: 1.2.2 * build: Thu Dec 22 2016 16:33:28 GMT+0800 (CST) */ (function ($) { UMEDITOR_CONFIG = window.UMEDITOR_CONFIG || {}; window.UM = { list: {}, 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 * * @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 = 'You say:"你好!Baidu & UEditor!"'; * UM.utils.unhtml(html); ==> <body>You say:"你好!Baidu & UEditor!"</body> * UM.utils.unhtml(html,/[<>]/g) ==> <body>You say:"你好!Baidu & UEditor!"</body> */ 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 { '<': '<', '&': '&', '"': '"', '>': '>', "'": ''' }[a] } }) : ''; }, /** * 将str中的转义字符还原成html字符 * @name html * @grammar UM.utils.html(str) => String //详细参见unhtml */ html: function (str) { return str ? str.replace(/&((g|l|quo)t|amp|#39);/g, function (m) { return { '<': '<', '&': '&', '"': '"', '>': '>', ''': "'" }[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(' ') + ' >'); 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 * *
* *
* xxx * * * ``` * @example * ```html * *
* * xxx *
* xxx * * * ``` */ /** * 取得node节点的下一个兄弟节点, 如果startFromChild的值为ture,则先获取其子节点, * 如果有子节点则直接返回第一个子节点;如果没有子节点或者startFromChild的值为false, * 则执行getNextDomNode(Node node)的查找过程。 * @method getNextDomNode * @param { Node } node 需要获取其后的兄弟节点的节点对象 * @param { Boolean } startFromChild 查找过程是否从其子节点开始 * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL * @see UE.dom.domUtils.getNextDomNode(Node) */ getNextDomNode: function (node, startFromChild, filterFn, guard) { return getDomNode(node, 'firstChild', 'nextSibling', startFromChild, filterFn, guard); }, getPreDomNode: function (node, startFromChild, filterFn, guard) { return getDomNode(node, 'lastChild', 'previousSibling', startFromChild, filterFn, guard); }, /** * 检测节点node是否属于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 * *
* * * * xxx * * * * ``` */ /** * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true, * 则忽略对右边兄弟节点的操作。 * @method clearEmptySibling * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点, * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作 * 则这些兄弟节点将被删除 * @see UE.dom.domUtils.clearEmptySibling(Node) */ /** * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true, * 则忽略对右边兄弟节点的操作, 如果ignorePre的值为true,则忽略对左边兄弟节点的操作。 * @method clearEmptySibling * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点, * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作 * @param { Boolean } ignorePre 是否忽略忽略对左边的兄弟节点的操作 * 则这些兄弟节点将被删除 * @see UE.dom.domUtils.clearEmptySibling(Node) */ clearEmptySibling: function (node, ignoreNext, ignorePre) { function clear(next, dir) { var tmpNode; while (next && !domUtils.isBookmarkNode(next) && (domUtils.isEmptyInlineElement(next) //这里不能把空格算进来会吧空格干掉,出现文字间的空格丢掉了 || !new RegExp('[^\t\n\r' + domUtils.fillChar + ']').test(next.nodeValue))) { tmpNode = next[dir]; domUtils.remove(next); next = tmpNode; } } !ignoreNext && clear(node.nextSibling, 'nextSibling'); !ignorePre && clear(node.previousSibling, 'previousSibling'); }, /** * 将一个文本节点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 * => 1 * => 1 * => 1 * xx => 0 */ isEmptyInlineElement: function (node) { if (node.nodeType != 1 || !dtd.$removeEmpty[node.tagName]) { return 0; } node = node.firstChild; while (node) { //如果是创建的bookmark就跳过 if (domUtils.isBookmarkNode(node)) { return 0; } if (node.nodeType == 1 && !domUtils.isEmptyInlineElement(node) || node.nodeType == 3 && !domUtils.isWhitespace(node) ) { return 0; } node = node.nextSibling; } return 1; }, /** * 检查节点node是否为块元素 * @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 * xxxxx * //Remove * UM.dom.domUtils.removeAttributes(node,["id","name"]); * //After remove * xxxxx */ removeAttributes: function (node, attrNames) { attrNames = utils.isArray(attrNames) ? attrNames : utils.trim(attrNames).replace(/[ ]{2,}/g, ' ').split(' '); for (var i = 0, ci; ci = attrNames[i++];) { ci = attrFix[ci] || ci; switch (ci) { case 'className': node[ci] = ''; break; case 'style': node.style.cssText = ''; !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 * * * * ``` */ 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'; }, 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 : * xx[xxx]x * //执行方法后 * xx|x * 注意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 * xxx|xx * 执行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 * xxx|xx * 执行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 * xxx|xx * 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 * xxx|xx * 执行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 * xx[xxxx]xxx * 执行后 * [xxxxxxxxx] * 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开始位置和结束位置都调整,参见adjustmentBoundary * @grammar range.shrinkBoundary(true) => Range //仅调整开始位置,忽略结束位置 * @example * xx[xxxxx] ==> xx[xxxxx] * x[xx]xxx ==> x[xx]xxx * [xxxxxxxxxxx] ==> [xxxxxxxxxxx] */ 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 : * |xxx * startContainer = xxx; startOffset = 0 * //执行后本方法后 * startContainer = ; startOffset = 0 * @example * Dom Element : * xx|x * startContainer = xxx; startOffset = 2 * //执行本方法后,xxx被实实在在地切分成两个TextNode * startContainer = ; 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 : * |xxx * startContainer = xxx; startOffset = 0 * //本方法执行后 * startContainer = ; startOffset = 0 * @example * Dom Element : * xxx| * startContainer = xxx; startOffset = 3 * //本方法执行后 * startContainer = ; 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

xxxx

xxxx]x

sdfsdf

* 待插入Node : *

ssss

* 执行本方法后的Range : * xxx[

ssss

x

xxxx

xxxx]x

sdfsdf

*/ 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 //参见shrinkBoundary * @example * xx[xxxxx] ==> xx[xxxxx] * x[xx]xxx ==> x[xx]xxx */ 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 * xxxx[]xxx */ 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) { //使用|x固定住光标 var tmpText = this.document.createTextNode(fillChar), tmp = this.document.createElement('span'); tmp.appendChild(this.document.createTextNode(fillChar)); start.parentNode.insertBefore(tmp, start); start.parentNode.insertBefore(tmpText, start); //当点b,i,u时,不能清除i上边的b removeFillData(this.document, tmpText); fillData = tmpText; mergeSibling(tmp, 'previousSibling'); mergeSibling(start, 'nextSibling'); nativeRange.moveStart('character', -1); nativeRange.collapse(true); } } this.moveToBookmark(bookmark); tmp && domUtils.remove(tmp); //IE在隐藏状态下不支持range操作,catch一下 try { nativeRange.select(); } catch (e) { } return this; } : function (notInsertFillData) { function checkOffset(rng) { function check(node, offset, dir) { if (node.nodeType == 3 && node.nodeValue.length < offset) { rng[dir + 'Offset'] = node.nodeValue.length } } check(rng.startContainer, rng.startOffset, 'start'); check(rng.endContainer, rng.endOffset, 'end'); } var win = domUtils.getWindow(this.document), sel = win.getSelection(), txtNode; //FF下关闭自动长高时滚动条在关闭dialog时会跳 //ff下如果不body.focus将不能定位闭合光标到编辑器内 browser.gecko ? this.body.focus() : win.focus(); if (sel) { sel.removeAllRanges(); // trace:870 chrome/safari后边是br对于闭合得range不能定位 所以去掉了判断 // this.startContainer.nodeType != 3 &&! ((child = this.startContainer.childNodes[this.startOffset]) && child.nodeType == 1 && child.tagName == 'BR' if (this.collapsed && !notInsertFillData) { // //opear如果没有节点接着,原生的不能够定位,不能在body的第一级插入空白节点 // if (notInsertFillData && browser.opera && !domUtils.isBody(this.startContainer) && this.startContainer.nodeType == 1) { // var tmp = this.document.createTextNode(''); // this.insertNode(tmp).setStart(tmp, 0).collapse(true); // } // //处理光标落在文本节点的情况 //处理以下的情况 //|xxxx //xxxx|xxxx //xxxx| var start = this.startContainer, child = start; if (start.nodeType == 1) { child = start.childNodes[this.startOffset]; } if (!(start.nodeType == 3 && this.startOffset) && (child ? (!child.previousSibling || child.previousSibling.nodeType != 3) : (!start.lastChild || start.lastChild.nodeType != 3) ) ) { txtNode = this.document.createTextNode(fillChar); //跟着前边走 this.insertNode(txtNode); removeFillData(this.document, txtNode); mergeSibling(txtNode, 'previousSibling'); mergeSibling(txtNode, 'nextSibling'); fillData = txtNode; this.setStart(txtNode, browser.webkit ? 1 : 0).collapse(true); } } var nativeRange = this.document.createRange(); if (this.collapsed && browser.opera && this.startContainer.nodeType == 1) { var child = this.startContainer.childNodes[this.startOffset]; if (!child) { //往前靠拢 child = this.startContainer.lastChild; if (child && domUtils.isBr(child)) { this.setStartBefore(child).collapse(true); } } else { //向后靠拢 while (child && domUtils.isBlockElm(child)) { if (child.nodeType == 1 && child.childNodes[0]) { child = child.childNodes[0] } else { break; } } child && this.setStartBefore(child).collapse(true) } } //是createAddress最后一位算的不准,现在这里进行微调 checkOffset(this); nativeRange.setStart(this.startContainer, this.startOffset); nativeRange.setEnd(this.endContainer, this.endOffset); sel.addRange(nativeRange); } return this; }, 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 = $(' '); 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下x[xx] 闭合后 x|xx tmpRange.text.length > 0 && tmpRange.moveStart('character', 1); tmpRange.collapse(1); start = tmpRange.parentElement(); parent = tmp = range.parentElement(); while (tmp = tmp.parentNode) { if (tmp == start) { start = parent; break; } } } else { 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 = '

' + (browser.ie ? '' : '
') + '

'; } //如果要求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 //若编辑器中只包含字符"<p><br /></p/>"会返回空。 * @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 ? '' : ''); }); utils.each(me.document.getElementsByTagName('script'), function (si) { headHtmlForIE9 += si.outerHTML; }); } return '' + (me.options.charset ? '' : '') + (headHtmlForIE9 || me.document.getElementsByTagName('head')[0].innerHTML) + headHtml.join('\n') + '' + '' + me.getContent(null, null, true) + ''; }, /** * 得到编辑器的纯文本内容,但会保留段落格式 * @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)[^>]*>(| )<\/\1>/gi, '\n') .replace(//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(/ /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 = '

' + (browser.ie ? ' ' : '
') + '

' + 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 * 默认有文本内容,或者有以下节点都不认为是空 * {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} * @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 = '

' + (ie ? '' : '
') + '

'; me.removeListener('firstBeforeExecCommand focus', clear); setTimeout(function () { me.focus(); me._selectionChange(); }, 0) } } return function (cont) { var me = this; me.body.innerHTML = '

' + cont + '

'; 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(//ig, "") //转换图片 .replace(/]*>[\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 ''; } 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(/

]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi, "

$1

") //去掉多余的属性 .replace(/\s+(class|lang|align)\s*=\s*(['"]?)([\w-]+)\2/ig, function (str, name, marks, val) { //保留list的标示 return name == 'class' && val == 'MsoListParagraph' ? str : '' }) //清除多余的font/span不能匹配 有可能是空格 .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(/'/g, '\'') .replace(/"/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 (!/]/.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, '  ')) } } function isElement(node, arr, formatter, current) { var attrhtml = ''; if (node.attrs) { attrhtml = []; var attrs = node.attrs; for (var a in attrs) { //这里就针对 //

'

//这里边的\"做转换,要不用innerHTML直接被截断了,属性src //有可能做的不够 attrhtml.push(a + (attrs[a] !== undefined ? '="' + (notTransAttrs[a] ? utils.html(attrs[a]).replace(/["]/g, function (a) { return '"' }) : 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(''); } 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('text'); * ``` */ 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('text'); * ``` */ 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 原来的方式 [^"'<>\/] 有\/就不能配对上
这样的标签了 //先去掉了,加上的原因忘了,这里先记录 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; } //如果结束是文本,就有可能丢掉,所以这里手动判断一下 //例如
  • sdfsdfsdf
  • 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]
    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 = '

    ' + (browser.ie ? '' : '
    ') + '

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

    |

    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 : '
    '; } 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 = '' + ci.alt + ''; if (ci['floatStyle'] == 'center') { str = '

    ' + str + '

    '; } html.push(str); } else { for (var i = 0; ci = opt[i++];) { str = '

    '; 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 = $('').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 = a.getAttr('href'); if (!/^(ftp|https?|\/|file)/.test(_href)) { _href = 'http://' + _href; } if (_href != 'http://undefined') a.setAttr('href', _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', a.getAttr('href')); }) }); me.commands['link'] = { execCommand: function (cmdName, opt) { var me = this; var rng = me.selection.getRange(); opt._href && (opt._href = utils.unhtml(opt._href, /[<">'](?:(amp|lt|quot|gt|#39|nbsp);)?/g)); opt.href && (opt.href = utils.unhtml(opt.href, /[<">'](?:(amp|lt|quot|gt|#39|nbsp);)?/g)); if (rng.collapsed) { var start = rng.startContainer; if (start = domUtils.findParentByTagName(start, 'a', true)) { $(start).attr(opt); rng.selectNode(start).select() } else { rng.insertNode($('' + opt.href + '').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(); $('').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('
    ' + this.getContent(null, null, true) + '
    '); 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 = $('

    ').appendTo(rng.startContainer).html(browser.ie ? ' ' : '
    ')[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 = "

    " + (ie ? "" : "
    ") + "

    "; 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会产生多余的 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(/> <').replace(/\s*\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 = ' '; 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 = '


    '; 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字符,也会产生 这里过滤掉 html = div.innerHTML;//.replace(/>(?:(\s| )*?)<'); //过滤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) } ; $('

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

    ' + (browser.ie ? '' : '
    ') + '

    '; }; } else { me.$body.css({ 'position': '', 'left': '', 'top': '' }); // me.body.style.cssText = bakCssText; var cont = sourceEditor.getContent() || '

    ' + (browser.ie ? '' : '
    ') + '

    '; //处理掉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 = '

    ' + (browser.ie ? '' : '
    ') + '

    '; } //要在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, '

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

    '); 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 = ' ' } }) }) }; ///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 ? '' + '' + '' : ''; d.open(); d.write('' + formula + '

    ' + c + '
    '); 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 ? '' : ''; } 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]; vi.url = utils.unhtml(vi.url, /[<">'](?:(amp|lt|quot|gt|#39|nbsp);)?/g); 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]) { var remove_check = pN.getAttribute('class'); //新增 if (remove_check && remove_check.indexOf('edui-body-container') > -1) { } else { domUtils.removeAttributes(pN, removeFormatAttributes); } } pN = bookmark.end.parentNode; if (bookmark.end && domUtils.isBlockElm(pN) && !dtd.$tableContent[pN.tagName] && !dtd.$list[pN.tagName]) { var remove_check = pN.getAttribute('class'); //新增 if (remove_check && remove_check.indexOf('edui-body-container') > -1) { } else { domUtils.removeAttributes(pN, removeFormatAttributes); } } range.moveToBookmark(bookmark).moveToBookmark(bookmark1); //清除冗余的代码 var node = range.startContainer, tmp, collapsed = range.collapsed; console.log(node); 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]
    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 = '

    ' + (browser.ie ? '' : '
    ') + '

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

    ' + (browser.ie ? ' ' : '
    ') + '

    '; 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', '' + latex + ''); 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(); } } }; /** * @file xssFilter.js * @desc xss过滤器 * @author robbenmu */ UM.plugins.xssFilter = function () { var config = UMEDITOR_CONFIG; var whiteList = config.whiteList; function filter(node) { var tagName = node.tagName; var attrs = node.attrs; if (!whiteList.hasOwnProperty(tagName)) { node.parentNode.removeChild(node); return false; } UM.utils.each(attrs, function (val, key) { if (whiteList[tagName].indexOf(key) === -1) { node.setAttr(key); } }); } // 添加inserthtml\paste等操作用的过滤规则 if (whiteList && config.xssFilterRules) { this.options.filterRules = function () { var result = {}; UM.utils.each(whiteList, function (val, key) { result[key] = function (node) { return filter(node); }; }); return result; }(); } var tagList = []; UM.utils.each(whiteList, function (val, key) { tagList.push(key); }); // 添加input过滤规则 // if (whiteList && config.inputXssFilter) { this.addInputRule(function (root) { root.traversal(function (node) { if (node.type !== 'element') { return false; } filter(node); }); }); } // 添加output过滤规则 // if (whiteList && config.outputXssFilter) { this.addOutputRule(function (root) { root.traversal(function (node) { if (node.type !== 'element') { return false; } filter(node); }); }); } }; (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) {%>
    <% }; %><%if(text) {%><%=text%><%}%>' + '<%if(caret && text){%><%}%>' + '<% if(caret) {%><% };%>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: '
    ' , 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: '
      ' + '<%for(var i=0,ci;ci=data[i++];){%>' + '<%if(ci.divider){%>
    • <%}else{%>' + '
    • class="<%= ci.active|| \'\' %> <%=ci.disabled||\'\' %>" <%}%> data-value="<%= ci.value%>">' + '<%= ci.label%>' + '
    • <%}%>' + '<%}%>' + '
    ', 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 = $('
  • ' + label + '
  • ').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: '
    data-original-title="<%=title%>"<%}%>>
    <%if(icon){%>
    <%}%><%if(text){%><%=text%><%}%>
    ' + '
    ' + '
    <\/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: '
    data-original-title="<%=title%>"<%}%>>
    <%if(icon){%>
    <%}%>
    style="background: <%=color%>"<%}%>>
    <%if(text){%><%=text%><%}%>
    ' + '
    ' + '
    <\/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: '
    ){%>onmousedown="return false"<%}%>' + '>
    <%=subtpl%>
    ' + '
    ' + '
    ', 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: '
    ' + '' + '' + '' + '' + '' + '' + '' + '' + '
    ', 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 = '
    ' + '' + '' + ''; for (var i = 0; i < COLORS.length; i++) { if (i && i % 10 === 0) { html += '' + (i == 60 ? '' : '') + ''; } html += i < 70 ? '' : ''; } html += '
    ' + opt.lang_themeColor + '
    ' + opt.lang_standardColor + '
    '; 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: "
      edui-combobox-<%=comboboxName%><%}%>\" unselectable=\"on\" onmousedown=\"return false\" role=\"menu\" aria-labelledby=\"dropdownMenu\">" + "<%if(autoRecord) {%>" + "<%for( var i=0, len = recordStack.length; i" + "<%var index = recordStack[i];%>" + "
    • <%if( selected == index ) {%> edui-combobox-checked<%}%>\" data-item-index=\"<%=index%>\" unselectable=\"on\" onmousedown=\"return false\">" + "" + "" + "
    • " + "<%}%>" + "<%if( i ) {%>" + "
    • " + "<%}%>" + "<%}%>" + "<%for( var i=0, label; label = items[i]; i++ ) {%>" + "
    • <%if( selected == i ) {%> edui-combobox-checked<%}%> edui-combobox-item-<%=i%>\" data-item-index=\"<%=i%>\" unselectable=\"on\" onmousedown=\"return false\">" + "" + "" + "
    • " + "<%}%>" + "
    ", 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: '
    ' + '
    ' + '
    ' + '

    <%=title%>

    ' + '
    ' + '
    ' + '
    ' + '<% if(cancellabel || oklabel) {%>' + '' + '<%}%>
    ', 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 = $('
    ').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: '
    ' + '
    ' + '
    ' + '
    ', 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: '
    ', 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 = $('
    ').insertBefore($editorCont); editor.$container = $container; editor.container = $container[0]; editor.$body = $editorCont; //修正在ie9+以上的版本中,自动长高收起时的,残影问题 if (browser.ie && browser.ie9above) { var $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($('
    ')); 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, overflowY: status.body.overflowY}); $(this.getEditorDocumentElement()).css({overflowX: status.html.overflowX, 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 = $('').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)