/*! * UEditorPlus * version: 2.0.0 */ (function(){ // editor.js UEDITOR_CONFIG = window.UEDITOR_CONFIG || {}; var baidu = window.baidu || {}; window.baidu = baidu; window.UE = baidu.editor = { plugins: {}, commands: {}, instants: {}, I18N: {}, _customizeUI: {}, version: "3.0.0" }; var dom = (UE.dom = {}); // core/browser.js /** * 浏览器判断模块 * @file * @module UE.browser * @since 1.2.6.1 */ /** * 提供浏览器检测的模块 * @unfile * @module UE.browser */ var browser = (UE.browser = (function() { var agent = navigator.userAgent.toLowerCase(), opera = window.opera, browser = { /** * @property {boolean} ie 检测当前浏览器是否为IE * @example * ```javascript * if ( UE.browser.ie ) { * console.log( '当前浏览器是IE' ); * } * ``` */ ie: /(msie\s|trident.*rv:)([\w.]+)/i.test(agent), /** * @property {boolean} opera 检测当前浏览器是否为Opera * @example * ```javascript * if ( UE.browser.opera ) { * console.log( '当前浏览器是Opera' ); * } * ``` */ opera: !!opera && opera.version, /** * @property {boolean} webkit 检测当前浏览器是否是webkit内核的浏览器 * @example * ```javascript * if ( UE.browser.webkit ) { * console.log( '当前浏览器是webkit内核浏览器' ); * } * ``` */ webkit: agent.indexOf(" applewebkit/") > -1, /** * @property {boolean} mac 检测当前浏览器是否是运行在mac平台下 * @example * ```javascript * if ( UE.browser.mac ) { * console.log( '当前浏览器运行在mac平台下' ); * } * ``` */ mac: agent.indexOf("macintosh") > -1, /** * @property {boolean} quirks 检测当前浏览器是否处于“怪异模式”下 * @example * ```javascript * if ( UE.browser.quirks ) { * console.log( '当前浏览器运行处于“怪异模式”' ); * } * ``` */ quirks: document.compatMode == "BackCompat" }; /** * @property {boolean} gecko 检测当前浏览器内核是否是gecko内核 * @example * ```javascript * if ( UE.browser.gecko ) { * console.log( '当前浏览器内核是gecko内核' ); * } * ``` */ browser.gecko = navigator.product == "Gecko" && !browser.webkit && !browser.opera && !browser.ie; var version = 0; // Internet Explorer 6.0+ if (browser.ie) { var v1 = agent.match(/(?:msie\s([\w.]+))/); var v2 = agent.match(/(?:trident.*rv:([\w.]+))/); if (v1 && v2 && v1[1] && v2[1]) { version = Math.max(v1[1] * 1, v2[1] * 1); } else if (v1 && v1[1]) { version = v1[1] * 1; } else if (v2 && v2[1]) { version = v2[1] * 1; } else { version = 0; } browser.ie11Compat = document.documentMode == 11; /** * @property { boolean } ie9Compat 检测浏览器模式是否为 IE9 兼容模式 * @warning 如果浏览器不是IE, 则该值为undefined * @example * ```javascript * if ( UE.browser.ie9Compat ) { * console.log( '当前浏览器运行在IE9兼容模式下' ); * } * ``` */ browser.ie9Compat = document.documentMode == 9; /** * @property { boolean } ie8 检测浏览器是否是IE8浏览器 * @warning 如果浏览器不是IE, 则该值为undefined * @example * ```javascript * if ( UE.browser.ie8 ) { * console.log( '当前浏览器是IE8浏览器' ); * } * ``` */ browser.ie8 = !!document.documentMode; /** * @property { boolean } ie8Compat 检测浏览器模式是否为 IE8 兼容模式 * @warning 如果浏览器不是IE, 则该值为undefined * @example * ```javascript * if ( UE.browser.ie8Compat ) { * console.log( '当前浏览器运行在IE8兼容模式下' ); * } * ``` */ browser.ie8Compat = document.documentMode == 8; /** * @property { boolean } ie7Compat 检测浏览器模式是否为 IE7 兼容模式 * @warning 如果浏览器不是IE, 则该值为undefined * @example * ```javascript * if ( UE.browser.ie7Compat ) { * console.log( '当前浏览器运行在IE7兼容模式下' ); * } * ``` */ browser.ie7Compat = (version == 7 && !document.documentMode) || document.documentMode == 7; /** * @property { boolean } ie6Compat 检测浏览器模式是否为 IE6 模式 或者怪异模式 * @warning 如果浏览器不是IE, 则该值为undefined * @example * ```javascript * if ( UE.browser.ie6Compat ) { * console.log( '当前浏览器运行在IE6模式或者怪异模式下' ); * } * ``` */ browser.ie6Compat = version < 7 || browser.quirks; browser.ie9above = version > 8; browser.ie9below = version < 9; browser.ie11above = version > 10; browser.ie11below = version < 11; } // Gecko. if (browser.gecko) { var geckoRelease = agent.match(/rv:([\d\.]+)/); if (geckoRelease) { geckoRelease = geckoRelease[1].split("."); version = geckoRelease[0] * 10000 + (geckoRelease[1] || 0) * 100 + (geckoRelease[2] || 0) * 1; } } /** * @property { Number } chrome 检测当前浏览器是否为Chrome, 如果是,则返回Chrome的大版本号 * @warning 如果浏览器不是chrome, 则该值为undefined * @example * ```javascript * if ( UE.browser.chrome ) { * console.log( '当前浏览器是Chrome' ); * } * ``` */ if (/chrome\/(\d+\.\d)/i.test(agent)) { browser.chrome = +RegExp["\x241"]; } /** * @property { Number } safari 检测当前浏览器是否为Safari, 如果是,则返回Safari的大版本号 * @warning 如果浏览器不是safari, 则该值为undefined * @example * ```javascript * if ( UE.browser.safari ) { * console.log( '当前浏览器是Safari' ); * } * ``` */ if ( /(\d+\.\d)?(?:\.\d)?\s+safari\/?(\d+\.\d+)?/i.test(agent) && !/chrome/i.test(agent) ) { browser.safari = +(RegExp["\x241"] || RegExp["\x242"]); } // Opera 9.50+ if (browser.opera) version = parseFloat(opera.version()); // WebKit 522+ (Safari 3+) if (browser.webkit) version = parseFloat(agent.match(/ applewebkit\/(\d+)/)[1]); /** * @property { Number } version 检测当前浏览器版本号 * @remind *
* console.log( wrapNode.innerHTML ); * * * UE.dom.domUtils.breakParent( node, parent ); * //拆分后 * //output: * console.log( wrapNode.innerHTML ); * * ``` */ breakParent: function(node, parent) { var tmpNode, parentClone = node, clone = node, leftNodes, rightNodes; do { parentClone = parentClone.parentNode; if (leftNodes) { tmpNode = parentClone.cloneNode(false); tmpNode.appendChild(leftNodes); leftNodes = tmpNode; tmpNode = parentClone.cloneNode(false); tmpNode.appendChild(rightNodes); rightNodes = tmpNode; } else { leftNodes = parentClone.cloneNode(false); rightNodes = leftNodes.cloneNode(false); } while ((tmpNode = clone.previousSibling)) { leftNodes.insertBefore(tmpNode, leftNodes.firstChild); } while ((tmpNode = clone.nextSibling)) { rightNodes.appendChild(tmpNode); } clone = parentClone; } while (parent !== parentClone); tmpNode = parent.parentNode; tmpNode.insertBefore(leftNodes, parent); tmpNode.insertBefore(rightNodes, parent); tmpNode.insertBefore(node, rightNodes); domUtils.remove(parent); return node; }, /** * 检查节点node是否是空inline节点 * @method isEmptyInlineElement * @param { Node } node 需要检测的节点对象 * @return { Number } 如果给定的节点是空的inline节点, 则返回1, 否则返回0。 * @example * ```html * => 1 * => 1 * => 1 * xx => 0 * ``` */ isEmptyInlineElement: function(node) { if (node.nodeType != 1 || !dtd.$removeEmpty[node.tagName]) { return 0; } node = node.firstChild; while (node) { //如果是创建的bookmark就跳过 if (domUtils.isBookmarkNode(node)) { return 0; } if ( (node.nodeType == 1 && !domUtils.isEmptyInlineElement(node)) || (node.nodeType == 3 && !domUtils.isWhitespace(node)) ) { return 0; } node = node.nextSibling; } return 1; }, /** * 删除node节点下首尾两端的空白文本子节点 * @method trimWhiteTextNode * @param { Element } node 需要执行删除操作的元素对象 * @example * ```javascript * var node = document.createElement("div"); * * node.appendChild( document.createTextNode( "" ) ); * * node.appendChild( document.createElement("div") ); * * node.appendChild( document.createTextNode( "" ) ); * * //3 * console.log( node.childNodes.length ); * * UE.dom.domUtils.trimWhiteTextNode( node ); * * //1 * console.log( node.childNodes.length ); * ``` */ trimWhiteTextNode: function(node) { function remove(dir) { var child; while ( (child = node[dir]) && child.nodeType == 3 && domUtils.isWhitespace(child) ) { node.removeChild(child); } } remove("firstChild"); remove("lastChild"); }, /** * 合并node节点下相同的子节点 * @name mergeChild * @desc * UE.dom.domUtils.mergeChild(node,tagName) //tagName要合并的子节点的标签 * @example *
xxaaxx
* ==> UE.dom.domUtils.mergeChild(node,'span') *xxaaxx
*/ mergeChild: function(node, tagName, attrs) { var list = domUtils.getElementsByTagName(node, node.tagName.toLowerCase()); for (var i = 0, ci; (ci = list[i++]); ) { if (!ci.parentNode || domUtils.isBookmarkNode(ci)) { continue; } //span单独处理 if (ci.tagName.toLowerCase() == "span") { if (node === ci.parentNode) { domUtils.trimWhiteTextNode(node); if (node.childNodes.length == 1) { node.style.cssText = ci.style.cssText + ";" + node.style.cssText; domUtils.remove(ci, true); continue; } } ci.style.cssText = node.style.cssText + ";" + ci.style.cssText; if (attrs) { var style = attrs.style; if (style) { style = style.split(";"); for (var j = 0, s; (s = style[j++]); ) { ci.style[utils.cssStyleToDomStyle(s.split(":")[0])] = s.split( ":" )[1]; } } } if (domUtils.isSameStyle(ci, node)) { domUtils.remove(ci, true); } continue; } if (domUtils.isSameElement(node, ci)) { domUtils.remove(ci, true); } } }, /** * 原生方法getElementsByTagName的封装 * @method getElementsByTagName * @param { Node } node 目标节点对象 * @param { String } tagName 需要查找的节点的tagName, 多个tagName以空格分割 * @return { Array } 符合条件的节点集合 */ getElementsByTagName: function(node, name, filter) { if (filter && utils.isString(filter)) { var className = filter; filter = function(node) { return domUtils.hasClass(node, className); }; } name = utils.trim(name).replace(/[ ]{2,}/g, " ").split(" "); var arr = []; for (var n = 0, ni; (ni = name[n++]); ) { var list = node.getElementsByTagName(ni); for (var i = 0, ci; (ci = list[i++]); ) { if (!filter || filter(ci)) arr.push(ci); } } return arr; }, /** * 将节点node提取到父节点上 * @method mergeToParent * @param { Element } node 需要提取的元素对象 * @example * ```html *xxxx[xxxx]x
==> range.applyInlineStyle("strong") ==>xxxx[xxxx]x
* ``` */ /** * 给range选区中的内容添加给定的inline标签, 并且为标签附加上一些初始化属性。 * @method applyInlineStyle * @param { String } tagName 需要添加的标签名 * @param { Object } attrs 跟随新添加的标签的属性 * @return { UE.dom.Range } 当前选区 * @example * ```html *xxxx[xxxx]x
* * ==> * * * range.applyInlineStyle("strong",{"style":"font-size:12px"}) * * ==> * *xxxx[xxxx]x
* ``` */ applyInlineStyle: function(tagName, attrs, list) { if (this.collapsed) return this; this.trimBoundary() .enlarge(false, function(node) { return node.nodeType == 1 && domUtils.isBlockElm(node); }) .adjustmentBoundary(); var bookmark = this.createBookmark(), end = bookmark.end, filterFn = function(node) { return node.nodeType == 1 ? node.tagName.toLowerCase() != "br" : !domUtils.isWhitespace(node); }, current = domUtils.getNextDomNode(bookmark.start, false, filterFn), node, pre, range = this.cloneRange(); while ( current && domUtils.getPosition(current, end) & domUtils.POSITION_PRECEDING ) { if (current.nodeType == 3 || dtd[tagName][current.tagName]) { range.setStartBefore(current); node = current; while ( node && (node.nodeType == 3 || dtd[tagName][node.tagName]) && node !== end ) { pre = node; node = domUtils.getNextDomNode( node, node.nodeType == 1, null, function(parent) { return dtd[tagName][parent.tagName]; } ); } var frag = range.setEndAfter(pre).extractContents(), elm; if (list && list.length > 0) { var level, top; top = level = list[0].cloneNode(false); for (var i = 1, ci; (ci = list[i++]); ) { level.appendChild(ci.cloneNode(false)); level = level.firstChild; } elm = level; } else { elm = range.document.createElement(tagName); } if (attrs) { domUtils.setAttributes(elm, attrs); } elm.appendChild(frag); //针对嵌套span的全局样式指定,做容错处理 if (elm.tagName == "SPAN" && attrs && attrs.style) { utils.each(elm.getElementsByTagName("span"), function(s) { s.style.cssText = s.style.cssText + ";" + attrs.style; }); } range.insertNode(list ? top : elm); //处理下滑线在a上的情况 var aNode; if ( tagName == "span" && attrs.style && /text\-decoration/.test(attrs.style) && (aNode = domUtils.findParentByTagName(elm, "a", true)) ) { domUtils.setAttributes(aNode, attrs); domUtils.remove(elm, true); elm = aNode; } else { domUtils.mergeSibling(elm); domUtils.clearEmptySibling(elm); } //去除子节点相同的 domUtils.mergeChild(elm, attrs); current = domUtils.getNextDomNode(elm, false, filterFn); domUtils.mergeToParent(elm); if (node === end) { break; } } else { current = domUtils.getNextDomNode(current, true, filterFn); } } return this.moveToBookmark(bookmark); }, /** * 移除当前选区内指定的inline标签,但保留其中的内容 * @method removeInlineStyle * @param { String } tagName 需要移除的标签名 * @return { UE.dom.Range } 当前的range对象 * @example * ```html * xx[xxxxyyyzz]z => range.removeInlineStyle(["em"]) => xx[xxxxyyyzz]z * ``` */ /** * 移除当前选区内指定的一组inline标签,但保留其中的内容 * @method removeInlineStyle * @param { Array } tagNameArr 需要移除的标签名的数组 * @return { UE.dom.Range } 当前的range对象 * @see UE.dom.Range:removeInlineStyle(String) */ removeInlineStyle: function(tagNames) { if (this.collapsed) return this; tagNames = utils.isArray(tagNames) ? tagNames : [tagNames]; this.shrinkBoundary().adjustmentBoundary(); var start = this.startContainer, end = this.endContainer; while (1) { if (start.nodeType == 1) { if (utils.indexOf(tagNames, start.tagName.toLowerCase()) > -1) { break; } if (start.tagName.toLowerCase() == "body") { start = null; break; } } start = start.parentNode; } while (1) { if (end.nodeType == 1) { if (utils.indexOf(tagNames, end.tagName.toLowerCase()) > -1) { break; } if (end.tagName.toLowerCase() == "body") { end = null; break; } } end = end.parentNode; } var bookmark = this.createBookmark(), frag, tmpRange; if (start) { tmpRange = this.cloneRange() .setEndBefore(bookmark.start) .setStartBefore(start); frag = tmpRange.extractContents(); tmpRange.insertNode(frag); domUtils.clearEmptySibling(start, true); start.parentNode.insertBefore(bookmark.start, start); } if (end) { tmpRange = this.cloneRange() .setStartAfter(bookmark.end) .setEndAfter(end); frag = tmpRange.extractContents(); tmpRange.insertNode(frag); domUtils.clearEmptySibling(end, false, true); end.parentNode.insertBefore(bookmark.end, end.nextSibling); } var current = domUtils.getNextDomNode(bookmark.start, false, function( node ) { return node.nodeType == 1; }), next; while (current && current !== bookmark.end) { next = domUtils.getNextDomNode(current, true, function(node) { return node.nodeType == 1; }); if (utils.indexOf(tagNames, current.tagName.toLowerCase()) > -1) { domUtils.remove(current, true); } current = next; } return this.moveToBookmark(bookmark); }, /** * 获取当前选中的自闭合的节点 * @method getClosedNode * @return { Node | NULL } 如果当前选中的是自闭合节点, 则返回该节点, 否则返回NULL */ getClosedNode: function() { var node; if (!this.collapsed) { var range = this.cloneRange().adjustmentBoundary().shrinkBoundary(); if (selectOneNode(range)) { var child = range.startContainer.childNodes[range.startOffset]; if ( child && child.nodeType === 1 && (dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName]) ) { node = child; } } } return node; }, /** * 在页面上高亮range所表示的选区 * @method select * @return { UE.dom.Range } 返回当前Range对象 */ //这里不区分ie9以上,trace:3824 select: browser.ie ? function(noFillData, textRange) { var nativeRange; if (!this.collapsed) this.shrinkBoundary(); var node = this.getClosedNode(); if (node && !textRange) { try { nativeRange = this.document.body.createControlRange(); nativeRange.addElement(node); nativeRange.select(); } catch (e) {} return this; } var bookmark = this.createBookmark(), start = bookmark.start, end; nativeRange = this.document.body.createTextRange(); nativeRange.moveToElementText(start); nativeRange.moveStart("character", 1); if (!this.collapsed) { var nativeRangeEnd = this.document.body.createTextRange(); end = bookmark.end; nativeRangeEnd.moveToElementText(end); nativeRange.setEndPoint("EndToEnd", nativeRangeEnd); } else { if (!noFillData && this.startContainer.nodeType != 3) { //使用|x固定住光标 var tmpText = this.document.createTextNode(fillChar), tmp = this.document.createElement("span"); tmp.appendChild(this.document.createTextNode(fillChar)); start.parentNode.insertBefore(tmp, start); start.parentNode.insertBefore(tmpText, start); //当点b,i,u时,不能清除i上边的b removeFillData(this.document, tmpText); fillData = tmpText; mergeSibling(tmp, "previousSibling"); mergeSibling(start, "nextSibling"); nativeRange.moveStart("character", -1); nativeRange.collapse(true); } } this.moveToBookmark(bookmark); tmp && domUtils.remove(tmp); //IE在隐藏状态下不支持range操作,catch一下 try { nativeRange.select(); } catch (e) {} return this; } : function(notInsertFillData) { function checkOffset(rng) { function check(node, offset, dir) { if (node.nodeType == 3 && node.nodeValue.length < offset) { rng[dir + "Offset"] = node.nodeValue.length; } } check(rng.startContainer, rng.startOffset, "start"); check(rng.endContainer, rng.endOffset, "end"); } var win = domUtils.getWindow(this.document), sel = win.getSelection(), txtNode; //FF下关闭自动长高时滚动条在关闭dialog时会跳 //ff下如果不body.focus将不能定位闭合光标到编辑器内 browser.gecko ? this.document.body.focus() : win.focus(); if (sel) { sel.removeAllRanges(); // trace:870 chrome/safari后边是br对于闭合得range不能定位 所以去掉了判断 // this.startContainer.nodeType != 3 &&! ((child = this.startContainer.childNodes[this.startOffset]) && child.nodeType == 1 && child.tagName == 'BR' if (this.collapsed && !notInsertFillData) { // //opear如果没有节点接着,原生的不能够定位,不能在body的第一级插入空白节点 // if (notInsertFillData && browser.opera && !domUtils.isBody(this.startContainer) && this.startContainer.nodeType == 1) { // var tmp = this.document.createTextNode(''); // this.insertNode(tmp).setStart(tmp, 0).collapse(true); // } // //处理光标落在文本节点的情况 //处理以下的情况 //|xxxx //xxxx|xxxx //xxxx| var start = this.startContainer, child = start; if (start.nodeType == 1) { child = start.childNodes[this.startOffset]; } if ( !(start.nodeType == 3 && this.startOffset) && (child ? !child.previousSibling || child.previousSibling.nodeType != 3 : !start.lastChild || start.lastChild.nodeType != 3) ) { txtNode = this.document.createTextNode(fillChar); //跟着前边走 this.insertNode(txtNode); removeFillData(this.document, txtNode); mergeSibling(txtNode, "previousSibling"); mergeSibling(txtNode, "nextSibling"); fillData = txtNode; this.setStart(txtNode, browser.webkit ? 1 : 0).collapse(true); } } var nativeRange = this.document.createRange(); if ( this.collapsed && browser.opera && this.startContainer.nodeType == 1 ) { var child = this.startContainer.childNodes[this.startOffset]; if (!child) { //往前靠拢 child = this.startContainer.lastChild; if (child && domUtils.isBr(child)) { this.setStartBefore(child).collapse(true); } } else { //向后靠拢 while (child && domUtils.isBlockElm(child)) { if (child.nodeType == 1 && child.childNodes[0]) { child = child.childNodes[0]; } else { break; } } child && this.setStartBefore(child).collapse(true); } } //是createAddress最后一位算的不准,现在这里进行微调 checkOffset(this); nativeRange.setStart(this.startContainer, this.startOffset); nativeRange.setEnd(this.endContainer, this.endOffset); sel.addRange(nativeRange); } return this; }, /** * 滚动到当前range开始的位置 * @method scrollToView * @param { Window } win 当前range对象所属的window对象 * @return { UE.dom.Range } 当前Range对象 */ /** * 滚动到距离当前range开始位置 offset 的位置处 * @method scrollToView * @param { Window } win 当前range对象所属的window对象 * @param { Number } offset 距离range开始位置处的偏移量, 如果为正数, 则向下偏移, 反之, 则向上偏移 * @return { UE.dom.Range } 当前Range对象 */ scrollToView: function(win, offset) { win = win ? window : domUtils.getWindow(this.document); var me = this, span = me.document.createElement("span"); //trace:717 span.innerHTML = " "; me.cloneRange().insertNode(span); domUtils.scrollToView(span, win, offset); domUtils.remove(span); return me; }, /** * 判断当前选区内容是否占位符 * @private * @method inFillChar * @return { Boolean } 如果是占位符返回true,否则返回false */ inFillChar: function() { var start = this.startContainer; if ( this.collapsed && start.nodeType == 3 && start.nodeValue.replace(new RegExp("^" + domUtils.fillChar), "") .length + 1 == start.nodeValue.length ) { return true; } return false; }, /** * 保存 * @method createAddress * @private * @return { Boolean } 返回开始和结束的位置 * @example * ```html * ** aaaa * * * bbbb * * *
* * * * ``` */ createAddress: function(ignoreEnd, ignoreTxt) { var addr = {}, me = this; function getAddress(isStart) { var node = isStart ? me.startContainer : me.endContainer; var parents = domUtils.findParents(node, true, function(node) { return !domUtils.isBody(node); }), addrs = []; for (var i = 0, ci; (ci = parents[i++]); ) { addrs.push(domUtils.getNodeIndex(ci, ignoreTxt)); } var firstIndex = 0; if (ignoreTxt) { if (node.nodeType == 3) { var tmpNode = node.previousSibling; while (tmpNode && tmpNode.nodeType == 3) { firstIndex += tmpNode.nodeValue.replace(fillCharReg, "").length; tmpNode = tmpNode.previousSibling; } firstIndex += isStart ? me.startOffset : me.endOffset; // - (fillCharReg.test(node.nodeValue) ? 1 : 0 ) } else { node = node.childNodes[isStart ? me.startOffset : me.endOffset]; if (node) { firstIndex = domUtils.getNodeIndex(node, ignoreTxt); } else { node = isStart ? me.startContainer : me.endContainer; var first = node.firstChild; while (first) { if (domUtils.isFillChar(first)) { first = first.nextSibling; continue; } firstIndex++; if (first.nodeType == 3) { while (first && first.nodeType == 3) { first = first.nextSibling; } } else { first = first.nextSibling; } } } } } else { firstIndex = isStart ? domUtils.isFillChar(node) ? 0 : me.startOffset : me.endOffset; } if (firstIndex < 0) { firstIndex = 0; } addrs.push(firstIndex); return addrs; } addr.startAddress = getAddress(true); if (!ignoreEnd) { addr.endAddress = me.collapsed ? [].concat(addr.startAddress) : getAddress(); } return addr; }, /** * 保存 * @method createAddress * @private * @return { Boolean } 返回开始和结束的位置 * @example * ```html * ** aaaa * * * bbbb * * *
* * * * ``` */ moveToAddress: function(addr, ignoreEnd) { var me = this; function getNode(address, isStart) { var tmpNode = me.document.body, parentNode, offset; for (var i = 0, ci, l = address.length; i < l; i++) { ci = address[i]; parentNode = tmpNode; tmpNode = tmpNode.childNodes[ci]; if (!tmpNode) { offset = ci; break; } } if (isStart) { if (tmpNode) { me.setStartBefore(tmpNode); } else { me.setStart(parentNode, offset); } } else { if (tmpNode) { me.setEndBefore(tmpNode); } else { me.setEnd(parentNode, offset); } } } getNode(addr.startAddress, true); !ignoreEnd && addr.endAddress && getNode(addr.endAddress); return me; }, /** * 判断给定的Range对象是否和当前Range对象表示的是同一个选区 * @method equals * @param { UE.dom.Range } 需要判断的Range对象 * @return { Boolean } 如果给定的Range对象与当前Range对象表示的是同一个选区, 则返回true, 否则返回false */ equals: function(rng) { for (var p in this) { if (this.hasOwnProperty(p)) { if (this[p] !== rng[p]) return false; } } return true; }, /** * 遍历range内的节点。每当遍历一个节点时, 都会执行参数项 doFn 指定的函数, 该函数的接受当前遍历的节点 * 作为其参数。 * @method traversal * @param { Function } doFn 对每个遍历的节点要执行的方法, 该方法接受当前遍历的节点作为其参数 * @return { UE.dom.Range } 当前range对象 * @example * ```html * * * * * * * * * * * ``` */ /** * 遍历range内的节点。 * 每当遍历一个节点时, 都会执行参数项 doFn 指定的函数, 该函数的接受当前遍历的节点 * 作为其参数。 * 可以通过参数项 filterFn 来指定一个过滤器, 只有符合该过滤器过滤规则的节点才会触 * 发doFn函数的执行 * @method traversal * @param { Function } doFn 对每个遍历的节点要执行的方法, 该方法接受当前遍历的节点作为其参数 * @param { Function } filterFn 过滤器, 该函数接受当前遍历的节点作为参数, 如果该节点满足过滤 * 规则, 请返回true, 该节点会触发doFn, 否则, 请返回false, 则该节点不 * 会触发doFn。 * @return { UE.dom.Range } 当前range对象 * @see UE.dom.Range:traversal(Function) * @example * ```html * * * * * * * * * * * ``` */ traversal: function(doFn, filterFn) { if (this.collapsed) return this; var bookmark = this.createBookmark(), end = bookmark.end, current = domUtils.getNextDomNode(bookmark.start, false, filterFn); while ( current && current !== end && domUtils.getPosition(current, end) & domUtils.POSITION_PRECEDING ) { var tmpNode = domUtils.getNextDomNode(current, false, filterFn); doFn(current); current = tmpNode; } return this.moveToBookmark(bookmark); } }; })(); // core/Selection.js /** * 选集 * @file * @module UE.dom * @class Selection * @since 1.2.6.1 */ /** * 选区集合 * @unfile * @module UE.dom * @class Selection */ (function() { function getBoundaryInformation(range, start) { var getIndex = domUtils.getNodeIndex; range = range.duplicate(); range.collapse(start); var parent = range.parentElement(); //如果节点里没有子节点,直接退出 if (!parent.hasChildNodes()) { return { container: parent, offset: 0 }; } var siblings = parent.children, child, testRange = range.duplicate(), startIndex = 0, endIndex = siblings.length - 1, index = -1, distance; while (startIndex <= endIndex) { index = Math.floor((startIndex + endIndex) / 2); child = siblings[index]; testRange.moveToElementText(child); var position = testRange.compareEndPoints("StartToStart", range); if (position > 0) { endIndex = index - 1; } else if (position < 0) { startIndex = index + 1; } else { //trace:1043 return { container: parent, offset: getIndex(child) }; } } if (index == -1) { testRange.moveToElementText(parent); testRange.setEndPoint("StartToStart", range); distance = testRange.text.replace(/(\r\n|\r)/g, "\n").length; siblings = parent.childNodes; if (!distance) { child = siblings[siblings.length - 1]; return { container: child, offset: child.nodeValue.length }; } var i = siblings.length; while (distance > 0) { distance -= siblings[--i].nodeValue.length; } return { container: siblings[i], offset: -distance }; } testRange.collapse(position > 0); testRange.setEndPoint(position > 0 ? "StartToStart" : "EndToStart", range); distance = testRange.text.replace(/(\r\n|\r)/g, "\n").length; if (!distance) { return dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName] ? { container: parent, offset: getIndex(child) + (position > 0 ? 0 : 1) } : { container: child, offset: position > 0 ? 0 : child.childNodes.length }; } while (distance > 0) { try { var pre = child; child = child[position > 0 ? "previousSibling" : "nextSibling"]; distance -= child.nodeValue.length; } catch (e) { return { container: parent, offset: getIndex(pre) }; } } return { container: child, offset: position > 0 ? -distance : child.nodeValue.length + distance }; } /** * 将ieRange转换为Range对象 * @param {Range} ieRange ieRange对象 * @param {Range} range Range对象 * @return {Range} range 返回转换后的Range对象 */ function transformIERangeToRange(ieRange, range) { if (ieRange.item) { range.selectNode(ieRange.item(0)); } else { var bi = getBoundaryInformation(ieRange, true); range.setStart(bi.container, bi.offset); if (ieRange.compareEndPoints("StartToEnd", ieRange) != 0) { bi = getBoundaryInformation(ieRange, false); range.setEnd(bi.container, bi.offset); } } return range; } /** * 获得ieRange * @param {Selection} sel Selection对象 * @return {ieRange} 得到ieRange */ function _getIERange(sel) { var ieRange; //ie下有可能报错 try { ieRange = sel.getNative().createRange(); } catch (e) { return null; } var el = ieRange.item ? ieRange.item(0) : ieRange.parentElement(); if ((el.ownerDocument || el) === sel.document) { return ieRange; } return null; } var Selection = (dom.Selection = function(doc) { var me = this, iframe; me.document = doc; if (browser.ie9below) { iframe = domUtils.getWindow(doc).frameElement; domUtils.on(iframe, "beforedeactivate", function() { me._bakIERange = me.getIERange(); }); domUtils.on(iframe, "activate", function() { try { if (!_getIERange(me) && me._bakIERange) { me._bakIERange.select(); } } catch (ex) {} me._bakIERange = null; }); } iframe = doc = null; }); Selection.prototype = { rangeInBody: function(rng, txtRange) { var node = browser.ie9below || txtRange ? rng.item ? rng.item() : rng.parentElement() : rng.startContainer; return node === this.document.body || domUtils.inDoc(node, this.document); }, /** * 获取原生seleciton对象 * @method getNative * @return { Object } 获得selection对象 * @example * ```javascript * editor.selection.getNative(); * ``` */ getNative: function() { var doc = this.document; try { return !doc ? null : browser.ie9below ? doc.selection : domUtils.getWindow(doc).getSelection(); } catch (e) { return null; } }, /** * 获得ieRange * @method getIERange * @return { Object } 返回ie原生的Range * @example * ```javascript * editor.selection.getIERange(); * ``` */ getIERange: function() { var ieRange = _getIERange(this); if (!ieRange) { if (this._bakIERange) { return this._bakIERange; } } return ieRange; }, /** * 缓存当前选区的range和选区的开始节点 * @method cache */ cache: function() { this.clear(); this._cachedRange = this.getRange(); this._cachedStartElement = this.getStart(); this._cachedStartElementPath = this.getStartElementPath(); }, /** * 获取选区开始位置的父节点到body * @method getStartElementPath * @return { Array } 返回父节点集合 * @example * ```javascript * editor.selection.getStartElementPath(); * ``` */ getStartElementPath: function() { if (this._cachedStartElementPath) { return this._cachedStartElementPath; } var start = this.getStart(); if (start) { return domUtils.findParents(start, true, null, true); } return []; }, /** * 清空缓存 * @method clear */ clear: function() { this._cachedStartElementPath = this._cachedRange = this._cachedStartElement = null; }, /** * 编辑器是否得到了选区 * @method isFocus */ isFocus: function() { try { if (browser.ie9below) { var nativeRange = _getIERange(this); return !!(nativeRange && this.rangeInBody(nativeRange)); } else { return !!this.getNative().rangeCount; } } catch (e) { return false; } }, /** * 获取选区对应的Range * @method getRange * @return { Object } 得到Range对象 * @example * ```javascript * editor.selection.getRange(); * ``` */ getRange: function() { var me = this; function optimze(range) { var child = me.document.body.firstChild, collapsed = range.collapsed; while (child && child.firstChild) { range.setStart(child, 0); child = child.firstChild; } if (!range.startContainer) { range.setStart(me.document.body, 0); } if (collapsed) { range.collapse(true); } } if (me._cachedRange != null) { return this._cachedRange; } var range = new baidu.editor.dom.Range(me.document); if (browser.ie9below) { var nativeRange = me.getIERange(); if (nativeRange) { //备份的_bakIERange可能已经实效了,dom树发生了变化比如从源码模式切回来,所以try一下,实效就放到body开始位置 try { transformIERangeToRange(nativeRange, range); } catch (e) { optimze(range); } } else { optimze(range); } } else { var sel = me.getNative(); if (sel && sel.rangeCount) { var firstRange = sel.getRangeAt(0); var lastRange = sel.getRangeAt(sel.rangeCount - 1); range .setStart(firstRange.startContainer, firstRange.startOffset) .setEnd(lastRange.endContainer, lastRange.endOffset); if ( range.collapsed && domUtils.isBody(range.startContainer) && !range.startOffset ) { optimze(range); } } else { //trace:1734 有可能已经不在dom树上了,标识的节点 if ( this._bakRange && domUtils.inDoc(this._bakRange.startContainer, this.document) ) { return this._bakRange; } optimze(range); } } return (this._bakRange = range); }, /** * 获取开始元素,用于状态反射 * @method getStart * @return { Element } 获得开始元素 * @example * ```javascript * editor.selection.getStart(); * ``` */ getStart: function() { if (this._cachedStartElement) { return this._cachedStartElement; } var range = browser.ie9below ? this.getIERange() : this.getRange(), tmpRange, start, tmp, parent; if (browser.ie9below) { if (!range) { //todo 给第一个值可能会有问题 return this.document.body.firstChild; } //control元素 if (range.item) { return range.item(0); } tmpRange = range.duplicate(); //修正ie下x[xx] 闭合后 x|xx tmpRange.text.length > 0 && tmpRange.moveStart("character", 1); tmpRange.collapse(1); start = tmpRange.parentElement(); parent = tmp = range.parentElement(); while ((tmp = tmp.parentNode)) { if (tmp == start) { start = parent; break; } } } else { range.shrinkBoundary(); start = range.startContainer; if (start.nodeType == 1 && start.hasChildNodes()) { start = start.childNodes[ Math.min(start.childNodes.length - 1, range.startOffset) ]; } if (start.nodeType == 3) { return start.parentNode; } } return start; }, /** * 得到选区中的文本 * @method getText * @return { String } 选区中包含的文本 * @example * ```javascript * editor.selection.getText(); * ``` */ getText: function() { var nativeSel, nativeRange; if (this.isFocus() && (nativeSel = this.getNative())) { nativeRange = browser.ie9below ? nativeSel.createRange() : nativeSel.getRangeAt(0); return browser.ie9below ? nativeRange.text : nativeRange.toString(); } return ""; }, /** * 清除选区 * @method clearRange * @example * ```javascript * editor.selection.clearRange(); * ``` */ clearRange: function() { this.getNative()[browser.ie9below ? "empty" : "removeAllRanges"](); } }; })(); // core/Editor.js /** * 编辑器主类,包含编辑器提供的大部分公用接口 * @file * @module UE * @class Editor * @since 1.2.6.1 */ /** * UEditor公用空间,UEditor所有的功能都挂载在该空间下 * @unfile * @module UE */ /** * UEditor的核心类,为用户提供与编辑器交互的接口。 * @unfile * @module UE * @class Editor */ (function() { var uid = 0, _selectionChangeTimer; /** * 获取编辑器的html内容,赋值到编辑器所在表单的textarea文本域里面 * @private * @method setValue * @param { UE.Editor } editor 编辑器事例 */ function setValue(form, editor) { var textarea; if (editor.options.textarea) { if (utils.isString(editor.options.textarea)) { for ( var i = 0, ti, tis = domUtils.getElementsByTagName(form, "textarea"); (ti = tis[i++]); ) { if (ti.id == "ueditor_textarea_" + editor.options.textarea) { textarea = ti; break; } } } else { textarea = editor.textarea; } } if (!textarea) { form.appendChild( (textarea = domUtils.createElement(document, "textarea", { name: editor.options.textarea, id: "ueditor_textarea_" + editor.options.textarea, style: "display:none" })) ); //不要产生多个textarea editor.textarea = textarea; } !textarea.getAttribute("name") && textarea.setAttribute("name", editor.options.textarea); textarea.value = editor.hasContents() ? editor.options.allHtmlEnabled ? editor.getAllHtml() : editor.getContent(null, null, true) : ""; } function loadPlugins(me) { //初始化插件 for (var pi in UE.plugins) { UE.plugins[pi].call(me); } } function checkCurLang(I18N) { for (var lang in I18N) { return lang; } } function langReadied(me) { me.langIsReady = true; me.fireEvent("langReady"); } /** * 编辑器准备就绪后会触发该事件 * @module UE * @class Editor * @event ready * @remind render方法执行完成之后,会触发该事件 * @remind * @example * ```javascript * editor.addListener( 'ready', function( editor ) { * editor.execCommand( 'focus' ); //编辑器家在完成后,让编辑器拿到焦点 * } ); * ``` */ /** * 执行destroy方法,会触发该事件 * @module UE * @class Editor * @event destroy * @see UE.Editor:destroy() */ /** * 执行reset方法,会触发该事件 * @module UE * @class Editor * @event reset * @see UE.Editor:reset() */ /** * 执行focus方法,会触发该事件 * @module UE * @class Editor * @event focus * @see UE.Editor:focus(Boolean) */ /** * 语言加载完成会触发该事件 * @module UE * @class Editor * @event langReady */ /** * 运行命令之后会触发该命令 * @module UE * @class Editor * @event beforeExecCommand */ /** * 运行命令之后会触发该命令 * @module UE * @class Editor * @event afterExecCommand */ /** * 运行命令之前会触发该命令 * @module UE * @class Editor * @event firstBeforeExecCommand */ /** * 在getContent方法执行之前会触发该事件 * @module UE * @class Editor * @event beforeGetContent * @see UE.Editor:getContent() */ /** * 在getContent方法执行之后会触发该事件 * @module UE * @class Editor * @event afterGetContent * @see UE.Editor:getContent() */ /** * 在getAllHtml方法执行时会触发该事件 * @module UE * @class Editor * @event getAllHtml * @see UE.Editor:getAllHtml() */ /** * 在setContent方法执行之前会触发该事件 * @module UE * @class Editor * @event beforeSetContent * @see UE.Editor:setContent(String) */ /** * 在setContent方法执行之后会触发该事件 * @module UE * @class Editor * @event afterSetContent * @see UE.Editor:setContent(String) */ /** * 每当编辑器内部选区发生改变时,将触发该事件 * @event selectionchange * @warning 该事件的触发非常频繁,不建议在该事件的处理过程中做重量级的处理 * @example * ```javascript * editor.addListener( 'selectionchange', function( editor ) { * console.log('选区发生改变'); * } */ /** * 在所有selectionchange的监听函数执行之前,会触发该事件 * @module UE * @class Editor * @event beforeSelectionChange * @see UE.Editor:selectionchange */ /** * 在所有selectionchange的监听函数执行完之后,会触发该事件 * @module UE * @class Editor * @event afterSelectionChange * @see UE.Editor:selectionchange */ /** * 编辑器内容发生改变时会触发该事件 * @module UE * @class Editor * @event contentChange */ /** * 以默认参数构建一个编辑器实例 * @constructor * @remind 通过 改构造方法实例化的编辑器,不带ui层.需要render到一个容器,编辑器实例才能正常渲染到页面 * @example * ```javascript * var editor = new UE.Editor(); * editor.execCommand('blod'); * ``` * @see UE.Config */ /** * 以给定的参数集合创建一个编辑器实例,对于未指定的参数,将应用默认参数。 * @constructor * @remind 通过 改构造方法实例化的编辑器,不带ui层.需要render到一个容器,编辑器实例才能正常渲染到页面 * @param { Object } setting 创建编辑器的参数 * @example * ```javascript * var editor = new UE.Editor(); * editor.execCommand('blod'); * ``` * @see UE.Config */ var Editor = (UE.Editor = function(options) { var me = this; me.uid = uid++; EventBase.call(me); me.commands = {}; me.options = utils.extend(utils.clone(options || {}), UEDITOR_CONFIG, true); me.shortcutkeys = {}; me.inputRules = []; me.outputRules = []; //设置默认的常用属性 me.setOpt(Editor.defaultOptions(me)); /* 尝试异步加载后台配置 */ me.loadServerConfig(); if (!utils.isEmptyObject(UE.I18N)) { //修改默认的语言类型 me.options.lang = checkCurLang(UE.I18N); UE.plugin.load(me); langReadied(me); } else { utils.loadFile( document, { src: me.options.langPath + me.options.lang + "/" + me.options.lang + ".js?20230319", tag: "script", type: "text/javascript", defer: "defer" }, function() { UE.plugin.load(me); langReadied(me); } ); } UE.instants["ueditorInstant" + me.uid] = me; }); Editor.prototype = { registerCommand: function(name, obj) { this.commands[name] = obj; }, /** * 编辑器对外提供的监听ready事件的接口, 通过调用该方法,达到的效果与监听ready事件是一致的 * @method ready * @param { Function } fn 编辑器ready之后所执行的回调, 如果在注册事件之前编辑器已经ready,将会 * 立即触发该回调。 * @remind 需要等待编辑器加载完成后才能执行的代码,可以使用该方法传入 * @example * ```javascript * editor.ready( function( editor ) { * editor.setContent('初始化完毕'); * } ); * ``` * @see UE.Editor.event:ready */ ready: function(fn) { var me = this; if (fn) { me.isReady ? fn.apply(me) : me.addListener("ready", fn); } }, /** * 该方法是提供给插件里面使用,设置配置项默认值 * @method setOpt * @warning 三处设置配置项的优先级: 实例化时传入参数 > setOpt()设置 > config文件里设置 * @warning 该方法仅供编辑器插件内部和编辑器初始化时调用,其他地方不能调用。 * @param { String } key 编辑器的可接受的选项名称 * @param { * } val 该选项可接受的值 * @example * ```javascript * editor.setOpt( 'initContent', '欢迎使用编辑器' ); * ``` */ /** * 该方法是提供给插件里面使用,以{key:value}集合的方式设置插件内用到的配置项默认值 * @method setOpt * @warning 三处设置配置项的优先级: 实例化时传入参数 > setOpt()设置 > config文件里设置 * @warning 该方法仅供编辑器插件内部和编辑器初始化时调用,其他地方不能调用。 * @param { Object } options 将要设置的选项的键值对对象 * @example * ```javascript * editor.setOpt( { * 'initContent': '欢迎使用编辑器' * } ); * ``` */ setOpt: function(key, val) { var obj = {}; if (utils.isString(key)) { obj[key] = val; } else { obj = key; } utils.extend(this.options, obj, true); }, getOpt: function(key) { return this.options[key]; }, /** * 销毁编辑器实例,使用textarea代替 * @method destroy * @example * ```javascript * editor.destroy(); * ``` */ destroy: function() { var me = this; me.fireEvent("destroy"); var container = me.container.parentNode; var textarea = me.textarea; if (!textarea) { textarea = document.createElement("textarea"); container.parentNode.insertBefore(textarea, container); } else { textarea.style.display = ""; } textarea.style.width = me.iframe.offsetWidth + "px"; textarea.style.height = me.iframe.offsetHeight + "px"; textarea.value = me.getContent(); textarea.id = me.key; container.innerHTML = ""; domUtils.remove(container); var key = me.key; //trace:2004 for (var p in me) { if (me.hasOwnProperty(p)) { delete this[p]; } } UE.delEditor(key); }, /** * 渲染编辑器的DOM到指定容器 * @method render * @param { String } containerId 指定一个容器ID * @remind 执行该方法,会触发ready事件 * @warning 必须且只能调用一次 */ /** * 渲染编辑器的DOM到指定容器 * @method render * @param { Element } containerDom 直接指定容器对象 * @remind 执行该方法,会触发ready事件 * @warning 必须且只能调用一次 */ render: function(container) { var me = this, options = me.options, getStyleValue = function(attr) { return parseInt(domUtils.getComputedStyle(container, attr)); }; if (utils.isString(container)) { container = document.getElementById(container); } if (container) { if (options.initialFrameWidth) { options.minFrameWidth = options.initialFrameWidth; } else { options.minFrameWidth = options.initialFrameWidth = container.offsetWidth; } if (options.initialFrameHeight) { options.minFrameHeight = options.initialFrameHeight; } else { options.initialFrameHeight = options.minFrameHeight = container.offsetHeight; } container.style.width = /%$/.test(options.initialFrameWidth) ? "100%" : options.initialFrameWidth - getStyleValue("padding-left") - getStyleValue("padding-right") + "px"; container.style.height = /%$/.test(options.initialFrameHeight) ? "100%" : options.initialFrameHeight - getStyleValue("padding-top") - getStyleValue("padding-bottom") + "px"; container.style.zIndex = options.zIndex; var additionCssHtml = []; for(var i in options.iframeCssUrlsAddition){ additionCssHtml.push("") } var html = (ie && browser.version < 9 ? "" : "") + "" + "" + "" + (options.iframeCssUrl ? "" : "") + (options.initialStyle ? "" : "") + additionCssHtml.join("") + "" + "" + "" + (options.iframeJsUrl ? "" : "") + ""; container.appendChild( domUtils.createElement(document, "iframe", { id: "ueditor_" + me.uid, width: "100%", height: "100%", frameborder: "0", //先注释掉了,加的原因忘记了,但开启会直接导致全屏模式下内容多时不会出现滚动条 // scrolling :'no', src: "javascript:void(function(){document.open();" + (options.customDomain && document.domain != location.hostname ? 'document.domain="' + document.domain + '";' : "") + 'document.write("' + html + '");document.close();}())' }) ); container.style.overflow = "hidden"; //解决如果是给定的百分比,会导致高度算不对的问题 setTimeout(function() { if (/%$/.test(options.initialFrameWidth)) { options.minFrameWidth = options.initialFrameWidth = container.offsetWidth; //如果这里给定宽度,会导致ie在拖动窗口大小时,编辑区域不随着变化 // container.style.width = options.initialFrameWidth + 'px'; } if (/%$/.test(options.initialFrameHeight)) { options.minFrameHeight = options.initialFrameHeight = container.offsetHeight; container.style.height = options.initialFrameHeight + "px"; } }); } }, /** * 编辑器初始化 * @method _setup * @private * @param { Element } doc 编辑器Iframe中的文档对象 */ _setup: function(doc) { var me = this, options = me.options; if (ie) { doc.body.disabled = true; doc.body.contentEditable = true; doc.body.disabled = false; } else { doc.body.contentEditable = true; } doc.body.spellcheck = false; me.document = doc; me.window = doc.defaultView || doc.parentWindow; me.iframe = me.window.frameElement; me.body = doc.body; me.selection = new dom.Selection(doc); //gecko初始化就能得到range,无法判断isFocus了 var geckoSel; if (browser.gecko && (geckoSel = this.selection.getNative())) { geckoSel.removeAllRanges(); } this._initEvents(); //为form提交提供一个隐藏的textarea for ( var form = this.iframe.parentNode; !domUtils.isBody(form); form = form.parentNode ) { if (form.tagName === "FORM") { me.form = form; if (me.options.autoSyncData) { domUtils.on(me.window, "blur", function() { setValue(form, me); }); domUtils.on(form, "submit", function() { me.fireEvent("beforesubmit"); }); } else { domUtils.on(form, "submit", function() { setValue(this, me); me.fireEvent("beforesubmit"); }); } break; } } if (options.initialContent) { if (options.autoClearinitialContent) { var oldExecCommand = me.execCommand; me.execCommand = function() { me.fireEvent("firstBeforeExecCommand"); return oldExecCommand.apply(me, arguments); }; this._setDefaultContent(options.initialContent); } else this.setContent(options.initialContent, false, true); } //编辑器不能为空内容 if (domUtils.isEmptyNode(me.body)) { me.body.innerHTML = "" + (browser.ie ? "" : "
") + "
123456
* var content = editor.getContent(); //返回值:123456
* ``` */ /** * 获取编辑器的内容。 可以通过参数定义编辑器内置的判空规则 * @method getContent * @param { Function } fn 自定的判空规则, 要求该方法返回一个boolean类型的值, * 代表当前编辑器的内容是否空, * 如果返回true, 则该方法将直接返回空字符串;如果返回false,则编辑器将返回 * 经过内置过滤规则处理后的内容。 * @remind 该方法在处理包含有初始化内容的时候能起到很好的作用。 * @warning 该方法获取到的是经过编辑器内置的过滤规则进行过滤后得到的内容 * @return { String } 编辑器的内容字符串 * @example * ```javascript * // editor 是一个编辑器的实例 * var content = editor.getContent( function ( editor ) { * return editor.body.innerHTML === '欢迎使用UEditor'; //返回空字符串 * } ); * ``` */ getContent: function(cmd, fn, notSetCursor, ignoreBlank, formatter) { var me = this; if (cmd && utils.isFunction(cmd)) { fn = cmd; cmd = ""; } if (fn ? !fn() : !this.hasContents()) { return ""; } me.fireEvent("beforegetcontent"); var root = UE.htmlparser(me.body.innerHTML, ignoreBlank); me.filterOutputRule(root); me.fireEvent("aftergetcontent", cmd, root); return root.toHtml(formatter); }, /** * 取得完整的html代码,可以直接显示成完整的html文档 * @method getAllHtml * @return { String } 编辑器的内容html文档字符串 * @eaxmple * ```javascript * editor.getAllHtml(); //返回格式大致是: ...... * ``` */ 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) + "" ); }, /** * 得到编辑器的纯文本内容,但会保留段落格式 * @method getPlainTxt * @return { String } 编辑器带段落格式的纯文本内容字符串 * @example * ```javascript * //编辑器html内容:1
2
* console.log(editor.getPlainTxt()); //输出:"1\n2\n * ``` */ getPlainTxt: function() { var reg = new RegExp(domUtils.fillChar, "g"), html = this.body.innerHTML.replace(/[\n\r]/g, ""); //ie要先去了\n在处理 html = html .replace(/<(p|div)[^>]*>(1
2
* console.log(editor.getPlainTxt()); //输出:"12 * ``` */ getContentTxt: function() { var reg = new RegExp(domUtils.fillChar, "g"); //取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0 return this.body[browser.ie ? "innerText" : "textContent"] .replace(reg, "") .replace(/\u00a0/g, " "); }, /** * 设置编辑器的内容,可修改编辑器当前的html内容 * @method setContent * @warning 通过该方法插入的内容,是经过编辑器内置的过滤规则进行过滤后得到的内容 * @warning 该方法会触发selectionchange事件 * @param { String } html 要插入的html内容 * @example * ```javascript * editor.getContent('test
'); * ``` */ /** * 设置编辑器的内容,可修改编辑器当前的html内容 * @method setContent * @warning 通过该方法插入的内容,是经过编辑器内置的过滤规则进行过滤后得到的内容 * @warning 该方法会触发selectionchange事件 * @param { String } html 要插入的html内容 * @param { Boolean } isAppendTo 若传入true,不清空原来的内容,在最后插入内容,否则,清空内容再插入 * @example * ```javascript * //假设设置前的编辑器内容是old text
* editor.setContent('new text
', true); //插入的结果是old text
new text
* ``` */ setContent: function(html, isAppendTo, notFireSelectionchange) { var me = this; me.fireEvent("beforesetcontent", html); var root = UE.htmlparser(html); me.filterInputRule(root); html = root.toHtml(); me.body.innerHTML = (isAppendTo ? me.body.innerHTML : "") + html; function isCdataDiv(node) { return node.tagName == "DIV" && node.getAttribute("cdata_tag"); } //给文本或者inline节点套p标签 if (me.options.enterTag == "p") { var child = this.body.firstChild, tmpNode; if ( !child || (child.nodeType == 1 && (dtd.$cdata[child.tagName] || isCdataDiv(child) || domUtils.isCustomeNode(child)) && child === this.body.lastChild) ) { this.body.innerHTML = "" +
(browser.ie ? " " : "
") +
"
" + (ie ? "" : "
") + "
' + cont + "
"; me.addListener("firstBeforeExecCommand focus", clear); }; })(), /** * 显示编辑器 * @method setShow * @example * ```javascript * editor.setShow() * ``` */ setShow: function() { var me = this, range = me.selection.getRange(); if (me.container.style.display == "none") { //有可能内容丢失了 try { range.moveToBookmark(me.lastBk); delete me.lastBk; } catch (e) { range.setStartAtFirst(me.body).collapse(true); } //ie下focus实效,所以做了个延迟 setTimeout(function() { range.select(true); }, 100); me.container.style.display = ""; } }, show: function() { return this.setShow(); }, /** * 隐藏编辑器 * @method setHide * @example * ```javascript * editor.setHide() * ``` */ setHide: function() { var me = this; if (!me.lastBk) { me.lastBk = me.selection.getRange().createBookmark(true); } me.container.style.display = "none"; }, hide: function() { return this.setHide(); }, /** * 根据指定的路径,获取对应的语言资源 * @method getLang * @param { String } path 路径根据的是lang目录下的语言文件的路径结构 * @return { Object | String } 根据路径返回语言资源的Json格式对象或者语言字符串 * @example * ```javascript * editor.getLang('contextMenu.delete'); //如果当前是中文,那返回是的是'删除' * ``` */ getLang: function(path) { var lang = UE.I18N[this.options.lang]; if (!lang) { throw Error("not import language file"); } path = (path || "").split("."); for (var i = 0, ci; (ci = path[i++]); ) { lang = lang[ci]; if (!lang) break; } return lang; }, /** * 计算编辑器html内容字符串的长度 * @method getContentLength * @return { Number } 返回计算的长度 * @example * ```javascript * //编辑器html内容132
* editor.getContentLength() //返回27 * ``` */ /** * 计算编辑器当前纯文本内容的长度 * @method getContentLength * @param { Boolean } ingoneHtml 传入true时,只按照纯文本来计算 * @return { Number } 返回计算的长度,内容中有hr/img/iframe标签,长度加1 * @example * ```javascript * //编辑器html内容132
* editor.getContentLength() //返回3 * ``` */ getContentLength: function(ingoneHtml, tagNames) { var count = this.getContent(false, false, true).length; if (ingoneHtml) { tagNames = (tagNames || []).concat(["hr", "img", "iframe"]); count = this.getContentTxt().replace(/[\t\r\n]+/g, "").length; for (var i = 0, ci; (ci = tagNames[i++]); ) { count += this.document.getElementsByTagName(ci).length; } } return count; }, /** * 注册输入过滤规则 * @method addInputRule * @param { Function } rule 要添加的过滤规则 * @example * ```javascript * editor.addInputRule(function(root){ * $.each(root.getNodesByTagName('div'),function(i,node){ * node.tagName="p"; * }); * }); * ``` */ addInputRule: function(rule) { this.inputRules.push(rule); }, /** * 执行注册的过滤规则 * @method filterInputRule * @param { UE.uNode } root 要过滤的uNode节点 * @remind 执行editor.setContent方法和执行'inserthtml'命令后,会运行该过滤函数 * @example * ```javascript * editor.filterInputRule(editor.body); * ``` * @see UE.Editor:addInputRule */ filterInputRule: function(root) { for (var i = 0, ci; (ci = this.inputRules[i++]); ) { ci.call(this, root); } }, /** * 注册输出过滤规则 * @method addOutputRule * @param { Function } rule 要添加的过滤规则 * @example * ```javascript * editor.addOutputRule(function(root){ * $.each(root.getNodesByTagName('p'),function(i,node){ * node.tagName="div"; * }); * }); * ``` */ addOutputRule: function(rule) { this.outputRules.push(rule); }, /** * 根据输出过滤规则,过滤编辑器内容 * @method filterOutputRule * @remind 执行editor.getContent方法的时候,会先运行该过滤函数 * @param { UE.uNode } root 要过滤的uNode节点 * @example * ```javascript * editor.filterOutputRule(editor.body); * ``` * @see UE.Editor:addOutputRule */ filterOutputRule: function(root) { for (var i = 0, ci; (ci = this.outputRules[i++]); ) { ci.call(this, root); } }, /** * 根据action名称获取请求的路径 * @method getActionUrl * @remind 假如没有设置serverUrl,会根据imageUrl设置默认的controller路径 * @param { String } action action名称 * @example * ```javascript * editor.getActionUrl('config'); //返回 "/ueditor/php/controller.php?action=config" * editor.getActionUrl('image'); //返回 "/ueditor/php/controller.php?action=uplaodimage" * editor.getActionUrl('scrawl'); //返回 "/ueditor/php/controller.php?action=uplaodscrawl" * editor.getActionUrl('imageManager'); //返回 "/ueditor/php/controller.php?action=listimage" * ``` */ getActionUrl: function(action) { var actionName = this.getOpt(action) || action, imageUrl = this.getOpt("imageUrl"), serverUrl = this.getOpt("serverUrl"); if (!serverUrl && imageUrl) { serverUrl = imageUrl.replace(/^(.*[\/]).+([\.].+)$/, "$1controller$2"); } if (serverUrl) { serverUrl = serverUrl + (serverUrl.indexOf("?") == -1 ? "?" : "&") + "action=" + (actionName || ""); return utils.formatUrl(serverUrl); } else { return ""; } } }; utils.inherits(Editor, EventBase); })(); // core/Editor.defaultoptions.js //维护编辑器一下默认的不在插件中的配置项 UE.Editor.defaultOptions = function(editor) { var _url = editor.options.UEDITOR_HOME_URL; return { isShow: true, initialContent: "", initialStyle: "", autoClearinitialContent: false, iframeCssUrl: _url + "themes/iframe.css?20221113", iframeCssUrlsAddition: [], textarea: "editorValue", focus: false, focusInEnd: true, autoClearEmptyNode: true, fullscreen: false, readonly: false, zIndex: 999, imagePopup: true, enterTag: "p", customDomain: false, lang: "zh-cn", langPath: _url + "lang/", theme: "default", themePath: _url + "themes/", allHtmlEnabled: false, scaleEnabled: false, tableNativeEditInFF: false, autoSyncData: true, fileNameFormat: "{time}{rand:6}" }; }; // core/loadconfig.js (function() { UE.Editor.prototype.loadServerConfig = function() { var me = this; setTimeout(function() { try { me.options.imageUrl && me.setOpt( "serverUrl", me.options.imageUrl.replace( /^(.*[\/]).+([\.].+)$/, "$1controller$2" ) ); var configUrl = me.getActionUrl("config"), isJsonp = utils.isCrossDomainUrl(configUrl); /* 发出ajax请求 */ me._serverConfigLoaded = false; configUrl && UE.ajax.request(configUrl, { method: "GET", dataType: isJsonp ? "jsonp" : "", headers: me.options.serverHeaders || {}, onsuccess: function(r) { try { var config = isJsonp ? r : eval("(" + r.responseText + ")"); utils.extend(me.options, config); // console.log('me.options.before',me.options); // console.log('server.config',config); // console.log('me.options.after',me.options); me.fireEvent("serverConfigLoaded"); me._serverConfigLoaded = true; } catch (e) { showErrorMsg(me.getLang("loadconfigFormatError")); } }, onerror: function() { showErrorMsg(me.getLang("loadconfigHttpError")); } }); } catch (e) { showErrorMsg(me.getLang("loadconfigError")); } }); function showErrorMsg(msg) { console && console.error(msg); //me.fireEvent('showMessage', { // 'title': msg, // 'type': 'error' //}); } }; UE.Editor.prototype.isServerConfigLoaded = function() { var me = this; return me._serverConfigLoaded || false; }; UE.Editor.prototype.afterConfigReady = function(handler) { if (!handler || !utils.isFunction(handler)) return; var me = this; var readyHandler = function() { handler.apply(me, arguments); me.removeListener("serverConfigLoaded", readyHandler); }; if (me.isServerConfigLoaded()) { handler.call(me, "serverConfigLoaded"); } else { me.addListener("serverConfigLoaded", readyHandler); } }; })(); // core/ajax.js /** * @file * @module UE.ajax * @since 1.2.6.1 */ /** * 提供对ajax请求的支持 * @module UE.ajax */ UE.ajax = (function() { //创建一个ajaxRequest对象 var fnStr = "XMLHttpRequest()"; try { new ActiveXObject("Msxml2.XMLHTTP"); fnStr = "ActiveXObject('Msxml2.XMLHTTP')"; } catch (e) { try { new ActiveXObject("Microsoft.XMLHTTP"); fnStr = "ActiveXObject('Microsoft.XMLHTTP')"; } catch (e) {} } var creatAjaxRequest = new Function("return new " + fnStr); /** * 将json参数转化成适合ajax提交的参数列表 * @param json */ function json2str(json) { var strArr = []; for (var i in json) { //忽略默认的几个参数 if ( i == "method" || i == "timeout" || i == "async" || i == "dataType" || i == "callback" ) continue; //忽略控制 if (json[i] == undefined || json[i] == null) continue; //传递过来的对象和函数不在提交之列 if ( !( (typeof json[i]).toLowerCase() == "function" || (typeof json[i]).toLowerCase() == "object" ) ) { strArr.push(encodeURIComponent(i) + "=" + encodeURIComponent(json[i])); } else if (utils.isArray(json[i])) { //支持传数组内容 for (var j = 0; j < json[i].length; j++) { strArr.push( encodeURIComponent(i) + "[]=" + encodeURIComponent(json[i][j]) ); } } } return strArr.join("&"); } function doAjax(url, ajaxOptions) { var xhr = creatAjaxRequest(), //是否超时 timeIsOut = false, //默认参数 defaultAjaxOptions = { method: "POST", timeout: 5000, async: true, headers: {}, data: {}, //需要传递对象的话只能覆盖 onsuccess: function() {}, onerror: function() {} }; if (typeof url === "object") { ajaxOptions = url; url = ajaxOptions.url; } if (!xhr || !url) return; var ajaxOpts = ajaxOptions ? utils.extend(defaultAjaxOptions, ajaxOptions) : defaultAjaxOptions; // console.log('ajaxOpts',ajaxOpts); var submitStr = json2str(ajaxOpts); // { name:"Jim",city:"Beijing" } --> "name=Jim&city=Beijing" //如果用户直接通过data参数传递json对象过来,则也要将此json对象转化为字符串 if (!utils.isEmptyObject(ajaxOpts.data)) { submitStr += (submitStr ? "&" : "") + json2str(ajaxOpts.data); } //超时检测 var timerID = setTimeout(function() { if (xhr.readyState !== 4) { timeIsOut = true; xhr.abort(); clearTimeout(timerID); } }, ajaxOpts.timeout); var method = ajaxOpts.method.toUpperCase(); var str = url + (url.indexOf("?") === -1 ? "?" : "&") + (method === "POST" ? "" : submitStr + "&noCache=" + +new Date()); xhr.open(method, str, ajaxOpts.async); xhr.onreadystatechange = function() { if (xhr.readyState === 4) { if (!timeIsOut && xhr.status === 200) { ajaxOpts.onsuccess(xhr); } else { ajaxOpts.onerror(xhr); } } }; if(ajaxOpts.headers){ for(var key in ajaxOpts.headers){ xhr.setRequestHeader(key,ajaxOpts.headers[key]); } } if (method === "POST") { xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.send(submitStr); } else { xhr.send(null); } } function doJsonp(url, opts) { var successhandler = opts.onsuccess || function() {}, scr = document.createElement("SCRIPT"), options = opts || {}, charset = options["charset"], callbackField = options["jsonp"] || "callback", callbackFnName, timeOut = options["timeOut"] || 0, timer, reg = new RegExp("(\\?|&)" + callbackField + "=([^&]*)"), matches; if (utils.isFunction(successhandler)) { callbackFnName = "bd__editor__" + Math.floor(Math.random() * 2147483648).toString(36); window[callbackFnName] = getCallBack(0); } else if (utils.isString(successhandler)) { callbackFnName = successhandler; } else { if ((matches = reg.exec(url))) { callbackFnName = matches[2]; } } url = url.replace(reg, "\x241" + callbackField + "=" + callbackFnName); if (url.search(reg) < 0) { url += (url.indexOf("?") < 0 ? "?" : "&") + callbackField + "=" + callbackFnName; } var queryStr = json2str(opts); // { name:"Jim",city:"Beijing" } --> "name=Jim&city=Beijing" //如果用户直接通过data参数传递json对象过来,则也要将此json对象转化为字符串 if (!utils.isEmptyObject(opts.data)) { queryStr += (queryStr ? "&" : "") + json2str(opts.data); } if (queryStr) { url = url.replace(/\?/, "?" + queryStr + "&"); } scr.onerror = getCallBack(1); if (timeOut) { timer = setTimeout(getCallBack(1), timeOut); } createScriptTag(scr, url, charset); function createScriptTag(scr, url, charset) { scr.setAttribute("type", "text/javascript"); scr.setAttribute("defer", "defer"); charset && scr.setAttribute("charset", charset); scr.setAttribute("src", url); document.getElementsByTagName("head")[0].appendChild(scr); } function getCallBack(onTimeOut) { return function() { try { if (onTimeOut) { options.onerror && options.onerror(); } else { try { clearTimeout(timer); successhandler.apply(window, arguments); } catch (e) {} } } catch (exception) { options.onerror && options.onerror.call(window, exception); } finally { options.oncomplete && options.oncomplete.apply(window, arguments); scr.parentNode && scr.parentNode.removeChild(scr); window[callbackFnName] = null; try { delete window[callbackFnName]; } catch (e) {} } }; } } return { /** * 根据给定的参数项,向指定的url发起一个ajax请求。 ajax请求完成后,会根据请求结果调用相应回调: 如果请求 * 成功, 则调用onsuccess回调, 失败则调用 onerror 回调 * @method request * @param { URLString } url ajax请求的url地址 * @param { Object } ajaxOptions ajax请求选项的键值对,支持的选项如下: * @example * ```javascript * //向sayhello.php发起一个异步的Ajax GET请求, 请求超时时间为10s, 请求完成后执行相应的回调。 * UE.ajax.requeset( 'sayhello.php', { * * //请求方法。可选值: 'GET', 'POST',默认值是'POST' * method: 'GET', * * //超时时间。 默认为5000, 单位是ms * timeout: 10000, * * //是否是异步请求。 true为异步请求, false为同步请求 * async: true, * * //请求携带的数据。如果请求为GET请求, data会经过stringify后附加到请求url之后。 * data: { * name: 'ueditor' * }, * * //请求成功后的回调, 该回调接受当前的XMLHttpRequest对象作为参数。 * onsuccess: function ( xhr ) { * console.log( xhr.responseText ); * }, * * //请求失败或者超时后的回调。 * onerror: function ( xhr ) { * alert( 'Ajax请求失败' ); * } * * } ); * ``` */ /** * 根据给定的参数项发起一个ajax请求, 参数项里必须包含一个url地址。 ajax请求完成后,会根据请求结果调用相应回调: 如果请求 * 成功, 则调用onsuccess回调, 失败则调用 onerror 回调。 * @method request * @warning 如果在参数项里未提供一个key为“url”的地址值,则该请求将直接退出。 * @param { Object } ajaxOptions ajax请求选项的键值对,支持的选项如下: * @example * ```javascript * * //向sayhello.php发起一个异步的Ajax POST请求, 请求超时时间为5s, 请求完成后不执行任何回调。 * UE.ajax.requeset( 'sayhello.php', { * * //请求的地址, 该项是必须的。 * url: 'sayhello.php' * * } ); * ``` */ request: function(url, opts) { if (opts && opts.dataType === "jsonp") { doJsonp(url, opts); } else { doAjax(url, opts); } }, getJSONP: function(url, data, fn) { var opts = { data: data, oncomplete: fn }; doJsonp(url, opts); } }; })(); // core/filterword.js /** * UE过滤word的静态方法 * @file */ /** * UEditor公用空间,UEditor所有的功能都挂载在该空间下 * @module UE */ /** * 根据传入html字符串过滤word * @module UE * @since 1.2.6.1 * @method filterWord * @param { String } html html字符串 * @return { String } 已过滤后的结果字符串 * @example * ```javascript * UE.filterWord(html); * ``` */ var filterWord = (UE.filterWord = (function() { //是否是word过来的内容 function isWordDocument(str) { return /(class="?Mso|style="[^"]*\bmso\-|w:WordDocument|<(v|o):|lang=)/gi.test( str ); } //去掉小数 function transUnit(v) { v = v.replace(/[\d.]+\w+/g, function(m) { return utils.transUnitToPx(m); }); return v; } function filterPasteWord(str) { return ( str .replace(/[\t\r\n]+/g, " ") .replace(//gi, "") //转换图片 .replace(/]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi, "
$1
" ) //去掉多余的属性 .replace(/\s+(class|lang|align)\s*=\s*(['"]?)([\w-]+)\2/gi, 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, "'") .replace(/[\d.]+(cm|pt)/g, function(str) { return utils.transUnitToPx(str); }) .split(/;\s*/g); for (var i = 0, v; (v = s[i]); i++) { var name, value, parts = v.split(":"); if (parts.length == 2) { name = parts[0].toLowerCase(); value = parts[1].toLowerCase(); if ( (/^(background)\w*/.test(name) && value.replace(/(initial|\s)/g, "").length == 0) || (/^(margin)\w*/.test(name) && /^0\w+$/.test(value)) ) { continue; } switch (name) { case "mso-padding-alt": case "mso-padding-top-alt": case "mso-padding-right-alt": case "mso-padding-bottom-alt": case "mso-padding-left-alt": case "mso-margin-alt": case "mso-margin-top-alt": case "mso-margin-right-alt": case "mso-margin-bottom-alt": case "mso-margin-left-alt": //ie下会出现挤到一起的情况 //case "mso-table-layout-alt": case "mso-height": case "mso-width": case "mso-vertical-align-alt": //trace:1819 ff下会解析出padding在table上 if (!/ 这样的标签了
//先去掉了,加上的原因忘了,这里先记录
//var re_tag = /<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\s\/<>]+)\s*((?:(?:"[^"]*")|(?:'[^']*')|[^"'<>])*)\/?>))/g,
//以上的正则表达式无法匹配:![]() "); } break; case "div": if (node.getAttr("cdata_tag")) { break; } //针对代码这里不处理插入代码的div val = node.getAttr("class"); if (val && /^line number\d+/.test(val)) { break; } if (!allowDivTransToP) { break; } var tmpNode, p = UE.uNode.createElement("p"); while ((tmpNode = node.firstChild())) { if ( tmpNode.type == "text" || !UE.dom.dtd.$block[tmpNode.tagName] ) { p.appendChild(tmpNode); } else { if (p.firstChild()) { node.parentNode.insertBefore(p, node); p = UE.uNode.createElement("p"); } else { node.parentNode.insertBefore(tmpNode, node); } } } if (p.firstChild()) { node.parentNode.insertBefore(p, node); } node.parentNode.removeChild(node); break; case "dl": node.tagName = "ul"; break; case "dt": case "dd": node.tagName = "li"; break; case "li": var className = node.getAttr("class"); if (!className || !/list\-/.test(className)) { node.setAttr(); } var tmpNodes = node.getNodesByTagName("ol ul"); UE.utils.each(tmpNodes, function(n) { node.parentNode.insertAfter(n, node); }); break; case "td": case "th": case "caption": if (!node.children || !node.children.length) { node.appendChild( browser.ie11below ? UE.uNode.createText(" ") : UE.uNode.createElement("br") ); } break; case "table": if (me.options.disabledTableInTable && tdParent(node)) { node.parentNode.insertBefore( UE.uNode.createText(node.innerText()), node ); node.parentNode.removeChild(node); } } } // if(node.type == 'comment'){ // node.parentNode.removeChild(node); // } }); }); //从编辑器出去的内容处理 me.addOutputRule(function(root) { var val; root.traversal(function(node) { if (node.type == "element") { if ( me.options.autoClearEmptyNode && dtd.$inline[node.tagName] && !dtd.$empty[node.tagName] && (!node.attrs || utils.isEmptyObject(node.attrs)) ) { if (!node.firstChild()) node.parentNode.removeChild(node); else if ( node.tagName == "span" && (!node.attrs || utils.isEmptyObject(node.attrs)) ) { node.parentNode.removeChild(node, true); } return; } switch (node.tagName) { case "div": if ((val = node.getAttr("cdata_tag"))) { node.tagName = val; node.appendChild(UE.uNode.createText(node.getAttr("cdata_data"))); node.setAttr({ cdata_tag: "", cdata_data: "", _ue_custom_node_: "" }); } break; case "a": if ((val = node.getAttr("_href"))) { node.setAttr({ href: utils.html(val), _href: "" }); } break; break; case "span": val = node.getAttr("id"); if (val && /^_baidu_bookmark_/i.test(val)) { node.parentNode.removeChild(node); } //将color的rgb格式转换为#16进制格式 if (me.getOpt("rgb2Hex")) { var cssStyle = node.getAttr("style"); if (cssStyle) { node.setAttr( "style", cssStyle.replace(/rgba?\(([\d,\s]+)\)/g, function(a, value) { var array = value.split(","); if (array.length > 3) return ""; value = "#"; for (var i = 0, color; (color = array[i++]); ) { color = parseInt( color.replace(/[^\d]/gi, ""), 10 ).toString(16); value += color.length == 1 ? "0" + color : color; } return value.toUpperCase(); }) ); } } break; case "img": if ((val = node.getAttr("_src"))) { node.setAttr({ src: node.getAttr("_src"), _src: "" }); } } } }); }); }; // plugins/inserthtml.js /** * 插入html字符串插件 * @file * @since 1.2.6.1 */ /** * 插入html代码 * @command inserthtml * @method execCommand * @param { String } cmd 命令字符串 * @param { String } html 插入的html字符串 * @remaind 插入的标签内容是在当前的选区位置上插入,如果当前是闭合状态,那直接插入内容, 如果当前是选中状态,将先清除当前选中内容后,再做插入 * @warning 注意:该命令会对当前选区的位置,对插入的内容进行过滤转换处理。 过滤的规则遵循html语意化的原则。 * @example * ```javascript * //xxx[BB]xxx 当前选区为非闭合选区,选中BB这两个文本 * //执行命令,插入CC * //插入后的效果 xxxCCxxx * // xx|xxx 当前选区为闭合状态 * //插入CC * //结果xx CC xxx * //xxxx |xxx 当前选区在两个p标签之间 * //插入 xxxx * //结果xxxx xxxx xxx * ``` */ UE.commands["inserthtml"] = { execCommand: function(command, html, notNeedFilter) { var me = this, range, div; if (!html) { return; } if (me.fireEvent("beforeinserthtml", html) === true) { return; } range = me.selection.getRange(); div = range.document.createElement("div"); div.style.display = "inline"; if (!notNeedFilter) { var root = UE.htmlparser(html); //如果给了过滤规则就先进行过滤 if (me.options.filterRules) { UE.filterNode(root, me.options.filterRules); } //执行默认的处理 me.filterInputRule(root); html = root.toHtml(); } div.innerHTML = utils.trim(html); if (!range.collapsed) { var tmpNode = range.startContainer; if (domUtils.isFillChar(tmpNode)) { range.setStartBefore(tmpNode); } tmpNode = range.endContainer; if (domUtils.isFillChar(tmpNode)) { range.setEndAfter(tmpNode); } range.txtToElmBoundary(); //结束边界可能放到了br的前边,要把br包含进来 // x[xxx]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 ? "" : " |<[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 { range.select(true); } catch (e) {} } setTimeout(function() { range = me.selection.getRange(); range.scrollToView( me.autoHeightEnabled, me.autoHeightEnabled ? domUtils.getXY(me.iframe).y : 0 ); me.fireEvent("afterinserthtml", html); }, 200); } }; // plugins/autotypeset.js /** * 自动排版 * @file * @since 1.2.6.1 */ /** * 对当前编辑器的内容执行自动排版, 排版的行为根据config配置文件里的“autotypeset”选项进行控制。 * @command autotypeset * @method execCommand * @param { String } cmd 命令字符串 * @example * ```javascript * editor.execCommand( 'autotypeset' ); * ``` */ UE.plugins["autotypeset"] = function() { this.setOpt({ // 自动排版参数 autotypeset: { // 合并空行 mergeEmptyline: true, // 去掉冗余的class removeClass: true, // 去掉空行 removeEmptyline: false, // 段落的排版方式,可以是 left,right,center,justify 去掉这个属性表示不执行排版 textAlign: "left", // 图片的浮动方式,独占一行剧中,左右浮动,默认: center,left,right,none 去掉这个属性表示不执行排版 imageBlockLine: "center", // 根据规则过滤没事粘贴进来的内容 pasteFilter: false, // 去掉所有的内嵌字号,使用编辑器默认的字号 clearFontSize: false, // 去掉所有的内嵌字体,使用编辑器默认的字体 clearFontFamily: false, // 去掉空节点 removeEmptyNode: false, // 可以去掉的标签 removeTagNames: utils.extend({ div: 1 }, dtd.$removeEmpty), // 行首缩进 indent: false, // 行首缩进的大小 indentValue: "2em", // 全角转半角 bdc2sb: false, // 半角转全角 tobdc: false } }); var me = this, opt = me.options.autotypeset, remainClass = { selectTdClass: 1, pagebreak: 1, anchorclass: 1 }, remainTag = { li: 1 }, tags = { div: 1, p: 1, //trace:2183 这些也认为是行 blockquote: 1, center: 1, h1: 1, h2: 1, h3: 1, h4: 1, h5: 1, h6: 1, span: 1 }, highlightCont; //升级了版本,但配置项目里没有autotypeset if (!opt) { return; } readLocalOpts(); function isLine(node, notEmpty) { if (!node || node.nodeType == 3) return 0; if (domUtils.isBr(node)) return 1; if (node && node.parentNode && tags[node.tagName.toLowerCase()]) { if ( (highlightCont && highlightCont.contains(node)) || node.getAttribute("pagebreak") ) { return 0; } return notEmpty ? !domUtils.isEmptyBlock(node) : domUtils.isEmptyBlock( node, new RegExp("[\\s" + domUtils.fillChar + "]", "g") ); } } function removeNotAttributeSpan(node) { if (!node.style.cssText) { domUtils.removeAttributes(node, ["style"]); if ( node.tagName.toLowerCase() == "span" && domUtils.hasNoAttributes(node) ) { domUtils.remove(node, true); } } } function autotype(type, html) { var me = this, cont; if (html) { if (!opt.pasteFilter) { return; } cont = me.document.createElement("div"); cont.innerHTML = html.html; } else { cont = me.document.body; } var nodes = domUtils.getElementsByTagName(cont, "*"); // 行首缩进,段落方向,段间距,段内间距 for (var i = 0, ci; (ci = nodes[i++]); ) { if (me.fireEvent("excludeNodeinautotype", ci) === true) { continue; } //font-size if (opt.clearFontSize && ci.style.fontSize) { domUtils.removeStyle(ci, "font-size"); removeNotAttributeSpan(ci); } //font-family if (opt.clearFontFamily && ci.style.fontFamily) { domUtils.removeStyle(ci, "font-family"); removeNotAttributeSpan(ci); } if (isLine(ci)) { //合并空行 if (opt.mergeEmptyline) { var next = ci.nextSibling, tmpNode, isBr = domUtils.isBr(ci); while (isLine(next)) { tmpNode = next; next = tmpNode.nextSibling; if (isBr && (!next || (next && !domUtils.isBr(next)))) { break; } domUtils.remove(tmpNode); } } //去掉空行,保留占位的空行 if ( opt.removeEmptyline && domUtils.inDoc(ci, cont) && !remainTag[ci.parentNode.tagName.toLowerCase()] ) { if (domUtils.isBr(ci)) { next = ci.nextSibling; if (next && !domUtils.isBr(next)) { continue; } } domUtils.remove(ci); continue; } } if (isLine(ci, true) && ci.tagName != "SPAN") { if (opt.indent) { ci.style.textIndent = opt.indentValue; } if (opt.textAlign) { ci.style.textAlign = opt.textAlign; } // if(opt.lineHeight) // ci.style.lineHeight = opt.lineHeight + 'cm'; } //去掉class,保留的class不去掉 if ( opt.removeClass && ci.className && !remainClass[ci.className.toLowerCase()] ) { if (highlightCont && highlightCont.contains(ci)) { continue; } domUtils.removeAttributes(ci, ["class"]); } //表情不处理 if ( opt.imageBlockLine && ci.tagName.toLowerCase() == "img" && !ci.getAttribute("emotion") ) { if (html) { var img = ci; switch (opt.imageBlockLine) { case "left": case "right": case "none": var pN = img.parentNode, tmpNode, pre, next; while (dtd.$inline[pN.tagName] || pN.tagName == "A") { pN = pN.parentNode; } tmpNode = pN; if ( tmpNode.tagName == "P" && domUtils.getStyle(tmpNode, "text-align") == "center" ) { if ( !domUtils.isBody(tmpNode) && domUtils.getChildCount(tmpNode, function(node) { return !domUtils.isBr(node) && !domUtils.isWhitespace(node); }) == 1 ) { pre = tmpNode.previousSibling; next = tmpNode.nextSibling; if ( pre && next && pre.nodeType == 1 && next.nodeType == 1 && pre.tagName == next.tagName && domUtils.isBlockElm(pre) ) { pre.appendChild(tmpNode.firstChild); while (next.firstChild) { pre.appendChild(next.firstChild); } domUtils.remove(tmpNode); domUtils.remove(next); } else { domUtils.setStyle(tmpNode, "text-align", ""); } } } domUtils.setStyle(img, "float", opt.imageBlockLine); break; case "center": if (me.queryCommandValue("imagefloat") != "center") { pN = img.parentNode; domUtils.setStyle(img, "float", "none"); tmpNode = img; while ( pN && domUtils.getChildCount(pN, function(node) { return !domUtils.isBr(node) && !domUtils.isWhitespace(node); }) == 1 && (dtd.$inline[pN.tagName] || pN.tagName == "A") ) { tmpNode = pN; pN = pN.parentNode; } var pNode = me.document.createElement("p"); domUtils.setAttributes(pNode, { style: "text-align:center" }); tmpNode.parentNode.insertBefore(pNode, tmpNode); pNode.appendChild(tmpNode); domUtils.setStyle(tmpNode, "float", ""); } } } else { var range = me.selection.getRange(); range.selectNode(ci).select(); me.execCommand("imagefloat", opt.imageBlockLine); } } //去掉冗余的标签 if (opt.removeEmptyNode) { if ( opt.removeTagNames[ci.tagName.toLowerCase()] && domUtils.hasNoAttributes(ci) && domUtils.isEmptyBlock(ci) ) { domUtils.remove(ci); } } } if (opt.tobdc) { var root = UE.htmlparser(cont.innerHTML); root.traversal(function(node) { if (node.type == "text") { node.data = ToDBC(node.data); } }); cont.innerHTML = root.toHtml(); } if (opt.bdc2sb) { var root = UE.htmlparser(cont.innerHTML); root.traversal(function(node) { if (node.type == "text") { node.data = DBC2SB(node.data); } }); cont.innerHTML = root.toHtml(); } if (html) { html.html = cont.innerHTML; } } if (opt.pasteFilter) { me.addListener("beforepaste", autotype); } function DBC2SB(str) { var result = ""; for (var i = 0; i < str.length; i++) { var code = str.charCodeAt(i); //获取当前字符的unicode编码 if (code >= 65281 && code <= 65373) { //在这个unicode编码范围中的是所有的英文字母已经各种字符 result += String.fromCharCode(str.charCodeAt(i) - 65248); //把全角字符的unicode编码转换为对应半角字符的unicode码 } else if (code == 12288) { //空格 result += String.fromCharCode(str.charCodeAt(i) - 12288 + 32); } else { result += str.charAt(i); } } return result; } function ToDBC(txtstring) { txtstring = utils.html(txtstring); var tmp = ""; var mark = ""; /*用于判断,如果是html尖括里的标记,则不进行全角的转换*/ for (var i = 0; i < txtstring.length; i++) { if (txtstring.charCodeAt(i) == 32) { tmp = tmp + String.fromCharCode(12288); } else if (txtstring.charCodeAt(i) < 127) { tmp = tmp + String.fromCharCode(txtstring.charCodeAt(i) + 65248); } else { tmp += txtstring.charAt(i); } } return tmp; } function readLocalOpts() { var cookieOpt = me.getPreferences("autotypeset"); utils.extend(me.options.autotypeset, cookieOpt); } me.commands["autotypeset"] = { execCommand: function() { me.removeListener("beforepaste", autotype); if (opt.pasteFilter) { me.addListener("beforepaste", autotype); } autotype.call(me); } }; }; // plugins/autosubmit.js /** * 快捷键提交 * @file * @since 1.2.6.1 */ /** * 提交表单 * @command autosubmit * @method execCommand * @param { String } cmd 命令字符串 * @example * ```javascript * editor.execCommand( 'autosubmit' ); * ``` */ UE.plugin.register("autosubmit", function() { return { shortcutkey: { autosubmit: "ctrl+13" //手动提交 }, commands: { autosubmit: { execCommand: function() { var me = this, form = domUtils.findParentByTagName(me.iframe, "form", false); if (form) { if (me.fireEvent("beforesubmit") === false) { return; } me.sync(); form.submit(); } } } } }; }); // plugins/background.js /** * 背景插件,为UEditor提供设置背景功能 * @file * @since 1.2.6.1 */ UE.plugin.register("background", function() { var me = this, cssRuleId = "editor_background", isSetColored, reg = new RegExp("body[\\s]*\\{(.+)\\}", "i"); function stringToObj(str) { var obj = {}, styles = str.split(";"); utils.each(styles, function(v) { var index = v.indexOf(":"), key = utils.trim(v.substr(0, index)).toLowerCase(); key && (obj[key] = utils.trim(v.substr(index + 1) || "")); }); return obj; } function setBackground(obj) { if (obj) { var styles = []; for (var name in obj) { if (obj.hasOwnProperty(name)) { styles.push(name + ":" + obj[name] + "; "); } } utils.cssRule( cssRuleId, styles.length ? "body{" + styles.join("") + "}" : "", me.document ); } else { utils.cssRule(cssRuleId, "", me.document); } } //重写editor.hasContent方法 var orgFn = me.hasContents; me.hasContents = function() { if (me.queryCommandValue("background")) { return true; } return orgFn.apply(me, arguments); }; return { bindEvents: { getAllHtml: function(type, headHtml) { var body = this.body, su = domUtils.getComputedStyle(body, "background-image"), url = ""; if (su.indexOf(me.options.imagePath) > 0) { url = su .substring(su.indexOf(me.options.imagePath), su.length - 1) .replace(/"|\(|\)/gi, ""); } else { url = su != "none" ? su.replace(/url\("?|"?\)/gi, "") : ""; } var html = ' "; headHtml.push(html); }, aftersetcontent: function() { if (isSetColored == false) setBackground(); } }, inputRule: function(root) { isSetColored = false; utils.each(root.getNodesByTagName("p"), function(p) { var styles = p.getAttr("data-background"); if (styles) { isSetColored = true; setBackground(stringToObj(styles)); p.parentNode.removeChild(p); } }); }, outputRule: function(root) { var me = this, styles = (utils.cssRule(cssRuleId, me.document) || "") .replace(/[\n\r]+/g, "") .match(reg); if (styles) { root.appendChild( UE.uNode.createElement( ' ' ) ); } }, commands: { background: { execCommand: function(cmd, obj) { setBackground(obj); }, queryCommandValue: function() { var me = this, styles = (utils.cssRule(cssRuleId, me.document) || "") .replace(/[\n\r]+/g, "") .match(reg); return styles ? stringToObj(styles[1]) : null; }, notNeedUndo: true } } }; }); // plugins/image.js /** * 图片插入、排版插件 * @file * @since 1.2.6.1 */ /** * 图片对齐方式 * @command imagefloat * @method execCommand * @remind 值center为独占一行居中 * @param { String } cmd 命令字符串 * @param { String } align 对齐方式,可传left、right、none、center * @remaind center表示图片独占一行 * @example * ```javascript * editor.execCommand( 'imagefloat', 'center' ); * ``` */ /** * 如果选区所在位置是图片区域 * @command imagefloat * @method queryCommandValue * @param { String } cmd 命令字符串 * @return { String } 返回图片对齐方式 * @example * ```javascript * editor.queryCommandValue( 'imagefloat' ); * ``` */ UE.commands["imagefloat"] = { execCommand: function(cmd, align) { var me = this, range = me.selection.getRange(); if (!range.collapsed) { var img = range.getClosedNode(); if (img && img.tagName == "IMG") { switch (align) { case "left": case "right": case "none": var pN = img.parentNode, tmpNode, pre, next; while (dtd.$inline[pN.tagName] || pN.tagName == "A") { pN = pN.parentNode; } tmpNode = pN; if ( tmpNode.tagName == "P" && domUtils.getStyle(tmpNode, "text-align") == "center" ) { if ( !domUtils.isBody(tmpNode) && domUtils.getChildCount(tmpNode, function(node) { return !domUtils.isBr(node) && !domUtils.isWhitespace(node); }) == 1 ) { pre = tmpNode.previousSibling; next = tmpNode.nextSibling; if ( pre && next && pre.nodeType == 1 && next.nodeType == 1 && pre.tagName == next.tagName && domUtils.isBlockElm(pre) ) { pre.appendChild(tmpNode.firstChild); while (next.firstChild) { pre.appendChild(next.firstChild); } domUtils.remove(tmpNode); domUtils.remove(next); } else { domUtils.setStyle(tmpNode, "text-align", ""); } } range.selectNode(img).select(); } domUtils.setStyle(img, "float", align == "none" ? "" : align); if (align == "none") { domUtils.removeAttributes(img, "align"); } break; case "center": if (me.queryCommandValue("imagefloat") != "center") { pN = img.parentNode; domUtils.setStyle(img, "float", ""); domUtils.removeAttributes(img, "align"); tmpNode = img; while ( pN && domUtils.getChildCount(pN, function(node) { return !domUtils.isBr(node) && !domUtils.isWhitespace(node); }) == 1 && (dtd.$inline[pN.tagName] || pN.tagName == "A") ) { tmpNode = pN; pN = pN.parentNode; } range.setStartBefore(tmpNode).setCursor(false); pN = me.document.createElement("div"); pN.appendChild(tmpNode); domUtils.setStyle(tmpNode, "float", ""); me.execCommand( "insertHtml", ' ' + pN.innerHTML + " " ); tmpNode = me.document.getElementById("_img_parent_tmp"); tmpNode.removeAttribute("id"); tmpNode = tmpNode.firstChild; range.selectNode(tmpNode).select(); //去掉后边多余的元素 next = tmpNode.parentNode.nextSibling; if (next && domUtils.isEmptyNode(next)) { domUtils.remove(next); } } break; } } } }, queryCommandValue: function() { var range = this.selection.getRange(), startNode, floatStyle; if (range.collapsed) { return "none"; } startNode = range.getClosedNode(); if (startNode && startNode.nodeType == 1 && startNode.tagName == "IMG") { floatStyle = domUtils.getComputedStyle(startNode, "float") || startNode.getAttribute("align"); if (floatStyle == "none") { floatStyle = domUtils.getComputedStyle( startNode.parentNode, "text-align" ) == "center" ? "center" : floatStyle; } return { left: 1, right: 1, center: 1 }[floatStyle] ? floatStyle : "none"; } return "none"; }, queryCommandState: function() { var range = this.selection.getRange(), startNode; if (range.collapsed) return -1; startNode = range.getClosedNode(); if (startNode && startNode.nodeType == 1 && startNode.tagName == "IMG") { return 0; } return -1; } }; /** * 插入图片 * @command insertimage * @method execCommand * @param { String } cmd 命令字符串 * @param { Object } opt 属性键值对,这些属性都将被复制到当前插入图片 * @remind 该命令第二个参数可接受一个图片配置项对象的数组,可以插入多张图片, * 此时数组的每一个元素都是一个Object类型的图片属性集合。 * @example * ```javascript * editor.execCommand( 'insertimage', { * src:'a/b/c.jpg', * width:'100', * height:'100' * } ); * ``` * @example * ```javascript * editor.execCommand( 'insertimage', [{ * src:'a/b/c.jpg', * width:'100', * height:'100' * },{ * src:'a/b/d.jpg', * width:'100', * height:'100' * }] ); * ``` */ UE.commands["insertimage"] = { execCommand: function(cmd, opt) { opt = utils.isArray(opt) ? opt : [opt]; if (!opt.length) { return; } var me = this, range = me.selection.getRange(), img = range.getClosedNode(); if (me.fireEvent("beforeinsertimage", opt) === true) { return; } if ( img && /img/i.test(img.tagName) && (img.className != "edui-faked-video" || img.className.indexOf("edui-upload-video") != -1) && !img.getAttribute("data-word-image") ) { var first = opt.shift(); var floatStyle = first["floatStyle"]; delete first["floatStyle"]; //// img.style.border = (first.border||0) +"px solid #000"; //// img.style.margin = (first.margin||0) +"px"; // img.style.cssText += ';margin:' + (first.margin||0) +"px;" + 'border:' + (first.border||0) +"px solid #000"; domUtils.setAttributes(img, first); me.execCommand("imagefloat", floatStyle); if (opt.length > 0) { range.setStartAfter(img).setCursor(false, true); me.execCommand("insertimage", opt); } } else { var html = [], str = "", ci; ci = opt[0]; if (opt.length == 1) { str = '' + str + " "; } html.push(str); } else { for (var i = 0; (ci = opt[i++]); ) { str = "" +
this.getContent(null, null, true) +
" "
);
d.close();
},
notNeedUndo: 1
};
// plugins/selectall.js
/**
* 全选
* @file
* @since 1.2.6.1
*/
/**
* 选中所有内容
* @command selectall
* @method execCommand
* @param { String } cmd 命令字符串
* @example
* ```javascript
* editor.execCommand( 'selectall' );
* ```
*/
UE.plugins["selectall"] = function() {
var me = this;
me.commands["selectall"] = {
execCommand: function() {
//去掉了原生的selectAll,因为会出现报错和当内容为空时,不能出现闭合状态的光标
var me = this,
body = me.body,
range = me.selection.getRange();
range.selectNodeContents(body);
if (domUtils.isEmptyBlock(body)) {
//opera不能自动合并到元素的里边,要手动处理一下
if (browser.opera && body.firstChild && body.firstChild.nodeType == 1) {
range.setStartAtFirst(body.firstChild);
}
range.collapse(true);
}
range.select(true);
},
notNeedUndo: 1
};
//快捷键
me.addshortcutkey({
selectAll: "ctrl+65"
});
};
// plugins/paragraph.js
/**
* 段落样式
* @file
* @since 1.2.6.1
*/
/**
* 段落格式
* @command paragraph
* @method execCommand
* @param { String } cmd 命令字符串
* @param {String} style 标签值为:'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
* @param {Object} attrs 标签的属性
* @example
* ```javascript
* editor.execCommand( 'Paragraph','h1','{
* class:'test'
* }' );
* ```
*/
/**
* 返回选区内节点标签名
* @command paragraph
* @method queryCommandValue
* @param { String } cmd 命令字符串
* @return { String } 节点标签名
* @example
* ```javascript
* editor.queryCommandValue( 'Paragraph' );
* ```
*/
UE.plugins["paragraph"] = function() {
var me = this,
block = domUtils.isBlockElm,
notExchange = ["TD", "LI", "PRE"],
doParagraph = function(range, style, attrs, sourceCmdName) {
var bookmark = range.createBookmark(),
filterFn = function(node) {
return node.nodeType == 1
? node.tagName.toLowerCase() != "br" &&
!domUtils.isBookmarkNode(node)
: !domUtils.isWhitespace(node);
},
para;
range.enlarge(true);
var bookmark2 = range.createBookmark(),
current = domUtils.getNextDomNode(bookmark2.start, false, filterFn),
tmpRange = range.cloneRange(),
tmpNode;
while (
current &&
!(
domUtils.getPosition(current, bookmark2.end) &
domUtils.POSITION_FOLLOWING
)
) {
if (current.nodeType === 3 || !block(current)) {
tmpRange.setStartBefore(current);
while (current && current !== bookmark2.end && !block(current)) {
tmpNode = current;
current = domUtils.getNextDomNode(current, false, null, function(
node
) {
return !block(node);
});
}
tmpRange.setEndAfter(tmpNode);
para = range.document.createElement(style);
if (attrs) {
domUtils.setAttributes(para, attrs);
if (
sourceCmdName &&
sourceCmdName === "customstyle" &&
attrs.style
) {
para.style.cssText = attrs.style;
}
}
para.appendChild(tmpRange.extractContents());
//需要内容占位
if (domUtils.isEmptyNode(para)) {
domUtils.fillChar(range.document, para);
}
tmpRange.insertNode(para);
var parent = para.parentNode;
//如果para上一级是一个block元素且不是body,td就删除它
if (
block(parent) &&
!domUtils.isBody(para.parentNode) &&
utils.indexOf(notExchange, parent.tagName) === -1
) {
//存储dir,style
if (!(sourceCmdName && sourceCmdName === "customstyle")) {
parent.getAttribute("dir") &&
para.setAttribute("dir", parent.getAttribute("dir"));
//trace:1070
parent.style.cssText &&
(para.style.cssText =
parent.style.cssText + ";" + para.style.cssText);
//trace:1030
parent.style.textAlign &&
!para.style.textAlign &&
(para.style.textAlign = parent.style.textAlign);
parent.style.textIndent &&
!para.style.textIndent &&
(para.style.textIndent = parent.style.textIndent);
parent.style.padding &&
!para.style.padding &&
(para.style.padding = parent.style.padding);
}
//trace:1706 选择的就是h1-6要删除
if (
attrs &&
/h\d/i.test(parent.tagName) &&
!/h\d/i.test(para.tagName)
) {
domUtils.setAttributes(parent, attrs);
if (
sourceCmdName &&
sourceCmdName === "customstyle" &&
attrs.style
) {
parent.style.cssText = attrs.style;
}
domUtils.remove(para.parentNode, true);
para = parent;
} else {
domUtils.remove(para.parentNode, true);
}
}
if (utils.indexOf(notExchange, parent.tagName) !== -1) {
current = parent;
} else {
current = para;
}
current = domUtils.getNextDomNode(current, false, filterFn);
} else {
current = domUtils.getNextDomNode(current, true, filterFn);
}
}
return range.moveToBookmark(bookmark2).moveToBookmark(bookmark);
};
me.setOpt("paragraph", {
p: "",
h1: "",
h2: "",
h3: "",
h4: "",
h5: "",
h6: ""
});
me.commands["paragraph"] = {
execCommand: function(cmdName, style, attrs, sourceCmdName) {
var range = this.selection.getRange();
//闭合时单独处理
if (range.collapsed) {
var txt = this.document.createTextNode("p");
range.insertNode(txt);
//去掉冗余的fillchar
if (browser.ie) {
var node = txt.previousSibling;
if (node && domUtils.isWhitespace(node)) {
domUtils.remove(node);
}
node = txt.nextSibling;
if (node && domUtils.isWhitespace(node)) {
domUtils.remove(node);
}
}
}
range = doParagraph(range, style, attrs, sourceCmdName);
if (txt) {
range.setStartBefore(txt).collapse(true);
pN = txt.parentNode;
domUtils.remove(txt);
if (domUtils.isBlockElm(pN) && domUtils.isEmptyNode(pN)) {
domUtils.fillNode(this.document, pN);
}
}
if (
browser.gecko &&
range.collapsed &&
range.startContainer.nodeType === 1
) {
var child = range.startContainer.childNodes[range.startOffset];
if (
child &&
child.nodeType === 1 &&
child.tagName.toLowerCase() === style
) {
range.setStart(child, 0).collapse(true);
}
}
//trace:1097 原来有true,原因忘了,但去了就不能清除多余的占位符了
range.select();
return true;
},
queryCommandValue: function() {
var node = domUtils.filterNodeList(
this.selection.getStartElementPath(),
"p h1 h2 h3 h4 h5 h6"
);
return node ? node.tagName.toLowerCase() : "";
}
};
};
// plugins/directionality.js
/**
* 设置文字输入的方向的插件
* @file
* @since 1.2.6.1
*/
(function() {
var block = domUtils.isBlockElm,
getObj = function(editor) {
// var startNode = editor.selection.getStart(),
// parents;
// if ( startNode ) {
// //查找所有的是block的父亲节点
// parents = domUtils.findParents( startNode, true, block, true );
// for ( var i = 0,ci; ci = parents[i++]; ) {
// if ( ci.getAttribute( 'dir' ) ) {
// return ci;
// }
// }
// }
return domUtils.filterNodeList(
editor.selection.getStartElementPath(),
function(n) {
return n && n.nodeType == 1 && n.getAttribute("dir");
}
);
},
doDirectionality = function(range, editor, forward) {
var bookmark,
filterFn = function(node) {
return node.nodeType == 1
? !domUtils.isBookmarkNode(node)
: !domUtils.isWhitespace(node);
},
obj = getObj(editor);
if (obj && range.collapsed) {
obj.setAttribute("dir", forward);
return range;
}
bookmark = range.createBookmark();
range.enlarge(true);
var bookmark2 = range.createBookmark(),
current = domUtils.getNextDomNode(bookmark2.start, false, filterFn),
tmpRange = range.cloneRange(),
tmpNode;
while (
current &&
!(
domUtils.getPosition(current, bookmark2.end) &
domUtils.POSITION_FOLLOWING
)
) {
if (current.nodeType == 3 || !block(current)) {
tmpRange.setStartBefore(current);
while (current && current !== bookmark2.end && !block(current)) {
tmpNode = current;
current = domUtils.getNextDomNode(current, false, null, function(
node
) {
return !block(node);
});
}
tmpRange.setEndAfter(tmpNode);
var common = tmpRange.getCommonAncestor();
if (!domUtils.isBody(common) && block(common)) {
//遍历到了block节点
common.setAttribute("dir", forward);
current = common;
} else {
//没有遍历到,添加一个block节点
var p = range.document.createElement("p");
p.setAttribute("dir", forward);
var frag = tmpRange.extractContents();
p.appendChild(frag);
tmpRange.insertNode(p);
current = p;
}
current = domUtils.getNextDomNode(current, false, filterFn);
} else {
current = domUtils.getNextDomNode(current, true, filterFn);
}
}
return range.moveToBookmark(bookmark2).moveToBookmark(bookmark);
};
/**
* 文字输入方向
* @command directionality
* @method execCommand
* @param { String } cmdName 命令字符串
* @param { String } forward 传入'ltr'表示从左向右输入,传入'rtl'表示从右向左输入
* @example
* ```javascript
* editor.execCommand( 'directionality', 'ltr');
* ```
*/
/**
* 查询当前选区的文字输入方向
* @command directionality
* @method queryCommandValue
* @param { String } cmdName 命令字符串
* @return { String } 返回'ltr'表示从左向右输入,返回'rtl'表示从右向左输入
* @example
* ```javascript
* editor.queryCommandValue( 'directionality');
* ```
*/
UE.commands["directionality"] = {
execCommand: function(cmdName, forward) {
var range = this.selection.getRange();
//闭合时单独处理
if (range.collapsed) {
var txt = this.document.createTextNode("d");
range.insertNode(txt);
}
doDirectionality(range, this, forward);
if (txt) {
range.setStartBefore(txt).collapse(true);
domUtils.remove(txt);
}
range.select();
return true;
},
queryCommandValue: function() {
var node = getObj(this);
return node ? node.getAttribute("dir") : "ltr";
}
};
})();
// plugins/horizontal.js
/**
* 插入分割线插件
* @file
* @since 1.2.6.1
*/
/**
* 插入分割线
* @command horizontal
* @method execCommand
* @param { String } cmdName 命令字符串
* @example
* ```javascript
* editor.execCommand( 'horizontal' );
* ```
*/
UE.plugins["horizontal"] = function() {
var me = this;
me.commands["horizontal"] = {
execCommand: function(cmdName) {
var me = this;
if (me.queryCommandState(cmdName) !== -1) {
me.execCommand("insertHtml", ""); var range = me.selection.getRange(), start = range.startContainer; if (start.nodeType == 1 && !start.childNodes[range.startOffset]) { var tmp; if ((tmp = start.childNodes[range.startOffset - 1])) { if (tmp.nodeType == 1 && tmp.tagName == "HR") { if (me.options.enterTag == "p") { tmp = me.document.createElement("p"); range.insertNode(tmp); range.setStart(tmp, 0).setCursor(); } else { tmp = me.document.createElement("br"); range.insertNode(tmp); range.setStartBefore(tmp).setCursor(); } } } } return true; } }, //边界在table里不能加分隔线 queryCommandState: function() { return domUtils.filterNodeList( this.selection.getStartElementPath(), "table" ) ? -1 : 0; } }; // me.addListener('delkeyup',function(){ // var rng = this.selection.getRange(); // if(browser.ie && browser.version > 8){ // rng.txtToElmBoundary(true); // if(domUtils.isStartInblock(rng)){ // var tmpNode = rng.startContainer; // var pre = tmpNode.previousSibling; // if(pre && domUtils.isTagNode(pre,'hr')){ // domUtils.remove(pre); // rng.select(); // return; // } // } // } // if(domUtils.isBody(rng.startContainer)){ // var hr = rng.startContainer.childNodes[rng.startOffset -1]; // if(hr && hr.nodeName == 'HR'){ // var next = hr.nextSibling; // if(next){ // rng.setStart(next,0) // }else if(hr.previousSibling){ // rng.setStartAtLast(hr.previousSibling) // }else{ // var p = this.document.createElement('p'); // hr.parentNode.insertBefore(p,hr); // domUtils.fillNode(this.document,p); // rng.setStart(p,0); // } // domUtils.remove(hr); // rng.setCursor(false,true); // } // } // }) me.addListener("delkeydown", function(name, evt) { var rng = this.selection.getRange(); rng.txtToElmBoundary(true); if (domUtils.isStartInblock(rng)) { var tmpNode = rng.startContainer; var pre = tmpNode.previousSibling; if (pre && domUtils.isTagNode(pre, "hr")) { domUtils.remove(pre); rng.select(); domUtils.preventDefault(evt); return true; } } }); }; // plugins/time.js /** * 插入时间和日期 * @file * @since 1.2.6.1 */ /** * 插入时间,默认格式:12:59:59 * @command time * @method execCommand * @param { String } cmd 命令字符串 * @example * ```javascript * editor.execCommand( 'time'); * ``` */ /** * 插入日期,默认格式:2013-08-30 * @command date * @method execCommand * @param { String } cmd 命令字符串 * @example * ```javascript * editor.execCommand( 'date'); * ``` */ UE.commands["time"] = UE.commands["date"] = { execCommand: function(cmd, format) { var date = new Date(); function formatTime(date, format) { var hh = ("0" + date.getHours()).slice(-2), ii = ("0" + date.getMinutes()).slice(-2), ss = ("0" + date.getSeconds()).slice(-2); format = format || "hh:ii:ss"; return format.replace(/hh/gi, hh).replace(/ii/gi, ii).replace(/ss/gi, ss); } function formatDate(date, format) { var yyyy = ("000" + date.getFullYear()).slice(-4), yy = yyyy.slice(-2), mm = ("0" + (date.getMonth() + 1)).slice(-2), dd = ("0" + date.getDate()).slice(-2); format = format || "yyyy-mm-dd"; return format .replace(/yyyy/gi, yyyy) .replace(/yy/gi, yy) .replace(/mm/gi, mm) .replace(/dd/gi, dd); } this.execCommand( "insertHtml", cmd == "time" ? formatTime(date, format) : formatDate(date, format) ); } }; // plugins/rowspacing.js /** * 段前段后间距插件 * @file * @since 1.2.6.1 */ /** * 设置段间距 * @command rowspacing * @method execCommand * @param { String } cmd 命令字符串 * @param { String } value 段间距的值,以px为单位 * @param { String } dir 间距位置,top或bottom,分别表示段前和段后 * @example * ```javascript * editor.execCommand( 'rowspacing', '10', 'top' ); * ``` */ UE.plugins["rowspacing"] = function() { var me = this; me.setOpt({ rowspacingtop: ["5", "10", "15", "20", "25"], rowspacingbottom: ["5", "10", "15", "20", "25"] }); me.commands["rowspacing"] = { execCommand: function(cmdName, value, dir) { this.execCommand("paragraph", "p", { style: "margin-" + dir + ":" + value + "px" }); return true; }, queryCommandValue: function(cmdName, dir) { var pN = domUtils.filterNodeList( this.selection.getStartElementPath(), function(node) { return domUtils.isBlockElm(node); } ), value; //trace:1026 if (pN) { value = domUtils .getComputedStyle(pN, "margin-" + dir) .replace(/[^\d]/g, ""); return !value ? 0 : value; } return 0; } }; }; // plugins/lineheight.js /** * 设置行内间距 * @file * @since 1.2.6.1 */ UE.plugins["lineheight"] = function() { var me = this; me.setOpt({ lineheight: ["1", "1.5", "1.75", "2", "3", "4", "5"] }); /** * 行距 * @command lineheight * @method execCommand * @param { String } cmdName 命令字符串 * @param { String } value 传入的行高值, 该值是当前字体的倍数, 例如: 1.5, 1.75 * @example * ```javascript * editor.execCommand( 'lineheight', 1.5); * ``` */ /** * 查询当前选区内容的行高大小 * @command lineheight * @method queryCommandValue * @param { String } cmd 命令字符串 * @return { String } 返回当前行高大小 * @example * ```javascript * editor.queryCommandValue( 'lineheight' ); * ``` */ me.commands["lineheight"] = { execCommand: function(cmdName, value) { this.execCommand("paragraph", "p", { style: "line-height:" + (value == "1" ? "normal" : value + "em") }); return true; }, queryCommandValue: function() { var pN = domUtils.filterNodeList( this.selection.getStartElementPath(), function(node) { return domUtils.isBlockElm(node); } ); if (pN) { var value = domUtils.getComputedStyle(pN, "line-height"); return value == "normal" ? 1 : value.replace(/[^\d.]*/gi, ""); } } }; }; // plugins/insertcode.js /** * 插入代码插件 * @file * @since 1.2.6.1 */ UE.plugins["insertcode"] = function() { var me = this; me.setOpt("insertcode", { as3: "ActionScript3", bash: "Bash/Shell", cpp: "C/C++", css: "Css", cf: "CodeFunction", "c#": "C#", delphi: "Delphi", diff: "Diff", erlang: "Erlang", groovy: "Groovy", html: "Html", java: "Java", jfx: "JavaFx", js: "Javascript", pl: "Perl", php: "Php", plain: "Plain Text", ps: "PowerShell", python: "Python", ruby: "Ruby", scala: "Scala", sql: "Sql", vb: "Vb", xml: "Xml" }); /** * 插入代码 * @command insertcode * @method execCommand * @param { String } cmd 命令字符串 * @param { String } lang 插入代码的语言 * @example * ```javascript * editor.execCommand( 'insertcode', 'javascript' ); * ``` */ /** * 如果选区所在位置是插入插入代码区域,返回代码的语言 * @command insertcode * @method queryCommandValue * @param { String } cmd 命令字符串 * @return { String } 返回代码的语言 * @example * ```javascript * editor.queryCommandValue( 'insertcode' ); * ``` */ me.commands["insertcode"] = { execCommand: function(cmd, lang) { var me = this, rng = me.selection.getRange(), pre = domUtils.findParentByTagName(rng.startContainer, "pre", true); if (pre) { pre.className = "brush:" + lang + ";toolbar:false;"; } else { var code = ""; if (rng.collapsed) { code = browser.ie && browser.ie11below ? browser.version <= 8 ? " " : "" : " "; } else { var frag = rng.extractContents(); var div = me.document.createElement("div"); div.appendChild(frag); utils.each( UE.filterNode( UE.htmlparser(div.innerHTML.replace(/[\r\t]/g, "")), me.options.filterTxtRules ).children, function(node) { if (browser.ie && browser.ie11below && browser.version > 8) { if (node.type == "element") { if (node.tagName == "br") { code += "\n"; } else if (!dtd.$empty[node.tagName]) { utils.each(node.children, function(cn) { if (cn.type == "element") { if (cn.tagName == "br") { code += "\n"; } else if (!dtd.$empty[node.tagName]) { code += cn.innerText(); } } else { code += cn.data; } }); if (!/\n$/.test(code)) { code += "\n"; } } } else { code += node.data + "\n"; } if (!node.nextSibling() && /\n$/.test(code)) { code = code.replace(/\n$/, ""); } } else { if (browser.ie && browser.ie11below) { if (node.type == "element") { if (node.tagName == "br") { code += " "; } else if (!dtd.$empty[node.tagName]) { utils.each(node.children, function(cn) { if (cn.type == "element") { if (cn.tagName == "br") { code += " "; } else if (!dtd.$empty[node.tagName]) { code += cn.innerText(); } } else { code += cn.data; } }); if (!/br>$/.test(code)) { code += " "; } } } else { code += node.data + " "; } if (!node.nextSibling() && / $/.test(code)) { code = code.replace(/ $/, ""); } } else { code += node.type == "element" ? dtd.$empty[node.tagName] ? "" : node.innerText() : node.data; if (!/br\/?\s*>$/.test(code)) { if (!node.nextSibling()) return; code += " "; } } } } ); } me.execCommand( "inserthtml", ' ' + code + "", true ); pre = me.document.getElementById("coder"); domUtils.removeAttributes(pre, "id"); var tmpNode = pre.previousSibling; if ( tmpNode && ((tmpNode.nodeType == 3 && tmpNode.nodeValue.length == 1 && browser.ie && browser.version == 6) || domUtils.isEmptyBlock(tmpNode)) ) { domUtils.remove(tmpNode); } var rng = me.selection.getRange(); if (domUtils.isEmptyBlock(pre)) { rng.setStart(pre, 0).setCursor(false, true); } else { rng.selectNodeContents(pre).select(); } } }, queryCommandValue: function() { var path = this.selection.getStartElementPath(); var lang = ""; utils.each(path, function(node) { if (node.nodeName == "PRE") { var match = node.className.match(/brush:([^;]+)/); lang = match && match[1] ? match[1] : ""; return false; } }); return lang; } }; me.addInputRule(function(root) { utils.each(root.getNodesByTagName("pre"), function(pre) { var brs = pre.getNodesByTagName("br"); if (brs.length) { browser.ie && browser.ie11below && browser.version > 8 && utils.each(brs, function(br) { var txt = UE.uNode.createText("\n"); br.parentNode.insertBefore(txt, br); br.parentNode.removeChild(br); }); return; } if (browser.ie && browser.ie11below && browser.version > 8) return; var code = pre.innerText().split(/\n/); pre.innerHTML(""); utils.each(code, function(c) { if (c.length) { pre.appendChild(UE.uNode.createText(c)); } pre.appendChild(UE.uNode.createElement("br")); }); }); }); me.addOutputRule(function(root) { utils.each(root.getNodesByTagName("pre"), function(pre) { var code = ""; utils.each(pre.children, function(n) { if (n.type == "text") { //在ie下文本内容有可能末尾带有\n要去掉 //trace:3396 code += n.data.replace(/[ ]/g, " ").replace(/\n$/, ""); } else { if (n.tagName == "br") { code += "\n"; } else { code += !dtd.$empty[n.tagName] ? "" : n.innerText(); } } }); pre.innerText(code.replace(/( |\n)+$/, "")); }); }); //不需要判断highlight的command列表 me.notNeedCodeQuery = { help: 1, undo: 1, redo: 1, source: 1, print: 1, searchreplace: 1, fullscreen: 1, preview: 1, insertparagraph: 1, elementpath: 1, insertcode: 1, inserthtml: 1, selectall: 1 }; //将queyCommamndState重置 var orgQuery = me.queryCommandState; me.queryCommandState = function(cmd) { var me = this; if ( !me.notNeedCodeQuery[cmd.toLowerCase()] && me.selection && me.queryCommandValue("insertcode") ) { return -1; } return UE.Editor.prototype.queryCommandState.apply(this, arguments); }; me.addListener("beforeenterkeydown", function() { var rng = me.selection.getRange(); var pre = domUtils.findParentByTagName(rng.startContainer, "pre", true); if (pre) { me.fireEvent("saveScene"); if (!rng.collapsed) { rng.deleteContents(); } if (!browser.ie || browser.ie9above) { var tmpNode = me.document.createElement("br"), pre; rng.insertNode(tmpNode).setStartAfter(tmpNode).collapse(true); var next = tmpNode.nextSibling; if (!next && (!browser.ie || browser.version > 10)) { rng.insertNode(tmpNode.cloneNode(false)); } else { rng.setStartAfter(tmpNode); } pre = tmpNode.previousSibling; var tmp; while (pre) { tmp = pre; pre = pre.previousSibling; if (!pre || pre.nodeName == "BR") { pre = tmp; break; } } if (pre) { var str = ""; while ( pre && pre.nodeName != "BR" && new RegExp("^[\\s" + domUtils.fillChar + "]*$").test(pre.nodeValue) ) { str += pre.nodeValue; pre = pre.nextSibling; } if (pre.nodeName != "BR") { var match = pre.nodeValue.match( new RegExp("^([\\s" + domUtils.fillChar + "]+)") ); if (match && match[1]) { str += match[1]; } } if (str) { str = me.document.createTextNode(str); rng.insertNode(str).setStartAfter(str); } } rng.collapse(true).select(true); } else { if (browser.version > 8) { var txt = me.document.createTextNode("\n"); var start = rng.startContainer; if (rng.startOffset == 0) { var preNode = start.previousSibling; if (preNode) { rng.insertNode(txt); var fillchar = me.document.createTextNode(" "); rng .setStartAfter(txt) .insertNode(fillchar) .setStart(fillchar, 0) .collapse(true) .select(true); } } else { rng.insertNode(txt).setStartAfter(txt); var fillchar = me.document.createTextNode(" "); start = rng.startContainer.childNodes[rng.startOffset]; if (start && !/^\n/.test(start.nodeValue)) { rng.setStartBefore(txt); } rng .insertNode(fillchar) .setStart(fillchar, 0) .collapse(true) .select(true); } } else { var tmpNode = me.document.createElement("br"); rng.insertNode(tmpNode); rng.insertNode(me.document.createTextNode(domUtils.fillChar)); rng.setStartAfter(tmpNode); pre = tmpNode.previousSibling; var tmp; while (pre) { tmp = pre; pre = pre.previousSibling; if (!pre || pre.nodeName == "BR") { pre = tmp; break; } } if (pre) { var str = ""; while ( pre && pre.nodeName != "BR" && new RegExp("^[ " + domUtils.fillChar + "]*$").test(pre.nodeValue) ) { str += pre.nodeValue; pre = pre.nextSibling; } if (pre.nodeName != "BR") { var match = pre.nodeValue.match( new RegExp("^([ " + domUtils.fillChar + "]+)") ); if (match && match[1]) { str += match[1]; } } str = me.document.createTextNode(str); rng.insertNode(str).setStartAfter(str); } rng.collapse(true).select(); } } me.fireEvent("saveScene"); return true; } }); me.addListener("tabkeydown", function(cmd, evt) { var rng = me.selection.getRange(); var pre = domUtils.findParentByTagName(rng.startContainer, "pre", true); if (pre) { me.fireEvent("saveScene"); if (evt.shiftKey) { } else { if (!rng.collapsed) { var bk = rng.createBookmark(); var start = bk.start.previousSibling; while (start) { if (pre.firstChild === start && !domUtils.isBr(start)) { pre.insertBefore(me.document.createTextNode(" "), start); break; } if (domUtils.isBr(start)) { pre.insertBefore( me.document.createTextNode(" "), start.nextSibling ); break; } start = start.previousSibling; } var end = bk.end; start = bk.start.nextSibling; if (pre.firstChild === bk.start) { pre.insertBefore( me.document.createTextNode(" "), start.nextSibling ); } while (start && start !== end) { if (domUtils.isBr(start) && start.nextSibling) { if (start.nextSibling === end) { break; } pre.insertBefore( me.document.createTextNode(" "), start.nextSibling ); } start = start.nextSibling; } rng.moveToBookmark(bk).select(); } else { var tmpNode = me.document.createTextNode(" "); rng .insertNode(tmpNode) .setStartAfter(tmpNode) .collapse(true) .select(true); } } me.fireEvent("saveScene"); return true; } }); me.addListener("beforeinserthtml", function(evtName, html) { var me = this, rng = me.selection.getRange(), pre = domUtils.findParentByTagName(rng.startContainer, "pre", true); if (pre) { if (!rng.collapsed) { rng.deleteContents(); } var htmlstr = ""; if (browser.ie && browser.version > 8) { utils.each( UE.filterNode(UE.htmlparser(html), me.options.filterTxtRules) .children, function(node) { if (node.type == "element") { if (node.tagName == "br") { htmlstr += "\n"; } else if (!dtd.$empty[node.tagName]) { utils.each(node.children, function(cn) { if (cn.type == "element") { if (cn.tagName == "br") { htmlstr += "\n"; } else if (!dtd.$empty[node.tagName]) { htmlstr += cn.innerText(); } } else { htmlstr += cn.data; } }); if (!/\n$/.test(htmlstr)) { htmlstr += "\n"; } } } else { htmlstr += node.data + "\n"; } if (!node.nextSibling() && /\n$/.test(htmlstr)) { htmlstr = htmlstr.replace(/\n$/, ""); } } ); var tmpNode = me.document.createTextNode( utils.html(htmlstr.replace(/ /g, " ")) ); rng.insertNode(tmpNode).selectNode(tmpNode).select(); } else { var frag = me.document.createDocumentFragment(); utils.each( UE.filterNode(UE.htmlparser(html), me.options.filterTxtRules) .children, function(node) { if (node.type == "element") { if (node.tagName == "br") { frag.appendChild(me.document.createElement("br")); } else if (!dtd.$empty[node.tagName]) { utils.each(node.children, function(cn) { if (cn.type == "element") { if (cn.tagName == "br") { frag.appendChild(me.document.createElement("br")); } else if (!dtd.$empty[node.tagName]) { frag.appendChild( me.document.createTextNode( utils.html(cn.innerText().replace(/ /g, " ")) ) ); } } else { frag.appendChild( me.document.createTextNode( utils.html(cn.data.replace(/ /g, " ")) ) ); } }); if (frag.lastChild.nodeName != "BR") { frag.appendChild(me.document.createElement("br")); } } } else { frag.appendChild( me.document.createTextNode( utils.html(node.data.replace(/ /g, " ")) ) ); } if (!node.nextSibling() && frag.lastChild.nodeName == "BR") { frag.removeChild(frag.lastChild); } } ); rng.insertNode(frag).select(); } return true; } }); //方向键的处理 me.addListener("keydown", function(cmd, evt) { var me = this, keyCode = evt.keyCode || evt.which; if (keyCode == 40) { var rng = me.selection.getRange(), pre, start = rng.startContainer; if ( rng.collapsed && (pre = domUtils.findParentByTagName(rng.startContainer, "pre", true)) && !pre.nextSibling ) { var last = pre.lastChild; while (last && last.nodeName == "BR") { last = last.previousSibling; } if ( last === start || (rng.startContainer === pre && rng.startOffset == pre.childNodes.length) ) { me.execCommand("insertparagraph"); domUtils.preventDefault(evt); } } } }); //trace:3395 me.addListener("delkeydown", function(type, evt) { var rng = this.selection.getRange(); rng.txtToElmBoundary(true); var start = rng.startContainer; if ( domUtils.isTagNode(start, "pre") && rng.collapsed && domUtils.isStartInblock(rng) ) { var p = me.document.createElement("p"); domUtils.fillNode(me.document, p); start.parentNode.insertBefore(p, start); domUtils.remove(start); rng.setStart(p, 0).setCursor(false, true); domUtils.preventDefault(evt); return true; } }); }; // plugins/cleardoc.js /** * 清空文档插件 * @file * @since 1.2.6.1 */ /** * 清空文档 * @command cleardoc * @method execCommand * @param { String } cmd 命令字符串 * @example * ```javascript * //editor 是编辑器实例 * editor.execCommand('cleardoc'); * ``` */ UE.commands["cleardoc"] = { execCommand: function(cmdName) { var me = this, enterTag = me.options.enterTag, range = me.selection.getRange(); if (enterTag == "br") { me.body.innerHTML = " "; range.setStart(me.body, 0).setCursor(); } else { me.body.innerHTML = " " + (ie ? "" : " ' ); node.parentNode.insertBefore(hr, node); node.parentNode.removeChild(node); } }); }); me.addOutputRule(function(node) { utils.each(node.getNodesByTagName("hr"), function(n) { if (n.getAttr("class") == "pagebreak") { var txt = UE.uNode.createText(me.options.pageBreakTag); n.parentNode.insertBefore(txt, n); n.parentNode.removeChild(n); } }); }); /** * 插入分页符 * @command pagebreak * @method execCommand * @param { String } cmd 命令字符串 * @remind 在表格中插入分页符会把表格切分成两部分 * @remind 获取编辑器内的数据时, 编辑器会把分页符转换成“_ueditor_page_break_tag_”字符串, * 以便于提交数据到服务器端后处理分页。 * @example * ```javascript * editor.execCommand( 'pagebreak'); //插入一个hr标签,带有样式类名pagebreak * ``` */ me.commands["pagebreak"] = { execCommand: function() { var range = me.selection.getRange(), hr = me.document.createElement("hr"); domUtils.setAttributes(hr, { class: "pagebreak", noshade: "noshade", size: "5" }); domUtils.unSelectable(hr); //table单独处理 var node = domUtils.findParentByTagName( range.startContainer, notBreakTags, true ), parents = [], pN; if (node) { switch (node.tagName) { case "TD": pN = node.parentNode; if (!pN.previousSibling) { var table = domUtils.findParentByTagName(pN, "table"); // var tableWrapDiv = table.parentNode; // if(tableWrapDiv && tableWrapDiv.nodeType == 1 // && tableWrapDiv.tagName == 'DIV' // && tableWrapDiv.getAttribute('dropdrag') // ){ // domUtils.remove(tableWrapDiv,true); // } table.parentNode.insertBefore(hr, table); parents = domUtils.findParents(hr, true); } else { pN.parentNode.insertBefore(hr, pN); parents = domUtils.findParents(hr); } pN = parents[1]; if (hr !== pN) { domUtils.breakParent(hr, pN); } //table要重写绑定一下拖拽 me.fireEvent("afteradjusttable", me.document); } } else { if (!range.collapsed) { range.deleteContents(); var start = range.startContainer; while ( !domUtils.isBody(start) && domUtils.isBlockElm(start) && domUtils.isEmptyNode(start) ) { range.setStartBefore(start).collapse(true); domUtils.remove(start); start = range.startContainer; } } range.insertNode(hr); var pN = hr.parentNode, nextNode; while (!domUtils.isBody(pN)) { domUtils.breakParent(hr, pN); nextNode = hr.nextSibling; if (nextNode && domUtils.isEmptyBlock(nextNode)) { domUtils.remove(nextNode); } pN = hr.parentNode; } nextNode = hr.nextSibling; var pre = hr.previousSibling; if (isHr(pre)) { domUtils.remove(pre); } else { pre && fillNode(pre); } if (!nextNode) { var p = me.document.createElement("p"); hr.parentNode.appendChild(p); domUtils.fillNode(me.document, p); range.setStart(p, 0).collapse(true); } else { if (isHr(nextNode)) { domUtils.remove(nextNode); } else { fillNode(nextNode); } range.setEndAfter(hr).collapse(false); } range.select(true); } } }; }; // plugins/wordimage.js ///import core ///commands 本地图片引导上传 ///commandsName WordImage ///commandsTitle 本地图片引导上传 ///commandsDialog dialogs\wordimage UE.plugin.register("wordimage", function() { var me = this, images = []; this.addListener("click", function (type, evt) { var el = evt.target || evt.srcElement; if ('IMG' == el.tagName && el.getAttribute('data-word-image')) { me.ui._dialogs.wordimageDialog && me.ui._dialogs.wordimageDialog.open(); } }); return { commands: { wordimage: { execCommand: function() { var images = domUtils.getElementsByTagName(me.body, "img"); var urlList = []; for (var i = 0, ci; (ci = images[i++]); ) { var url = ci.getAttribute("data-word-image"); url && urlList.push(url); } return urlList; }, queryCommandState: function() { images = domUtils.getElementsByTagName(me.body, "img"); for (var i = 0, ci; (ci = images[i++]); ) { if (ci.getAttribute("data-word-image")) { return 1; } } return -1; }, notNeedUndo: true } }, inputRule: function(root) { utils.each(root.getNodesByTagName("img"), function(img) { var attrs = img.attrs, flag = parseInt(attrs.width) < 128 || parseInt(attrs.height) < 43, opt = me.options, src = opt.UEDITOR_HOME_URL + "themes/default/images/spacer.gif"; if (attrs["src"] && /^(?:(file:\/+))/.test(attrs["src"])) { img.setAttr({ width: attrs.width, height: attrs.height, alt: attrs.alt, 'data-word-image': attrs.src, src: src, style: "background:url(" + (flag ? opt.themePath + opt.theme + "/images/word.gif" : opt.langPath + opt.lang + "/images/localimage.png") + ") no-repeat center center;border:1px solid #ddd" }); } }); } }; }); // plugins/autosave.js UE.plugin.register("autosave", function () { var me = this, saveKey = null; function save(editor) { var saveData; if (!editor.hasContents()) { //这里不能调用命令来删除, 会造成事件死循环 saveKey && me.removePreferences(saveKey); return; } editor._autoSaveTimer = null; saveData = me.body.innerHTML; if ( editor.fireEvent("beforeautosave", { content: saveData }) === false ) { return; } // console.log('autosave', saveKey, saveData); me.setPreferences(saveKey, saveData); editor.fireEvent("afterautosave", { content: saveData }); } return { defaultOptions: { autoSaveEnable: true, autoSaveRestore: false, autoSaveKey: null, }, bindEvents: { ready: function () { saveKey = me.getOpt('autoSaveKey'); if (!saveKey) { var _suffix = "_DraftsData", key = null; if (me.key) { key = me.key + _suffix; } else { key = (me.container.parentNode.id || "ue-common") + _suffix; } saveKey = (location.protocol + location.host + location.pathname).replace( /[.:\/]/g, "_" ) + key; } if (me.getOpt('autoSaveRestore')) { var data = me.getPreferences(saveKey); // console.log('saveKey', saveKey, data); if (data) { me.body.innerHTML = data; me.fireEvent('showmessage',{ type:'info', content:me.getLang('autosave').autoRestoreTip }) } } // console.log('saveKey', saveKey); }, beforesubmit: function(){ if (!me.getOpt("autoSaveEnable") || !saveKey) { return; } me.execCommand('clear_auto_save_content'); }, contentchange: function () { if(!me.isReady){ return; } if (!me.getOpt("autoSaveEnable") || !saveKey) { return; } if (me._autoSaveTimer) { window.clearTimeout(me._autoSaveTimer); } me._autoSaveTimer = window.setTimeout(function () { save(me); }, 1000); } }, commands: { clear_auto_save_content: { execCommand: function (cmd, name) { if (saveKey && me.getPreferences(saveKey)) { me.removePreferences(saveKey); } }, notNeedUndo: true, ignoreContentChange: true }, set_auto_save_content: { execCommand: function (cmd, name) { save(me); }, notNeedUndo: true, ignoreContentChange: true }, get_auto_save_content: { execCommand: function (cmd, name) { return me.getPreferences(saveKey) || ""; }, notNeedUndo: true, ignoreContentChange: true }, auto_save_restore: { execCommand: function (cmd, name) { if (saveKey) { me.body.innerHTML = me.getPreferences(saveKey) || " " + domUtils.fillHtml + " "; me.focus(true); } }, queryCommandState: function () { return saveKey ? (me.getPreferences(saveKey) === null ? -1 : 0) : -1; }, notNeedUndo: true, ignoreContentChange: true } } }; }); // plugins/formula.js UE.plugin.register("formula", function () { var me = this, images = []; return { commands: { formula: { execCommand: function (cmdName, value) { var range = me.selection.getRange(), img = range.getClosedNode(); value = encodeURIComponent(value); var formulaConfig = me.getOpt('formulaConfig'); var src = formulaConfig.imageUrlTemplate.replace(/\{\}/, value); if (img) { img.setAttribute("src", src); } else { me.execCommand("insertHtml", '"); } //去掉末尾的空白 var p = li.firstChild(); var lastChild = p.lastChild(); if ( lastChild && lastChild.type === "text" && /^\s*$/.test(lastChild.data) ) { p.removeChild(lastChild); } }); if (me.options.autoTransWordToList) { var orderlisttype = { num1: /^\d+\)/, decimal: /^\d+\./, "lower-alpha": /^[a-z]+\)/, "upper-alpha": /^[A-Z]+\./, cn: /^[\u4E00\u4E8C\u4E09\u56DB\u516d\u4e94\u4e03\u516b\u4e5d]+[\u3001]/, cn2: /^\([\u4E00\u4E8C\u4E09\u56DB\u516d\u4e94\u4e03\u516b\u4e5d]+\)/ }, unorderlisttype = { square: "n" }; function checkListType(content, container) { var span = container.firstChild(); if ( span && span.type === "element" && span.tagName === "span" && /Wingdings|Symbol/.test(span.getStyle("font-family")) ) { for (var p in unorderlisttype) { if (unorderlisttype[p] == span.data) { return p; } } return "disc"; } for (var p in orderlisttype) { if (orderlisttype[p].test(content)) { return p; } } } utils.each(root.getNodesByTagName("p"), function(node) { if (node.getAttr("class") !== "MsoListParagraph") { return; } //word粘贴过来的会带有margin要去掉,但这样也可能会误命中一些央视 node.setStyle("margin", ""); node.setStyle("margin-left", ""); node.setAttr("class", ""); function appendLi(list, p, type) { if (list.tagName === "ol") { if (browser.ie) { var first = p.firstChild(); if ( first.type === "element" && first.tagName === "span" && orderlisttype[type].test(first.innerText()) ) { p.removeChild(first); } } else { p.innerHTML(p.innerHTML().replace(orderlisttype[type], "")); } } else { p.removeChild(p.firstChild()); } var li = UE.uNode.createElement("li"); li.appendChild(p); list.appendChild(li); } var tmp = node, type, cacheNode = node; if ( node.parentNode.tagName !== "li" && (type = checkListType(node.innerText(), node)) ) { var list = UE.uNode.createElement( me.options.insertorderedlist.hasOwnProperty(type) ? "ol" : "ul" ); if (customStyle[type]) { list.setAttr("class", "custom_" + type); } else { list.setStyle("list-style-type", type); } while ( node && node.parentNode.tagName !== "li" && checkListType(node.innerText(), node) ) { tmp = node.nextSibling(); if (!tmp) { node.parentNode.insertBefore(list, node); } appendLi(list, node, type); node = tmp; } if (!list.parentNode && node && node.parentNode) { node.parentNode.insertBefore(list, node); } } var span = cacheNode.firstChild(); if ( span && span.type == "element" && span.tagName == "span" && /^\s*( )+\s*$/.test(span.innerText()) ) { span.parentNode.removeChild(span); } }); } }); //调整索引标签 me.addListener("contentchange", function() { adjustListStyle(me.document); }); function adjustListStyle(doc, ignore) { utils.each(domUtils.getElementsByTagName(doc, "ol ul"), function(node) { if (!domUtils.inDoc(node, doc)) return; var parent = node.parentNode; if (parent.tagName === node.tagName) { var nodeStyleType = getStyle(node) || (node.tagName === "OL" ? "decimal" : "disc"), parentStyleType = getStyle(parent) || (parent.tagName === "OL" ? "decimal" : "disc"); if (nodeStyleType === parentStyleType) { var styleIndex = utils.indexOf( listStyle[node.tagName], nodeStyleType ); styleIndex = styleIndex + 1 === listStyle[node.tagName].length ? 0 : styleIndex + 1; setListStyle(node, listStyle[node.tagName][styleIndex]); } } var index = 0, type = 2; if (domUtils.hasClass(node, /custom_/)) { if ( !( /[ou]l/i.test(parent.tagName) && domUtils.hasClass(parent, /custom_/) ) ) { type = 1; } } else { if ( /[ou]l/i.test(parent.tagName) && domUtils.hasClass(parent, /custom_/) ) { type = 3; } } var style = domUtils.getStyle(node, "list-style-type"); style && (node.style.cssText = "list-style-type:" + style); node.className = utils.trim(node.className.replace(/list-paddingleft-\w+/, "")) + " list-paddingleft-" + type; utils.each(domUtils.getElementsByTagName(node, "li"), function(li) { li.style.cssText && (li.style.cssText = ""); if (!li.firstChild) { domUtils.remove(li); return; } if (li.parentNode !== node) { return; } index++; if (domUtils.hasClass(node, /custom_/)) { var paddingLeft = 1, currentStyle = getStyle(node); if (node.tagName === "OL") { if (currentStyle) { switch (currentStyle) { case "cn": case "cn1": case "cn2": if ( index > 10 && (index % 10 === 0 || (index > 10 && index < 20)) ) { paddingLeft = 2; } else if (index > 20) { paddingLeft = 3; } break; case "num2": if (index > 9) { paddingLeft = 2; } } } li.className = "list-" + customStyle[currentStyle] + index + " " + "list-" + currentStyle + "-paddingleft-" + paddingLeft; } else { li.className = "list-" + customStyle[currentStyle] + " " + "list-" + currentStyle + "-paddingleft"; } } else { li.className = li.className.replace(/list-[\w\-]+/gi, ""); } var className = li.getAttribute("class"); if (className !== null && !className.replace(/\s/g, "")) { domUtils.removeAttributes(li, "class"); } }); !ignore && adjustList( node, node.tagName.toLowerCase(), getStyle(node) || domUtils.getStyle(node, "list-style-type"), true ); }); } function adjustList(list, tag, style, ignoreEmpty) { var nextList = list.nextSibling; if ( nextList && nextList.nodeType === 1 && nextList.tagName.toLowerCase() === tag && (getStyle(nextList) || domUtils.getStyle(nextList, "list-style-type") || (tag == "ol" ? "decimal" : "disc")) == style ) { domUtils.moveChild(nextList, list); if (nextList.childNodes.length === 0) { domUtils.remove(nextList); } } if (nextList && domUtils.isFillChar(nextList)) { domUtils.remove(nextList); } var preList = list.previousSibling; if ( preList && preList.nodeType === 1 && preList.tagName.toLowerCase() == tag && (getStyle(preList) || domUtils.getStyle(preList, "list-style-type") || (tag == "ol" ? "decimal" : "disc")) === style ) { domUtils.moveChild(list, preList); } if (preList && domUtils.isFillChar(preList)) { domUtils.remove(preList); } !ignoreEmpty && domUtils.isEmptyBlock(list) && domUtils.remove(list); if (getStyle(list)) { adjustListStyle(list.ownerDocument, true); } } function setListStyle(list, style) { if (customStyle[style]) { list.className = "custom_" + style; } try { domUtils.setStyle(list, "list-style-type", style); } catch (e) {} } function clearEmptySibling(node) { var tmpNode = node.previousSibling; if (tmpNode && domUtils.isEmptyBlock(tmpNode)) { domUtils.remove(tmpNode); } tmpNode = node.nextSibling; if (tmpNode && domUtils.isEmptyBlock(tmpNode)) { domUtils.remove(tmpNode); } } me.addListener("keydown", function(type, evt) { function preventAndSave() { evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false); me.fireEvent("contentchange"); me.undoManger && me.undoManger.save(); } function findList(node, filterFn) { while (node && !domUtils.isBody(node)) { if (filterFn(node)) { return null; } if (node.nodeType === 1 && /[ou]l/i.test(node.tagName)) { return node; } node = node.parentNode; } return null; } var keyCode = evt.keyCode || evt.which; if (keyCode === 13 && !evt.shiftKey) { //回车 var rng = me.selection.getRange(), parent = domUtils.findParent( rng.startContainer, function(node) { return domUtils.isBlockElm(node); }, true ), li = domUtils.findParentByTagName(rng.startContainer, "li", true); if (parent && parent.tagName !== "PRE" && !li) { var html = parent.innerHTML.replace( new RegExp(domUtils.fillChar, "g"), "" ); if (/^\s*1\s*\.[^\d]/.test(html)) { parent.innerHTML = html.replace(/^\s*1\s*\./, ""); rng.setStartAtLast(parent).collapse(true).select(); me.__hasEnterExecCommand = true; me.execCommand("insertorderedlist"); me.__hasEnterExecCommand = false; } } var range = me.selection.getRange(), start = findList(range.startContainer, function(node) { return node.tagName === "TABLE"; }), end = range.collapsed ? start : findList(range.endContainer, function(node) { return node.tagName === "TABLE"; }); if (start && end && start === end) { if (!range.collapsed) { start = domUtils.findParentByTagName( range.startContainer, "li", true ); end = domUtils.findParentByTagName(range.endContainer, "li", true); if (start && end && start === end) { range.deleteContents(); li = domUtils.findParentByTagName(range.startContainer, "li", true); if (li && domUtils.isEmptyBlock(li)) { pre = li.previousSibling; next = li.nextSibling; p = me.document.createElement("p"); domUtils.fillNode(me.document, p); parentList = li.parentNode; if (pre && next) { range.setStart(next, 0).collapse(true).select(true); domUtils.remove(li); } else { if ((!pre && !next) || !pre) { parentList.parentNode.insertBefore(p, parentList); } else { li.parentNode.parentNode.insertBefore( p, parentList.nextSibling ); } domUtils.remove(li); if (!parentList.firstChild) { domUtils.remove(parentList); } range.setStart(p, 0).setCursor(); } preventAndSave(); return; } } else { var tmpRange = range.cloneRange(), bk = tmpRange.collapse(false).createBookmark(); range.deleteContents(); tmpRange.moveToBookmark(bk); var li = domUtils.findParentByTagName( tmpRange.startContainer, "li", true ); clearEmptySibling(li); tmpRange.select(); preventAndSave(); return; } } li = domUtils.findParentByTagName(range.startContainer, "li", true); if (li) { if (domUtils.isEmptyBlock(li)) { bk = range.createBookmark(); var parentList = li.parentNode; if (li !== parentList.lastChild) { domUtils.breakParent(li, parentList); clearEmptySibling(li); } else { parentList.parentNode.insertBefore(li, parentList.nextSibling); if (domUtils.isEmptyNode(parentList)) { domUtils.remove(parentList); } } //嵌套不处理 if (!dtd.$list[li.parentNode.tagName]) { if (!domUtils.isBlockElm(li.firstChild)) { p = me.document.createElement("p"); li.parentNode.insertBefore(p, li); while (li.firstChild) { p.appendChild(li.firstChild); } domUtils.remove(li); } else { domUtils.remove(li, true); } } range.moveToBookmark(bk).select(); } else { var first = li.firstChild; if (!first || !domUtils.isBlockElm(first)) { var p = me.document.createElement("p"); !li.firstChild && domUtils.fillNode(me.document, p); while (li.firstChild) { p.appendChild(li.firstChild); } li.appendChild(p); first = p; } var span = me.document.createElement("span"); range.insertNode(span); domUtils.breakParent(span, li); var nextLi = span.nextSibling; first = nextLi.firstChild; if (!first) { p = me.document.createElement("p"); domUtils.fillNode(me.document, p); nextLi.appendChild(p); first = p; } if (domUtils.isEmptyNode(first)) { first.innerHTML = ""; domUtils.fillNode(me.document, first); } range.setStart(first, 0).collapse(true).shrinkBoundary().select(); domUtils.remove(span); var pre = nextLi.previousSibling; if (pre && domUtils.isEmptyBlock(pre)) { pre.innerHTML = ""; domUtils.fillNode(me.document, pre.firstChild); } } // } preventAndSave(); } } } if (keyCode === 8) { //修中ie中li下的问题 range = me.selection.getRange(); if (range.collapsed && domUtils.isStartInblock(range)) { tmpRange = range.cloneRange().trimBoundary(); li = domUtils.findParentByTagName(range.startContainer, "li", true); //要在li的最左边,才能处理 if (li && domUtils.isStartInblock(tmpRange)) { start = domUtils.findParentByTagName(range.startContainer, "p", true); if (start && start !== li.firstChild) { var parentList = domUtils.findParentByTagName(start, ["ol", "ul"]); domUtils.breakParent(start, parentList); clearEmptySibling(start); me.fireEvent("contentchange"); range.setStart(start, 0).setCursor(false, true); me.fireEvent("saveScene"); domUtils.preventDefault(evt); return; } if (li && (pre = li.previousSibling)) { if (keyCode === 46 && li.childNodes.length) { return; } //有可能上边的兄弟节点是个2级菜单,要追加到2级菜单的最后的li if (dtd.$list[pre.tagName]) { pre = pre.lastChild; } me.undoManger && me.undoManger.save(); first = li.firstChild; if (domUtils.isBlockElm(first)) { if (domUtils.isEmptyNode(first)) { // range.setEnd(pre, pre.childNodes.length).shrinkBoundary().collapse().select(true); pre.appendChild(first); range.setStart(first, 0).setCursor(false, true); //first不是唯一的节点 while (li.firstChild) { pre.appendChild(li.firstChild); } } else { span = me.document.createElement("span"); range.insertNode(span); //判断pre是否是空的节点,如果是 " + (browser.ie ? "" : " " + (browser.ie ? "" : " " + (browser.ie ? "" : " "); 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);
}
}
} else {
evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
if (!range.collapsed) {
range.deleteContents();
start = range.startContainer;
if (
start.nodeType == 1 &&
(start = start.childNodes[range.startOffset])
) {
while (start.nodeType == 1) {
if (dtd.$empty[start.tagName]) {
range.setStartBefore(start).setCursor();
if (me.undoManger) {
me.undoManger.save();
}
return false;
}
if (!start.firstChild) {
var br = range.document.createElement("br");
start.appendChild(br);
range.setStart(start, 0).setCursor();
if (me.undoManger) {
me.undoManger.save();
}
return false;
}
start = start.firstChild;
}
if (start === range.startContainer.childNodes[range.startOffset]) {
br = range.document.createElement("br");
range.insertNode(br).setCursor();
} else {
range.setStart(start, 0).setCursor();
}
} else {
br = range.document.createElement("br");
range.insertNode(br).setStartAfter(br).setCursor();
}
} else {
br = range.document.createElement("br");
range.insertNode(br);
var parent = br.parentNode;
if (parent.lastChild === br) {
br.parentNode.insertBefore(br.cloneNode(true), br);
range.setStartBefore(br);
} else {
range.setStartAfter(br);
}
range.setCursor();
}
}
}
});
};
// plugins/keystrokes.js
/* 处理特殊键的兼容性问题 */
UE.plugins["keystrokes"] = function() {
var me = this;
var collapsed = true;
me.addListener("keydown", function(type, evt) {
var keyCode = evt.keyCode || evt.which,
rng = me.selection.getRange();
//处理全选的情况
if (
!rng.collapsed &&
!(evt.ctrlKey || evt.shiftKey || evt.altKey || evt.metaKey) &&
((keyCode >= 65 && keyCode <= 90) ||
(keyCode >= 48 && keyCode <= 57) ||
(keyCode >= 96 && keyCode <= 111) ||
{
13: 1,
8: 1,
46: 1
}[keyCode])
) {
var tmpNode = rng.startContainer;
if (domUtils.isFillChar(tmpNode)) {
rng.setStartBefore(tmpNode);
}
tmpNode = rng.endContainer;
if (domUtils.isFillChar(tmpNode)) {
rng.setEndAfter(tmpNode);
}
rng.txtToElmBoundary();
//结束边界可能放到了br的前边,要把br包含进来
// x[xxx] " + (browser.ie ? "" : " " }); rng.insertNode(tmpNode).setStart(tmpNode, 0).setCursor(false, true); } } //chrome下如果删除了inline标签,浏览器会有记忆,在输入文字还是会套上刚才删除的标签,所以这里再选一次就不会了 if ( !collapsed && (rng.startContainer.nodeType == 3 || (rng.startContainer.nodeType == 1 && domUtils.isEmptyBlock(rng.startContainer))) ) { if (browser.ie) { var span = rng.document.createElement("span"); rng.insertNode(span).setStartBefore(span).collapse(true); rng.select(); domUtils.remove(span); } else { rng.select(); } } } }); }; // plugins/fiximgclick.js ///import core ///commands 修复chrome下图片不能点击的问题,出现八个角可改变大小 ///commandsName FixImgClick ///commandsTitle 修复chrome下图片不能点击的问题,出现八个角可改变大小 //修复chrome下图片不能点击的问题,出现八个角可改变大小 UE.plugins["fiximgclick"] = (function() { var elementUpdated = false; function Scale() { this.editor = null; this.resizer = null; this.cover = null; this.doc = document; this.prePos = { x: 0, y: 0 }; this.startPos = { x: 0, y: 0 }; } (function() { var rect = [ //[left, top, width, height] [0, 0, -1, -1], [0, 0, 0, -1], [0, 0, 1, -1], [0, 0, -1, 0], [0, 0, 1, 0], [0, 0, -1, 1], [0, 0, 0, 1], [0, 0, 1, 1] ]; Scale.prototype = { init: function(editor) { var me = this; me.editor = editor; me.startPos = this.prePos = { x: 0, y: 0 }; me.dragId = -1; var hands = [], cover = (me.cover = document.createElement("div")), resizer = (me.resizer = document.createElement("div")); cover.id = me.editor.ui.id + "_imagescale_cover"; cover.style.cssText = "position:absolute;display:none;z-index:" + me.editor.options.zIndex + ";filter:alpha(opacity=0); opacity:0;background:#CCC;"; domUtils.on(cover, "mousedown click", function() { me.hide(); }); for (i = 0; i < 8; i++) { hands.push( '' ); } resizer.id = me.editor.ui.id + "_imagescale"; resizer.className = "edui-editor-imagescale"; resizer.innerHTML = hands.join(""); resizer.style.cssText += ";display:none;border:1px solid #3b77ff;z-index:" + me.editor.options.zIndex + ";"; me.editor.ui.getDom().appendChild(cover); me.editor.ui.getDom().appendChild(resizer); me.initStyle(); me.initEvents(); }, initStyle: function() { utils.cssRule( "imagescale", ".edui-editor-imagescale{display:none;position:absolute;border:1px solid #38B2CE;cursor:hand;-webkit-box-sizing: content-box;-moz-box-sizing: content-box;box-sizing: content-box;}" + ".edui-editor-imagescale span{position:absolute;width:6px;height:6px;overflow:hidden;font-size:0px;display:block;background-color:#3C9DD0;}" + ".edui-editor-imagescale .edui-editor-imagescale-hand0{cursor:nw-resize;top:0;margin-top:-4px;left:0;margin-left:-4px;}" + ".edui-editor-imagescale .edui-editor-imagescale-hand1{cursor:n-resize;top:0;margin-top:-4px;left:50%;margin-left:-4px;}" + ".edui-editor-imagescale .edui-editor-imagescale-hand2{cursor:ne-resize;top:0;margin-top:-4px;left:100%;margin-left:-3px;}" + ".edui-editor-imagescale .edui-editor-imagescale-hand3{cursor:w-resize;top:50%;margin-top:-4px;left:0;margin-left:-4px;}" + ".edui-editor-imagescale .edui-editor-imagescale-hand4{cursor:e-resize;top:50%;margin-top:-4px;left:100%;margin-left:-3px;}" + ".edui-editor-imagescale .edui-editor-imagescale-hand5{cursor:sw-resize;top:100%;margin-top:-3px;left:0;margin-left:-4px;}" + ".edui-editor-imagescale .edui-editor-imagescale-hand6{cursor:s-resize;top:100%;margin-top:-3px;left:50%;margin-left:-4px;}" + ".edui-editor-imagescale .edui-editor-imagescale-hand7{cursor:se-resize;top:100%;margin-top:-3px;left:100%;margin-left:-3px;}" ); }, initEvents: function() { var me = this; me.startPos.x = me.startPos.y = 0; me.isDraging = false; }, _eventHandler: function(e) { var me = this; switch (e.type) { case "mousedown": var hand = e.target || e.srcElement, hand; if ( hand.className.indexOf("edui-editor-imagescale-hand") != -1 && me.dragId == -1 ) { me.dragId = hand.className.slice(-1); me.startPos.x = me.prePos.x = e.clientX; me.startPos.y = me.prePos.y = e.clientY; domUtils.on(me.doc, "mousemove", me.proxy(me._eventHandler, me)); } break; case "mousemove": if (me.dragId != -1) { me.updateContainerStyle(me.dragId, { x: e.clientX - me.prePos.x, y: e.clientY - me.prePos.y }); me.prePos.x = e.clientX; me.prePos.y = e.clientY; elementUpdated = true; me.updateTargetElement(); } break; case "mouseup": if (me.dragId != -1) { me.updateContainerStyle(me.dragId, { x: e.clientX - me.prePos.x, y: e.clientY - me.prePos.y }); me.updateTargetElement(); if (me.target.parentNode) me.attachTo(me.target); me.dragId = -1; } domUtils.un(me.doc, "mousemove", me.proxy(me._eventHandler, me)); //修复只是点击挪动点,但没有改变大小,不应该触发contentchange if (elementUpdated) { elementUpdated = false; me.editor.fireEvent("contentchange"); } break; default: break; } }, updateTargetElement: function() { var me = this; domUtils.setStyles(me.target, { width: me.resizer.style.width, height: me.resizer.style.height }); me.target.width = parseInt(me.resizer.style.width); me.target.height = parseInt(me.resizer.style.height); me.attachTo(me.target); }, updateContainerStyle: function(dir, offset) { var me = this, dom = me.resizer, tmp; if (rect[dir][0] != 0) { tmp = parseInt(dom.style.left) + offset.x; dom.style.left = me._validScaledProp("left", tmp) + "px"; } if (rect[dir][1] != 0) { tmp = parseInt(dom.style.top) + offset.y; dom.style.top = me._validScaledProp("top", tmp) + "px"; } if (rect[dir][2] != 0) { tmp = dom.clientWidth + rect[dir][2] * offset.x; dom.style.width = me._validScaledProp("width", tmp) + "px"; } if (rect[dir][3] != 0) { tmp = dom.clientHeight + rect[dir][3] * offset.y; dom.style.height = me._validScaledProp("height", tmp) + "px"; } }, _validScaledProp: function(prop, value) { var ele = this.resizer, wrap = document; value = isNaN(value) ? 0 : value; switch (prop) { case "left": return value < 0 ? 0 : value + ele.clientWidth > wrap.clientWidth ? wrap.clientWidth - ele.clientWidth : value; case "top": return value < 0 ? 0 : value + ele.clientHeight > wrap.clientHeight ? wrap.clientHeight - ele.clientHeight : value; case "width": return value <= 0 ? 1 : value + ele.offsetLeft > wrap.clientWidth ? wrap.clientWidth - ele.offsetLeft : value; case "height": return value <= 0 ? 1 : value + ele.offsetTop > wrap.clientHeight ? wrap.clientHeight - ele.offsetTop : value; } }, hideCover: function() { this.cover.style.display = "none"; }, showCover: function() { var me = this, editorPos = domUtils.getXY(me.editor.ui.getDom()), iframePos = domUtils.getXY(me.editor.iframe); domUtils.setStyles(me.cover, { width: me.editor.iframe.offsetWidth + "px", height: me.editor.iframe.offsetHeight + "px", top: iframePos.y - editorPos.y + "px", left: iframePos.x - editorPos.x + "px", position: "absolute", display: "" }); }, show: function(targetObj) { var me = this; me.resizer.style.display = "block"; if (targetObj) me.attachTo(targetObj); domUtils.on(this.resizer, "mousedown", me.proxy(me._eventHandler, me)); domUtils.on(me.doc, "mouseup", me.proxy(me._eventHandler, me)); me.showCover(); me.editor.fireEvent("afterscaleshow", me); me.editor.fireEvent("saveScene"); }, hide: function() { var me = this; me.hideCover(); me.resizer.style.display = "none"; domUtils.un(me.resizer, "mousedown", me.proxy(me._eventHandler, me)); domUtils.un(me.doc, "mouseup", me.proxy(me._eventHandler, me)); me.editor.fireEvent("afterscalehide", me); }, proxy: function(fn, context) { return function(e) { return fn.apply(context || this, arguments); }; }, attachTo: function(targetObj) { var me = this, target = (me.target = targetObj), resizer = this.resizer, imgPos = domUtils.getXY(target), iframePos = domUtils.getXY(me.editor.iframe), editorPos = domUtils.getXY(resizer.parentNode); domUtils.setStyles(resizer, { width: target.width + "px", height: target.height + "px", left: iframePos.x + imgPos.x - me.editor.document.body.scrollLeft - editorPos.x - parseInt(resizer.style.borderLeftWidth) + "px", top: iframePos.y + imgPos.y - me.editor.document.body.scrollTop - editorPos.y - parseInt(resizer.style.borderTopWidth) + "px" }); } }; })(); return function() { var me = this, imageScale; me.setOpt("imageScaleEnabled", true); if (!browser.ie && me.options.imageScaleEnabled) { me.addListener("click", function(type, e) { var range = me.selection.getRange(), img = range.getClosedNode(); if (img && img.tagName == "IMG" && me.body.contentEditable != "false") { if ( img.getAttribute("anchorname") || domUtils.hasClass(img, "loadingclass") || domUtils.hasClass(img, "loaderrorclass") ) { return; } if (!imageScale) { imageScale = new Scale(); imageScale.init(me); me.ui.getDom().appendChild(imageScale.resizer); var _keyDownHandler = function(e) { imageScale.hide(); if (imageScale.target) me.selection.getRange().selectNode(imageScale.target).select(); }, _mouseDownHandler = function(e) { var ele = e.target || e.srcElement; if ( ele && (ele.className === undefined || ele.className.indexOf("edui-editor-imagescale") == -1) ) { _keyDownHandler(e); } }, timer; me.addListener("afterscaleshow", function(e) { me.addListener("beforekeydown", _keyDownHandler); me.addListener("beforemousedown", _mouseDownHandler); domUtils.on(document, "keydown", _keyDownHandler); domUtils.on(document, "mousedown", _mouseDownHandler); me.selection.getNative().removeAllRanges(); }); me.addListener("afterscalehide", function(e) { me.removeListener("beforekeydown", _keyDownHandler); me.removeListener("beforemousedown", _mouseDownHandler); domUtils.un(document, "keydown", _keyDownHandler); domUtils.un(document, "mousedown", _mouseDownHandler); var target = imageScale.target; if (target.parentNode) { me.selection.getRange().selectNode(target).select(); } }); //TODO 有iframe的情况,mousedown不能往下传。。 domUtils.on(imageScale.resizer, "mousedown", function(e) { me.selection.getNative().removeAllRanges(); var ele = e.target || e.srcElement; if ( ele && ele.className.indexOf("edui-editor-imagescale-hand") == -1 ) { timer = setTimeout(function() { imageScale.hide(); if (imageScale.target) me.selection.getRange().selectNode(ele).select(); }, 200); } }); domUtils.on(imageScale.resizer, "mouseup", function(e) { var ele = e.target || e.srcElement; if ( ele && ele.className.indexOf("edui-editor-imagescale-hand") == -1 ) { clearTimeout(timer); } }); } imageScale.show(img); } else { if (imageScale && imageScale.resizer.style.display != "none") imageScale.hide(); } }); } if (browser.webkit) { me.addListener("click", function(type, e) { if (e.target.tagName == "IMG" && me.body.contentEditable != "false") { var range = new dom.Range(me.document); range.selectNode(e.target).select(); } }); } }; })(); // plugins/autolink.js ///import core ///commands 为非ie浏览器自动添加a标签 ///commandsName AutoLink ///commandsTitle 自动增加链接 /** * @description 为非ie浏览器自动添加a标签 * @author zhanyi */ UE.plugin.register( "autolink", function() { var cont = 0; return !browser.ie ? { bindEvents: { reset: function() { cont = 0; }, keydown: function(type, evt) { var me = this; var keyCode = evt.keyCode || evt.which; if (keyCode == 32 || keyCode == 13) { var sel = me.selection.getNative(), range = sel.getRangeAt(0).cloneRange(), offset, charCode; var start = range.startContainer; while (start.nodeType == 1 && range.startOffset > 0) { start = range.startContainer.childNodes[range.startOffset - 1]; if (!start) { break; } range.setStart( start, start.nodeType == 1 ? start.childNodes.length : start.nodeValue.length ); range.collapse(true); start = range.startContainer; } do { if (range.startOffset == 0) { start = range.startContainer.previousSibling; while (start && start.nodeType == 1) { start = start.lastChild; } if (!start || domUtils.isFillChar(start)) { break; } offset = start.nodeValue.length; } else { start = range.startContainer; offset = range.startOffset; } range.setStart(start, offset - 1); charCode = range.toString().charCodeAt(0); } while (charCode != 160 && charCode != 32); if ( range .toString() .replace(new RegExp(domUtils.fillChar, "g"), "") .match(/(?:https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.)/i) ) { while (range.toString().length) { if ( /^(?:https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.)/i.test( range.toString() ) ) { break; } try { range.setStart( range.startContainer, range.startOffset + 1 ); } catch (e) { //trace:2121 var start = range.startContainer; while (!(next = start.nextSibling)) { if (domUtils.isBody(start)) { return; } start = start.parentNode; } range.setStart(next, 0); } } //range的开始边界已经在a标签里的不再处理 if ( domUtils.findParentByTagName( range.startContainer, "a", true ) ) { return; } var a = me.document.createElement("a"), text = me.document.createTextNode(" "), href; me.undoManger && me.undoManger.save(); a.appendChild(range.extractContents()); a.href = a.innerHTML = a.innerHTML.replace(/<[^>]+>/g, ""); href = a .getAttribute("href") .replace(new RegExp(domUtils.fillChar, "g"), ""); href = /^(?:https?:\/\/)/gi.test(href) ? href : "http://" + href; a.setAttribute("_src", utils.html(href)); a.href = utils.html(href); range.insertNode(a); a.parentNode.insertBefore(text, a.nextSibling); range.setStart(text, 0); range.collapse(true); sel.removeAllRanges(); sel.addRange(range); me.undoManger && me.undoManger.save(); } } } } } : {}; }, function() { var keyCodes = { 37: 1, 38: 1, 39: 1, 40: 1, 13: 1, 32: 1 }; function checkIsCludeLink(node) { if (node.nodeType == 3) { return null; } if (node.nodeName == "A") { return node; } var lastChild = node.lastChild; while (lastChild) { if (lastChild.nodeName == "A") { return lastChild; } if (lastChild.nodeType == 3) { if (domUtils.isWhitespace(lastChild)) { lastChild = lastChild.previousSibling; continue; } return null; } lastChild = lastChild.lastChild; } } browser.ie && this.addListener("keyup", function(cmd, evt) { var me = this, keyCode = evt.keyCode; if (keyCodes[keyCode]) { var rng = me.selection.getRange(); var start = rng.startContainer; if (keyCode == 13) { while ( start && !domUtils.isBody(start) && !domUtils.isBlockElm(start) ) { start = start.parentNode; } if (start && !domUtils.isBody(start) && start.nodeName == "P") { var pre = start.previousSibling; if (pre && pre.nodeType == 1) { var pre = checkIsCludeLink(pre); if (pre && !pre.getAttribute("_href")) { domUtils.remove(pre, true); } } } } else if (keyCode == 32) { if (start.nodeType == 3 && /^\s$/.test(start.nodeValue)) { start = start.previousSibling; if ( start && start.nodeName == "A" && !start.getAttribute("_href") ) { domUtils.remove(start, true); } } } else { start = domUtils.findParentByTagName(start, "a", true); if (start && !start.getAttribute("_href")) { var bk = rng.createBookmark(); domUtils.remove(start, true); rng.moveToBookmark(bk).select(true); } } } }); } ); // plugins/autoheight.js ///import core ///commands 当输入内容超过编辑器高度时,编辑器自动增高 ///commandsName AutoHeight,autoHeightEnabled ///commandsTitle 自动增高 /** * @description 自动伸展 * @author zhanyi */ UE.plugins["autoheight"] = function() { var me = this; //提供开关,就算加载也可以关闭 me.autoHeightEnabled = me.options.autoHeightEnabled !== false; if (!me.autoHeightEnabled) { return; } var bakOverflow, lastHeight = 0, options = me.options, currentHeight, timer; function adjustHeight() { var me = this; clearTimeout(timer); if (isFullscreen) return; if ( !me.queryCommandState || (me.queryCommandState && me.queryCommandState("source") != 1) ) { timer = setTimeout(function() { var node = me.body.lastChild; while (node && node.nodeType != 1) { node = node.previousSibling; } if (node && node.nodeType == 1) { node.style.clear = "both"; currentHeight = Math.max( domUtils.getXY(node).y + node.offsetHeight + 25, Math.max(options.minFrameHeight, options.initialFrameHeight) ); if (currentHeight !== lastHeight) { me.iframe.parentNode.style.transition = 'width 0.3s, height 0.3s, easy-in-out'; if (currentHeight !== parseInt(me.iframe.parentNode.style.height)) { me.iframe.parentNode.style.height = currentHeight + "px"; } me.body.style.height = currentHeight + "px"; lastHeight = currentHeight; } domUtils.removeStyle(node, "clear"); } }, 50); } } var isFullscreen; me.addListener("fullscreenchanged", function(cmd, f) { isFullscreen = f; }); me.addListener("destroy", function() { domUtils.un(me.window, "scroll", fixedScrollTop); me.removeListener( "contentchange afterinserthtml keyup mouseup", adjustHeight ); }); me.enableAutoHeight = function() { var me = this; if (!me.autoHeightEnabled) { return; } var doc = me.document; me.autoHeightEnabled = true; bakOverflow = doc.body.style.overflowY; doc.body.style.overflowY = "hidden"; me.addListener("contentchange afterinserthtml keyup mouseup", adjustHeight); //ff不给事件算得不对 setTimeout(function() { adjustHeight.call(me); }, browser.gecko ? 100 : 0); me.fireEvent("autoheightchanged", me.autoHeightEnabled); }; me.disableAutoHeight = function() { me.body.style.overflowY = bakOverflow || ""; me.removeListener("contentchange", adjustHeight); me.removeListener("keyup", adjustHeight); me.removeListener("mouseup", adjustHeight); me.autoHeightEnabled = false; me.fireEvent("autoheightchanged", me.autoHeightEnabled); }; me.on("setHeight", function() { me.disableAutoHeight(); }); me.addListener("ready", function() { me.enableAutoHeight(); //trace:1764 var timer; domUtils.on( browser.ie ? me.body : me.document, browser.webkit ? "dragover" : "drop", function() { clearTimeout(timer); timer = setTimeout(function() { //trace:3681 adjustHeight.call(me); }, 100); } ); //修复内容过多时,回到顶部,顶部内容被工具栏遮挡问题 domUtils.on(me.window, "scroll", fixedScrollTop); }); var lastScrollY; function fixedScrollTop() { if (!me.window) return; if (lastScrollY === null) { lastScrollY = me.window.scrollY; } else if (me.window.scrollY == 0 && lastScrollY != 0) { me.window.scrollTo(0, 0); lastScrollY = null; } } }; // plugins/autofloat.js ///import core ///commands 悬浮工具栏 ///commandsName AutoFloat,autoFloatEnabled ///commandsTitle 悬浮工具栏 /** * modified by chengchao01 * 注意: 引入此功能后,在IE6下会将body的背景图片覆盖掉! */ UE.plugins["autofloat"] = function() { var me = this, lang = me.getLang(); me.setOpt({ topOffset: 0 }); var optsAutoFloatEnabled = me.options.autoFloatEnabled !== false, topOffset = me.options.topOffset; //如果不固定toolbar的位置,则直接退出 if (!optsAutoFloatEnabled) { return; } var uiUtils = UE.ui.uiUtils, LteIE6 = browser.ie && browser.version <= 6, quirks = browser.quirks; function checkHasUI() { if (!UE.ui) { alert(lang.autofloatMsg); return 0; } return 1; } function fixIE6FixedPos() { var docStyle = document.body.style; docStyle.backgroundImage = 'url("about:blank")'; docStyle.backgroundAttachment = "fixed"; } var bakCssText, placeHolder = document.createElement("div"), toolbarBox, orgTop, getPosition, flag = true; //ie7模式下需要偏移 function setFloating() { var toobarBoxPos = domUtils.getXY(toolbarBox), origalFloat = domUtils.getComputedStyle(toolbarBox, "position"), origalLeft = domUtils.getComputedStyle(toolbarBox, "left"); toolbarBox.style.width = toolbarBox.offsetWidth + "px"; toolbarBox.style.zIndex = me.options.zIndex * 1 + 1; toolbarBox.parentNode.insertBefore(placeHolder, toolbarBox); if (LteIE6 || (quirks && browser.ie)) { if (toolbarBox.style.position != "absolute") { toolbarBox.style.position = "absolute"; } toolbarBox.style.top = (document.body.scrollTop || document.documentElement.scrollTop) - orgTop + topOffset + "px"; } else { if (browser.ie7Compat && flag) { flag = false; toolbarBox.style.left = domUtils.getXY(toolbarBox).x - document.documentElement.getBoundingClientRect().left + 2 + "px"; } if (toolbarBox.style.position != "fixed") { toolbarBox.style.position = "fixed"; toolbarBox.style.top = topOffset + "px"; (origalFloat == "absolute" || origalFloat == "relative") && parseFloat(origalLeft) && (toolbarBox.style.left = toobarBoxPos.x + "px"); } } } function unsetFloating() { flag = true; if (placeHolder.parentNode) { placeHolder.parentNode.removeChild(placeHolder); } toolbarBox.style.cssText = bakCssText; } me.unsetFloating = unsetFloating; function updateFloating() { var rect3 = getPosition(me.container); var offset = me.options.toolbarTopOffset || 0; if (rect3.top < 0 && rect3.bottom - toolbarBox.offsetHeight > offset) { setFloating(); } else { unsetFloating(); } } var defer_updateFloating = utils.defer( function() { updateFloating(); }, browser.ie ? 200 : 100, true ); me.addListener("destroy", function() { domUtils.un(window, ["scroll", "resize"], updateFloating); me.removeListener("keydown", defer_updateFloating); }); me.addListener("ready", function() { if (checkHasUI(me)) { //加载了ui组件,但在new时,没有加载ui,导致编辑器实例上没有ui类,所以这里做判断 if (!me.ui) { return; } getPosition = uiUtils.getClientRect; toolbarBox = me.ui.getDom("toolbarbox"); orgTop = getPosition(toolbarBox).top; bakCssText = toolbarBox.style.cssText; placeHolder.style.height = toolbarBox.offsetHeight + "px"; if (LteIE6) { fixIE6FixedPos(); } domUtils.on(window, ["scroll", "resize"], updateFloating); me.addListener("keydown", defer_updateFloating); me.addListener("beforefullscreenchange", function(t, enabled) { if (enabled) { unsetFloating(); } }); me.addListener("fullscreenchanged", function(t, enabled) { if (!enabled) { updateFloating(); } }); me.addListener("sourcemodechanged", function(t, enabled) { setTimeout(function() { updateFloating(); }, 0); }); me.addListener("clearDoc", function() { setTimeout(function() { updateFloating(); }, 0); }); } }); }; // plugins/video.js /** * video插件, 为UEditor提供视频插入支持 * @file * @since 1.2.6.1 */ UE.plugins["video"] = function() { var me = this; /** * 创建插入视频字符窜 * @param url 视频地址 * @param width 视频宽度 * @param height 视频高度 * @param align 视频对齐 * @param toEmbed 是否以flash代替显示 * @param addParagraph 是否需要添加P 标签 */ function creatInsertStr(url, width, height, id, align, classname, type) { var str; switch (type) { case 'iframe': str = ' |