').html(lang.errorNotSupport)).hide();
+ return;
+ } else if (!editor.getOpt('videoActionName')) {
+ $('#filePickerReady').after($('
').html(lang.errorLoadConfig)).hide();
+ return;
+ }
+
+ uploader = _this.uploader = WebUploader.create({
+ pick: {
+ id: '#filePickerReady',
+ label: lang.uploadSelectFile
+ },
+ swf: '../../third-party/webuploader/Uploader.swf',
+ server: actionUrl,
+ fileVal: editor.getOpt('videoFieldName'),
+ duplicate: true,
+ fileSingleSizeLimit: fileMaxSize,
+ compress: false
+ });
+ uploader.addButton({
+ id: '#filePickerBlock'
+ });
+ uploader.addButton({
+ id: '#filePickerBtn',
+ label: lang.uploadAddFile
+ });
+
+ setState('pedding');
+
+ // 当有文件添加进来时执行,负责view的创建
+ function addFile(file) {
+ var $li = $('
' +
+ '' + file.name + '
' +
+ '' +
+ '
' +
+ ''),
+
+ $btns = $('
' +
+ '' + lang.uploadDelete + '' +
+ '' + lang.uploadTurnRight + '' +
+ '' + lang.uploadTurnLeft + '
').appendTo($li),
+ $prgress = $li.find('p.progress span'),
+ $wrap = $li.find('p.imgWrap'),
+ $info = $('
').hide().appendTo($li),
+
+ showError = function (code) {
+ switch (code) {
+ case 'exceed_size':
+ text = lang.errorExceedSize;
+ break;
+ case 'interrupt':
+ text = lang.errorInterrupt;
+ break;
+ case 'http':
+ text = lang.errorHttp;
+ break;
+ case 'not_allow_type':
+ text = lang.errorFileType;
+ break;
+ default:
+ text = lang.errorUploadRetry;
+ break;
+ }
+ $info.text(text).show();
+ };
+
+ if (file.getStatus() === 'invalid') {
+ showError(file.statusText);
+ } else {
+ $wrap.text(lang.uploadPreview);
+ if ('|png|jpg|jpeg|bmp|gif|'.indexOf('|'+file.ext.toLowerCase()+'|') == -1) {
+ $wrap.empty().addClass('notimage').append('
' +
+ '
' + file.name + '');
+ } else {
+ if (browser.ie && browser.version <= 7) {
+ $wrap.text(lang.uploadNoPreview);
+ } else {
+ uploader.makeThumb(file, function (error, src) {
+ if (error || !src || (/^data:/.test(src) && browser.ie && browser.version <= 7)) {
+ $wrap.text(lang.uploadNoPreview);
+ } else {
+ var $img = $('

');
+ $wrap.empty().append($img);
+ $img.on('error', function () {
+ $wrap.text(lang.uploadNoPreview);
+ });
+ }
+ }, thumbnailWidth, thumbnailHeight);
+ }
+ }
+ percentages[ file.id ] = [ file.size, 0 ];
+ file.rotation = 0;
+
+ /* 检查文件格式 */
+ if (!file.ext || acceptExtensions.indexOf(file.ext.toLowerCase()) == -1) {
+ showError('not_allow_type');
+ uploader.removeFile(file);
+ }
+ }
+
+ file.on('statuschange', function (cur, prev) {
+ if (prev === 'progress') {
+ $prgress.hide().width(0);
+ } else if (prev === 'queued') {
+ $li.off('mouseenter mouseleave');
+ $btns.remove();
+ }
+ // 成功
+ if (cur === 'error' || cur === 'invalid') {
+ showError(file.statusText);
+ percentages[ file.id ][ 1 ] = 1;
+ } else if (cur === 'interrupt') {
+ showError('interrupt');
+ } else if (cur === 'queued') {
+ percentages[ file.id ][ 1 ] = 0;
+ } else if (cur === 'progress') {
+ $info.hide();
+ $prgress.css('display', 'block');
+ } else if (cur === 'complete') {
+ }
+
+ $li.removeClass('state-' + prev).addClass('state-' + cur);
+ });
+
+ $li.on('mouseenter', function () {
+ $btns.stop().animate({height: 30});
+ });
+ $li.on('mouseleave', function () {
+ $btns.stop().animate({height: 0});
+ });
+
+ $btns.on('click', 'span', function () {
+ var index = $(this).index(),
+ deg;
+
+ switch (index) {
+ case 0:
+ uploader.removeFile(file);
+ return;
+ case 1:
+ file.rotation += 90;
+ break;
+ case 2:
+ file.rotation -= 90;
+ break;
+ }
+
+ if (supportTransition) {
+ deg = 'rotate(' + file.rotation + 'deg)';
+ $wrap.css({
+ '-webkit-transform': deg,
+ '-mos-transform': deg,
+ '-o-transform': deg,
+ 'transform': deg
+ });
+ } else {
+ $wrap.css('filter', 'progid:DXImageTransform.Microsoft.BasicImage(rotation=' + (~~((file.rotation / 90) % 4 + 4) % 4) + ')');
+ }
+
+ });
+
+ $li.insertBefore($filePickerBlock);
+ }
+
+ // 负责view的销毁
+ function removeFile(file) {
+ var $li = $('#' + file.id);
+ delete percentages[ file.id ];
+ updateTotalProgress();
+ $li.off().find('.file-panel').off().end().remove();
+ }
+
+ function updateTotalProgress() {
+ var loaded = 0,
+ total = 0,
+ spans = $progress.children(),
+ percent;
+
+ $.each(percentages, function (k, v) {
+ total += v[ 0 ];
+ loaded += v[ 0 ] * v[ 1 ];
+ });
+
+ percent = total ? loaded / total : 0;
+
+ spans.eq(0).text(Math.round(percent * 100) + '%');
+ spans.eq(1).css('width', Math.round(percent * 100) + '%');
+ updateStatus();
+ }
+
+ function setState(val, files) {
+
+ if (val != state) {
+
+ var stats = uploader.getStats();
+
+ $upload.removeClass('state-' + state);
+ $upload.addClass('state-' + val);
+
+ switch (val) {
+
+ /* 未选择文件 */
+ case 'pedding':
+ $queue.addClass('element-invisible');
+ $statusBar.addClass('element-invisible');
+ $placeHolder.removeClass('element-invisible');
+ $progress.hide(); $info.hide();
+ uploader.refresh();
+ break;
+
+ /* 可以开始上传 */
+ case 'ready':
+ $placeHolder.addClass('element-invisible');
+ $queue.removeClass('element-invisible');
+ $statusBar.removeClass('element-invisible');
+ $progress.hide(); $info.show();
+ $upload.text(lang.uploadStart);
+ uploader.refresh();
+ break;
+
+ /* 上传中 */
+ case 'uploading':
+ $progress.show(); $info.hide();
+ $upload.text(lang.uploadPause);
+ break;
+
+ /* 暂停上传 */
+ case 'paused':
+ $progress.show(); $info.hide();
+ $upload.text(lang.uploadContinue);
+ break;
+
+ case 'confirm':
+ $progress.show(); $info.hide();
+ $upload.text(lang.uploadStart);
+
+ stats = uploader.getStats();
+ if (stats.successNum && !stats.uploadFailNum) {
+ setState('finish');
+ return;
+ }
+ break;
+
+ case 'finish':
+ $progress.hide(); $info.show();
+ if (stats.uploadFailNum) {
+ $upload.text(lang.uploadRetry);
+ } else {
+ $upload.text(lang.uploadStart);
+ }
+ break;
+ }
+
+ state = val;
+ updateStatus();
+
+ }
+
+ if (!_this.getQueueCount()) {
+ $upload.addClass('disabled')
+ } else {
+ $upload.removeClass('disabled')
+ }
+
+ }
+
+ function updateStatus() {
+ var text = '', stats;
+
+ if (state === 'ready') {
+ text = lang.updateStatusReady.replace('_', fileCount).replace('_KB', WebUploader.formatSize(fileSize));
+ } else if (state === 'confirm') {
+ stats = uploader.getStats();
+ if (stats.uploadFailNum) {
+ text = lang.updateStatusConfirm.replace('_', stats.successNum).replace('_', stats.successNum);
+ }
+ } else {
+ stats = uploader.getStats();
+ text = lang.updateStatusFinish.replace('_', fileCount).
+ replace('_KB', WebUploader.formatSize(fileSize)).
+ replace('_', stats.successNum);
+
+ if (stats.uploadFailNum) {
+ text += lang.updateStatusError.replace('_', stats.uploadFailNum);
+ }
+ }
+
+ $info.html(text);
+ }
+
+ uploader.on('fileQueued', function (file) {
+ /* 选择文件后设置上传相关的url和自定义参数 */
+ editor.getOpt("videoUploadService")(_this, editor).setUploadData(file);
+
+ fileCount++;
+ fileSize += file.size;
+
+ if (fileCount === 1) {
+ $placeHolder.addClass('element-invisible');
+ $statusBar.show();
+ }
+
+ addFile(file);
+ });
+
+ uploader.on('fileDequeued', function (file) {
+ fileCount--;
+ fileSize -= file.size;
+
+ removeFile(file);
+ updateTotalProgress();
+ });
+
+ uploader.on('filesQueued', function (file) {
+ if (!uploader.isInProgress() && (state == 'pedding' || state == 'finish' || state == 'confirm' || state == 'ready')) {
+ setState('ready');
+ }
+ updateTotalProgress();
+ });
+
+ uploader.on('all', function (type, files) {
+ switch (type) {
+ case 'uploadFinished':
+ setState('confirm', files);
+ break;
+ case 'startUpload':
+ /* 设置Uploader配置项 */
+ editor.getOpt("videoUploadService")(_this, editor).setUploaderOptions(uploader);
+ setState('uploading', files);
+ break;
+ case 'stopUpload':
+ setState('paused', files);
+ break;
+ }
+ });
+
+ uploader.on('uploadBeforeSend', function (object, data, headers) {
+ //这里可以通过data对象添加POST参数
+ editor.getOpt("videoUploadService")(_this, editor).setFormData(object, data, headers);
+ });
+
+ uploader.on('uploadProgress', function (file, percentage) {
+ var $li = $('#' + file.id),
+ $percent = $li.find('.progress span');
+
+ $percent.css('width', percentage * 100 + '%');
+ percentages[ file.id ][ 1 ] = percentage;
+ updateTotalProgress();
+ });
+
+ uploader.on('uploadSuccess', function (file, res) {
+ var $file = $('#' + file.id);
+ try {
+ if (editor.getOpt("videoUploadService")(_this, editor).getResponseSuccess(res)) {
+ uploadVideoList.push({
+ 'url': res.url,
+ 'type': res.mimetype,
+ 'original':res.original || ''
+ });
+ $file.append('
');
+ } else {
+ $file.find('.error').text(res.message).show();
+ }
+ } catch (e) {
+ $file.find('.error').text(lang.errorServerUpload).show();
+ }
+ });
+
+ uploader.on('uploadError', function (file, code) {
+ });
+ uploader.on('error', function (code, file) {
+ if (code == 'Q_TYPE_DENIED' || code == 'F_EXCEED_SIZE') {
+ addFile(file);
+ }
+ });
+ uploader.on('uploadComplete', function (file, ret) {
+ });
+
+ $upload.on('click', function () {
+ if ($(this).hasClass('disabled')) {
+ return false;
+ }
+
+ if (state === 'ready') {
+ window.setTimeout(function() {
+ uploader.upload();
+ }, 500);
+ } else if (state === 'paused') {
+ window.setTimeout(function() {
+ uploader.upload();
+ }, 500);
+ } else if (state === 'uploading') {
+ uploader.stop();
+ }
+ });
+
+ $upload.addClass('state-' + state);
+ updateTotalProgress();
+ },
+ getQueueCount: function () {
+ var file, i, status, readyFile = 0, files = this.uploader.getFiles();
+ for (i = 0; file = files[i++]; ) {
+ status = file.getStatus();
+ if (status == 'queued' || status == 'uploading' || status == 'progress') readyFile++;
+ }
+ return readyFile;
+ },
+ refresh: function(){
+ this.uploader.refresh();
+ }
+ };
+
+})();
\ No newline at end of file
diff --git a/src/assets/node_modules/@notadd/neditor/dialogs/webapp/webapp.html b/src/assets/node_modules/@notadd/neditor/dialogs/webapp/webapp.html
new file mode 100644
index 0000000..1614377
--- /dev/null
+++ b/src/assets/node_modules/@notadd/neditor/dialogs/webapp/webapp.html
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/assets/node_modules/@notadd/neditor/dialogs/wordimage/fClipboard_ueditor.swf b/src/assets/node_modules/@notadd/neditor/dialogs/wordimage/fClipboard_ueditor.swf
new file mode 100644
index 0000000..ac5d27f
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/dialogs/wordimage/fClipboard_ueditor.swf differ
diff --git a/src/assets/node_modules/@notadd/neditor/dialogs/wordimage/imageUploader.swf b/src/assets/node_modules/@notadd/neditor/dialogs/wordimage/imageUploader.swf
new file mode 100644
index 0000000..2a554ca
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/dialogs/wordimage/imageUploader.swf differ
diff --git a/src/assets/node_modules/@notadd/neditor/dialogs/wordimage/tangram.js b/src/assets/node_modules/@notadd/neditor/dialogs/wordimage/tangram.js
new file mode 100644
index 0000000..2ebd8fd
--- /dev/null
+++ b/src/assets/node_modules/@notadd/neditor/dialogs/wordimage/tangram.js
@@ -0,0 +1,1495 @@
+// Copyright (c) 2009, Baidu Inc. All rights reserved.
+//
+// Licensed under the BSD License
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http:// tangram.baidu.com/license.html
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+ /**
+ * @namespace T Tangram七巧板
+ * @name T
+ * @version 1.6.0
+*/
+
+/**
+ * 声明baidu包
+ * @author: allstar, erik, meizz, berg
+ */
+var T,
+ baidu = T = baidu || {version: "1.5.0"};
+baidu.guid = "$BAIDU$";
+baidu.$$ = window[baidu.guid] = window[baidu.guid] || {global:{}};
+
+/**
+ * 使用flash资源封装的一些功能
+ * @namespace baidu.flash
+ */
+baidu.flash = baidu.flash || {};
+
+/**
+ * 操作dom的方法
+ * @namespace baidu.dom
+ */
+baidu.dom = baidu.dom || {};
+
+
+/**
+ * 从文档中获取指定的DOM元素
+ * @name baidu.dom.g
+ * @function
+ * @grammar baidu.dom.g(id)
+ * @param {string|HTMLElement} id 元素的id或DOM元素.
+ * @shortcut g,T.G
+ * @meta standard
+ * @see baidu.dom.q
+ *
+ * @return {HTMLElement|null} 获取的元素,查找不到时返回null,如果参数不合法,直接返回参数.
+ */
+baidu.dom.g = function(id) {
+ if (!id) return null;
+ if ('string' == typeof id || id instanceof String) {
+ return document.getElementById(id);
+ } else if (id.nodeName && (id.nodeType == 1 || id.nodeType == 9)) {
+ return id;
+ }
+ return null;
+};
+baidu.g = baidu.G = baidu.dom.g;
+
+
+/**
+ * 操作数组的方法
+ * @namespace baidu.array
+ */
+
+baidu.array = baidu.array || {};
+
+
+/**
+ * 遍历数组中所有元素
+ * @name baidu.array.each
+ * @function
+ * @grammar baidu.array.each(source, iterator[, thisObject])
+ * @param {Array} source 需要遍历的数组
+ * @param {Function} iterator 对每个数组元素进行调用的函数,该函数有两个参数,第一个为数组元素,第二个为数组索引值,function (item, index)。
+ * @param {Object} [thisObject] 函数调用时的this指针,如果没有此参数,默认是当前遍历的数组
+ * @remark
+ * each方法不支持对Object的遍历,对Object的遍历使用baidu.object.each 。
+ * @shortcut each
+ * @meta standard
+ *
+ * @returns {Array} 遍历的数组
+ */
+
+baidu.each = baidu.array.forEach = baidu.array.each = function (source, iterator, thisObject) {
+ var returnValue, item, i, len = source.length;
+
+ if ('function' == typeof iterator) {
+ for (i = 0; i < len; i++) {
+ item = source[i];
+ returnValue = iterator.call(thisObject || source, item, i);
+
+ if (returnValue === false) {
+ break;
+ }
+ }
+ }
+ return source;
+};
+
+/**
+ * 对语言层面的封装,包括类型判断、模块扩展、继承基类以及对象自定义事件的支持。
+ * @namespace baidu.lang
+ */
+baidu.lang = baidu.lang || {};
+
+
+/**
+ * 判断目标参数是否为function或Function实例
+ * @name baidu.lang.isFunction
+ * @function
+ * @grammar baidu.lang.isFunction(source)
+ * @param {Any} source 目标参数
+ * @version 1.2
+ * @see baidu.lang.isString,baidu.lang.isObject,baidu.lang.isNumber,baidu.lang.isArray,baidu.lang.isElement,baidu.lang.isBoolean,baidu.lang.isDate
+ * @meta standard
+ * @returns {boolean} 类型判断结果
+ */
+baidu.lang.isFunction = function (source) {
+ return '[object Function]' == Object.prototype.toString.call(source);
+};
+
+/**
+ * 判断目标参数是否string类型或String对象
+ * @name baidu.lang.isString
+ * @function
+ * @grammar baidu.lang.isString(source)
+ * @param {Any} source 目标参数
+ * @shortcut isString
+ * @meta standard
+ * @see baidu.lang.isObject,baidu.lang.isNumber,baidu.lang.isArray,baidu.lang.isElement,baidu.lang.isBoolean,baidu.lang.isDate
+ *
+ * @returns {boolean} 类型判断结果
+ */
+baidu.lang.isString = function (source) {
+ return '[object String]' == Object.prototype.toString.call(source);
+};
+baidu.isString = baidu.lang.isString;
+
+
+/**
+ * 判断浏览器类型和特性的属性
+ * @namespace baidu.browser
+ */
+baidu.browser = baidu.browser || {};
+
+
+/**
+ * 判断是否为opera浏览器
+ * @property opera opera版本号
+ * @grammar baidu.browser.opera
+ * @meta standard
+ * @see baidu.browser.ie,baidu.browser.firefox,baidu.browser.safari,baidu.browser.chrome
+ * @returns {Number} opera版本号
+ */
+
+/**
+ * opera 从10开始不是用opera后面的字符串进行版本的判断
+ * 在Browser identification最后添加Version + 数字进行版本标识
+ * opera后面的数字保持在9.80不变
+ */
+baidu.browser.opera = /opera(\/| )(\d+(\.\d+)?)(.+?(version\/(\d+(\.\d+)?)))?/i.test(navigator.userAgent) ? + ( RegExp["\x246"] || RegExp["\x242"] ) : undefined;
+
+
+/**
+ * 在目标元素的指定位置插入HTML代码
+ * @name baidu.dom.insertHTML
+ * @function
+ * @grammar baidu.dom.insertHTML(element, position, html)
+ * @param {HTMLElement|string} element 目标元素或目标元素的id
+ * @param {string} position 插入html的位置信息,取值为beforeBegin,afterBegin,beforeEnd,afterEnd
+ * @param {string} html 要插入的html
+ * @remark
+ *
+ * 对于position参数,大小写不敏感
+ * 参数的意思:beforeBegin<span>afterBegin this is span! beforeEnd</span> afterEnd
+ * 此外,如果使用本函数插入带有script标签的HTML字符串,script标签对应的脚本将不会被执行。
+ *
+ * @shortcut insertHTML
+ * @meta standard
+ *
+ * @returns {HTMLElement} 目标元素
+ */
+baidu.dom.insertHTML = function (element, position, html) {
+ element = baidu.dom.g(element);
+ var range,begin;
+ if (element.insertAdjacentHTML && !baidu.browser.opera) {
+ element.insertAdjacentHTML(position, html);
+ } else {
+ range = element.ownerDocument.createRange();
+ position = position.toUpperCase();
+ if (position == 'AFTERBEGIN' || position == 'BEFOREEND') {
+ range.selectNodeContents(element);
+ range.collapse(position == 'AFTERBEGIN');
+ } else {
+ begin = position == 'BEFOREBEGIN';
+ range[begin ? 'setStartBefore' : 'setEndAfter'](element);
+ range.collapse(begin);
+ }
+ range.insertNode(range.createContextualFragment(html));
+ }
+ return element;
+};
+
+baidu.insertHTML = baidu.dom.insertHTML;
+
+/**
+ * 操作flash对象的方法,包括创建flash对象、获取flash对象以及判断flash插件的版本号
+ * @namespace baidu.swf
+ */
+baidu.swf = baidu.swf || {};
+
+
+/**
+ * 浏览器支持的flash插件版本
+ * @property version 浏览器支持的flash插件版本
+ * @grammar baidu.swf.version
+ * @return {String} 版本号
+ * @meta standard
+ */
+baidu.swf.version = (function () {
+ var n = navigator;
+ if (n.plugins && n.mimeTypes.length) {
+ var plugin = n.plugins["Shockwave Flash"];
+ if (plugin && plugin.description) {
+ return plugin.description
+ .replace(/([a-zA-Z]|\s)+/, "")
+ .replace(/(\s)+r/, ".") + ".0";
+ }
+ } else if (window.ActiveXObject && !window.opera) {
+ for (var i = 12; i >= 2; i--) {
+ try {
+ var c = new ActiveXObject('ShockwaveFlash.ShockwaveFlash.' + i);
+ if (c) {
+ var version = c.GetVariable("$version");
+ return version.replace(/WIN/g,'').replace(/,/g,'.');
+ }
+ } catch(e) {}
+ }
+ }
+})();
+
+/**
+ * 操作字符串的方法
+ * @namespace baidu.string
+ */
+baidu.string = baidu.string || {};
+
+
+/**
+ * 对目标字符串进行html编码
+ * @name baidu.string.encodeHTML
+ * @function
+ * @grammar baidu.string.encodeHTML(source)
+ * @param {string} source 目标字符串
+ * @remark
+ * 编码字符有5个:&<>"'
+ * @shortcut encodeHTML
+ * @meta standard
+ * @see baidu.string.decodeHTML
+ *
+ * @returns {string} html编码后的字符串
+ */
+baidu.string.encodeHTML = function (source) {
+ return String(source)
+ .replace(/&/g,'&')
+ .replace(//g,'>')
+ .replace(/"/g, """)
+ .replace(/'/g, "'");
+};
+
+baidu.encodeHTML = baidu.string.encodeHTML;
+
+/**
+ * 创建flash对象的html字符串
+ * @name baidu.swf.createHTML
+ * @function
+ * @grammar baidu.swf.createHTML(options)
+ *
+ * @param {Object} options 创建flash的选项参数
+ * @param {string} options.id 要创建的flash的标识
+ * @param {string} options.url flash文件的url
+ * @param {String} options.errorMessage 未安装flash player或flash player版本号过低时的提示
+ * @param {string} options.ver 最低需要的flash player版本号
+ * @param {string} options.width flash的宽度
+ * @param {string} options.height flash的高度
+ * @param {string} options.align flash的对齐方式,允许值:middle/left/right/top/bottom
+ * @param {string} options.base 设置用于解析swf文件中的所有相对路径语句的基本目录或URL
+ * @param {string} options.bgcolor swf文件的背景色
+ * @param {string} options.salign 设置缩放的swf文件在由width和height设置定义的区域内的位置。允许值:l/r/t/b/tl/tr/bl/br
+ * @param {boolean} options.menu 是否显示右键菜单,允许值:true/false
+ * @param {boolean} options.loop 播放到最后一帧时是否重新播放,允许值: true/false
+ * @param {boolean} options.play flash是否在浏览器加载时就开始播放。允许值:true/false
+ * @param {string} options.quality 设置flash播放的画质,允许值:low/medium/high/autolow/autohigh/best
+ * @param {string} options.scale 设置flash内容如何缩放来适应设置的宽高。允许值:showall/noborder/exactfit
+ * @param {string} options.wmode 设置flash的显示模式。允许值:window/opaque/transparent
+ * @param {string} options.allowscriptaccess 设置flash与页面的通信权限。允许值:always/never/sameDomain
+ * @param {string} options.allownetworking 设置swf文件中允许使用的网络API。允许值:all/internal/none
+ * @param {boolean} options.allowfullscreen 是否允许flash全屏。允许值:true/false
+ * @param {boolean} options.seamlesstabbing 允许设置执行无缝跳格,从而使用户能跳出flash应用程序。该参数只能在安装Flash7及更高版本的Windows中使用。允许值:true/false
+ * @param {boolean} options.devicefont 设置静态文本对象是否以设备字体呈现。允许值:true/false
+ * @param {boolean} options.swliveconnect 第一次加载flash时浏览器是否应启动Java。允许值:true/false
+ * @param {Object} options.vars 要传递给flash的参数,支持JSON或string类型。
+ *
+ * @see baidu.swf.create
+ * @meta standard
+ * @returns {string} flash对象的html字符串
+ */
+baidu.swf.createHTML = function (options) {
+ options = options || {};
+ var version = baidu.swf.version,
+ needVersion = options['ver'] || '6.0.0',
+ vUnit1, vUnit2, i, k, len, item, tmpOpt = {},
+ encodeHTML = baidu.string.encodeHTML;
+ for (k in options) {
+ tmpOpt[k] = options[k];
+ }
+ options = tmpOpt;
+ if (version) {
+ version = version.split('.');
+ needVersion = needVersion.split('.');
+ for (i = 0; i < 3; i++) {
+ vUnit1 = parseInt(version[i], 10);
+ vUnit2 = parseInt(needVersion[i], 10);
+ if (vUnit2 < vUnit1) {
+ break;
+ } else if (vUnit2 > vUnit1) {
+ return '';
+ }
+ }
+ } else {
+ return '';
+ }
+
+ var vars = options['vars'],
+ objProperties = ['classid', 'codebase', 'id', 'width', 'height', 'align'];
+ options['align'] = options['align'] || 'middle';
+ options['classid'] = 'clsid:d27cdb6e-ae6d-11cf-96b8-444553540000';
+ options['codebase'] = 'http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0';
+ options['movie'] = options['url'] || '';
+ delete options['vars'];
+ delete options['url'];
+ if ('string' == typeof vars) {
+ options['flashvars'] = vars;
+ } else {
+ var fvars = [];
+ for (k in vars) {
+ item = vars[k];
+ fvars.push(k + "=" + encodeURIComponent(item));
+ }
+ options['flashvars'] = fvars.join('&');
+ }
+ var str = ['
');
+
+ return str.join('');
+};
+
+
+/**
+ * 在页面中创建一个flash对象
+ * @name baidu.swf.create
+ * @function
+ * @grammar baidu.swf.create(options[, container])
+ *
+ * @param {Object} options 创建flash的选项参数
+ * @param {string} options.id 要创建的flash的标识
+ * @param {string} options.url flash文件的url
+ * @param {String} options.errorMessage 未安装flash player或flash player版本号过低时的提示
+ * @param {string} options.ver 最低需要的flash player版本号
+ * @param {string} options.width flash的宽度
+ * @param {string} options.height flash的高度
+ * @param {string} options.align flash的对齐方式,允许值:middle/left/right/top/bottom
+ * @param {string} options.base 设置用于解析swf文件中的所有相对路径语句的基本目录或URL
+ * @param {string} options.bgcolor swf文件的背景色
+ * @param {string} options.salign 设置缩放的swf文件在由width和height设置定义的区域内的位置。允许值:l/r/t/b/tl/tr/bl/br
+ * @param {boolean} options.menu 是否显示右键菜单,允许值:true/false
+ * @param {boolean} options.loop 播放到最后一帧时是否重新播放,允许值: true/false
+ * @param {boolean} options.play flash是否在浏览器加载时就开始播放。允许值:true/false
+ * @param {string} options.quality 设置flash播放的画质,允许值:low/medium/high/autolow/autohigh/best
+ * @param {string} options.scale 设置flash内容如何缩放来适应设置的宽高。允许值:showall/noborder/exactfit
+ * @param {string} options.wmode 设置flash的显示模式。允许值:window/opaque/transparent
+ * @param {string} options.allowscriptaccess 设置flash与页面的通信权限。允许值:always/never/sameDomain
+ * @param {string} options.allownetworking 设置swf文件中允许使用的网络API。允许值:all/internal/none
+ * @param {boolean} options.allowfullscreen 是否允许flash全屏。允许值:true/false
+ * @param {boolean} options.seamlesstabbing 允许设置执行无缝跳格,从而使用户能跳出flash应用程序。该参数只能在安装Flash7及更高版本的Windows中使用。允许值:true/false
+ * @param {boolean} options.devicefont 设置静态文本对象是否以设备字体呈现。允许值:true/false
+ * @param {boolean} options.swliveconnect 第一次加载flash时浏览器是否应启动Java。允许值:true/false
+ * @param {Object} options.vars 要传递给flash的参数,支持JSON或string类型。
+ *
+ * @param {HTMLElement|string} [container] flash对象的父容器元素,不传递该参数时在当前代码位置创建flash对象。
+ * @meta standard
+ * @see baidu.swf.createHTML,baidu.swf.getMovie
+ */
+baidu.swf.create = function (options, target) {
+ options = options || {};
+ var html = baidu.swf.createHTML(options)
+ || options['errorMessage']
+ || '';
+
+ if (target && 'string' == typeof target) {
+ target = document.getElementById(target);
+ }
+ baidu.dom.insertHTML( target || document.body ,'beforeEnd',html );
+};
+/**
+ * 判断是否为ie浏览器
+ * @name baidu.browser.ie
+ * @field
+ * @grammar baidu.browser.ie
+ * @returns {Number} IE版本号
+ */
+baidu.browser.ie = baidu.ie = /msie (\d+\.\d+)/i.test(navigator.userAgent) ? (document.documentMode || + RegExp['\x241']) : undefined;
+
+/**
+ * 移除数组中的项
+ * @name baidu.array.remove
+ * @function
+ * @grammar baidu.array.remove(source, match)
+ * @param {Array} source 需要移除项的数组
+ * @param {Any} match 要移除的项
+ * @meta standard
+ * @see baidu.array.removeAt
+ *
+ * @returns {Array} 移除后的数组
+ */
+baidu.array.remove = function (source, match) {
+ var len = source.length;
+
+ while (len--) {
+ if (len in source && source[len] === match) {
+ source.splice(len, 1);
+ }
+ }
+ return source;
+};
+
+/**
+ * 判断目标参数是否Array对象
+ * @name baidu.lang.isArray
+ * @function
+ * @grammar baidu.lang.isArray(source)
+ * @param {Any} source 目标参数
+ * @meta standard
+ * @see baidu.lang.isString,baidu.lang.isObject,baidu.lang.isNumber,baidu.lang.isElement,baidu.lang.isBoolean,baidu.lang.isDate
+ *
+ * @returns {boolean} 类型判断结果
+ */
+baidu.lang.isArray = function (source) {
+ return '[object Array]' == Object.prototype.toString.call(source);
+};
+
+
+
+/**
+ * 将一个变量转换成array
+ * @name baidu.lang.toArray
+ * @function
+ * @grammar baidu.lang.toArray(source)
+ * @param {mix} source 需要转换成array的变量
+ * @version 1.3
+ * @meta standard
+ * @returns {array} 转换后的array
+ */
+baidu.lang.toArray = function (source) {
+ if (source === null || source === undefined)
+ return [];
+ if (baidu.lang.isArray(source))
+ return source;
+ if (typeof source.length !== 'number' || typeof source === 'string' || baidu.lang.isFunction(source)) {
+ return [source];
+ }
+ if (source.item) {
+ var l = source.length, array = new Array(l);
+ while (l--)
+ array[l] = source[l];
+ return array;
+ }
+
+ return [].slice.call(source);
+};
+
+/**
+ * 获得flash对象的实例
+ * @name baidu.swf.getMovie
+ * @function
+ * @grammar baidu.swf.getMovie(name)
+ * @param {string} name flash对象的名称
+ * @see baidu.swf.create
+ * @meta standard
+ * @returns {HTMLElement} flash对象的实例
+ */
+baidu.swf.getMovie = function (name) {
+ var movie = document[name], ret;
+ return baidu.browser.ie == 9 ?
+ movie && movie.length ?
+ (ret = baidu.array.remove(baidu.lang.toArray(movie),function(item){
+ return item.tagName.toLowerCase() != "embed";
+ })).length == 1 ? ret[0] : ret
+ : movie
+ : movie || window[name];
+};
+
+
+baidu.flash._Base = (function(){
+
+ var prefix = 'bd__flash__';
+
+ /**
+ * 创建一个随机的字符串
+ * @private
+ * @return {String}
+ */
+ function _createString(){
+ return prefix + Math.floor(Math.random() * 2147483648).toString(36);
+ };
+
+ /**
+ * 检查flash状态
+ * @private
+ * @param {Object} target flash对象
+ * @return {Boolean}
+ */
+ function _checkReady(target){
+ if(typeof target !== 'undefined' && typeof target.flashInit !== 'undefined' && target.flashInit()){
+ return true;
+ }else{
+ return false;
+ }
+ };
+
+ /**
+ * 调用之前进行压栈的函数
+ * @private
+ * @param {Array} callQueue 调用队列
+ * @param {Object} target flash对象
+ * @return {Null}
+ */
+ function _callFn(callQueue, target){
+ var result = null;
+
+ callQueue = callQueue.reverse();
+ baidu.each(callQueue, function(item){
+ result = target.call(item.fnName, item.params);
+ item.callBack(result);
+ });
+ };
+
+ /**
+ * 为传入的匿名函数创建函数名
+ * @private
+ * @param {String|Function} fun 传入的匿名函数或者函数名
+ * @return {String}
+ */
+ function _createFunName(fun){
+ var name = '';
+
+ if(baidu.lang.isFunction(fun)){
+ name = _createString();
+ window[name] = function(){
+ fun.apply(window, arguments);
+ };
+
+ return name;
+ }else if(baidu.lang.isString){
+ return fun;
+ }
+ };
+
+ /**
+ * 绘制flash
+ * @private
+ * @param {Object} options 创建参数
+ * @return {Object}
+ */
+ function _render(options){
+ if(!options.id){
+ options.id = _createString();
+ }
+
+ var container = options.container || '';
+ delete(options.container);
+
+ baidu.swf.create(options, container);
+
+ return baidu.swf.getMovie(options.id);
+ };
+
+ return function(options, callBack){
+ var me = this,
+ autoRender = (typeof options.autoRender !== 'undefined' ? options.autoRender : true),
+ createOptions = options.createOptions || {},
+ target = null,
+ isReady = false,
+ callQueue = [],
+ timeHandle = null,
+ callBack = callBack || [];
+
+ /**
+ * 将flash文件绘制到页面上
+ * @public
+ * @return {Null}
+ */
+ me.render = function(){
+ target = _render(createOptions);
+
+ if(callBack.length > 0){
+ baidu.each(callBack, function(funName, index){
+ callBack[index] = _createFunName(options[funName] || new Function());
+ });
+ }
+ me.call('setJSFuncName', [callBack]);
+ };
+
+ /**
+ * 返回flash状态
+ * @return {Boolean}
+ */
+ me.isReady = function(){
+ return isReady;
+ };
+
+ /**
+ * 调用flash接口的统一入口
+ * @param {String} fnName 调用的函数名
+ * @param {Array} params 传入的参数组成的数组,若不许要参数,需传入空数组
+ * @param {Function} [callBack] 异步调用后将返回值作为参数的调用回调函数,如无返回值,可以不传入此参数
+ * @return {Null}
+ */
+ me.call = function(fnName, params, callBack){
+ if(!fnName) return null;
+ callBack = callBack || new Function();
+
+ var result = null;
+
+ if(isReady){
+ result = target.call(fnName, params);
+ callBack(result);
+ }else{
+ callQueue.push({
+ fnName: fnName,
+ params: params,
+ callBack: callBack
+ });
+
+ (!timeHandle) && (timeHandle = setInterval(_check, 200));
+ }
+ };
+
+ /**
+ * 为传入的匿名函数创建函数名
+ * @public
+ * @param {String|Function} fun 传入的匿名函数或者函数名
+ * @return {String}
+ */
+ me.createFunName = function(fun){
+ return _createFunName(fun);
+ };
+
+ /**
+ * 检查flash是否ready, 并进行调用
+ * @private
+ * @return {Null}
+ */
+ function _check(){
+ if(_checkReady(target)){
+ clearInterval(timeHandle);
+ timeHandle = null;
+ _call();
+
+ isReady = true;
+ }
+ };
+
+ /**
+ * 调用之前进行压栈的函数
+ * @private
+ * @return {Null}
+ */
+ function _call(){
+ _callFn(callQueue, target);
+ callQueue = [];
+ }
+
+ autoRender && me.render();
+ };
+})();
+
+
+
+/**
+ * 创建flash based imageUploader
+ * @class
+ * @grammar baidu.flash.imageUploader(options)
+ * @param {Object} createOptions 创建flash时需要的参数,请参照baidu.swf.create文档
+ * @config {Object} vars 创建imageUploader时所需要的参数
+ * @config {Number} vars.gridWidth 每一个预览图片所占的宽度,应该为flash寛的整除
+ * @config {Number} vars.gridHeight 每一个预览图片所占的高度,应该为flash高的整除
+ * @config {Number} vars.picWidth 单张预览图片的宽度
+ * @config {Number} vars.picHeight 单张预览图片的高度
+ * @config {String} vars.uploadDataFieldName POST请求中图片数据的key,默认值'picdata'
+ * @config {String} vars.picDescFieldName POST请求中图片描述的key,默认值'picDesc'
+ * @config {Number} vars.maxSize 文件的最大体积,单位'MB'
+ * @config {Number} vars.compressSize 上传前如果图片体积超过该值,会先压缩
+ * @config {Number} vars.maxNum:32 最大上传多少个文件
+ * @config {Number} vars.compressLength 能接受的最大边长,超过该值会等比压缩
+ * @config {String} vars.url 上传的url地址
+ * @config {Number} vars.mode mode == 0时,是使用滚动条,mode == 1时,拉伸flash, 默认值为0
+ * @see baidu.swf.createHTML
+ * @param {String} backgroundUrl 背景图片路径
+ * @param {String} listBacgroundkUrl 布局控件背景
+ * @param {String} buttonUrl 按钮图片不背景
+ * @param {String|Function} selectFileCallback 选择文件的回调
+ * @param {String|Function} exceedFileCallback文件超出限制的最大体积时的回调
+ * @param {String|Function} deleteFileCallback 删除文件的回调
+ * @param {String|Function} startUploadCallback 开始上传某个文件时的回调
+ * @param {String|Function} uploadCompleteCallback 某个文件上传完成的回调
+ * @param {String|Function} uploadErrorCallback 某个文件上传失败的回调
+ * @param {String|Function} allCompleteCallback 全部上传完成时的回调
+ * @param {String|Function} changeFlashHeight 改变Flash的高度,mode==1的时候才有用
+ */
+baidu.flash.imageUploader = baidu.flash.imageUploader || function(options){
+
+ var me = this,
+ options = options || {},
+ _flash = new baidu.flash._Base(options, [
+ 'selectFileCallback',
+ 'exceedFileCallback',
+ 'deleteFileCallback',
+ 'startUploadCallback',
+ 'uploadCompleteCallback',
+ 'uploadErrorCallback',
+ 'allCompleteCallback',
+ 'changeFlashHeight'
+ ]);
+ /**
+ * 开始或回复上传图片
+ * @public
+ * @return {Null}
+ */
+ me.upload = function(){
+ _flash.call('upload');
+ };
+
+ /**
+ * 暂停上传图片
+ * @public
+ * @return {Null}
+ */
+ me.pause = function(){
+ _flash.call('pause');
+ };
+ me.addCustomizedParams = function(index,obj){
+ _flash.call('addCustomizedParams',[index,obj]);
+ }
+};
+
+/**
+ * 操作原生对象的方法
+ * @namespace baidu.object
+ */
+baidu.object = baidu.object || {};
+
+
+/**
+ * 将源对象的所有属性拷贝到目标对象中
+ * @author erik
+ * @name baidu.object.extend
+ * @function
+ * @grammar baidu.object.extend(target, source)
+ * @param {Object} target 目标对象
+ * @param {Object} source 源对象
+ * @see baidu.array.merge
+ * @remark
+ *
+1.目标对象中,与源对象key相同的成员将会被覆盖。
+2.源对象的prototype成员不会拷贝。
+
+ * @shortcut extend
+ * @meta standard
+ *
+ * @returns {Object} 目标对象
+ */
+baidu.extend =
+baidu.object.extend = function (target, source) {
+ for (var p in source) {
+ if (source.hasOwnProperty(p)) {
+ target[p] = source[p];
+ }
+ }
+
+ return target;
+};
+
+
+
+
+
+/**
+ * 创建flash based fileUploader
+ * @class
+ * @grammar baidu.flash.fileUploader(options)
+ * @param {Object} options
+ * @config {Object} createOptions 创建flash时需要的参数,请参照baidu.swf.create文档
+ * @config {String} createOptions.width
+ * @config {String} createOptions.height
+ * @config {Number} maxNum 最大可选文件数
+ * @config {Function|String} selectFile
+ * @config {Function|String} exceedMaxSize
+ * @config {Function|String} deleteFile
+ * @config {Function|String} uploadStart
+ * @config {Function|String} uploadComplete
+ * @config {Function|String} uploadError
+ * @config {Function|String} uploadProgress
+ */
+baidu.flash.fileUploader = baidu.flash.fileUploader || function(options){
+ var me = this,
+ options = options || {};
+
+ options.createOptions = baidu.extend({
+ wmod: 'transparent'
+ },options.createOptions || {});
+
+ var _flash = new baidu.flash._Base(options, [
+ 'selectFile',
+ 'exceedMaxSize',
+ 'deleteFile',
+ 'uploadStart',
+ 'uploadComplete',
+ 'uploadError',
+ 'uploadProgress'
+ ]);
+
+ _flash.call('setMaxNum', options.maxNum ? [options.maxNum] : [1]);
+
+ /**
+ * 设置当鼠标移动到flash上时,是否变成手型
+ * @public
+ * @param {Boolean} isCursor
+ * @return {Null}
+ */
+ me.setHandCursor = function(isCursor){
+ _flash.call('setHandCursor', [isCursor || false]);
+ };
+
+ /**
+ * 设置鼠标相应函数名
+ * @param {String|Function} fun
+ */
+ me.setMSFunName = function(fun){
+ _flash.call('setMSFunName',[_flash.createFunName(fun)]);
+ };
+
+ /**
+ * 执行上传操作
+ * @param {String} url 上传的url
+ * @param {String} fieldName 上传的表单字段名
+ * @param {Object} postData 键值对,上传的POST数据
+ * @param {Number|Array|null|-1} [index]上传的文件序列
+ * Int值上传该文件
+ * Array一次串行上传该序列文件
+ * -1/null上传所有文件
+ * @return {Null}
+ */
+ me.upload = function(url, fieldName, postData, index){
+
+ if(typeof url !== 'string' || typeof fieldName !== 'string') return null;
+ if(typeof index === 'undefined') index = -1;
+
+ _flash.call('upload', [url, fieldName, postData, index]);
+ };
+
+ /**
+ * 取消上传操作
+ * @public
+ * @param {Number|-1} index
+ */
+ me.cancel = function(index){
+ if(typeof index === 'undefined') index = -1;
+ _flash.call('cancel', [index]);
+ };
+
+ /**
+ * 删除文件
+ * @public
+ * @param {Number|Array} [index] 要删除的index,不传则全部删除
+ * @param {Function} callBack
+ * */
+ me.deleteFile = function(index, callBack){
+
+ var callBackAll = function(list){
+ callBack && callBack(list);
+ };
+
+ if(typeof index === 'undefined'){
+ _flash.call('deleteFilesAll', [], callBackAll);
+ return;
+ };
+
+ if(typeof index === 'Number') index = [index];
+ index.sort(function(a,b){
+ return b-a;
+ });
+ baidu.each(index, function(item){
+ _flash.call('deleteFileBy', item, callBackAll);
+ });
+ };
+
+ /**
+ * 添加文件类型,支持macType
+ * @public
+ * @param {Object|Array[Object]} type {description:String, extention:String}
+ * @return {Null};
+ */
+ me.addFileType = function(type){
+ var type = type || [[]];
+
+ if(type instanceof Array) type = [type];
+ else type = [[type]];
+ _flash.call('addFileTypes', type);
+ };
+
+ /**
+ * 设置文件类型,支持macType
+ * @public
+ * @param {Object|Array[Object]} type {description:String, extention:String}
+ * @return {Null};
+ */
+ me.setFileType = function(type){
+ var type = type || [[]];
+
+ if(type instanceof Array) type = [type];
+ else type = [[type]];
+ _flash.call('setFileTypes', type);
+ };
+
+ /**
+ * 设置可选文件的数量限制
+ * @public
+ * @param {Number} num
+ * @return {Null}
+ */
+ me.setMaxNum = function(num){
+ _flash.call('setMaxNum', [num]);
+ };
+
+ /**
+ * 设置可选文件大小限制,以兆M为单位
+ * @public
+ * @param {Number} num,0为无限制
+ * @return {Null}
+ */
+ me.setMaxSize = function(num){
+ _flash.call('setMaxSize', [num]);
+ };
+
+ /**
+ * @public
+ */
+ me.getFileAll = function(callBack){
+ _flash.call('getFileAll', [], callBack);
+ };
+
+ /**
+ * @public
+ * @param {Number} index
+ * @param {Function} [callBack]
+ */
+ me.getFileByIndex = function(index, callBack){
+ _flash.call('getFileByIndex', [], callBack);
+ };
+
+ /**
+ * @public
+ * @param {Number} index
+ * @param {function} [callBack]
+ */
+ me.getStatusByIndex = function(index, callBack){
+ _flash.call('getStatusByIndex', [], callBack);
+ };
+};
+
+/**
+ * 使用动态script标签请求服务器资源,包括由服务器端的回调和浏览器端的回调
+ * @namespace baidu.sio
+ */
+baidu.sio = baidu.sio || {};
+
+/**
+ *
+ * @param {HTMLElement} src script节点
+ * @param {String} url script节点的地址
+ * @param {String} [charset] 编码
+ */
+baidu.sio._createScriptTag = function(scr, url, charset){
+ scr.setAttribute('type', 'text/javascript');
+ charset && scr.setAttribute('charset', charset);
+ scr.setAttribute('src', url);
+ document.getElementsByTagName('head')[0].appendChild(scr);
+};
+
+/**
+ * 删除script的属性,再删除script标签,以解决修复内存泄漏的问题
+ *
+ * @param {HTMLElement} src script节点
+ */
+baidu.sio._removeScriptTag = function(scr){
+ if (scr.clearAttributes) {
+ scr.clearAttributes();
+ } else {
+ for (var attr in scr) {
+ if (scr.hasOwnProperty(attr)) {
+ delete scr[attr];
+ }
+ }
+ }
+ if(scr && scr.parentNode){
+ scr.parentNode.removeChild(scr);
+ }
+ scr = null;
+};
+
+
+/**
+ * 通过script标签加载数据,加载完成由浏览器端触发回调
+ * @name baidu.sio.callByBrowser
+ * @function
+ * @grammar baidu.sio.callByBrowser(url, opt_callback, opt_options)
+ * @param {string} url 加载数据的url
+ * @param {Function|string} opt_callback 数据加载结束时调用的函数或函数名
+ * @param {Object} opt_options 其他可选项
+ * @config {String} [charset] script的字符集
+ * @config {Integer} [timeOut] 超时时间,超过这个时间将不再响应本请求,并触发onfailure函数
+ * @config {Function} [onfailure] timeOut设定后才生效,到达超时时间时触发本函数
+ * @remark
+ * 1、与callByServer不同,callback参数只支持Function类型,不支持string。
+ * 2、如果请求了一个不存在的页面,callback函数在IE/opera下也会被调用,因此使用者需要在onsuccess函数中判断数据是否正确加载。
+ * @meta standard
+ * @see baidu.sio.callByServer
+ */
+baidu.sio.callByBrowser = function (url, opt_callback, opt_options) {
+ var scr = document.createElement("SCRIPT"),
+ scriptLoaded = 0,
+ options = opt_options || {},
+ charset = options['charset'],
+ callback = opt_callback || function(){},
+ timeOut = options['timeOut'] || 0,
+ timer;
+ scr.onload = scr.onreadystatechange = function () {
+ if (scriptLoaded) {
+ return;
+ }
+
+ var readyState = scr.readyState;
+ if ('undefined' == typeof readyState
+ || readyState == "loaded"
+ || readyState == "complete") {
+ scriptLoaded = 1;
+ try {
+ callback();
+ clearTimeout(timer);
+ } finally {
+ scr.onload = scr.onreadystatechange = null;
+ baidu.sio._removeScriptTag(scr);
+ }
+ }
+ };
+
+ if( timeOut ){
+ timer = setTimeout(function(){
+ scr.onload = scr.onreadystatechange = null;
+ baidu.sio._removeScriptTag(scr);
+ options.onfailure && options.onfailure();
+ }, timeOut);
+ }
+
+ baidu.sio._createScriptTag(scr, url, charset);
+};
+
+/**
+ * 通过script标签加载数据,加载完成由服务器端触发回调
+ * @name baidu.sio.callByServer
+ * @function
+ * @grammar baidu.sio.callByServer(url, callback[, opt_options])
+ * @param {string} url 加载数据的url.
+ * @param {Function|string} callback 服务器端调用的函数或函数名。如果没有指定本参数,将在URL中寻找options['queryField']做为callback的方法名.
+ * @param {Object} opt_options 加载数据时的选项.
+ * @config {string} [charset] script的字符集
+ * @config {string} [queryField] 服务器端callback请求字段名,默认为callback
+ * @config {Integer} [timeOut] 超时时间(单位:ms),超过这个时间将不再响应本请求,并触发onfailure函数
+ * @config {Function} [onfailure] timeOut设定后才生效,到达超时时间时触发本函数
+ * @remark
+ * 如果url中已经包含key为“options['queryField']”的query项,将会被替换成callback中参数传递或自动生成的函数名。
+ * @meta standard
+ * @see baidu.sio.callByBrowser
+ */
+baidu.sio.callByServer = /**@function*/function(url, callback, opt_options) {
+ var scr = document.createElement('SCRIPT'),
+ prefix = 'bd__cbs__',
+ callbackName,
+ callbackImpl,
+ options = opt_options || {},
+ charset = options['charset'],
+ queryField = options['queryField'] || 'callback',
+ timeOut = options['timeOut'] || 0,
+ timer,
+ reg = new RegExp('(\\?|&)' + queryField + '=([^&]*)'),
+ matches;
+
+ if (baidu.lang.isFunction(callback)) {
+ callbackName = prefix + Math.floor(Math.random() * 2147483648).toString(36);
+ window[callbackName] = getCallBack(0);
+ } else if(baidu.lang.isString(callback)){
+ callbackName = callback;
+ } else {
+ if (matches = reg.exec(url)) {
+ callbackName = matches[2];
+ }
+ }
+
+ if( timeOut ){
+ timer = setTimeout(getCallBack(1), timeOut);
+ }
+ url = url.replace(reg, '\x241' + queryField + '=' + callbackName);
+
+ if (url.search(reg) < 0) {
+ url += (url.indexOf('?') < 0 ? '?' : '&') + queryField + '=' + callbackName;
+ }
+ baidu.sio._createScriptTag(scr, url, charset);
+
+ /*
+ * 返回一个函数,用于立即(挂在window上)或者超时(挂在setTimeout中)时执行
+ */
+ function getCallBack(onTimeOut){
+ /*global callbackName, callback, scr, options;*/
+ return function(){
+ try {
+ if( onTimeOut ){
+ options.onfailure && options.onfailure();
+ }else{
+ callback.apply(window, arguments);
+ clearTimeout(timer);
+ }
+ window[callbackName] = null;
+ delete window[callbackName];
+ } catch (exception) {
+ } finally {
+ baidu.sio._removeScriptTag(scr);
+ }
+ }
+ }
+};
+
+/**
+ * 通过请求一个图片的方式令服务器存储一条日志
+ * @function
+ * @grammar baidu.sio.log(url)
+ * @param {string} url 要发送的地址.
+ * @author: int08h,leeight
+ */
+baidu.sio.log = function(url) {
+ var img = new Image(),
+ key = 'tangram_sio_log_' + Math.floor(Math.random() *
+ 2147483648).toString(36);
+ window[key] = img;
+
+ img.onload = img.onerror = img.onabort = function() {
+ img.onload = img.onerror = img.onabort = null;
+
+ window[key] = null;
+ img = null;
+ };
+ img.src = url;
+};
+
+
+
+/*
+ * Tangram
+ * Copyright 2009 Baidu Inc. All rights reserved.
+ *
+ * path: baidu/json.js
+ * author: erik
+ * version: 1.1.0
+ * date: 2009/12/02
+ */
+
+
+/**
+ * 操作json对象的方法
+ * @namespace baidu.json
+ */
+baidu.json = baidu.json || {};
+/*
+ * Tangram
+ * Copyright 2009 Baidu Inc. All rights reserved.
+ *
+ * path: baidu/json/parse.js
+ * author: erik, berg
+ * version: 1.2
+ * date: 2009/11/23
+ */
+
+
+
+/**
+ * 将字符串解析成json对象。注:不会自动祛除空格
+ * @name baidu.json.parse
+ * @function
+ * @grammar baidu.json.parse(data)
+ * @param {string} source 需要解析的字符串
+ * @remark
+ * 该方法的实现与ecma-262第五版中规定的JSON.parse不同,暂时只支持传入一个参数。后续会进行功能丰富。
+ * @meta standard
+ * @see baidu.json.stringify,baidu.json.decode
+ *
+ * @returns {JSON} 解析结果json对象
+ */
+baidu.json.parse = function (data) {
+ //2010/12/09:更新至不使用原生parse,不检测用户输入是否正确
+ return (new Function("return (" + data + ")"))();
+};
+/*
+ * Tangram
+ * Copyright 2009 Baidu Inc. All rights reserved.
+ *
+ * path: baidu/json/decode.js
+ * author: erik, cat
+ * version: 1.3.4
+ * date: 2010/12/23
+ */
+
+
+
+/**
+ * 将字符串解析成json对象,为过时接口,今后会被baidu.json.parse代替
+ * @name baidu.json.decode
+ * @function
+ * @grammar baidu.json.decode(source)
+ * @param {string} source 需要解析的字符串
+ * @meta out
+ * @see baidu.json.encode,baidu.json.parse
+ *
+ * @returns {JSON} 解析结果json对象
+ */
+baidu.json.decode = baidu.json.parse;
+/*
+ * Tangram
+ * Copyright 2009 Baidu Inc. All rights reserved.
+ *
+ * path: baidu/json/stringify.js
+ * author: erik
+ * version: 1.1.0
+ * date: 2010/01/11
+ */
+
+
+
+/**
+ * 将json对象序列化
+ * @name baidu.json.stringify
+ * @function
+ * @grammar baidu.json.stringify(value)
+ * @param {JSON} value 需要序列化的json对象
+ * @remark
+ * 该方法的实现与ecma-262第五版中规定的JSON.stringify不同,暂时只支持传入一个参数。后续会进行功能丰富。
+ * @meta standard
+ * @see baidu.json.parse,baidu.json.encode
+ *
+ * @returns {string} 序列化后的字符串
+ */
+baidu.json.stringify = (function () {
+ /**
+ * 字符串处理时需要转义的字符表
+ * @private
+ */
+ var escapeMap = {
+ "\b": '\\b',
+ "\t": '\\t',
+ "\n": '\\n',
+ "\f": '\\f',
+ "\r": '\\r',
+ '"' : '\\"',
+ "\\": '\\\\'
+ };
+
+ /**
+ * 字符串序列化
+ * @private
+ */
+ function encodeString(source) {
+ if (/["\\\x00-\x1f]/.test(source)) {
+ source = source.replace(
+ /["\\\x00-\x1f]/g,
+ function (match) {
+ var c = escapeMap[match];
+ if (c) {
+ return c;
+ }
+ c = match.charCodeAt();
+ return "\\u00"
+ + Math.floor(c / 16).toString(16)
+ + (c % 16).toString(16);
+ });
+ }
+ return '"' + source + '"';
+ }
+
+ /**
+ * 数组序列化
+ * @private
+ */
+ function encodeArray(source) {
+ var result = ["["],
+ l = source.length,
+ preComma, i, item;
+
+ for (i = 0; i < l; i++) {
+ item = source[i];
+
+ switch (typeof item) {
+ case "undefined":
+ case "function":
+ case "unknown":
+ break;
+ default:
+ if(preComma) {
+ result.push(',');
+ }
+ result.push(baidu.json.stringify(item));
+ preComma = 1;
+ }
+ }
+ result.push("]");
+ return result.join("");
+ }
+
+ /**
+ * 处理日期序列化时的补零
+ * @private
+ */
+ function pad(source) {
+ return source < 10 ? '0' + source : source;
+ }
+
+ /**
+ * 日期序列化
+ * @private
+ */
+ function encodeDate(source){
+ return '"' + source.getFullYear() + "-"
+ + pad(source.getMonth() + 1) + "-"
+ + pad(source.getDate()) + "T"
+ + pad(source.getHours()) + ":"
+ + pad(source.getMinutes()) + ":"
+ + pad(source.getSeconds()) + '"';
+ }
+
+ return function (value) {
+ switch (typeof value) {
+ case 'undefined':
+ return 'undefined';
+
+ case 'number':
+ return isFinite(value) ? String(value) : "null";
+
+ case 'string':
+ return encodeString(value);
+
+ case 'boolean':
+ return String(value);
+
+ default:
+ if (value === null) {
+ return 'null';
+ } else if (value instanceof Array) {
+ return encodeArray(value);
+ } else if (value instanceof Date) {
+ return encodeDate(value);
+ } else {
+ var result = ['{'],
+ encode = baidu.json.stringify,
+ preComma,
+ item;
+
+ for (var key in value) {
+ if (Object.prototype.hasOwnProperty.call(value, key)) {
+ item = value[key];
+ switch (typeof item) {
+ case 'undefined':
+ case 'unknown':
+ case 'function':
+ break;
+ default:
+ if (preComma) {
+ result.push(',');
+ }
+ preComma = 1;
+ result.push(encode(key) + ':' + encode(item));
+ }
+ }
+ }
+ result.push('}');
+ return result.join('');
+ }
+ }
+ };
+})();
+/*
+ * Tangram
+ * Copyright 2009 Baidu Inc. All rights reserved.
+ *
+ * path: baidu/json/encode.js
+ * author: erik, cat
+ * version: 1.3.4
+ * date: 2010/12/23
+ */
+
+
+
+/**
+ * 将json对象序列化,为过时接口,今后会被baidu.json.stringify代替
+ * @name baidu.json.encode
+ * @function
+ * @grammar baidu.json.encode(value)
+ * @param {JSON} value 需要序列化的json对象
+ * @meta out
+ * @see baidu.json.decode,baidu.json.stringify
+ *
+ * @returns {string} 序列化后的字符串
+ */
+baidu.json.encode = baidu.json.stringify;
diff --git a/src/assets/node_modules/@notadd/neditor/dialogs/wordimage/wordimage.html b/src/assets/node_modules/@notadd/neditor/dialogs/wordimage/wordimage.html
new file mode 100644
index 0000000..670db71
--- /dev/null
+++ b/src/assets/node_modules/@notadd/neditor/dialogs/wordimage/wordimage.html
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/assets/node_modules/@notadd/neditor/dialogs/wordimage/wordimage.js b/src/assets/node_modules/@notadd/neditor/dialogs/wordimage/wordimage.js
new file mode 100644
index 0000000..b3a075d
--- /dev/null
+++ b/src/assets/node_modules/@notadd/neditor/dialogs/wordimage/wordimage.js
@@ -0,0 +1,157 @@
+/**
+ * Created by JetBrains PhpStorm.
+ * User: taoqili
+ * Date: 12-1-30
+ * Time: 下午12:50
+ * To change this template use File | Settings | File Templates.
+ */
+
+
+
+var wordImage = {};
+//(function(){
+var g = baidu.g,
+ flashObj,flashContainer;
+
+wordImage.init = function(opt, callbacks) {
+ showLocalPath("localPath");
+ //createCopyButton("clipboard","localPath");
+ createFlashUploader(opt, callbacks);
+ addUploadListener();
+ addOkListener();
+};
+
+function hideFlash(){
+ flashObj = null;
+ flashContainer.innerHTML = "";
+}
+function addOkListener() {
+ dialog.onok = function() {
+ if (!imageUrls.length) return;
+ var urlPrefix = editor.getOpt('imageUrlPrefix'),
+ images = domUtils.getElementsByTagName(editor.document,"img");
+ editor.fireEvent('saveScene');
+ for (var i = 0,img; img = images[i++];) {
+ var src = img.getAttribute("word_img");
+ if (!src) continue;
+ for (var j = 0,url; url = imageUrls[j++];) {
+ if (src.indexOf(url.original.replace(" ","")) != -1) {
+ img.src = urlPrefix + url.url;
+ img.setAttribute("_src", urlPrefix + url.url); //同时修改"_src"属性
+ img.setAttribute("title",url.title);
+ domUtils.removeAttributes(img, ["word_img","style","width","height"]);
+ editor.fireEvent("selectionchange");
+ break;
+ }
+ }
+ }
+ editor.fireEvent('saveScene');
+ hideFlash();
+ };
+ dialog.oncancel = function(){
+ hideFlash();
+ }
+}
+
+/**
+ * 绑定开始上传事件
+ */
+function addUploadListener() {
+ g("upload").onclick = function () {
+ flashObj.upload();
+ this.style.display = "none";
+ };
+}
+
+function showLocalPath(id) {
+ //单张编辑
+ var img = editor.selection.getRange().getClosedNode();
+ var images = editor.execCommand('wordimage');
+ if(images.length==1 || img && img.tagName == 'IMG'){
+ g(id).value = images[0];
+ return;
+ }
+ var path = images[0];
+ var leftSlashIndex = path.lastIndexOf("/")||0, //不同版本的doc和浏览器都可能影响到这个符号,故直接判断两种
+ rightSlashIndex = path.lastIndexOf("\\")||0,
+ separater = leftSlashIndex > rightSlashIndex ? "/":"\\" ;
+
+ path = path.substring(0, path.lastIndexOf(separater)+1);
+ g(id).value = path;
+}
+
+function createFlashUploader(opt, callbacks) {
+ //由于lang.flashI18n是静态属性,不可以直接进行修改,否则会影响到后续内容
+ var i18n = utils.extend({},lang.flashI18n);
+ //处理图片资源地址的编码,补全等问题
+ for(var i in i18n){
+ if(!(i in {"lang":1,"uploadingTF":1,"imageTF":1,"textEncoding":1}) && i18n[i]){
+ i18n[i] = encodeURIComponent(editor.options.langPath + editor.options.lang + "/images/" + i18n[i]);
+ }
+ }
+ opt = utils.extend(opt,i18n,false);
+ var option = {
+ createOptions:{
+ id:'flash',
+ url:opt.flashUrl,
+ width:opt.width,
+ height:opt.height,
+ errorMessage:lang.flashError,
+ wmode:browser.safari ? 'transparent' : 'window',
+ ver:'10.0.0',
+ vars:opt,
+ container:opt.container
+ }
+ };
+
+ option = extendProperty(callbacks, option);
+ flashObj = new baidu.flash.imageUploader(option);
+ flashContainer = $G(opt.container);
+}
+
+function extendProperty(fromObj, toObj) {
+ for (var i in fromObj) {
+ if (!toObj[i]) {
+ toObj[i] = fromObj[i];
+ }
+ }
+ return toObj;
+}
+
+//})();
+
+function getPasteData(id) {
+ baidu.g("msg").innerHTML = lang.copySuccess + "";
+ setTimeout(function() {
+ baidu.g("msg").innerHTML = "";
+ }, 5000);
+ return baidu.g(id).value;
+}
+
+function createCopyButton(id, dataFrom) {
+ baidu.swf.create({
+ id:"copyFlash",
+ url:"fClipboard_neditor.swf",
+ width:"58",
+ height:"25",
+ errorMessage:"",
+ bgColor:"#CBCBCB",
+ wmode:"transparent",
+ ver:"10.0.0",
+ vars:{
+ tid:dataFrom
+ }
+ }, id
+ );
+
+ var clipboard = baidu.swf.getMovie("copyFlash");
+ var clipinterval = setInterval(function() {
+ if (clipboard && clipboard.flashInit) {
+ clearInterval(clipinterval);
+ clipboard.setHandCursor(true);
+ clipboard.setContentFuncName("getPasteData");
+ //clipboard.setMEFuncName("mouseEventHandler");
+ }
+ }, 500);
+}
+createCopyButton("clipboard", "localPath");
\ No newline at end of file
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/en/en.js b/src/assets/node_modules/@notadd/neditor/i18n/en/en.js
new file mode 100644
index 0000000..0ccead1
--- /dev/null
+++ b/src/assets/node_modules/@notadd/neditor/i18n/en/en.js
@@ -0,0 +1,684 @@
+/**
+ * Created with JetBrains PhpStorm.
+ * User: taoqili
+ * Date: 12-6-12
+ * Time: 下午6:57
+ * To change this template use File | Settings | File Templates.
+ */
+UE.I18N['en'] = {
+ 'labelMap':{
+ 'anchor':'Anchor', 'undo':'Undo', 'redo':'Redo', 'bold':'Bold', 'indent':'Indent', 'snapscreen':'SnapScreen',
+ 'italic':'Italic', 'underline':'Underline', 'strikethrough':'Strikethrough', 'subscript':'SubScript','fontborder':'text border',
+ 'superscript':'SuperScript', 'formatmatch':'Format Match', 'source':'Source', 'blockquote':'BlockQuote',
+ 'pasteplain':'PastePlain', 'selectall':'SelectAll', 'print':'Print', 'preview':'Preview',
+ 'horizontal':'Horizontal', 'removeformat':'RemoveFormat', 'time':'Time', 'date':'Date',
+ 'unlink':'Unlink', 'insertrow':'InsertRow', 'insertcol':'InsertCol', 'mergeright':'MergeRight', 'mergedown':'MergeDown',
+ 'deleterow':'DeleteRow', 'deletecol':'DeleteCol', 'splittorows':'SplitToRows','insertcode':'insert code',
+ 'splittocols':'SplitToCols', 'splittocells':'SplitToCells','deletecaption':'DeleteCaption','inserttitle':'InsertTitle',
+ 'mergecells':'MergeCells', 'deletetable':'DeleteTable', 'cleardoc':'Clear', 'insertparagraphbeforetable':"InsertParagraphBeforeTable",
+ 'fontfamily':'FontFamily', 'fontsize':'FontSize', 'paragraph':'Paragraph','simpleupload':'Single Image','insertimage':'Multi Image','edittable':'Edit Table', 'edittd':'Edit Td','link':'Link',
+ 'emotion':'Emotion', 'spechars':'Spechars', 'searchreplace':'SearchReplace', 'map':'BaiduMap', 'gmap':'GoogleMap',
+ 'insertvideo':'Video', 'help':'Help', 'justifyleft':'JustifyLeft', 'justifyright':'JustifyRight', 'justifycenter':'JustifyCenter',
+ 'justifyjustify':'Justify', 'forecolor':'FontColor', 'backcolor':'BackColor', 'insertorderedlist':'OL',
+ 'insertunorderedlist':'UL', 'fullscreen':'FullScreen', 'directionalityltr':'EnterFromLeft', 'directionalityrtl':'EnterFromRight',
+ 'rowspacingtop':'RowSpacingTop', 'rowspacingbottom':'RowSpacingBottom', 'pagebreak':'PageBreak', 'insertframe':'Iframe', 'imagenone':'Default',
+ 'imageleft':'ImageLeft', 'imageright':'ImageRight', 'attachment':'Attachment', 'imagecenter':'ImageCenter', 'wordimage':'WordImage',
+ 'lineheight':'LineHeight','edittip':'EditTip','customstyle':'CustomStyle', 'scrawl':'Scrawl', 'autotypeset':'AutoTypeset',
+ 'webapp':'WebAPP', 'touppercase':'UpperCase', 'tolowercase':'LowerCase','template':'Template','background':'Background','inserttable':'InsertTable',
+ 'music':'Music', 'charts': 'charts','drafts': 'Load from Drafts'
+ },
+ 'insertorderedlist':{
+ 'num':'1,2,3...',
+ 'num1':'1),2),3)...',
+ 'num2':'(1),(2),(3)...',
+ 'cn':'一,二,三....',
+ 'cn1':'一),二),三)....',
+ 'cn2':'(一),(二),(三)....',
+ 'decimal':'1,2,3...',
+ 'lower-alpha':'a,b,c...',
+ 'lower-roman':'i,ii,iii...',
+ 'upper-alpha':'A,B,C...',
+ 'upper-roman':'I,II,III...'
+ },
+ 'insertunorderedlist':{
+ 'circle':'○ Circle',
+ 'disc':'● Circle dot',
+ 'square':'■ Rectangle ',
+ 'dash' :'- Dash',
+ 'dot' : '。dot'
+ },
+ 'paragraph':{'p':'Paragraph', 'h1':'Title 1', 'h2':'Title 2', 'h3':'Title 3', 'h4':'Title 4', 'h5':'Title 5', 'h6':'Title 6'},
+ 'fontfamily':{
+ 'songti':'Sim Sun',
+ 'kaiti':'Sim Kai',
+ 'heiti':'Sim Hei',
+ 'lishu':'Sim Li',
+ 'yahei': 'Microsoft YaHei',
+ 'andaleMono':'Andale Mono',
+ 'arial': 'Arial',
+ 'arialBlack':'Arial Black',
+ 'comicSansMs':'Comic Sans MS',
+ 'impact':'Impact',
+ 'timesNewRoman':'Times New Roman'
+ },
+ 'customstyle':{
+ 'tc':'Title center',
+ 'tl':'Title left',
+ 'im':'Important',
+ 'hi':'Highlight'
+ },
+ 'autoupload': {
+ 'exceedSizeError': 'File Size Exceed',
+ 'exceedTypeError': 'File Type Not Allow',
+ 'jsonEncodeError': 'Server Return Format Error',
+ 'loading':"loading...",
+ 'loadError':"load error",
+ 'errorLoadConfig': 'Server config not loaded, upload can not work.',
+ },
+ 'simpleupload':{
+ 'exceedSizeError': 'File Size Exceed',
+ 'exceedTypeError': 'File Type Not Allow',
+ 'jsonEncodeError': 'Server Return Format Error',
+ 'loading':"loading...",
+ 'loadError':"load error",
+ 'errorLoadConfig': 'Server config not loaded, upload can not work.',
+ },
+ 'elementPathTip':"Path",
+ 'wordCountTip':"Word Count",
+ 'wordCountMsg':'{#count} characters entered,{#leave} left. ',
+ 'wordOverFlowMsg':'
The number of characters has exceeded allowable maximum values, the server may refuse to save!',
+ 'ok':"OK",
+ 'cancel':"Cancel",
+ 'closeDialog':"closeDialog",
+ 'tableDrag':"You must import the file uiUtils.js before drag! ",
+ 'autofloatMsg':"The plugin AutoFloat depends on EditorUI!",
+ 'loadconfigError': 'Get server config error.',
+ 'loadconfigFormatError': 'Server config format error.',
+ 'loadconfigHttpError': 'Get server config http error.',
+ 'snapScreen_plugin':{
+ 'browserMsg':"Only IE supported!",
+ 'callBackErrorMsg':"The callback data is wrong,please check the config!",
+ 'uploadErrorMsg':"Upload error,please check your server environment! "
+ },
+ 'insertcode':{
+ 'as3':'ActionScript 3',
+ 'bash':'Bash/Shell',
+ 'cpp':'C/C++',
+ 'css':'CSS',
+ 'cf':'ColdFusion',
+ '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':'Visual Basic',
+ 'xml':'XML'
+ },
+ 'confirmClear':"Do you confirm to clear the Document?",
+ 'contextMenu':{
+ 'delete':"Delete",
+ 'selectall':"Select all",
+ 'deletecode':"Delete Code",
+ 'cleardoc':"Clear Document",
+ 'confirmclear':"Do you confirm to clear the Document?",
+ 'unlink':"Unlink",
+ 'paragraph':"Paragraph",
+ 'edittable':"Table property",
+ 'aligncell':'Align cell',
+ 'aligntable':'Table alignment',
+ 'tableleft':'Left float',
+ 'tablecenter':'Center',
+ 'tableright':'Right float',
+ 'aligntd':'Cell alignment',
+ 'edittd':"Cell property",
+ 'setbordervisible':'set table edge visible',
+ 'table':"Table",
+ 'justifyleft':'Justify Left',
+ 'justifyright':'Justify Right',
+ 'justifycenter':'Justify Center',
+ 'justifyjustify':'Default',
+ 'deletetable':"Delete table",
+ 'insertparagraphbefore':"InsertedBeforeLine",
+ 'insertparagraphafter':'InsertedAfterLine',
+ 'inserttable':'Insert table',
+ 'insertcaption':'Insert caption',
+ 'deletecaption':'Delete Caption',
+ 'inserttitle':'Insert Title',
+ 'deletetitle':'Delete Title',
+ 'inserttitlecol':'Insert Title Col',
+ 'deletetitlecol':'Delete Title Col',
+ 'averageDiseRow':'AverageDise Row',
+ 'averageDisCol':'AverageDis Col',
+ 'deleterow':"Delete row",
+ 'deletecol':"Delete col",
+ 'insertrow':"Insert row",
+ 'insertcol':"Insert col",
+ 'insertrownext':'Insert Row Next',
+ 'insertcolnext':'Insert Col Next',
+ 'mergeright':"Merge right",
+ 'mergeleft':"Merge left",
+ 'mergedown':"Merge down",
+ 'mergecells':"Merge cells",
+ 'splittocells':"Split to cells",
+ 'splittocols':"Split to Cols",
+ 'splittorows':"Split to Rows",
+ 'tablesort':'Table sorting',
+ 'enablesort':'Sorting Enable',
+ 'disablesort':'Sorting Disable',
+ 'reversecurrent':'Reverse current',
+ 'orderbyasc':'Order By ASCII',
+ 'reversebyasc':'Reverse By ASCII',
+ 'orderbynum':'Order By Num',
+ 'reversebynum':'Reverse By Num',
+ 'borderbk':'Border shading',
+ 'setcolor':'interlaced color',
+ 'unsetcolor':'Cancel interlacedcolor',
+ 'setbackground':'Background interlaced',
+ 'unsetbackground':'Cancel Bk interlaced',
+ 'redandblue':'Blue and red',
+ 'threecolorgradient':'Three-color gradient',
+ 'copy':"Copy(Ctrl + c)",
+ 'copymsg':"Browser does not support. Please use 'Ctrl + c' instead!",
+ 'paste':"Paste(Ctrl + v)",
+ 'pastemsg':"Browser does not support. Please use 'Ctrl + v' instead!"
+ },
+ 'copymsg': "Browser does not support. Please use 'Ctrl + c' instead!",
+ 'pastemsg': "Browser does not support. Please use 'Ctrl + v' instead!",
+ 'anthorMsg':"Link",
+ 'clearColor':'Clear',
+ 'standardColor':'Standard color',
+ 'themeColor':'Theme color',
+ 'property':'Property',
+ 'default':'Default',
+ 'modify':'Modify',
+ 'justifyleft':'Justify Left',
+ 'justifyright':'Justify Right',
+ 'justifycenter':'Justify Center',
+ 'justify':'Default',
+ 'clear':'Clear',
+ 'anchorMsg':'Anchor',
+ 'delete':'Delete',
+ 'clickToUpload':"Click to upload",
+ 'unset':'Language hasn\'t been set!',
+ 't_row':'row',
+ 't_col':'col',
+ 'pasteOpt':'Paste Option',
+ 'pasteSourceFormat':"Keep Source Formatting",
+ 'tagFormat':'Keep tag',
+ 'pasteTextFormat':'Keep Text only',
+ 'more':'More',
+ 'autoTypeSet':{
+ 'mergeLine':"Merge empty line",
+ 'delLine':"Del empty line",
+ 'removeFormat':"Remove format",
+ 'indent':"Indent",
+ 'alignment':"Alignment",
+ 'imageFloat':"Image float",
+ 'removeFontsize':"Remove font size",
+ 'removeFontFamily':"Remove fontFamily",
+ 'removeHtml':"Remove redundant HTML code",
+ 'pasteFilter':"Paste filter",
+ 'run':"Done",
+ 'symbol':'Symbol Conversion',
+ 'bdc2sb':'Full-width to Half-width',
+ 'tobdc':'Half-width to Full-width'
+ },
+
+ 'background':{
+ 'static':{
+ 'lang_background_normal':'Normal',
+ 'lang_background_local':'Online',
+ 'lang_background_set':'Background Set',
+ 'lang_background_none':'No Background',
+ 'lang_background_colored':'Colored Background',
+ 'lang_background_color':'Color Set',
+ 'lang_background_netimg':'Net-Image',
+ 'lang_background_align':'Align Type',
+ 'lang_background_position':'Position',
+ 'repeatType':{'options':["Center", "Repeat-x", "Repeat-y", "Tile","Custom"]}
+ },
+ 'noUploadImage':"No pictures has been uploaded!",
+ 'toggleSelect':'Change the active state by click!\n Image Size: '
+ },
+ //===============dialog i18N=======================
+ 'insertimage':{
+ 'static':{
+ 'lang_tab_remote':"Insert",
+ 'lang_tab_upload':"Local",
+ 'lang_tab_online':"Manager",
+ 'lang_tab_search':"Search",
+ 'lang_input_url':"Address:",
+ 'lang_input_size':"Size:",
+ 'lang_input_width':"Width",
+ 'lang_input_height':"Height",
+ 'lang_input_border':"Border:",
+ 'lang_input_vhspace':"Margins:",
+ 'lang_input_title':"Title:",
+ 'lang_input_align':'Image Float Style:',
+ 'lang_imgLoading':"Loading...",
+ 'lang_start_upload':"Start Upload",
+ 'lock':{'title':"Lock rate"},
+ 'searchType':{'title':"ImageType", 'options':["All", "Avatar", "Facial", "Cartoon", "StickFigure", "GIF", "StaticImage"]},
+ 'searchTxt':{'value':"Enter the search keyword!"},
+ 'searchBtn':{'value':"Search"},
+ 'searchReset':{'value':"Clear"},
+ 'noneAlign':{'title':'None Float'},
+ 'leftAlign':{'title':'Left Float'},
+ 'rightAlign':{'title':'Right Float'},
+ 'centerAlign':{'title':'Center In A Line'}
+ },
+ 'uploadSelectFile':'Select File',
+ 'uploadAddFile':'Add File',
+ 'uploadStart':'Start Upload',
+ 'uploadPause':'Pause Upload',
+ 'uploadContinue':'Continue Upload',
+ 'uploadRetry':'Retry Upload',
+ 'uploadDelete':'Delete',
+ 'uploadTurnLeft':'Turn Left',
+ 'uploadTurnRight':'Turn Right',
+ 'uploadPreview':'Doing Preview',
+ 'uploadNoPreview':'Can Not Preview',
+ 'updateStatusReady': 'Selected _ pictures, total _KB.',
+ 'updateStatusConfirm': '_ uploaded successfully and _ upload failed',
+ 'updateStatusFinish': 'Total _ pictures (_KB), _ uploaded successfully',
+ 'updateStatusError': ' and _ upload failed',
+ 'errorNotSupport': 'WebUploader does not support the browser you are using. Please upgrade your browser or flash player',
+ 'errorLoadConfig': 'Server config not loaded, upload can not work.',
+ 'errorExceedSize':'File Size Exceed',
+ 'errorFileType':'File Type Not Allow',
+ 'errorInterrupt':'File Upload Interrupted',
+ 'errorUploadRetry':'Upload Error, Please Retry.',
+ 'errorHttp':'Http Error',
+ 'errorServerUpload':'Server Result Error.',
+ 'remoteLockError':"Cannot Lock the Proportion between width and height",
+ 'numError':"Please enter the correct Num. e.g 123,400",
+ 'imageUrlError':"The image format may be wrong!",
+ 'imageLoadError':"Error,please check the network or URL!",
+ 'searchRemind':"Enter the search keyword!",
+ 'searchLoading':"Image is loading,please wait...",
+ 'searchRetry':" Sorry,can't find the image,please try again!"
+ },
+ 'attachment':{
+ 'static':{
+ 'lang_tab_upload': 'Upload',
+ 'lang_tab_online': 'Online',
+ 'lang_start_upload':"Start upload",
+ 'lang_drop_remind':"You can drop files here, a single maximum of 300 files"
+ },
+ 'uploadSelectFile':'Select File',
+ 'uploadAddFile':'Add File',
+ 'uploadStart':'Start Upload',
+ 'uploadPause':'Pause Upload',
+ 'uploadContinue':'Continue Upload',
+ 'uploadRetry':'Retry Upload',
+ 'uploadDelete':'Delete',
+ 'uploadTurnLeft':'Turn Left',
+ 'uploadTurnRight':'Turn Right',
+ 'uploadPreview':'Doing Preview',
+ 'updateStatusReady': 'Selected _ files, total _KB.',
+ 'updateStatusConfirm': '_ uploaded successfully and _ upload failed',
+ 'updateStatusFinish': 'Total _ files (_KB), _ uploaded successfully',
+ 'updateStatusError': ' and _ upload failed',
+ 'errorNotSupport': 'WebUploader does not support the browser you are using. Please upgrade your browser or flash player',
+ 'errorLoadConfig': 'Server config not loaded, upload can not work.',
+ 'errorExceedSize':'File Size Exceed',
+ 'errorFileType':'File Type Not Allow',
+ 'errorInterrupt':'File Upload Interrupted',
+ 'errorUploadRetry':'Upload Error, Please Retry.',
+ 'errorHttp':'Http Error',
+ 'errorServerUpload':'Server Result Error.'
+ },
+
+ 'insertvideo':{
+ 'static':{
+ 'lang_tab_insertV':"Video",
+ 'lang_tab_searchV':"Search",
+ 'lang_tab_uploadV':"Upload",
+ 'lang_video_url':" URL ",
+ 'lang_video_size':"Video Size",
+ 'lang_videoW':"Width",
+ 'lang_videoH':"Height",
+ 'lang_alignment':"Alignment",
+ 'videoSearchTxt':{'value':"Enter the search keyword!"},
+ 'videoType':{'options':["All", "Hot", "Entertainment", "Funny", "Sports", "Science", "variety"]},
+ 'videoSearchBtn':{'value':"Search in Baidu"},
+ 'videoSearchReset':{'value':"Clear result"},
+
+ 'lang_input_fileStatus':' No file uploaded!',
+ 'startUpload':{'style':"background:url(upload.png) no-repeat;"},
+
+ 'lang_upload_size':"Video Size",
+ 'lang_upload_width':"Width",
+ 'lang_upload_height':"Height",
+ 'lang_upload_alignment':"Alignment",
+ 'lang_format_advice':"Recommends mp4 format."
+ },
+ 'numError':"Please enter the correct Num. e.g 123,400",
+ 'floatLeft':"Float left",
+ 'floatRight':"Float right",
+ 'default':"Default",
+ 'block':"Display in block",
+ 'urlError':"The video url format may be wrong!",
+ 'loading':" The video is loading, please wait…",
+ 'clickToSelect':"Click to select",
+ 'goToSource':'Visit source video ',
+ 'noVideo':" Sorry,can't find the video,please try again!",
+
+ 'browseFiles':'Open files',
+ 'uploadSuccess':'Upload Successful!',
+ 'delSuccessFile':'Remove from the success of the queue',
+ 'delFailSaveFile':'Remove the save failed file',
+ 'statusPrompt':' file(s) uploaded! ',
+ 'flashVersionError':'The current Flash version is too low, please update FlashPlayer,then try again!',
+ 'flashLoadingError':'The Flash failed loading! Please check the path or network state',
+ 'fileUploadReady':'Wait for uploading...',
+ 'delUploadQueue':'Remove from the uploading queue ',
+ 'limitPrompt1':'Can not choose more than single',
+ 'limitPrompt2':'file(s)!Please choose again!',
+ 'delFailFile':'Remove failure file',
+ 'fileSizeLimit':'File size exceeds the limit!',
+ 'emptyFile':'Can not upload an empty file!',
+ 'fileTypeError':'File type error!',
+ 'unknownError':'Unknown error!',
+ 'fileUploading':'Uploading,please wait...',
+ 'cancelUpload':'Cancel upload',
+ 'netError':'Network error',
+ 'failUpload':'Upload failed',
+ 'serverIOError':'Server IO error!',
+ 'noAuthority':'No Permission!',
+ 'fileNumLimit':'Upload limit to the number',
+ 'failCheck':'Authentication fails, the upload is skipped!',
+ 'fileCanceling':'Cancel, please wait...',
+ 'stopUploading':'Upload has stopped...',
+
+ 'uploadSelectFile':'Select File',
+ 'uploadAddFile':'Add File',
+ 'uploadStart':'Start Upload',
+ 'uploadPause':'Pause Upload',
+ 'uploadContinue':'Continue Upload',
+ 'uploadRetry':'Retry Upload',
+ 'uploadDelete':'Delete',
+ 'uploadTurnLeft':'Turn Left',
+ 'uploadTurnRight':'Turn Right',
+ 'uploadPreview':'Doing Preview',
+ 'updateStatusReady': 'Selected _ files, total _KB.',
+ 'updateStatusConfirm': '_ uploaded successfully and _ upload failed',
+ 'updateStatusFinish': 'Total _ files (_KB), _ uploaded successfully',
+ 'updateStatusError': ' and _ upload failed',
+ 'errorNotSupport': 'WebUploader does not support the browser you are using. Please upgrade your browser or flash player',
+ 'errorLoadConfig': 'Server config not loaded, upload can not work.',
+ 'errorExceedSize':'File Size Exceed',
+ 'errorFileType':'File Type Not Allow',
+ 'errorInterrupt':'File Upload Interrupted',
+ 'errorUploadRetry':'Upload Error, Please Retry.',
+ 'errorHttp':'Http Error',
+ 'errorServerUpload':'Server Result Error.'
+ },
+ 'webapp':{
+ 'tip1':"This function provided by Baidu APP,please apply for baidu APPKey webmaster first!",
+ 'tip2':"And then open the file neditor.config.js to set it! ",
+ 'applyFor':"APPLY FOR",
+ 'anthorApi':"Baidu API"
+ },
+ 'template':{
+ 'static':{
+ 'lang_template_bkcolor':'Background Color',
+ 'lang_template_clear' : 'Keep Content',
+ 'lang_template_select':'Select Template'
+ },
+ 'blank':"Blank",
+ 'blog':"Blog",
+ 'resume':"Resume",
+ 'richText':"Rich Text",
+ 'scrPapers':"Scientific Papers"
+ },
+ scrawl:{
+ 'static':{
+ 'lang_input_previousStep':"Previous",
+ 'lang_input_nextsStep':"Next",
+ 'lang_input_clear':'Clear',
+ 'lang_input_addPic':'AddImage',
+ 'lang_input_ScalePic':'ScaleImage',
+ 'lang_input_removePic':'RemoveImage',
+ 'J_imgTxt':{title:'Add background image'}
+ },
+ 'noScarwl':"No paint, a white paper...",
+ 'scrawlUpLoading':"Image is uploading, please wait...",
+ 'continueBtn':"Try again",
+ 'imageError':"Image failed to load!",
+ 'backgroundUploading':'Image is uploading,please wait...'
+ },
+ 'music':{
+ 'static':{
+ 'lang_input_tips':"Input singer/song/album, search you interested in music!",
+ 'J_searchBtn':{value:'Search songs'}
+ },
+ 'emptyTxt':'Not search to the relevant music results, please change a keyword try.',
+ 'chapter':'Songs',
+ 'singer':'Singer',
+ 'special':'Album',
+ 'listenTest':'Audition'
+ },
+ anchor:{
+ 'static':{
+ 'lang_input_anchorName':'Anchor Name:'
+ }
+ },
+ 'charts':{
+ 'static':{
+ 'lang_data_source':'Data source:',
+ 'lang_chart_format': 'Chart format:',
+ 'lang_data_align': 'Align',
+ 'lang_chart_align_same': 'Consistent with the X-axis Y-axis',
+ 'lang_chart_align_reverse': 'X-axis Y-axis opposite',
+ 'lang_chart_title': 'Title',
+ 'lang_chart_main_title': 'main title:',
+ 'lang_chart_sub_title': 'sub title:',
+ 'lang_chart_x_title': 'X-axis title:',
+ 'lang_chart_y_title': 'Y-axis title:',
+ 'lang_chart_tip': 'Prompt',
+ 'lang_cahrt_tip_prefix': 'prefix:',
+ 'lang_cahrt_tip_description': '仅饼图有效, 当鼠标移动到饼图中相应的块上时,提示框内的文字的前缀',
+ 'lang_chart_data_unit': 'Unit',
+ 'lang_chart_data_unit_title': 'unit:',
+ 'lang_chart_data_unit_description': '显示在每个数据点上的数据的单位, 比如: 温度的单位 ℃',
+ 'lang_chart_type': 'Chart type:',
+ 'lang_prev_btn': 'Previous',
+ 'lang_next_btn': 'Next'
+ }
+ },
+ emotion:{
+ 'static':{
+ 'lang_input_choice':'Choice',
+ 'lang_input_Tuzki':'Tuzki',
+ 'lang_input_lvdouwa':'LvDouWa',
+ 'lang_input_BOBO':'BOBO',
+ 'lang_input_babyCat':'BabyCat',
+ 'lang_input_bubble':'Bubble',
+ 'lang_input_youa':'YouA'
+ }
+ },
+ gmap:{
+ 'static':{
+ 'lang_input_address':'Address:',
+ 'lang_input_search':'Search',
+ 'address':{value:"Beijing"}
+ },
+ searchError:'Unable to locate the address!'
+ },
+ help:{
+ 'static':{
+ 'lang_input_about':'About',
+ 'lang_input_shortcuts':'Shortcuts',
+ 'lang_input_introduction':"UEditor is developed by Baidu Co.ltd. It is lightweight, customizable , focusing on user experience and etc. , UEditor is based on open source BSD license , allowing free use and redistribution.",
+ 'lang_Txt_shortcuts':'Shortcuts',
+ 'lang_Txt_func':'Function',
+ 'lang_Txt_bold':'Bold',
+ 'lang_Txt_copy':'Copy',
+ 'lang_Txt_cut':'Cut',
+ 'lang_Txt_Paste':'Paste',
+ 'lang_Txt_undo':'Undo',
+ 'lang_Txt_redo':'Redo',
+ 'lang_Txt_italic':'Italic',
+ 'lang_Txt_underline':'Underline',
+ 'lang_Txt_selectAll':'Select All',
+ 'lang_Txt_visualEnter':'Submit',
+ 'lang_Txt_fullscreen':'Fullscreen'
+ }
+ },
+ insertframe:{
+ 'static':{
+ 'lang_input_address':'Address:',
+ 'lang_input_width':'Width:',
+ 'lang_input_height':'height:',
+ 'lang_input_isScroll':'Enable scrollbars:',
+ 'lang_input_frameborder':'Show frame border:',
+ 'lang_input_alignMode':'Alignment:',
+ 'align':{title:"Alignment", options:["Default", "Left", "Right", "Center"]}
+ },
+ 'enterAddress':'Please enter an address!'
+ },
+ link:{
+ 'static':{
+ 'lang_input_text':'Text:',
+ 'lang_input_url':'URL:',
+ 'lang_input_title':'Title:',
+ 'lang_input_target':'open in new window:'
+ },
+ 'validLink':'Supports only effective when a link is selected',
+ 'httpPrompt':'The hyperlink you enter should start with "http|https|ftp://"!'
+ },
+ map:{
+ 'static':{
+ lang_city:"City",
+ lang_address:"Address",
+ city:{value:"Beijing"},
+ lang_search:"Search",
+ lang_dynamicmap:"Dynamic map"
+ },
+ cityMsg:"Please enter the city name!",
+ errorMsg:"Can't find the place!"
+ },
+ searchreplace:{
+ 'static':{
+ lang_tab_search:"Search",
+ lang_tab_replace:"Replace",
+ lang_search1:"Search",
+ lang_search2:"Search",
+ lang_replace:"Replace",
+ lang_searchReg:'Support regular expression ,which starts and ends with a slash ,for example "/expression/"',
+ lang_searchReg1:'Support regular expression ,which starts and ends with a slash ,for example "/expression/"',
+ lang_case_sensitive1:"Case sense",
+ lang_case_sensitive2:"Case sense",
+ nextFindBtn:{value:"Next"},
+ preFindBtn:{value:"Preview"},
+ nextReplaceBtn:{value:"Next"},
+ preReplaceBtn:{value:"Preview"},
+ repalceBtn:{value:"Replace"},
+ repalceAllBtn:{value:"Replace all"}
+ },
+ getEnd:"Has the search to the bottom!",
+ getStart:"Has the search to the top!",
+ countMsg:"Altogether replaced {#count} character(s)!"
+ },
+ snapscreen:{
+ 'static':{
+ lang_showMsg:"You should install the UEditor screenshots program first!",
+ lang_download:"Download!",
+ lang_step1:"Step1:Download the program and then run it",
+ lang_step2:"Step2:After complete install,try to click the button again"
+ }
+ },
+ spechars:{
+ 'static':{},
+ tsfh:"Special",
+ lmsz:"Roman",
+ szfh:"Numeral",
+ rwfh:"Japanese",
+ xlzm:"The Greek",
+ ewzm:"Russian",
+ pyzm:"Phonetic",
+ yyyb:"English",
+ zyzf:"Others"
+ },
+ 'edittable':{
+ 'static':{
+ 'lang_tableStyle':'Table style',
+ 'lang_insertCaption':'Add table header row',
+ 'lang_insertTitle':'Add table title row',
+ 'lang_insertTitleCol':'Add table title col',
+ 'lang_tableSize':'Automatically adjust table size',
+ 'lang_autoSizeContent':'Adaptive by form text',
+ 'lang_orderbycontent':"Table of contents sortable",
+ 'lang_autoSizePage':'Page width adaptive',
+ 'lang_example':'Example',
+ 'lang_borderStyle':'Table Border',
+ 'lang_color':'Color:'
+ },
+ captionName:'Caption',
+ titleName:'Title',
+ cellsName:'text',
+ errorMsg:'There are merged cells, can not sort.'
+ },
+ 'edittip':{
+ 'static':{
+ lang_delRow:'Delete entire row',
+ lang_delCol:'Delete entire col'
+ }
+ },
+ 'edittd':{
+ 'static':{
+ lang_tdBkColor:'Background Color:'
+ }
+ },
+ 'formula':{
+ 'static':{
+ }
+ },
+ wordimage:{
+ 'static':{
+ lang_resave:"The re-save step",
+ uploadBtn:{src:"upload.png", alt:"Upload"},
+ clipboard:{style:"background: url(copy.png) -153px -1px no-repeat;"},
+ lang_step:" 1. Click top button to copy the url and then open the dialog to paste it. 2. Open after choose photos uploaded process."
+ },
+ fileType:"Image",
+ flashError:"Flash initialization failed!",
+ netError:"Network error! Please try again!",
+ copySuccess:"URL has been copied!",
+
+ 'flashI18n':{
+ lang:encodeURI( '{"UploadingState":"totalNum: ${a},uploadComplete: ${b}", "BeforeUpload":"waitingNum: ${a}", "ExceedSize":"Size exceed${a}", "ErrorInPreview":"Preview failed", "DefaultDescription":"Description", "LoadingImage":"Loading..."}' ),
+ uploadingTF:encodeURI( '{"font":"Arial", "size":12, "color":"0x000", "bold":"true", "italic":"false", "underline":"false"}' ),
+ imageTF:encodeURI( '{"font":"Arial", "size":11, "color":"red", "bold":"false", "italic":"false", "underline":"false"}' ),
+ textEncoding:"utf-8",
+ addImageSkinURL:"addImage.png",
+ allDeleteBtnUpSkinURL:"allDeleteBtnUpSkin.png",
+ allDeleteBtnHoverSkinURL:"allDeleteBtnHoverSkin.png",
+ rotateLeftBtnEnableSkinURL:"rotateLeftEnable.png",
+ rotateLeftBtnDisableSkinURL:"rotateLeftDisable.png",
+ rotateRightBtnEnableSkinURL:"rotateRightEnable.png",
+ rotateRightBtnDisableSkinURL:"rotateRightDisable.png",
+ deleteBtnEnableSkinURL:"deleteEnable.png",
+ deleteBtnDisableSkinURL:"deleteDisable.png",
+ backgroundURL:'',
+ listBackgroundURL:'',
+ buttonURL:'button.png'
+ }
+ },
+ 'autosave': {
+ 'success':'Local conservation success'
+ }
+};
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/en/images/addimage.png b/src/assets/node_modules/@notadd/neditor/i18n/en/images/addimage.png
new file mode 100644
index 0000000..3a2fd17
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/i18n/en/images/addimage.png differ
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/en/images/alldeletebtnhoverskin.png b/src/assets/node_modules/@notadd/neditor/i18n/en/images/alldeletebtnhoverskin.png
new file mode 100644
index 0000000..355eeab
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/i18n/en/images/alldeletebtnhoverskin.png differ
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/en/images/alldeletebtnupskin.png b/src/assets/node_modules/@notadd/neditor/i18n/en/images/alldeletebtnupskin.png
new file mode 100644
index 0000000..61658ce
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/i18n/en/images/alldeletebtnupskin.png differ
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/en/images/background.png b/src/assets/node_modules/@notadd/neditor/i18n/en/images/background.png
new file mode 100644
index 0000000..d5bf5fd
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/i18n/en/images/background.png differ
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/en/images/button.png b/src/assets/node_modules/@notadd/neditor/i18n/en/images/button.png
new file mode 100644
index 0000000..098874c
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/i18n/en/images/button.png differ
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/en/images/copy.png b/src/assets/node_modules/@notadd/neditor/i18n/en/images/copy.png
new file mode 100644
index 0000000..f982e8b
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/i18n/en/images/copy.png differ
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/en/images/deletedisable.png b/src/assets/node_modules/@notadd/neditor/i18n/en/images/deletedisable.png
new file mode 100644
index 0000000..c8ee750
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/i18n/en/images/deletedisable.png differ
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/en/images/deleteenable.png b/src/assets/node_modules/@notadd/neditor/i18n/en/images/deleteenable.png
new file mode 100644
index 0000000..26acc88
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/i18n/en/images/deleteenable.png differ
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/en/images/listbackground.png b/src/assets/node_modules/@notadd/neditor/i18n/en/images/listbackground.png
new file mode 100644
index 0000000..4f82ccd
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/i18n/en/images/listbackground.png differ
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/en/images/localimage.png b/src/assets/node_modules/@notadd/neditor/i18n/en/images/localimage.png
new file mode 100644
index 0000000..12c8e6a
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/i18n/en/images/localimage.png differ
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/en/images/music.png b/src/assets/node_modules/@notadd/neditor/i18n/en/images/music.png
new file mode 100644
index 0000000..69c5a9a
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/i18n/en/images/music.png differ
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/en/images/rotateleftdisable.png b/src/assets/node_modules/@notadd/neditor/i18n/en/images/rotateleftdisable.png
new file mode 100644
index 0000000..741526e
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/i18n/en/images/rotateleftdisable.png differ
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/en/images/rotateleftenable.png b/src/assets/node_modules/@notadd/neditor/i18n/en/images/rotateleftenable.png
new file mode 100644
index 0000000..e164ddb
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/i18n/en/images/rotateleftenable.png differ
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/en/images/rotaterightdisable.png b/src/assets/node_modules/@notadd/neditor/i18n/en/images/rotaterightdisable.png
new file mode 100644
index 0000000..5a78c26
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/i18n/en/images/rotaterightdisable.png differ
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/en/images/rotaterightenable.png b/src/assets/node_modules/@notadd/neditor/i18n/en/images/rotaterightenable.png
new file mode 100644
index 0000000..d768531
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/i18n/en/images/rotaterightenable.png differ
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/en/images/upload.png b/src/assets/node_modules/@notadd/neditor/i18n/en/images/upload.png
new file mode 100644
index 0000000..7bb15b3
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/i18n/en/images/upload.png differ
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/ja-jp/images/copy.png b/src/assets/node_modules/@notadd/neditor/i18n/ja-jp/images/copy.png
new file mode 100644
index 0000000..66b6fe7
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/i18n/ja-jp/images/copy.png differ
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/ja-jp/images/localimage.png b/src/assets/node_modules/@notadd/neditor/i18n/ja-jp/images/localimage.png
new file mode 100644
index 0000000..ebacdef
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/i18n/ja-jp/images/localimage.png differ
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/ja-jp/images/music.png b/src/assets/node_modules/@notadd/neditor/i18n/ja-jp/images/music.png
new file mode 100644
index 0000000..a11dae8
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/i18n/ja-jp/images/music.png differ
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/ja-jp/images/upload.png b/src/assets/node_modules/@notadd/neditor/i18n/ja-jp/images/upload.png
new file mode 100644
index 0000000..60561e4
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/i18n/ja-jp/images/upload.png differ
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/ja-jp/ja-jp.js b/src/assets/node_modules/@notadd/neditor/i18n/ja-jp/ja-jp.js
new file mode 100644
index 0000000..f274f2a
--- /dev/null
+++ b/src/assets/node_modules/@notadd/neditor/i18n/ja-jp/ja-jp.js
@@ -0,0 +1,665 @@
+/**
+ */
+UE.I18N['ja-jp'] = {
+ 'labelMap':{
+ 'anchor':'アンカー', 'undo':'アンドゥー', 'redo':'リドゥー', 'bold':'太字', 'indent':'インデント', 'snapscreen':'スクリーンショット',
+ 'italic':'斜体', 'underline':'下線', 'strikethrough':'取り消し線', 'subscript':'下付き','fontborder':'囲み線',
+ 'superscript':'上付き', 'formatmatch':'書式のコピー/貼り付け', 'source':'ソースコード', 'blockquote':'参考資料',
+ 'pasteplain':'テキストのみ保持', 'selectall':'すべて選択','print':'印刷', 'preview':'プレビュー',
+ 'horizontal':'セパレーター', 'removeformat':'フォーマットをクリア', 'time':'タイム', 'date':'デート',
+ 'unlink':'リンクを解除する', 'insertrow':'前に行を挿入', 'insertcol':'前に列を挿入', 'mergeright':'右にセルをマージ', 'mergedown':'下にセールをマージ',
+ 'deleterow':'行を削除', 'deletecol':'列を削除', 'splittorows':'行に分割',
+ 'splittocols':'列に分割', 'splittocells':'セルを分割','deletecaption':'テーブルのヘッダを削除する','inserttitle':'タイトルを挿入する',
+ 'mergecells':'複数のセルをマージする', 'deletetable':'テーブルを削除する', 'cleardoc':'ドキュメントをクリアする','insertparagraphbeforetable':"テーブルの前に行を挿入する",'insertcode':'コード言語',
+ 'fontfamily':'フォント', 'fontsize':'フォントサイズ', 'paragraph':'段落書式', 'simpleupload':'単一画像アップロード', 'insertimage':'複数画像アップロード','edittable':'表属性','edittd':'セル属性', 'link':'ハイパーリンク',
+ 'emotion':'絵文字', 'spechars':'特殊文字', 'searchreplace':'置換', 'map':'Baiduマップ', 'gmap':'Googleマップ',
+ 'insertvideo':'ビデオ', 'help':'ヘルプ','justifyleft':'左揃え', 'justifyright':'右揃え', 'justifycenter':'中央揃え',
+ 'justifyjustify':'両端揃え', 'forecolor':'フォントの色', 'backcolor':'背景色', 'insertorderedlist':'順序付きリスト',
+ 'insertunorderedlist':'順序付けられていないリスト', 'fullscreen':'フルスクリーン', 'directionalityltr':'左から右へ', 'directionalityrtl':'右から左へ',
+ 'rowspacingtop':'段落前の間隔', 'rowspacingbottom':'段落後の間隔', 'pagebreak':'ページ区切り', 'insertframe':'Iframeの挿入', 'imagenone':'デフォルト',
+ 'imageleft':'左フロート', 'imageright':'右フロート', 'attachment':'添付ファイル', 'imagecenter':'中央揃え', 'wordimage':'画像ダンプ',
+ 'lineheight':'行間隔','edittip' :'編集チップ','customstyle':'カスタムタイトル', 'autotypeset':'自動レイアウト',
+ 'webapp':'baiduapp','touppercase':'大文字', 'tolowercase':'小文字','background':'背景','template':'テンプレート','scrawl':'绘画',
+ 'music':'音楽', 'inserttable':'表の挿入','drafts': '下書きから読み込み', 'charts': 'チャート'
+ },
+ 'insertorderedlist':{
+ 'num':'1,2,3...',
+ 'num1':'1),2),3)...',
+ 'num2':'(1),(2),(3)...',
+ 'cn':'一,二,三....',
+ 'cn1':'一),二),三)....',
+ 'cn2':'(一),(二),(三)....',
+ 'decimal':'1,2,3...',
+ 'lower-alpha':'a,b,c...',
+ 'lower-roman':'i,ii,iii...',
+ 'upper-alpha':'A,B,C...',
+ 'upper-roman':'I,II,III...'
+ },
+ 'insertunorderedlist':{
+ 'circle':'○ サークル',
+ 'disc':'● ディスク',
+ 'square':'■ スクエア ',
+ 'dash' :'— ダッシュ',
+ 'dot':' 。 ドット'
+ },
+ 'paragraph':{'p':'段落', 'h1':'見出し1', 'h2':'見出し2', 'h3':'見出し3', 'h4':'見出し4', 'h5':'見出し5', 'h6':'見出し6'},
+ 'fontfamily':{
+ 'songti':'明朝体',
+ 'kaiti':'楷書体',
+ 'heiti':'ゴチック体',
+ 'lishu':'隷書',
+ 'yahei':'Msyh',
+ 'andaleMono':'andale mono',
+ 'arial': 'arial',
+ 'arialBlack':'arial black',
+ 'comicSansMs':'comic sans ms',
+ 'impact':'impact',
+ 'timesNewRoman':'times new roman'
+ },
+ 'customstyle':{
+ 'tc':'タイトル中央揃え',
+ 'tl':'タイトル左揃え',
+ 'im':'強調',
+ 'hi':'強調斜体'
+ },
+ 'autoupload': {
+ 'exceedSizeError': 'ファイルサイズが上限を超えています',
+ 'exceedTypeError': 'ファイル形式が許可されていません',
+ 'jsonEncodeError': 'サーバーは形式エラーを返しました',
+ 'loading':"アップロード中...",
+ 'loadError':"アップロードエラー",
+ 'errorLoadConfig': 'バックグラウンド設定が正しく読み込まれていないため、プラグインのアップロードが正しく動作できません!'
+ },
+ 'simpleupload':{
+ 'exceedSizeError': 'ファイルサイズが上限を超えています',
+ 'exceedTypeError': 'ファイル形式が許可されていません',
+ 'jsonEncodeError': 'サーバーは形式エラーを返しました',
+ 'loading':"アップロード中...",
+ 'loadError':"アップロードエラー",
+ 'errorLoadConfig': 'バックグラウンド設定が正しく読み込まれていないため、プラグインのアップロードが正しく動作できません!'
+ },
+ 'elementPathTip':"エレメントパス",
+ 'wordCountTip':"文字カウント",
+ 'wordCountMsg':'現在、{#count}文字が入力されました。また、{#leave}文字を入力できます。 ',
+ 'wordOverFlowMsg':'
文字の数が許容される最大値を超えているため、サーバーは保存を拒否する可能性があります。!',
+ 'ok':"ok",
+ 'cancel':"キャンセル",
+ 'closeDialog':"閉じる",
+ 'tableDrag':"表をドラッグするにはuiUtils.jsを導入しなければなりません!",
+ 'autofloatMsg':"ツールバーのフロートはエディタUIと関連しているので、UIファイルを導入しなければなりません!",
+ 'loadconfigError': 'バックグラウンド設定リクエスト獲得エラー、アップロードできません!',
+ 'loadconfigFormatError': 'バックグラウンド設定項目は形式エラーを返します。アップロードできません!',
+ 'loadconfigHttpError': 'バックグラウンド設定項目をリクエストするhttpエラー、アップロードできません!',
+ 'snapScreen_plugin':{
+ 'browserMsg':"IEブラウザのみをサポートしています!",
+ 'callBackErrorMsg':"サーバーはデータエラーを返しました。設定項目を確認してからもう一度お試しください。",
+ 'uploadErrorMsg':"スクリーンショットのアップロードは失敗しました。サーバー側の環境を確認してください!! "
+ },
+ 'insertcode':{
+ 'as3':'ActionScript 3',
+ 'bash':'Bash/Shell',
+ 'cpp':'C/C++',
+ 'css':'CSS',
+ 'cf':'ColdFusion',
+ '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':'Visual Basic',
+ 'xml':'XML'
+ },
+ 'confirmClear':"現在のドキュメントをクリアしてもよろしいですか?",
+ 'contextMenu':{
+ 'delete':"削除",
+ 'selectall':"すべて選択",
+ 'deletecode':"コードを削除する",
+ 'cleardoc':"ドキュメントをクリア",
+ 'confirmclear':"現在のドキュメントをクリアしてもよろしいですか?",
+ 'unlink':"ハイパーリンクを削除",
+ 'paragraph':"段落書式",
+ 'edittable':"表属性",
+ 'aligntd':"セルの配置",
+ 'aligntable':'表の配置',
+ 'tableleft':'左フロート',
+ 'tablecenter':'中央表示',
+ 'tableright':'右フロート',
+ 'edittd':"セル属性",
+ 'setbordervisible':'表エッジ表示の設定',
+ 'justifyleft':'左揃え',
+ 'justifyright':'右揃え',
+ 'justifycenter':'中央揃え',
+ 'justifyjustify':'両端揃え',
+ 'table':"表",
+ 'inserttable':'表の挿入',
+ 'deletetable':"表の削除",
+ 'insertparagraphbefore':"前に段落を挿入する",
+ 'insertparagraphafter':'後に段落を挿入する',
+ 'deleterow':"現在の行を削除する",
+ 'deletecol':"現在の列を削除する",
+ 'insertrow':"前に行を挿入する",
+ 'insertcol':"左に列を挿入する",
+ 'insertrownext':'後に行を挿入する',
+ 'insertcolnext':'右に列を挿入する',
+ 'insertcaption':'表名を挿入する',
+ 'deletecaption':'表名を削除する',
+ 'inserttitle':'表のタイトル行を挿入',
+ 'deletetitle':'表のタイトル行を削除',
+ 'inserttitlecol':'表のタイトル列を挿入',
+ 'deletetitlecol':'表のタイトル列を削除',
+ 'averageDiseRow':'各行の平均分布',
+ 'averageDisCol':'各列の平均分布',
+ 'mergeright':"右にマージする",
+ 'mergeleft':"左にマージする",
+ 'mergedown':"下にマージする",
+ 'mergecells':"セルをマージする",
+ 'splittocells':"セルを完全に分割する",
+ 'splittocols':"列に分割",
+ 'splittorows':"行に分割",
+ 'tablesort':'表のソート',
+ 'enablesort':'表のソート可能を設定',
+ 'disablesort':'表のソート可能を取り消し',
+ 'reversecurrent':'逆ソート',
+ 'orderbyasc':'ASCIIによって昇順',
+ 'reversebyasc':'ASCIIによって降順',
+ 'orderbynum':'数値によって昇順',
+ 'reversebynum':'数値によって降順',
+ 'borderbk':'ボーダーシェーディング',
+ 'setcolor':'テーブルのインターレース変色',
+ 'unsetcolor':'テーブルのインターレース変色をキャンセルする',
+ 'setbackground':'選択の背景インターレース',
+ 'unsetbackground':'選択の背景をキャンセルする',
+ 'redandblue':'赤と青間隔',
+ 'threecolorgradient':'3色グラデーション',
+ 'copy':"コピー(Ctrl + c)",
+ 'copymsg': "ブラウザはこれをサポートしません。「Ctrl + c」を使ってください",
+ 'paste':"貼り付け(Ctrl + v)",
+ 'pastemsg': "ブラウザはこれをサポートしません。「Ctrl + v」を使ってください"
+ },
+ 'copymsg': "ブラウザはこれをサポートしません。「Ctrl + c」を使ってください",
+ 'pastemsg': "ブラウザはこれをサポートしません。「Ctrl + v」を使ってください",
+ 'anthorMsg':"リンク",
+ 'clearColor':'色をクリア',
+ 'standardColor':'標準色',
+ 'themeColor':'テーマの色',
+ 'property':'属性',
+ 'default':'デフォルト',
+ 'modify':'変更',
+ 'justifyleft':'左揃え',
+ 'justifyright':'右揃え',
+ 'justifycenter':'中央揃え',
+ 'justify':'デフォルト',
+ 'clear':'クリア',
+ 'anchorMsg':'アンカー',
+ 'delete':'削除',
+ 'clickToUpload':"アップロード",
+ 'unset':'言語ファイルが設定されていません',
+ 't_row':'行',
+ 't_col':'列',
+ 'more':'その他',
+ 'pasteOpt':'貼り付けのオプション',
+ 'pasteSourceFormat':"元の書式を保持",
+ 'tagFormat':'ラベルのみを保持',
+ 'pasteTextFormat':'テキストのみを保持',
+ 'autoTypeSet':{
+ 'mergeLine':"空白行をマージ",
+ 'delLine':"空白行をクリア",
+ 'removeFormat':"書式をクリア",
+ 'indent':"字下げ",
+ 'alignment':"配置",
+ 'imageFloat':"画像フロート",
+ 'removeFontsize':"フォントサイズをクリア",
+ 'removeFontFamily':"フォントをクリア",
+ 'removeHtml':"HTMLコードをクリア",
+ 'pasteFilter':"貼り付けフィルタ",
+ 'run':"実行",
+ 'symbol':'シンボル変換',
+ 'bdc2sb':'全角から半角',
+ 'tobdc':'半角から全角'
+ },
+
+ 'background':{
+ 'static':{
+ 'lang_background_normal':'背景設定',
+ 'lang_background_local':'オンライン画像',
+ 'lang_background_set':'オプション',
+ 'lang_background_none':'背景色なし',
+ 'lang_background_colored':'背景色あり',
+ 'lang_background_color':'色の設定',
+ 'lang_background_netimg':'ネットワーク画像',
+ 'lang_background_align':'配置',
+ 'lang_background_position':'正確な位置付け',
+ 'repeatType':{'options':["中央揃え", "横方向の繰り返し", "縦方向の繰り返し", "タイル","カスタム"]}
+
+ },
+ 'noUploadImage':"まだ画像がアップロードされていません!",
+ 'toggleSelect':"クリックして選択ステータスを切り替え\n元のサイズ: "
+ },
+ //===============dialog i18N=======================
+ 'insertimage':{
+ 'static':{
+ 'lang_tab_remote':"画像を挿入", //ノード
+ 'lang_tab_upload':"ローカルアップロード",
+ 'lang_tab_online':"オンライン管理",
+ 'lang_tab_search':"画像検索",
+ 'lang_input_url':"アドレス:",
+ 'lang_input_size':"サイズ:",
+ 'lang_input_width':"幅",
+ 'lang_input_height':"高さ",
+ 'lang_input_border':"ボーダー:",
+ 'lang_input_vhspace':"マージン:",
+ 'lang_input_title':"説明:",
+ 'lang_input_align':'画像フロート形式:',
+ 'lang_imgLoading':" 画像読み込み中...",
+ 'lang_start_upload':"アップロード",
+ 'lock':{'title':"アスペクト比をロック"}, //属性
+ 'searchType':{'title':"イメージタイプ", 'options':["ニュース", "壁紙", "エモーション", "胸像"]}, //select的option
+ 'searchTxt':{'value':"検索キーワードを入力してください"},
+ 'searchBtn':{'value':"Baidu"},
+ 'searchReset':{'value':"検索をクリア"},
+ 'noneAlign':{'title':'ノーフロート'},
+ 'leftAlign':{'title':'左フロート'},
+ 'rightAlign':{'title':'右フロート'},
+ 'centerAlign':{'title':'中央揃え、一行に占める'}
+ },
+ 'uploadSelectFile':'クリックして画像を選択',
+ 'uploadAddFile':'追加を続ける',
+ 'uploadStart':'アップロードを開始する',
+ 'uploadPause':'アップロードを停止する',
+ 'uploadContinue':'アップロードを続ける',
+ 'uploadRetry':'アップロードを再試行する',
+ 'uploadDelete':'削除',
+ 'uploadTurnLeft':'左に回転',
+ 'uploadTurnRight':'右に回転',
+ 'uploadPreview':'プレビュー',
+ 'uploadNoPreview':'プレビューできません',
+ 'updateStatusReady': '_枚の画像を選択しました。合計で_KBです。',
+ 'updateStatusConfirm': '_枚の画像はアップロードしましたが。_枚の画像はアップロードしませんでした。',
+ 'updateStatusFinish': '合計で_枚(KB)です。_枚の画像はアップロードしました',
+ 'updateStatusError': '_枚の画像はアップロードしませんでした。',
+ 'errorNotSupport': 'WebUploaderはこのブラウザをサポートしません!IEブラウザを使用している場合、flashプレーヤーをアップグレードしてみてください。',
+ 'errorLoadConfig': 'バックググラウンド設定項目が正しく読み込まれていないため、アップロードプラグインは正しく動作できません!',
+ 'errorExceedSize':'フィイルサイズは超えました',
+ 'errorFileType':'ファイル形式は許可されていません',
+ 'errorInterrupt':'ファイル転送は中断しました',
+ 'errorUploadRetry':'アップロードに失敗しました。もう一度お試しください',
+ 'errorHttp':'httpリクエストエラー',
+ 'errorServerUpload':'サーバーはエラーを返しました',
+ 'remoteLockError':"幅と高さは正しくないため、設定できません",
+ 'numError':"正しい長さまたは幅の値を入力してください!たとえば、123,400",
+ 'imageUrlError':"許可されない画像形式または画像フィールド!",
+ 'imageLoadError':"イメージロードは失敗しました。リンクアドレス、ネット状態をチャックしてください!",
+ 'searchRemind':"検索キーワードを入力してください",
+ 'searchLoading':"イメージ読み込み中、お待ちください...",
+ 'searchRetry':" :( 申し訳ございません、写真が見つかりません!もう一度お試しください!"
+ },
+ 'attachment':{
+ 'static':{
+ 'lang_tab_upload': '添付ファイルをアップロード',
+ 'lang_tab_online': 'オンライン添付ファイル',
+ 'lang_start_upload':"アップロード",
+ 'lang_drop_remind':"ファイルをここでドラッグして、一回、100個までのファイルを選択できます"
+ },
+ 'uploadSelectFile':'ファイルを選択する',
+ 'uploadAddFile':'追加を続ける',
+ 'uploadStart':'アップロード',
+ 'uploadPause':'アップロードを停止する',
+ 'uploadContinue':'アップロードを続ける',
+ 'uploadRetry':'アップロードを再試行する',
+ 'uploadDelete':'削除',
+ 'uploadTurnLeft':'左に回転',
+ 'uploadTurnRight':'右に回転',
+ 'uploadPreview':'プレビュー',
+ 'updateStatusReady': '_枚の画像を選択しました。合計で_KBです。',
+ 'updateStatusConfirm': '_枚の画像はアップロードしましたが。_枚の画像はアップロードしませんでした',
+ 'updateStatusFinish': '合計で_枚(KB)です。_枚の画像はアップロードしました',
+ 'updateStatusError': ',_枚の画像はアップロードしませんでした。',
+ 'errorNotSupport': 'WebUploaderはこのブラウザをサポートしません!IEブラウザを使用している場合、flashプレーヤーをアップグレードしてみてください。',
+ 'errorLoadConfig': 'バックググラウンド設定項目が正しく読み込まれていないため、アップロードプラグインは正しく動作できません!',
+ 'errorExceedSize':'フィイルサイズは超えました',
+ 'errorFileType':'ファイル形式は許可されていません',
+ 'errorInterrupt':'ファイル転送は中断しました',
+ 'errorUploadRetry':'アップロードに失敗しました。もう一度お試しください。',
+ 'errorHttp':'httpリクエストエラー',
+ 'errorServerUpload':'サーバーはエラーを返しました'
+ },
+ 'insertvideo':{
+ 'static':{
+ 'lang_tab_insertV':"動画を挿入",
+ 'lang_tab_searchV':"動画を検索",
+ 'lang_tab_uploadV':"動画をアップロード",
+ 'lang_video_url':"動画URL",
+ 'lang_video_size':"動画サイズ",
+ 'lang_videoW':"幅",
+ 'lang_videoH':"高さ",
+ 'lang_alignment':"配置",
+ 'videoSearchTxt':{'value':"検索キーワードを入力してください!"},
+ 'videoType':{'options':["すべて", "人気", "娯楽", "お笑い", "スポーツ", "科学技術", "バラエティ"]},
+ 'videoSearchBtn':{'value':"baidu"},
+ 'videoSearchReset':{'value':"結果をクリア"},
+
+ 'lang_input_fileStatus':' ファイルをアップロードしていません',
+ 'startUpload':{'style':"background:url(upload.png) no-repeat;"},
+
+ 'lang_upload_size':"動画サイズ",
+ 'lang_upload_width':"幅",
+ 'lang_upload_height':"高さ",
+ 'lang_upload_alignment':"配置",
+ 'lang_format_advice':"mp4形式をおすすめします。."
+
+ },
+ 'numError':"123,400などの正しい値を入力してください",
+ 'floatLeft':"左フロート",
+ 'floatRight':"右フロート",
+ '"default"':"デフォルト",
+ 'block':"一行に占める",
+ 'urlError':"動画URLは間違っています。チャックしてからもう一度お試しください!",
+ 'loading':" 動画読み込み中、しばらくお待ちください...",
+ 'clickToSelect':"クリックして選択",
+ 'goToSource':'訪問元の動画',
+ 'noVideo':" 申し訳ございません。ビデオが見つかりませんでした、もう一度お試しください!",
+
+ 'browseFiles':'ファイルブラウザ',
+ 'uploadSuccess':'アップロードしました!',
+ 'delSuccessFile':'アップロード済みのファイルから削除',
+ 'delFailSaveFile':'アップロード失敗ファイルを削除する',
+ 'statusPrompt':'のファイルがアップロードされました! ',
+ 'flashVersionError':'現在のFlashバージョンは低すぎます。FlashPlayerを更新してもう一度お試しください!',
+ 'flashLoadingError':'Flashロードに失敗しました!パスまたはネットワーク状態を確認してください',
+ 'fileUploadReady':'アップロード待ち……',
+ 'delUploadQueue':'アップロード中のファイルから削除',
+ 'limitPrompt1':' までのファイルを選択できません',
+ 'limitPrompt2':' のファイル!もう一度選択してください!',
+ 'delFailFile':'アップロード失敗したファイルを削除する',
+ 'fileSizeLimit':'ファイルサイズが上限を超えました!',
+ 'emptyFile':'文字なしのファイルをアップロードできません!',
+ 'fileTypeError':'ファイルタイプが許可されていません!',
+ 'unknownError':'不明なエラー!',
+ 'fileUploading':'アップロード中、お待ちください...',
+ 'cancelUpload':'アップロードをキャンセル',
+ 'netError':'ネットワークエラー',
+ 'failUpload':'アップロードに失敗しました!',
+ 'serverIOError':'サーバーIOエラー!',
+ 'noAuthority':'許可なし!',
+ 'fileNumLimit':'アップロード数量制限',
+ 'failCheck':'認証に失敗しました。今回のアップロードは省略されました!',
+ 'fileCanceling':'キャンセル中、お待ちください...',
+ 'stopUploading':'アップロードが停止されました...',
+
+ 'uploadSelectFile':'ファイルを選択',
+ 'uploadAddFile':'追加を続ける',
+ 'uploadStart':'アップロード',
+ 'uploadPause':'アップロードを停止する',
+ 'uploadContinue':'アップロードを続ける',
+ 'uploadRetry':'アップロードを再試行する',
+ 'uploadDelete':'削除',
+ 'uploadTurnLeft':'左に回転',
+ 'uploadTurnRight':'右に回転',
+ 'uploadPreview':'プレビュー',
+ 'updateStatusReady': 'のファイルを選択し、合計で_KBです。',
+ 'updateStatusConfirm': '_枚のファイルをアップロードしました、_枚のファイルがアップロードしませんでした',
+ 'updateStatusFinish': '合計__(_KB)です、_のファイルをアップロードしました',
+ 'updateStatusError': ',_枚はアップロードしませんでした。。',
+ 'errorNotSupport': 'WebUploaderはこのブラウザをサポートしません!IEブラウザを使用している場合、flashプレーヤーをアップグレードしてみてください。',
+ 'errorLoadConfig': 'バックググラウンド設定項目が正しく読み込まれていないため、アップロードプラグインは正しく動作できません!',
+ 'errorExceedSize':'フィイルサイズは超えました',
+ 'errorFileType':'ファイル形式は許可されていません',
+ 'errorInterrupt':'ファイル転送は中断しました',
+ 'errorUploadRetry':'アップロードに失敗しました。もう一度お試しください。',
+ 'errorHttp':'httpリクエストエラー',
+ 'errorServerUpload':'サーバーがエラーを返しました'
+ },
+ 'webapp':{
+ 'tip1':"この機能はBaiduによって提供されています。このページが表示された場合、まずサイト管理者はBaidu APPKeyを申し込んでください!",
+ 'tip2':"申し込んでから、neditor.config.jsからのappkeyを設定してください! ",
+ 'applyFor':"申し込む",
+ 'anthorApi':"Baidu API"
+ },
+ 'template':{
+ 'static':{
+ 'lang_template_bkcolor':'背景色',
+ 'lang_template_clear' : '元のコンテンツを保持',
+ 'lang_template_select' : 'テンプレートを選択'
+ },
+ 'blank':"空白のテクスト",
+ 'blog':"ブログ",
+ 'resume':"履歴書",
+ 'richText':"イメージとテキスト",
+ 'sciPapers':"技術論文"
+
+
+ },
+ 'scrawl':{
+ 'static':{
+ 'lang_input_previousStep':"前",
+ 'lang_input_nextsStep':"次へ",
+ 'lang_input_clear':'クリア',
+ 'lang_input_addPic':'背景を追加',
+ 'lang_input_ScalePic':'背景を拡大',
+ 'lang_input_removePic':'背景を削除',
+ 'J_imgTxt':{title:'背景画像を追加'}
+ },
+ 'noScarwl':"何もかかっていない、空白の紙~",
+ 'scrawlUpLoading':"落書きのアップロード中、心配しないでください~",
+ 'continueBtn':"続行",
+ 'imageError':"やばい、画像の読み込みに失敗しました!",
+ 'backgroundUploading':'背景画像のアップロード中、心配しないでください~'
+ },
+ 'music':{
+ 'static':{
+ 'lang_input_tips':"歌手/音楽/アルバムを入力し、興味のある音楽を検索してください!",
+ 'J_searchBtn':{value:'音楽を検索'}
+ },
+ 'emptyTxt':'検索条件と一致する結果がありません。別のキーワードを変更してみてください。',
+ 'chapter':'音楽',
+ 'singer':'歌手',
+ 'special':'アルバム',
+ 'listenTest':'試聴'
+ },
+ 'anchor':{
+ 'static':{
+ 'lang_input_anchorName':'アンカー名:'
+ }
+ },
+ 'charts':{
+ 'static':{
+ 'lang_data_source':'データソース:',
+ 'lang_chart_format': 'グラフ形式:',
+ 'lang_data_align': 'データの配置',
+ 'lang_chart_align_same': 'データソースはグラフのXY軸と一致しています',
+ 'lang_chart_align_reverse': 'データソースはチャートXY軸と逆です',
+ 'lang_chart_title': 'チャートのタイトル',
+ 'lang_chart_main_title': 'メインタイトル:',
+ 'lang_chart_sub_title': 'サブタイトル:',
+ 'lang_chart_x_title': 'X軸タイトル:',
+ 'lang_chart_y_title': 'Y軸タイトル:',
+ 'lang_chart_tip': 'チップ文字',
+ 'lang_cahrt_tip_prefix': 'チップ文字プレフィックス:',
+ 'lang_cahrt_tip_description': '円グラフのみ有効です。マウスが円グラフに移動したとき、プロンプトボックス内のテキストのプレフィックスを表示します',
+ 'lang_chart_data_unit': 'データ単位',
+ 'lang_chart_data_unit_title': '単位:',
+ 'lang_chart_data_unit_description': '各データポイントに表示されるデータの単位。例えば、温度の単位°C',
+ 'lang_chart_type': 'グラフタイプ',
+ 'lang_prev_btn': '前',
+ 'lang_next_btn': '次へ'
+ }
+ },
+ 'emotion':{
+ 'static':{
+ 'lang_input_choice':'選択',
+ 'lang_input_Tuzki':'Tuzki',
+ 'lang_input_BOBO':'BOBO',
+ 'lang_input_lvdouwa':'カブトムシ',
+ 'lang_input_babyCat':'baby猫',
+ 'lang_input_bubble':'バブル',
+ 'lang_input_youa':'はい'
+ }
+ },
+ 'gmap':{
+ 'static':{
+ 'lang_input_address':'アドレス',
+ 'lang_input_search':'検索',
+ 'address':{value:"北京"}
+ },
+ searchError:'このアドレスが見つかりません!'
+ },
+ 'help':{
+ 'static':{
+ 'lang_input_about':'UEditorについて',
+ 'lang_input_shortcuts':'ショートカットキー',
+ 'lang_input_introduction':'UEditorは、Baidu WebフロントエンドのR&D部門によって開発された豊富なテキストWebエディタです。軽量で、カスタマイズ可能で、ユーザ優先などの特長が持っています。オープンソースはBSDプロトコルに基づいており、コードの自由な使用と変更が可能です。',
+
+ 'lang_Txt_shortcuts':'ショートカットキー',
+ 'lang_Txt_func':'機能',
+ 'lang_Txt_bold':'選択された文字列を太字にします',
+ 'lang_Txt_copy':'選択されたコンテンツをコピーする',
+ 'lang_Txt_cut':'選択されたコンテンツをカットする',
+ 'lang_Txt_Paste':'貼り付け',
+ 'lang_Txt_undo':'最後の操作を再実行する',
+ 'lang_Txt_redo':'最後の操作を元に戻す',
+ 'lang_Txt_italic':'選択された文字列を斜体にします',
+ 'lang_Txt_underline':'選択された文字列に下線を引きます',
+ 'lang_Txt_selectAll':'すべて選択',
+ 'lang_Txt_visualEnter':'ソフトリターン',
+ 'lang_Txt_fullscreen':'フルスクリーン'
+ }
+ },
+ 'insertframe':{
+ 'static':{
+ 'lang_input_address':'アドレス:',
+ 'lang_input_width':'幅',
+ 'lang_input_height':'高さ:',
+ 'lang_input_isScroll':'スクロールバーを許可',
+ 'lang_input_frameborder':'フレーム枠を表示',
+ 'lang_input_alignMode':'配置',
+ 'align':{title:"配置", options:["デフォルト", "左揃え", "右揃え", "中央揃え"]}
+ },
+ 'enterAddress':'アドレスを入力してください!'
+ },
+ 'link':{
+ 'static':{
+ 'lang_input_text':'コンテンツ:',
+ 'lang_input_url':'リンクアドレス:',
+ 'lang_input_title':'タイトル:',
+ 'lang_input_target':'新しいウィンドウで開きますか:'
+ },
+ 'validLink':'リンクを選択する場合のみ、有効',
+ 'httpPrompt':'ご入力いただいたハイパーリンクにはhttpなどのプロトコル名が含まれておらず、デフォルトはhttp://を追加します'
+ },
+ 'map':{
+ 'static':{
+ lang_city:"都市",
+ lang_address:"アドレス",
+ city:{value:"北京"},
+ lang_search:"検索",
+ lang_dynamicmap:"ダイナミックマップを挿入する"
+ },
+ cityMsg:"都市を選んでください",
+ errorMsg:"申し訳ございませんが、場所が見つかりません!"
+ },
+ 'searchreplace':{
+ 'static':{
+ lang_tab_search:"検索",
+ lang_tab_replace:"置換",
+ lang_search1:"検索",
+ lang_search2:"検索",
+ lang_replace:"置換",
+ lang_searchReg:'正規表現をサポートします。前後方スラッシュを追加する場合は正規表現です。例えば、“/expression/”',
+ lang_searchReg1:'正規表現をサポートします。前後方スラッシュを追加する場合は正規表現です。例えば、“/expression/”',
+ lang_case_sensitive1:"大文字と小文字を区別",
+ lang_case_sensitive2:"大文字と小文字を区別",
+ nextFindBtn:{value:"次へ"},
+ preFindBtn:{value:"前"},
+ nextReplaceBtn:{value:"次へ"},
+ preReplaceBtn:{value:"前"},
+ repalceBtn:{value:"置換"},
+ repalceAllBtn:{value:"すべて置換"}
+ },
+ getEnd:"既にテキストの最後まで検索しました!",
+ getStart:"テキストのトップまで検索しました",
+ countMsg:"合計で{#count}を置き換えました!"
+ },
+ 'snapscreen':{
+ 'static':{
+ lang_showMsg:"スクリーンショット機能を使うにはUEditorプラグインをインストールする必要があります!",
+ lang_download:"クリックしてダウンロード",
+ lang_step1:"ステップ1:UEditorプラグインをダウンロードしてからインストールしてください。",
+ lang_step2:"ステップ2:インストールした後に使用できますが、うまくいかない場合は、ブラウザを再起動して試してください!"
+ }
+ },
+ 'spechars':{
+ 'static':{},
+ tsfh:"特殊文字",
+ lmsz:"ローマ",
+ szfh:"数字",
+ rwfh:"日本語",
+ xlzm:"ギリシャ",
+ ewzm:"ロシア語",
+ pyzm:"ピンイン文字",
+ yyyb:"英語音声",
+ zyzf:"その他"
+ },
+ 'edittable':{
+ 'static':{
+ 'lang_tableStyle':'表の様式',
+ 'lang_insertCaption':'表名の行を追加',
+ 'lang_insertTitle':'表の見出し行を追加',
+ 'lang_insertTitleCol':'表の見出し列を追加',
+ 'lang_orderbycontent':"表の内容をソート可能にする",
+ 'lang_tableSize':'表のサイズを自動的に調整する',
+ 'lang_autoSizeContent':'表のテキストによる適応',
+ 'lang_autoSizePage':'ページ幅による適応',
+ 'lang_example':'例',
+ 'lang_borderStyle':'表の外枠',
+ 'lang_color':'色:'
+ },
+ captionName:'表名',
+ titleName:'タイトル',
+ cellsName:'コンテンツ',
+ errorMsg:'マージされたセルがあるので、ソートできません'
+ },
+ 'edittip':{
+ 'static':{
+ lang_delRow:'行全体を削除する',
+ lang_delCol:'列全体を削除する'
+ }
+ },
+ 'edittd':{
+ 'static':{
+ lang_tdBkColor:'背景色:'
+ }
+ },
+ 'formula':{
+ 'static':{
+ }
+ },
+ 'wordimage':{
+ 'static':{
+ lang_resave:"保存ステップ",
+ uploadBtn:{src:"upload.png",alt:"アップロード"},
+ clipboard:{style:"background: url(copy.png) -153px -1px no-repeat;"},
+ lang_step:"1、トップのコピーボタンをクリックしてアドレスをクリップボードにコピーする; 2.写真の追加ボタンをクリックしてダイアログボックスで「Ctrl+V」を押してください3.開いてて画像アップロードプロセスを選択してください。"
+ },
+ 'fileType':"イメージ",
+ 'flashError':"初期化に失敗しました。プラグインが正しくインストールされているか確認してください!",
+ 'netError':"ネットエラー、もう一度お試しください!",
+ 'copySuccess':"イメージURLがコピーされました!",
+ 'flashI18n':{} //ブランクがある場合、中国語を表示
+ },
+ 'autosave': {
+ 'saving':'保存中...',
+ 'success':'ローカル保存成功'
+ }
+};
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/zh-cn/images/copy.png b/src/assets/node_modules/@notadd/neditor/i18n/zh-cn/images/copy.png
new file mode 100644
index 0000000..b2536aa
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/i18n/zh-cn/images/copy.png differ
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/zh-cn/images/localimage.png b/src/assets/node_modules/@notadd/neditor/i18n/zh-cn/images/localimage.png
new file mode 100644
index 0000000..7303c36
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/i18n/zh-cn/images/localimage.png differ
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/zh-cn/images/music.png b/src/assets/node_modules/@notadd/neditor/i18n/zh-cn/images/music.png
new file mode 100644
index 0000000..842cb93
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/i18n/zh-cn/images/music.png differ
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/zh-cn/images/upload.png b/src/assets/node_modules/@notadd/neditor/i18n/zh-cn/images/upload.png
new file mode 100644
index 0000000..08d4d92
Binary files /dev/null and b/src/assets/node_modules/@notadd/neditor/i18n/zh-cn/images/upload.png differ
diff --git a/src/assets/node_modules/@notadd/neditor/i18n/zh-cn/zh-cn.js b/src/assets/node_modules/@notadd/neditor/i18n/zh-cn/zh-cn.js
new file mode 100644
index 0000000..d2fdadf
--- /dev/null
+++ b/src/assets/node_modules/@notadd/neditor/i18n/zh-cn/zh-cn.js
@@ -0,0 +1,669 @@
+/**
+ * Created with JetBrains PhpStorm.
+ * User: taoqili
+ * Date: 12-6-12
+ * Time: 下午5:02
+ * To change this template use File | Settings | File Templates.
+ */
+UE.I18N['zh-cn'] = {
+ 'labelMap':{
+ 'anchor':'锚点', 'undo':'撤销', 'redo':'重做', 'bold':'加粗', 'indent':'首行缩进', 'snapscreen':'截图',
+ 'italic':'斜体', 'underline':'下划线', 'strikethrough':'删除线', 'subscript':'下标','fontborder':'字符边框',
+ 'superscript':'上标', 'formatmatch':'格式刷', 'source':'源代码', 'blockquote':'引用',
+ 'pasteplain':'纯文本粘贴模式', 'selectall':'全选', 'print':'打印', 'preview':'预览',
+ 'horizontal':'分隔线', 'removeformat':'清除格式', 'time':'时间', 'date':'日期',
+ 'unlink':'取消链接', 'insertrow':'前插入行', 'insertcol':'前插入列', 'mergeright':'右合并单元格', 'mergedown':'下合并单元格',
+ 'deleterow':'删除行', 'deletecol':'删除列', 'splittorows':'拆分成行',
+ 'splittocols':'拆分成列', 'splittocells':'完全拆分单元格','deletecaption':'删除表格标题','inserttitle':'插入标题',
+ 'mergecells':'合并多个单元格', 'deletetable':'删除表格', 'cleardoc':'清空文档','insertparagraphbeforetable':"表格前插入行",'insertcode':'代码语言',
+ 'fontfamily':'字体', 'fontsize':'字号', 'paragraph':'段落格式', 'simpleupload':'单图上传', 'insertimage':'多图上传','edittable':'表格属性','edittd':'单元格属性', 'link':'超链接',
+ 'emotion':'表情', 'spechars':'特殊字符', 'searchreplace':'查询替换', 'map':'Baidu地图', 'gmap':'Google地图',
+ 'insertvideo':'视频', 'help':'帮助', 'justifyleft':'居左对齐', 'justifyright':'居右对齐', 'justifycenter':'居中对齐',
+ 'justifyjustify':'两端对齐', 'forecolor':'字体颜色', 'backcolor':'背景色', 'insertorderedlist':'有序列表',
+ 'insertunorderedlist':'无序列表', 'fullscreen':'全屏', 'directionalityltr':'从左向右输入', 'directionalityrtl':'从右向左输入',
+ 'rowspacingtop':'段前距', 'rowspacingbottom':'段后距', 'pagebreak':'分页', 'insertframe':'插入Iframe', 'imagenone':'默认',
+ 'imageleft':'左浮动', 'imageright':'右浮动', 'attachment':'附件', 'imagecenter':'居中', 'wordimage':'图片转存',
+ 'lineheight':'行间距','edittip' :'编辑提示','customstyle':'自定义标题', 'autotypeset':'自动排版',
+ 'webapp':'百度应用','touppercase':'字母大写', 'tolowercase':'字母小写','background':'背景','template':'模板','scrawl':'涂鸦',
+ 'music':'音乐','inserttable':'插入表格','drafts': '从草稿箱加载', 'charts': '图表'
+ },
+ 'insertorderedlist':{
+ 'num':'1,2,3...',
+ 'num1':'1),2),3)...',
+ 'num2':'(1),(2),(3)...',
+ 'cn':'一,二,三....',
+ 'cn1':'一),二),三)....',
+ 'cn2':'(一),(二),(三)....',
+ 'decimal':'1,2,3...',
+ 'lower-alpha':'a,b,c...',
+ 'lower-roman':'i,ii,iii...',
+ 'upper-alpha':'A,B,C...',
+ 'upper-roman':'I,II,III...'
+ },
+ 'insertunorderedlist':{
+ 'circle':'○ 大圆圈',
+ 'disc':'● 小黑点',
+ 'square':'■ 小方块 ',
+ 'dash' :'— 破折号',
+ 'dot':' 。 小圆圈'
+ },
+ 'paragraph':{'p':'段落', 'h1':'标题 1', 'h2':'标题 2', 'h3':'标题 3', 'h4':'标题 4', 'h5':'标题 5', 'h6':'标题 6'},
+ 'fontfamily':{
+ 'songti':'宋体',
+ 'kaiti':'楷体',
+ 'heiti':'黑体',
+ 'lishu':'隶书',
+ 'yahei':'微软雅黑',
+ 'andaleMono':'andale mono',
+ 'arial': 'arial',
+ 'arialBlack':'arial black',
+ 'comicSansMs':'comic sans ms',
+ 'impact':'impact',
+ 'timesNewRoman':'times new roman'
+ },
+ 'customstyle':{
+ 'tc':'标题居中',
+ 'tl':'标题居左',
+ 'im':'强调',
+ 'hi':'明显强调'
+ },
+ 'autoupload': {
+ 'exceedSizeError': '文件大小超出限制',
+ 'exceedTypeError': '文件格式不允许',
+ 'jsonEncodeError': '服务器返回格式错误',
+ 'loading':"正在上传...",
+ 'loadError':"上传错误",
+ 'errorLoadConfig': '后端配置项没有正常加载,上传插件不能正常使用!'
+ },
+ 'simpleupload':{
+ 'exceedSizeError': '文件大小超出限制',
+ 'exceedTypeError': '文件格式不允许',
+ 'jsonEncodeError': '服务器返回格式错误',
+ 'loading':"正在上传...",
+ 'loadError':"上传错误",
+ 'errorLoadConfig': '后端配置项没有正常加载,上传插件不能正常使用!'
+ },
+ 'elementPathTip':"元素路径",
+ 'wordCountTip':"字数统计",
+ 'wordCountMsg':'当前已输入{#count}个字符, 您还可以输入{#leave}个字符。 ',
+ 'wordOverFlowMsg':'
字数超出最大允许值,服务器可能拒绝保存!',
+ 'ok':"确认",
+ 'cancel':"取消",
+ 'closeDialog':"关闭对话框",
+ 'tableDrag':"表格拖动必须引入uiUtils.js文件!",
+ 'autofloatMsg':"工具栏浮动依赖编辑器UI,您首先需要引入UI文件!",
+ 'loadconfigError': '获取后台配置项请求出错,上传功能将不能正常使用!',
+ 'loadconfigFormatError': '后台配置项返回格式出错,上传功能将不能正常使用!',
+ 'loadconfigHttpError': '请求后台配置项http错误,上传功能将不能正常使用!',
+ 'snapScreen_plugin':{
+ 'browserMsg':"仅支持IE浏览器!",
+ 'callBackErrorMsg':"服务器返回数据有误,请检查配置项之后重试。",
+ 'uploadErrorMsg':"截图上传失败,请检查服务器端环境! "
+ },
+ 'insertcode':{
+ 'as3':'ActionScript 3',
+ 'bash':'Bash/Shell',
+ 'cpp':'C/C++',
+ 'css':'CSS',
+ 'cf':'ColdFusion',
+ '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':'Visual Basic',
+ 'xml':'XML'
+ },
+ 'confirmClear':"确定清空当前文档么?",
+ 'contextMenu':{
+ 'delete':"删除",
+ 'selectall':"全选",
+ 'deletecode':"删除代码",
+ 'cleardoc':"清空文档",
+ 'confirmclear':"确定清空当前文档么?",
+ 'unlink':"删除超链接",
+ 'paragraph':"段落格式",
+ 'edittable':"表格属性",
+ 'aligntd':"单元格对齐方式",
+ 'aligntable':'表格对齐方式',
+ 'tableleft':'左浮动',
+ 'tablecenter':'居中显示',
+ 'tableright':'右浮动',
+ 'edittd':"单元格属性",
+ 'setbordervisible':'设置表格边线可见',
+ 'justifyleft':'左对齐',
+ 'justifyright':'右对齐',
+ 'justifycenter':'居中对齐',
+ 'justifyjustify':'两端对齐',
+ 'table':"表格",
+ 'inserttable':'插入表格',
+ 'deletetable':"删除表格",
+ 'insertparagraphbefore':"前插入段落",
+ 'insertparagraphafter':'后插入段落',
+ 'deleterow':"删除当前行",
+ 'deletecol':"删除当前列",
+ 'insertrow':"前插入行",
+ 'insertcol':"左插入列",
+ 'insertrownext':'后插入行',
+ 'insertcolnext':'右插入列',
+ 'insertcaption':'插入表格名称',
+ 'deletecaption':'删除表格名称',
+ 'inserttitle':'插入表格标题行',
+ 'deletetitle':'删除表格标题行',
+ 'inserttitlecol':'插入表格标题列',
+ 'deletetitlecol':'删除表格标题列',
+ 'averageDiseRow':'平均分布各行',
+ 'averageDisCol':'平均分布各列',
+ 'mergeright':"向右合并",
+ 'mergeleft':"向左合并",
+ 'mergedown':"向下合并",
+ 'mergecells':"合并单元格",
+ 'splittocells':"完全拆分单元格",
+ 'splittocols':"拆分成列",
+ 'splittorows':"拆分成行",
+ 'tablesort':'表格排序',
+ 'enablesort':'设置表格可排序',
+ 'disablesort':'取消表格可排序',
+ 'reversecurrent':'逆序当前',
+ 'orderbyasc':'按ASCII字符升序',
+ 'reversebyasc':'按ASCII字符降序',
+ 'orderbynum':'按数值大小升序',
+ 'reversebynum':'按数值大小降序',
+ 'borderbk':'边框底纹',
+ 'setcolor':'表格隔行变色',
+ 'unsetcolor':'取消表格隔行变色',
+ 'setbackground':'选区背景隔行',
+ 'unsetbackground':'取消选区背景',
+ 'redandblue':'红蓝相间',
+ 'threecolorgradient':'三色渐变',
+ 'copy':"复制(Ctrl + c)",
+ 'copymsg': "浏览器不支持,请使用 'Ctrl + c'",
+ 'paste':"粘贴(Ctrl + v)",
+ 'pastemsg': "浏览器不支持,请使用 'Ctrl + v'"
+ },
+ 'copymsg': "浏览器不支持,请使用 'Ctrl + c'",
+ 'pastemsg': "浏览器不支持,请使用 'Ctrl + v'",
+ 'anthorMsg':"链接",
+ 'clearColor':'清空颜色',
+ 'standardColor':'标准颜色',
+ 'themeColor':'主题颜色',
+ 'property':'属性',
+ 'default':'默认',
+ 'modify':'修改',
+ 'justifyleft':'左对齐',
+ 'justifyright':'右对齐',
+ 'justifycenter':'居中',
+ 'justify':'默认',
+ 'clear':'清除',
+ 'anchorMsg':'锚点',
+ 'delete':'删除',
+ 'clickToUpload':"点击上传",
+ 'unset':'尚未设置语言文件',
+ 't_row':'行',
+ 't_col':'列',
+ 'more':'更多',
+ 'pasteOpt':'粘贴选项',
+ 'pasteSourceFormat':"保留源格式",
+ 'tagFormat':'只保留标签',
+ 'pasteTextFormat':'只保留文本',
+ 'autoTypeSet':{
+ 'mergeLine':"合并空行",
+ 'delLine':"清除空行",
+ 'removeFormat':"清除格式",
+ 'indent':"首行缩进",
+ 'alignment':"对齐方式",
+ 'imageFloat':"图片浮动",
+ 'removeFontsize':"清除字号",
+ 'removeFontFamily':"清除字体",
+ 'removeHtml':"清除冗余HTML代码",
+ 'pasteFilter':"粘贴过滤",
+ 'run':"执行",
+ 'symbol':'符号转换',
+ 'bdc2sb':'全角转半角',
+ 'tobdc':'半角转全角'
+ },
+
+ 'background':{
+ 'static':{
+ 'lang_background_normal':'背景设置',
+ 'lang_background_local':'在线图片',
+ 'lang_background_set':'选项',
+ 'lang_background_none':'无背景色',
+ 'lang_background_colored':'有背景色',
+ 'lang_background_color':'颜色设置',
+ 'lang_background_netimg':'网络图片',
+ 'lang_background_align':'对齐方式',
+ 'lang_background_position':'精确定位',
+ 'repeatType':{'options':["居中", "横向重复", "纵向重复", "平铺","自定义"]}
+
+ },
+ 'noUploadImage':"当前未上传过任何图片!",
+ 'toggleSelect':"单击可切换选中状态\n原图尺寸: "
+ },
+ //===============dialog i18N=======================
+ 'insertimage':{
+ 'static':{
+ // 'lang_tab_remote':"插入图片", //节点
+ 'lang_tab_upload':"本地上传",
+ 'lang_tab_online':"在线管理",
+ // 'lang_tab_search':"图片搜索",
+ 'lang_input_url':"地 址:",
+ 'lang_input_size':"大 小:",
+ 'lang_input_width':"宽度",
+ 'lang_input_height':"高度",
+ 'lang_input_border':"边 框:",
+ 'lang_input_vhspace':"边 距:",
+ 'lang_input_title':"描 述:",
+ 'lang_input_align':'图片浮动方式:',
+ 'lang_imgLoading':" 图片加载中……",
+ 'lang_start_upload':"开始上传",
+ 'lock':{'title':"锁定宽高比例"}, //属性
+ 'searchType':{'title':"图片类型", 'options':["全部类型", "头像图片", "面部特写", "卡通画", "简笔画", "动态图片", "静态图片"]}, //select的option
+ 'searchTxt':{'value':"请输入搜索关键词"},
+ 'searchBtn':{'value':"百度一下"},
+ 'searchReset':{'value':"清空搜索"},
+ 'noneAlign':{'title':'无浮动'},
+ 'leftAlign':{'title':'左浮动'},
+ 'rightAlign':{'title':'右浮动'},
+ 'centerAlign':{'title':'居中独占一行'}
+ },
+ 'uploadSelectFile':'点击选择图片',
+ 'uploadAddFile':'继续添加',
+ 'uploadStart':'开始上传',
+ 'uploadPause':'暂停上传',
+ 'uploadContinue':'继续上传',
+ 'uploadRetry':'重试上传',
+ 'uploadDelete':'删除',
+ 'uploadTurnLeft':'向左旋转',
+ 'uploadTurnRight':'向右旋转',
+ 'uploadPreview':'预览中',
+ 'uploadNoPreview':'不能预览',
+ 'updateStatusReady': '选中_张图片,共_KB。',
+ 'updateStatusConfirm': '已成功上传_张照片,_张照片上传失败',
+ 'updateStatusFinish': '共_张(_KB),_张上传成功',
+ 'updateStatusError': ',_张上传失败。',
+ 'errorNotSupport': 'WebUploader 不支持您的浏览器!如果你使用的是IE浏览器,请尝试升级 flash 播放器。',
+ 'errorLoadConfig': '后端配置项没有正常加载,上传插件不能正常使用!',
+ 'errorExceedSize':'文件大小超出',
+ 'errorFileType':'文件格式不允许',
+ 'errorInterrupt':'文件传输中断',
+ 'errorUploadRetry':'上传失败,请重试',
+ 'errorHttp':'http请求错误',
+ 'errorServerUpload':'服务器返回出错',
+ 'remoteLockError':"宽高不正确,不能所定比例",
+ 'numError':"请输入正确的长度或者宽度值!例如:123,400",
+ 'imageUrlError':"不允许的图片格式或者图片域!",
+ 'imageLoadError':"图片加载失败!请检查链接地址或网络状态!",
+ 'searchRemind':"请输入搜索关键词",
+ 'searchLoading':"图片加载中,请稍后……",
+ 'searchRetry':" :( ,抱歉,没有找到图片!请重试一次!"
+ },
+ 'attachment':{
+ 'static':{
+ 'lang_tab_upload': '上传附件',
+ 'lang_tab_online': '在线附件',
+ 'lang_start_upload':"开始上传",
+ 'lang_drop_remind':"可以将文件拖到这里,单次最多可选100个文件"
+ },
+ 'uploadSelectFile':'点击选择文件',
+ 'uploadAddFile':'继续添加',
+ 'uploadStart':'开始上传',
+ 'uploadPause':'暂停上传',
+ 'uploadContinue':'继续上传',
+ 'uploadRetry':'重试上传',
+ 'uploadDelete':'删除',
+ 'uploadTurnLeft':'向左旋转',
+ 'uploadTurnRight':'向右旋转',
+ 'uploadPreview':'预览中',
+ 'updateStatusReady': '选中_个文件,共_KB。',
+ 'updateStatusConfirm': '已成功上传_个文件,_个文件上传失败',
+ 'updateStatusFinish': '共_个(_KB),_个上传成功',
+ 'updateStatusError': ',_张上传失败。',
+ 'errorNotSupport': 'WebUploader 不支持您的浏览器!如果你使用的是IE浏览器,请尝试升级 flash 播放器。',
+ 'errorLoadConfig': '后端配置项没有正常加载,上传插件不能正常使用!',
+ 'errorExceedSize':'文件大小超出',
+ 'errorFileType':'文件格式不允许',
+ 'errorInterrupt':'文件传输中断',
+ 'errorUploadRetry':'上传失败,请重试',
+ 'errorHttp':'http请求错误',
+ 'errorServerUpload':'服务器返回出错'
+ },
+ 'insertvideo':{
+ 'static':{
+ 'lang_tab_insertV':"插入视频",
+ 'lang_tab_searchV':"搜索视频",
+ 'lang_tab_uploadV':"上传视频",
+ 'lang_video_url':"视频网址",
+ 'lang_video_size':"视频尺寸",
+ 'lang_videoW':"宽度",
+ 'lang_videoH':"高度",
+ 'lang_alignment':"对齐方式",
+ 'videoSearchTxt':{'value':"请输入搜索关键字!"},
+ 'videoType':{'options':["全部", "热门", "娱乐", "搞笑", "体育", "科技", "综艺"]},
+ 'videoSearchBtn':{'value':"百度一下"},
+ 'videoSearchReset':{'value':"清空结果"},
+
+ 'lang_input_fileStatus':' 当前未上传文件',
+ 'startUpload':{'style':"background:url(upload.png) no-repeat;"},
+
+ 'lang_upload_size':"视频尺寸",
+ 'lang_upload_width':"宽度",
+ 'lang_upload_height':"高度",
+ 'lang_upload_alignment':"对齐方式",
+ 'lang_format_advice':"建议使用mp4格式."
+
+ },
+ 'numError':"请输入正确的数值,如123,400",
+ 'floatLeft':"左浮动",
+ 'floatRight':"右浮动",
+ 'default':"默认",
+ 'block':"独占一行",
+ 'urlError':"输入的视频地址有误,请检查后再试!",
+ 'loading':" 视频加载中,请等待……",
+ 'clickToSelect':"点击选中",
+ 'goToSource':'访问源视频',
+ 'noVideo':" 抱歉,找不到对应的视频,请重试!",
+
+ 'browseFiles':'浏览文件',
+ 'uploadSuccess':'上传成功!',
+ 'delSuccessFile':'从成功队列中移除',
+ 'delFailSaveFile':'移除保存失败文件',
+ 'statusPrompt':' 个文件已上传! ',
+ 'flashVersionError':'当前Flash版本过低,请更新FlashPlayer后重试!',
+ 'flashLoadingError':'Flash加载失败!请检查路径或网络状态',
+ 'fileUploadReady':'等待上传……',
+ 'delUploadQueue':'从上传队列中移除',
+ 'limitPrompt1':'单次不能选择超过',
+ 'limitPrompt2':'个文件!请重新选择!',
+ 'delFailFile':'移除失败文件',
+ 'fileSizeLimit':'文件大小超出限制!',
+ 'emptyFile':'空文件无法上传!',
+ 'fileTypeError':'文件类型不允许!',
+ 'unknownError':'未知错误!',
+ 'fileUploading':'上传中,请等待……',
+ 'cancelUpload':'取消上传',
+ 'netError':'网络错误',
+ 'failUpload':'上传失败!',
+ 'serverIOError':'服务器IO错误!',
+ 'noAuthority':'无权限!',
+ 'fileNumLimit':'上传个数限制',
+ 'failCheck':'验证失败,本次上传被跳过!',
+ 'fileCanceling':'取消中,请等待……',
+ 'stopUploading':'上传已停止……',
+
+ 'uploadSelectFile':'点击选择文件',
+ 'uploadAddFile':'继续添加',
+ 'uploadStart':'开始上传',
+ 'uploadPause':'暂停上传',
+ 'uploadContinue':'继续上传',
+ 'uploadRetry':'重试上传',
+ 'uploadDelete':'删除',
+ 'uploadTurnLeft':'向左旋转',
+ 'uploadTurnRight':'向右旋转',
+ 'uploadPreview':'预览中',
+ 'updateStatusReady': '选中_个文件,共_KB。',
+ 'updateStatusConfirm': '成功上传_个,_个失败',
+ 'updateStatusFinish': '共_个(_KB),_个成功上传',
+ 'updateStatusError': ',_张上传失败。',
+ 'errorNotSupport': 'WebUploader 不支持您的浏览器!如果你使用的是IE浏览器,请尝试升级 flash 播放器。',
+ 'errorLoadConfig': '后端配置项没有正常加载,上传插件不能正常使用!',
+ 'errorExceedSize':'文件大小超出',
+ 'errorFileType':'文件格式不允许',
+ 'errorInterrupt':'文件传输中断',
+ 'errorUploadRetry':'上传失败,请重试',
+ 'errorHttp':'http请求错误',
+ 'errorServerUpload':'服务器返回出错'
+ },
+ 'webapp':{
+ 'tip1':"本功能由百度APP提供,如看到此页面,请各位站长首先申请百度APPKey!",
+ 'tip2':"申请完成之后请至neditor.config.js中配置获得的appkey! ",
+ 'applyFor':"点此申请",
+ 'anthorApi':"百度API"
+ },
+ 'template':{
+ 'static':{
+ 'lang_template_bkcolor':'背景颜色',
+ 'lang_template_clear' : '保留原有内容',
+ 'lang_template_select' : '选择模板'
+ },
+ 'blank':"空白文档",
+ 'blog':"博客文章",
+ 'resume':"个人简历",
+ 'richText':"图文混排",
+ 'sciPapers':"科技论文"
+
+
+ },
+ 'scrawl':{
+ 'static':{
+ 'lang_input_previousStep':"上一步",
+ 'lang_input_nextsStep':"下一步",
+ 'lang_input_clear':'清空',
+ 'lang_input_addPic':'添加背景',
+ 'lang_input_ScalePic':'缩放背景',
+ 'lang_input_removePic':'删除背景',
+ 'J_imgTxt':{title:'添加背景图片'}
+ },
+ 'noScarwl':"尚未作画,白纸一张~",
+ 'scrawlUpLoading':"涂鸦上传中,别急哦~",
+ 'continueBtn':"继续",
+ 'imageError':"糟糕,图片读取失败了!",
+ 'backgroundUploading':'背景图片上传中,别急哦~'
+ },
+ 'music':{
+ 'static':{
+ 'lang_input_tips':"输入歌手/歌曲/专辑,搜索您感兴趣的音乐!",
+ 'J_searchBtn':{value:'搜索歌曲'}
+ },
+ 'emptyTxt':'未搜索到相关音乐结果,请换一个关键词试试。',
+ 'chapter':'歌曲',
+ 'singer':'歌手',
+ 'special':'专辑',
+ 'listenTest':'试听'
+ },
+ 'anchor':{
+ 'static':{
+ 'lang_input_anchorName':'锚点名字:'
+ }
+ },
+ 'charts':{
+ 'static':{
+ 'lang_data_source':'数据源:',
+ 'lang_chart_format': '图表格式:',
+ 'lang_data_align': '数据对齐方式',
+ 'lang_chart_align_same': '数据源与图表X轴Y轴一致',
+ 'lang_chart_align_reverse': '数据源与图表X轴Y轴相反',
+ 'lang_chart_title': '图表标题',
+ 'lang_chart_main_title': '主标题:',
+ 'lang_chart_sub_title': '子标题:',
+ 'lang_chart_x_title': 'X轴标题:',
+ 'lang_chart_y_title': 'Y轴标题:',
+ 'lang_chart_tip': '提示文字',
+ 'lang_cahrt_tip_prefix': '提示文字前缀:',
+ 'lang_cahrt_tip_description': '仅饼图有效, 当鼠标移动到饼图中相应的块上时,提示框内的文字的前缀',
+ 'lang_chart_data_unit': '数据单位',
+ 'lang_chart_data_unit_title': '单位:',
+ 'lang_chart_data_unit_description': '显示在每个数据点上的数据的单位, 比如: 温度的单位 ℃',
+ 'lang_chart_type': '图表类型:',
+ 'lang_prev_btn': '上一个',
+ 'lang_next_btn': '下一个'
+ }
+ },
+ 'emotion':{
+ 'static':{
+ 'lang_input_choice':'精选',
+ 'lang_input_Tuzki':'兔斯基',
+ 'lang_input_BOBO':'BOBO',
+ 'lang_input_lvdouwa':'绿豆蛙',
+ 'lang_input_babyCat':'baby猫',
+ 'lang_input_bubble':'泡泡',
+ 'lang_input_youa':'有啊'
+ }
+ },
+ 'gmap':{
+ 'static':{
+ 'lang_input_address':'地址',
+ 'lang_input_search':'搜索',
+ 'address':{value:"北京"}
+ },
+ searchError:'无法定位到该地址!'
+ },
+ 'help':{
+ 'static':{
+ 'lang_input_about':'关于UEditor',
+ 'lang_input_shortcuts':'快捷键',
+ 'lang_input_introduction':'UEditor是由百度web前端研发部开发的所见即所得富文本web编辑器,具有轻量,可定制,注重用户体验等特点。开源基于BSD协议,允许自由使用和修改代码。',
+ 'lang_Txt_shortcuts':'快捷键',
+ 'lang_Txt_func':'功能',
+ 'lang_Txt_bold':'给选中字设置为加粗',
+ 'lang_Txt_copy':'复制选中内容',
+ 'lang_Txt_cut':'剪切选中内容',
+ 'lang_Txt_Paste':'粘贴',
+ 'lang_Txt_undo':'重新执行上次操作',
+ 'lang_Txt_redo':'撤销上一次操作',
+ 'lang_Txt_italic':'给选中字设置为斜体',
+ 'lang_Txt_underline':'给选中字加下划线',
+ 'lang_Txt_selectAll':'全部选中',
+ 'lang_Txt_visualEnter':'软回车',
+ 'lang_Txt_fullscreen':'全屏'
+ }
+ },
+ 'insertframe':{
+ 'static':{
+ 'lang_input_address':'地址:',
+ 'lang_input_width':'宽度:',
+ 'lang_input_height':'高度:',
+ 'lang_input_isScroll':'允许滚动条:',
+ 'lang_input_frameborder':'显示框架边框:',
+ 'lang_input_alignMode':'对齐方式:',
+ 'align':{title:"对齐方式", options:["默认", "左对齐", "右对齐", "居中"]}
+ },
+ 'enterAddress':'请输入地址!'
+ },
+ 'link':{
+ 'static':{
+ 'lang_input_text':'文本内容:',
+ 'lang_input_url':'链接地址:',
+ 'lang_input_title':'标题:',
+ 'lang_input_target':'是否在新窗口打开:'
+ },
+ 'validLink':'只支持选中一个链接时生效',
+ 'httpPrompt':'您输入的超链接中不包含http等协议名称,默认将为您添加http://前缀'
+ },
+ 'map':{
+ 'static':{
+ lang_city:"城市",
+ lang_address:"地址",
+ city:{value:"北京"},
+ lang_search:"搜索",
+ lang_dynamicmap:"插入动态地图"
+ },
+ cityMsg:"请选择城市",
+ errorMsg:"抱歉,找不到该位置!"
+ },
+ 'searchreplace':{
+ 'static':{
+ lang_tab_search:"查找",
+ lang_tab_replace:"替换",
+ lang_search1:"查找",
+ lang_search2:"查找",
+ lang_replace:"替换",
+ lang_searchReg:'支持正则表达式,添加前后斜杠标示为正则表达式,例如“/表达式/”',
+ lang_searchReg1:'支持正则表达式,添加前后斜杠标示为正则表达式,例如“/表达式/”',
+ lang_case_sensitive1:"区分大小写",
+ lang_case_sensitive2:"区分大小写",
+ nextFindBtn:{value:"下一个"},
+ preFindBtn:{value:"上一个"},
+ nextReplaceBtn:{value:"下一个"},
+ preReplaceBtn:{value:"上一个"},
+ repalceBtn:{value:"替换"},
+ repalceAllBtn:{value:"全部替换"}
+ },
+ getEnd:"已经搜索到文章末尾!",
+ getStart:"已经搜索到文章头部",
+ countMsg:"总共替换了{#count}处!"
+ },
+ 'snapscreen':{
+ 'static':{
+ lang_showMsg:"截图功能需要首先安装UEditor截图插件! ",
+ lang_download:"点此下载",
+ lang_step1:"第一步,下载UEditor截图插件并运行安装。",
+ lang_step2:"第二步,插件安装完成后即可使用,如不生效,请重启浏览器后再试!"
+ }
+ },
+ 'spechars':{
+ 'static':{},
+ tsfh:"特殊字符",
+ lmsz:"罗马字符",
+ szfh:"数学字符",
+ rwfh:"日文字符",
+ xlzm:"希腊字母",
+ ewzm:"俄文字符",
+ pyzm:"拼音字母",
+ yyyb:"英语音标",
+ zyzf:"其他"
+ },
+ 'edittable':{
+ 'static':{
+ 'lang_tableStyle':'表格样式',
+ 'lang_insertCaption':'添加表格名称行',
+ 'lang_insertTitle':'添加表格标题行',
+ 'lang_insertTitleCol':'添加表格标题列',
+ 'lang_orderbycontent':"使表格内容可排序",
+ 'lang_tableSize':'自动调整表格尺寸',
+ 'lang_autoSizeContent':'按表格文字自适应',
+ 'lang_autoSizePage':'按页面宽度自适应',
+ 'lang_example':'示例',
+ 'lang_borderStyle':'表格边框',
+ 'lang_color':'颜色:'
+ },
+ captionName:'表格名称',
+ titleName:'标题',
+ cellsName:'内容',
+ errorMsg:'有合并单元格,不可排序'
+ },
+ 'edittip':{
+ 'static':{
+ lang_delRow:'删除整行',
+ lang_delCol:'删除整列'
+ }
+ },
+ 'edittd':{
+ 'static':{
+ lang_tdBkColor:'背景颜色:'
+ }
+ },
+ 'formula':{
+ 'static':{
+ }
+ },
+ 'wordimage':{
+ 'static':{
+ lang_resave:"转存步骤",
+ uploadBtn:{src:"upload.png",alt:"上传"},
+ clipboard:{style:"background: url(copy.png) -153px -1px no-repeat;"},
+ lang_step:"1、点击顶部复制按钮,将地址复制到剪贴板;2、点击添加照片按钮,在弹出的对话框中使用Ctrl+V粘贴地址;3、点击打开后选择图片上传流程。"
+ },
+ 'fileType':"图片",
+ 'flashError':"FLASH初始化失败,请检查FLASH插件是否正确安装!",
+ 'netError':"网络连接错误,请重试!",
+ 'copySuccess':"图片地址已经复制!",
+ 'flashI18n':{} //留空默认中文
+ },
+ 'autosave': {
+ 'saving':'保存中...',
+ 'success':'本地保存成功'
+ }
+};
diff --git a/src/assets/node_modules/@notadd/neditor/index.html b/src/assets/node_modules/@notadd/neditor/index.html
new file mode 100644
index 0000000..e87f959
--- /dev/null
+++ b/src/assets/node_modules/@notadd/neditor/index.html
@@ -0,0 +1,179 @@
+
+
+
+
完整demo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
完整demo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/node_modules/@notadd/neditor/neditor.all.js b/src/assets/node_modules/@notadd/neditor/neditor.all.js
new file mode 100644
index 0000000..e940267
--- /dev/null
+++ b/src/assets/node_modules/@notadd/neditor/neditor.all.js
@@ -0,0 +1,33076 @@
+/*!
+ * neditor
+ * version: 2.1.18
+ * build: Wed May 22 2019 17:18:08 GMT+0800 (GMT+08:00)
+ */
+
+(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: "1.5.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
+ *
edui1_iframeholder
+ * - IE系列返回值为5,6,7,8,9,10等
+ * - gecko系列会返回10900,158900等
+ * - webkit系列会返回其build号 (如 522等)
+ *
+ * @example
+ * ```javascript
+ * console.log( '当前浏览器版本号是: ' + UE.browser.version );
+ * ```
+ */
+ browser.version = version;
+
+ /**
+ * @property { boolean } isCompatible 检测当前浏览器是否能够与UEditor良好兼容
+ * @example
+ * ```javascript
+ * if ( UE.browser.isCompatible ) {
+ * console.log( '浏览器与UEditor能够良好兼容' );
+ * }
+ * ```
+ */
+ browser.isCompatible =
+ !browser.mobile &&
+ ((browser.ie && version >= 6) ||
+ (browser.gecko && version >= 10801) ||
+ (browser.opera && version >= 9.5) ||
+ (browser.air && version >= 1) ||
+ (browser.webkit && version >= 522) ||
+ false);
+ return browser;
+})());
+//快捷方式
+var ie = browser.ie,
+ webkit = browser.webkit,
+ gecko = browser.gecko,
+ opera = browser.opera;
+
+
+// core/utils.js
+/**
+ * 工具函数包
+ * @file
+ * @module UE.utils
+ * @since 1.2.6.1
+ */
+
+/**
+ * UEditor封装使用的静态工具函数
+ * @module UE.utils
+ * @unfile
+ */
+
+var utils = (UE.utils = {
+ /**
+ * 用给定的迭代器遍历对象
+ * @method each
+ * @param { Object } obj 需要遍历的对象
+ * @param { Function } iterator 迭代器, 该方法接受两个参数, 第一个参数是当前所处理的value, 第二个参数是当前遍历对象的key
+ * @example
+ * ```javascript
+ * var demoObj = {
+ * key1: 1,
+ * key2: 2
+ * };
+ *
+ * //output: key1: 1, key2: 2
+ * UE.utils.each( demoObj, funciton ( value, key ) {
+ *
+ * console.log( key + ":" + value );
+ *
+ * } );
+ * ```
+ */
+
+ /**
+ * 用给定的迭代器遍历数组或类数组对象
+ * @method each
+ * @param { Array } array 需要遍历的数组或者类数组
+ * @param { Function } iterator 迭代器, 该方法接受两个参数, 第一个参数是当前所处理的value, 第二个参数是当前遍历对象的key
+ * @example
+ * ```javascript
+ * var divs = document.getElmentByTagNames( "div" );
+ *
+ * //output: 0: DIV, 1: DIV ...
+ * UE.utils.each( divs, funciton ( value, key ) {
+ *
+ * console.log( key + ":" + value.tagName );
+ *
+ * } );
+ * ```
+ */
+ each: function(obj, iterator, context) {
+ if (obj == null) return;
+ if (obj.length === +obj.length) {
+ for (var i = 0, l = obj.length; i < l; i++) {
+ if (iterator.call(context, obj[i], i, obj) === false) return false;
+ }
+ } else {
+ for (var key in obj) {
+ if (obj.hasOwnProperty(key)) {
+ if (iterator.call(context, obj[key], key, obj) === false)
+ return false;
+ }
+ }
+ }
+ },
+
+ /**
+ * 以给定对象作为原型创建一个新对象
+ * @method makeInstance
+ * @param { Object } protoObject 该对象将作为新创建对象的原型
+ * @return { Object } 新的对象, 该对象的原型是给定的protoObject对象
+ * @example
+ * ```javascript
+ *
+ * var protoObject = { sayHello: function () { console.log('Hello UEditor!'); } };
+ *
+ * var newObject = UE.utils.makeInstance( protoObject );
+ * //output: Hello UEditor!
+ * newObject.sayHello();
+ * ```
+ */
+ makeInstance: function(obj) {
+ var noop = new Function();
+ noop.prototype = obj;
+ obj = new noop();
+ noop.prototype = null;
+ return obj;
+ },
+
+ /**
+ * 将source对象中的属性扩展到target对象上
+ * @method extend
+ * @remind 该方法将强制把source对象上的属性复制到target对象上
+ * @see UE.utils.extend(Object,Object,Boolean)
+ * @param { Object } target 目标对象, 新的属性将附加到该对象上
+ * @param { Object } source 源对象, 该对象的属性会被附加到target对象上
+ * @return { Object } 返回target对象
+ * @example
+ * ```javascript
+ *
+ * var target = { name: 'target', sex: 1 },
+ * source = { name: 'source', age: 17 };
+ *
+ * UE.utils.extend( target, source );
+ *
+ * //output: { name: 'source', sex: 1, age: 17 }
+ * console.log( target );
+ *
+ * ```
+ */
+
+ /**
+ * 将source对象中的属性扩展到target对象上, 根据指定的isKeepTarget值决定是否保留目标对象中与
+ * 源对象属性名相同的属性值。
+ * @method extend
+ * @param { Object } target 目标对象, 新的属性将附加到该对象上
+ * @param { Object } source 源对象, 该对象的属性会被附加到target对象上
+ * @param { Boolean } isKeepTarget 是否保留目标对象中与源对象中属性名相同的属性
+ * @return { Object } 返回target对象
+ * @example
+ * ```javascript
+ *
+ * var target = { name: 'target', sex: 1 },
+ * source = { name: 'source', age: 17 };
+ *
+ * UE.utils.extend( target, source, true );
+ *
+ * //output: { name: 'target', sex: 1, age: 17 }
+ * console.log( target );
+ *
+ * ```
+ */
+ extend: function(t, s, b) {
+ if (s) {
+ for (var k in s) {
+ if (!b || !t.hasOwnProperty(k)) {
+ t[k] = s[k];
+ }
+ }
+ }
+ return t;
+ },
+
+ /**
+ * 将给定的多个对象的属性复制到目标对象target上
+ * @method extend2
+ * @remind 该方法将强制把源对象上的属性复制到target对象上
+ * @remind 该方法支持两个及以上的参数, 从第二个参数开始, 其属性都会被复制到第一个参数上。 如果遇到同名的属性,
+ * 将会覆盖掉之前的值。
+ * @param { Object } target 目标对象, 新的属性将附加到该对象上
+ * @param { Object... } source 源对象, 支持多个对象, 该对象的属性会被附加到target对象上
+ * @return { Object } 返回target对象
+ * @example
+ * ```javascript
+ *
+ * var target = {},
+ * source1 = { name: 'source', age: 17 },
+ * source2 = { title: 'dev' };
+ *
+ * UE.utils.extend2( target, source1, source2 );
+ *
+ * //output: { name: 'source', age: 17, title: 'dev' }
+ * console.log( target );
+ *
+ * ```
+ */
+ extend2: function(t) {
+ var a = arguments;
+ for (var i = 1; i < a.length; i++) {
+ var x = a[i];
+ for (var k in x) {
+ if (!t.hasOwnProperty(k)) {
+ t[k] = x[k];
+ }
+ }
+ }
+ return t;
+ },
+
+ /**
+ * 模拟继承机制, 使得subClass继承自superClass
+ * @method inherits
+ * @param { Object } subClass 子类对象
+ * @param { Object } superClass 超类对象
+ * @warning 该方法只能让subClass继承超类的原型, subClass对象自身的属性和方法不会被继承
+ * @return { Object } 继承superClass后的子类对象
+ * @example
+ * ```javascript
+ * function SuperClass(){
+ * this.name = "小李";
+ * }
+ *
+ * SuperClass.prototype = {
+ * hello:function(str){
+ * console.log(this.name + str);
+ * }
+ * }
+ *
+ * function SubClass(){
+ * this.name = "小张";
+ * }
+ *
+ * UE.utils.inherits(SubClass,SuperClass);
+ *
+ * var sub = new SubClass();
+ * //output: '小张早上好!
+ * sub.hello("早上好!");
+ * ```
+ */
+ inherits: function(subClass, superClass) {
+ var oldP = subClass.prototype,
+ newP = utils.makeInstance(superClass.prototype);
+ utils.extend(newP, oldP, true);
+ subClass.prototype = newP;
+ return (newP.constructor = subClass);
+ },
+
+ /**
+ * 用指定的context对象作为函数fn的上下文
+ * @method bind
+ * @param { Function } fn 需要绑定上下文的函数对象
+ * @param { Object } content 函数fn新的上下文对象
+ * @return { Function } 一个新的函数, 该函数作为原始函数fn的代理, 将完成fn的上下文调换工作。
+ * @example
+ * ```javascript
+ *
+ * var name = 'window',
+ * newTest = null;
+ *
+ * function test () {
+ * console.log( this.name );
+ * }
+ *
+ * newTest = UE.utils.bind( test, { name: 'object' } );
+ *
+ * //output: object
+ * newTest();
+ *
+ * //output: window
+ * test();
+ *
+ * ```
+ */
+ bind: function(fn, context) {
+ return function() {
+ return fn.apply(context, arguments);
+ };
+ },
+
+ /**
+ * 创建延迟指定时间后执行的函数fn
+ * @method defer
+ * @param { Function } fn 需要延迟执行的函数对象
+ * @param { int } delay 延迟的时间, 单位是毫秒
+ * @warning 该方法的时间控制是不精确的,仅仅只能保证函数的执行是在给定的时间之后,
+ * 而不能保证刚好到达延迟时间时执行。
+ * @return { Function } 目标函数fn的代理函数, 只有执行该函数才能起到延时效果
+ * @example
+ * ```javascript
+ * var start = 0;
+ *
+ * function test(){
+ * console.log( new Date() - start );
+ * }
+ *
+ * var testDefer = UE.utils.defer( test, 1000 );
+ * //
+ * start = new Date();
+ * //output: (大约在1000毫秒之后输出) 1000
+ * testDefer();
+ * ```
+ */
+
+ /**
+ * 创建延迟指定时间后执行的函数fn, 如果在延迟时间内再次执行该方法, 将会根据指定的exclusion的值,
+ * 决定是否取消前一次函数的执行, 如果exclusion的值为true, 则取消执行,反之,将继续执行前一个方法。
+ * @method defer
+ * @param { Function } fn 需要延迟执行的函数对象
+ * @param { int } delay 延迟的时间, 单位是毫秒
+ * @param { Boolean } exclusion 如果在延迟时间内再次执行该函数,该值将决定是否取消执行前一次函数的执行,
+ * 值为true表示取消执行, 反之则将在执行前一次函数之后才执行本次函数调用。
+ * @warning 该方法的时间控制是不精确的,仅仅只能保证函数的执行是在给定的时间之后,
+ * 而不能保证刚好到达延迟时间时执行。
+ * @return { Function } 目标函数fn的代理函数, 只有执行该函数才能起到延时效果
+ * @example
+ * ```javascript
+ *
+ * function test(){
+ * console.log(1);
+ * }
+ *
+ * var testDefer = UE.utils.defer( test, 1000, true );
+ *
+ * //output: (两次调用仅有一次输出) 1
+ * testDefer();
+ * testDefer();
+ * ```
+ */
+ defer: function(fn, delay, exclusion) {
+ var timerID;
+ return function() {
+ if (exclusion) {
+ clearTimeout(timerID);
+ }
+ timerID = setTimeout(fn, delay);
+ };
+ },
+
+ /**
+ * 获取元素item在数组array中首次出现的位置, 如果未找到item, 则返回-1
+ * @method indexOf
+ * @remind 该方法的匹配过程使用的是恒等“===”
+ * @param { Array } array 需要查找的数组对象
+ * @param { * } item 需要在目标数组中查找的值
+ * @return { int } 返回item在目标数组array中首次出现的位置, 如果在数组中未找到item, 则返回-1
+ * @example
+ * ```javascript
+ * var item = 1,
+ * arr = [ 3, 4, 6, 8, 1, 1, 2 ];
+ *
+ * //output: 4
+ * console.log( UE.utils.indexOf( arr, item ) );
+ * ```
+ */
+
+ /**
+ * 获取元素item数组array中首次出现的位置, 如果未找到item, 则返回-1。通过start的值可以指定搜索的起始位置。
+ * @method indexOf
+ * @remind 该方法的匹配过程使用的是恒等“===”
+ * @param { Array } array 需要查找的数组对象
+ * @param { * } item 需要在目标数组中查找的值
+ * @param { int } start 搜索的起始位置
+ * @return { int } 返回item在目标数组array中的start位置之后首次出现的位置, 如果在数组中未找到item, 则返回-1
+ * @example
+ * ```javascript
+ * var item = 1,
+ * arr = [ 3, 4, 6, 8, 1, 2, 8, 3, 2, 1, 1, 4 ];
+ *
+ * //output: 9
+ * console.log( UE.utils.indexOf( arr, item, 5 ) );
+ * ```
+ */
+ indexOf: function(array, item, start) {
+ var index = -1;
+ start = this.isNumber(start) ? start : 0;
+ this.each(array, function(v, i) {
+ if (i >= start && v === item) {
+ index = i;
+ return false;
+ }
+ });
+ return index;
+ },
+
+ /**
+ * 移除数组array中所有的元素item
+ * @method removeItem
+ * @param { Array } array 要移除元素的目标数组
+ * @param { * } item 将要被移除的元素
+ * @remind 该方法的匹配过程使用的是恒等“===”
+ * @example
+ * ```javascript
+ * var arr = [ 4, 5, 7, 1, 3, 4, 6 ];
+ *
+ * UE.utils.removeItem( arr, 4 );
+ * //output: [ 5, 7, 1, 3, 6 ]
+ * console.log( arr );
+ *
+ * ```
+ */
+ removeItem: function(array, item) {
+ for (var i = 0, l = array.length; i < l; i++) {
+ if (array[i] === item) {
+ array.splice(i, 1);
+ i--;
+ }
+ }
+ },
+
+ /**
+ * 删除字符串str的首尾空格
+ * @method trim
+ * @param { String } str 需要删除首尾空格的字符串
+ * @return { String } 删除了首尾的空格后的字符串
+ * @example
+ * ```javascript
+ *
+ * var str = " UEdtior ";
+ *
+ * //output: 9
+ * console.log( str.length );
+ *
+ * //output: 7
+ * console.log( UE.utils.trim( " UEdtior " ).length );
+ *
+ * //output: 9
+ * console.log( str.length );
+ *
+ * ```
+ */
+ trim: function(str) {
+ return str.replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, "");
+ },
+
+ /**
+ * 将字符串str以','分隔成数组后,将该数组转换成哈希对象, 其生成的hash对象的key为数组中的元素, value为1
+ * @method listToMap
+ * @warning 该方法在生成的hash对象中,会为每一个key同时生成一个另一个全大写的key。
+ * @param { String } str 该字符串将被以','分割为数组, 然后进行转化
+ * @return { Object } 转化之后的hash对象
+ * @example
+ * ```javascript
+ *
+ * //output: Object {UEdtior: 1, UEDTIOR: 1, Hello: 1, HELLO: 1}
+ * console.log( UE.utils.listToMap( 'UEdtior,Hello' ) );
+ *
+ * ```
+ */
+
+ /**
+ * 将字符串数组转换成哈希对象, 其生成的hash对象的key为数组中的元素, value为1
+ * @method listToMap
+ * @warning 该方法在生成的hash对象中,会为每一个key同时生成一个另一个全大写的key。
+ * @param { Array } arr 字符串数组
+ * @return { Object } 转化之后的hash对象
+ * @example
+ * ```javascript
+ *
+ * //output: Object {UEdtior: 1, UEDTIOR: 1, Hello: 1, HELLO: 1}
+ * console.log( UE.utils.listToMap( [ 'UEdtior', 'Hello' ] ) );
+ *
+ * ```
+ */
+ listToMap: function(list) {
+ if (!list) return {};
+ list = utils.isArray(list) ? list : list.split(",");
+ for (var i = 0, ci, obj = {}; (ci = list[i++]); ) {
+ obj[ci.toUpperCase()] = obj[ci] = 1;
+ }
+ return obj;
+ },
+
+ /**
+ * 将str中的html符号转义,将转义“',&,<,",>,”,“”七个字符
+ * @method unhtml
+ * @param { String } str 需要转义的字符串
+ * @return { String } 转义后的字符串
+ * @example
+ * ```javascript
+ * var html = '&';
+ *
+ * //output: <body>&</body>
+ * console.log( UE.utils.unhtml( html ) );
+ *
+ * ```
+ */
+ unhtml: function(str, reg) {
+ return str
+ ? str.replace(
+ reg || /[&<">'](?:(amp|lt|ldquo|rdquo|quot|gt|#39|nbsp|#\d+);)?/g,
+ function(a, b) {
+ if (b) {
+ return a;
+ } else {
+ return {
+ "<": "<",
+ "&": "&",
+ '"': """,
+ "“": "“",
+ "”": "”",
+ ">": ">",
+ "'": "'"
+ }[a];
+ }
+ }
+ )
+ : "";
+ },
+
+ /**
+ * 将str中的转义字符还原成html字符
+ * @see UE.utils.unhtml(String);
+ * @method html
+ * @param { String } str 需要逆转义的字符串
+ * @return { String } 逆转义后的字符串
+ * @example
+ * ```javascript
+ *
+ * var str = '<body>&</body>';
+ *
+ * //output: &
+ * console.log( UE.utils.html( str ) );
+ *
+ * ```
+ */
+ html: function(str) {
+ return str
+ ? str.replace(/&((g|l|quo|ldquo|rdquo)t|amp|#39|nbsp);/g, function(m) {
+ return {
+ "<": "<",
+ "&": "&",
+ """: '"',
+ "“": "“",
+ "”": "”",
+ ">": ">",
+ "'": "'",
+ " ": " "
+ }[m];
+ })
+ : "";
+ },
+
+ /**
+ * 将css样式转换为驼峰的形式
+ * @method cssStyleToDomStyle
+ * @param { String } cssName 需要转换的css样式名
+ * @return { String } 转换成驼峰形式后的css样式名
+ * @example
+ * ```javascript
+ *
+ * var str = 'border-top';
+ *
+ * //output: borderTop
+ * console.log( UE.utils.cssStyleToDomStyle( str ) );
+ *
+ * ```
+ */
+ cssStyleToDomStyle: (function() {
+ var test = document.createElement("div").style,
+ cache = {
+ float: test.cssFloat != undefined
+ ? "cssFloat"
+ : test.styleFloat != undefined ? "styleFloat" : "float"
+ };
+
+ return function(cssName) {
+ return (
+ cache[cssName] ||
+ (cache[cssName] = cssName.toLowerCase().replace(/-./g, function(match) {
+ return match.charAt(1).toUpperCase();
+ }))
+ );
+ };
+ })(),
+
+ /**
+ * 动态加载文件到doc中
+ * @method loadFile
+ * @param { DomDocument } document 需要加载资源文件的文档对象
+ * @param { Object } options 加载资源文件的属性集合, 取值请参考代码示例
+ * @example
+ * ```javascript
+ *
+ * UE.utils.loadFile( document, {
+ * src:"test.js",
+ * tag:"script",
+ * type:"text/javascript",
+ * defer:"defer"
+ * } );
+ *
+ * ```
+ */
+
+ /**
+ * 动态加载文件到doc中,加载成功后执行的回调函数fn
+ * @method loadFile
+ * @param { DomDocument } document 需要加载资源文件的文档对象
+ * @param { Object } options 加载资源文件的属性集合, 该集合支持的值是script标签和style标签支持的所有属性。
+ * @param { Function } fn 资源文件加载成功之后执行的回调
+ * @warning 对于在同一个文档中多次加载同一URL的文件, 该方法会在第一次加载之后缓存该请求,
+ * 在此之后的所有同一URL的请求, 将会直接触发回调。
+ * @example
+ * ```javascript
+ *
+ * UE.utils.loadFile( document, {
+ * src:"test.js",
+ * tag:"script",
+ * type:"text/javascript",
+ * defer:"defer"
+ * }, function () {
+ * console.log('加载成功');
+ * } );
+ *
+ * ```
+ */
+ loadFile: (function() {
+ var tmpList = [];
+
+ function getItem(doc, obj) {
+ try {
+ for (var i = 0, ci; (ci = tmpList[i++]); ) {
+ if (ci.doc === doc && ci.url == (obj.src || obj.href)) {
+ return ci;
+ }
+ }
+ } catch (e) {
+ return null;
+ }
+ }
+
+ return function(doc, obj, fn) {
+ var item = getItem(doc, obj);
+ if (item) {
+ if (item.ready) {
+ fn && fn();
+ } else {
+ item.funs.push(fn);
+ }
+ return;
+ }
+ tmpList.push({
+ doc: doc,
+ url: obj.src || obj.href,
+ funs: [fn]
+ });
+ if (!doc.body) {
+ var html = [];
+ for (var p in obj) {
+ if (p == "tag") continue;
+ html.push(p + '="' + obj[p] + '"');
+ }
+ doc.write(
+ "<" + obj.tag + " " + html.join(" ") + " >" + obj.tag + ">"
+ );
+ return;
+ }
+ if (obj.id && doc.getElementById(obj.id)) {
+ return;
+ }
+ var element = doc.createElement(obj.tag);
+ delete obj.tag;
+ for (var p in obj) {
+ element.setAttribute(p, obj[p]);
+ }
+ element.onload = element.onreadystatechange = function() {
+ if (!this.readyState || /loaded|complete/.test(this.readyState)) {
+ item = getItem(doc, obj);
+ if (item.funs.length > 0) {
+ item.ready = 1;
+ for (var fi; (fi = item.funs.pop()); ) {
+ fi();
+ }
+ }
+ element.onload = element.onreadystatechange = null;
+ }
+ };
+ element.onerror = function() {
+ throw Error(
+ "The load " +
+ (obj.href || obj.src) +
+ " fails,check the url settings of file neditor.config.js "
+ );
+ };
+ doc.getElementsByTagName("head")[0].appendChild(element);
+ };
+ })(),
+
+ /**
+ * 判断obj对象是否为空
+ * @method isEmptyObject
+ * @param { * } obj 需要判断的对象
+ * @remind 如果判断的对象是NULL, 将直接返回true, 如果是数组且为空, 返回true, 如果是字符串, 且字符串为空,
+ * 返回true, 如果是普通对象, 且该对象没有任何实例属性, 返回true
+ * @return { Boolean } 对象是否为空
+ * @example
+ * ```javascript
+ *
+ * //output: true
+ * console.log( UE.utils.isEmptyObject( {} ) );
+ *
+ * //output: true
+ * console.log( UE.utils.isEmptyObject( [] ) );
+ *
+ * //output: true
+ * console.log( UE.utils.isEmptyObject( "" ) );
+ *
+ * //output: false
+ * console.log( UE.utils.isEmptyObject( { key: 1 } ) );
+ *
+ * //output: false
+ * console.log( UE.utils.isEmptyObject( [1] ) );
+ *
+ * //output: false
+ * console.log( UE.utils.isEmptyObject( "1" ) );
+ *
+ * ```
+ */
+ isEmptyObject: function(obj) {
+ if (obj == null) return true;
+ if (this.isArray(obj) || this.isString(obj)) return obj.length === 0;
+ for (var key in obj) if (obj.hasOwnProperty(key)) return false;
+ return true;
+ },
+
+ /**
+ * 把rgb格式的颜色值转换成16进制格式
+ * @method fixColor
+ * @param { String } rgb格式的颜色值
+ * @param { String }
+ * @example
+ * rgb(255,255,255) => "#ffffff"
+ */
+ fixColor: function(name, value) {
+ if (/color/i.test(name) && /rgba?/.test(value)) {
+ var array = value.split(",");
+ if (array.length > 3) return "";
+ value = "#";
+ for (var i = 0, color; (color = array[i++]); ) {
+ color = parseInt(color.replace(/[^\d]/gi, ""), 10).toString(16);
+ value += color.length == 1 ? "0" + color : color;
+ }
+ value = value.toUpperCase();
+ }
+ return value;
+ },
+ /**
+ * 只针对border,padding,margin做了处理,因为性能问题
+ * @public
+ * @function
+ * @param {String} val style字符串
+ */
+ optCss: function(val) {
+ var padding, margin, border;
+ val = val.replace(/(padding|margin|border)\-([^:]+):([^;]+);?/gi, function(
+ str,
+ key,
+ name,
+ val
+ ) {
+ if (val.split(" ").length == 1) {
+ switch (key) {
+ case "padding":
+ !padding && (padding = {});
+ padding[name] = val;
+ return "";
+ case "margin":
+ !margin && (margin = {});
+ margin[name] = val;
+ return "";
+ case "border":
+ return val == "initial" ? "" : str;
+ }
+ }
+ return str;
+ });
+
+ function opt(obj, name) {
+ if (!obj) {
+ return "";
+ }
+ var t = obj.top,
+ b = obj.bottom,
+ l = obj.left,
+ r = obj.right,
+ val = "";
+ if (!t || !l || !b || !r) {
+ for (var p in obj) {
+ val += ";" + name + "-" + p + ":" + obj[p] + ";";
+ }
+ } else {
+ val +=
+ ";" +
+ name +
+ ":" +
+ (t == b && b == l && l == r
+ ? t
+ : t == b && l == r
+ ? t + " " + l
+ : l == r
+ ? t + " " + l + " " + b
+ : t + " " + r + " " + b + " " + l) +
+ ";";
+ }
+ return val;
+ }
+
+ val += opt(padding, "padding") + opt(margin, "margin");
+ return val
+ .replace(/^[ \n\r\t;]*|[ \n\r\t]*$/, "")
+ .replace(/;([ \n\r\t]+)|\1;/g, ";")
+ .replace(/(&((l|g)t|quot|#39))?;{2,}/g, function(a, b) {
+ return b ? b + ";;" : ";";
+ });
+ },
+
+ /**
+ * 克隆对象
+ * @method clone
+ * @param { Object } source 源对象
+ * @return { Object } source的一个副本
+ */
+
+ /**
+ * 深度克隆对象,将source的属性克隆到target对象, 会覆盖target重名的属性。
+ * @method clone
+ * @param { Object } source 源对象
+ * @param { Object } target 目标对象
+ * @return { Object } 附加了source对象所有属性的target对象
+ */
+ clone: function(source, target) {
+ var tmp;
+ target = target || {};
+ for (var i in source) {
+ if (source.hasOwnProperty(i)) {
+ tmp = source[i];
+ if (typeof tmp == "object") {
+ target[i] = utils.isArray(tmp) ? [] : {};
+ utils.clone(source[i], target[i]);
+ } else {
+ target[i] = tmp;
+ }
+ }
+ }
+ return target;
+ },
+
+ /**
+ * 把cm/pt为单位的值转换为px为单位的值
+ * @method transUnitToPx
+ * @param { String } 待转换的带单位的字符串
+ * @return { String } 转换为px为计量单位的值的字符串
+ * @example
+ * ```javascript
+ *
+ * //output: 500px
+ * console.log( UE.utils.transUnitToPx( '20cm' ) );
+ *
+ * //output: 27px
+ * console.log( UE.utils.transUnitToPx( '20pt' ) );
+ *
+ * ```
+ */
+ transUnitToPx: function(val) {
+ if (!/(pt|cm)/.test(val)) {
+ return val;
+ }
+ var unit;
+ val.replace(/([\d.]+)(\w+)/, function(str, v, u) {
+ val = v;
+ unit = u;
+ });
+ switch (unit) {
+ case "cm":
+ val = parseFloat(val) * 25;
+ break;
+ case "pt":
+ val = Math.round(parseFloat(val) * 96 / 72);
+ }
+ return val + (val ? "px" : "");
+ },
+
+ /**
+ * 在dom树ready之后执行给定的回调函数
+ * @method domReady
+ * @remind 如果在执行该方法的时候, dom树已经ready, 那么回调函数将立刻执行
+ * @param { Function } fn dom树ready之后的回调函数
+ * @example
+ * ```javascript
+ *
+ * UE.utils.domReady( function () {
+ *
+ * console.log('123');
+ *
+ * } );
+ *
+ * ```
+ */
+ domReady: (function() {
+ var fnArr = [];
+
+ function doReady(doc) {
+ //确保onready只执行一次
+ doc.isReady = true;
+ for (var ci; (ci = fnArr.pop()); ci()) {}
+ }
+
+ return function(onready, win) {
+ win = win || window;
+ var doc = win.document;
+ onready && fnArr.push(onready);
+ if (doc.readyState === "complete") {
+ doReady(doc);
+ } else {
+ doc.isReady && doReady(doc);
+ if (browser.ie && browser.version != 11) {
+ (function() {
+ if (doc.isReady) return;
+ try {
+ doc.documentElement.doScroll("left");
+ } catch (error) {
+ setTimeout(arguments.callee, 0);
+ return;
+ }
+ doReady(doc);
+ })();
+ win.attachEvent("onload", function() {
+ doReady(doc);
+ });
+ } else {
+ doc.addEventListener(
+ "DOMContentLoaded",
+ function() {
+ doc.removeEventListener(
+ "DOMContentLoaded",
+ arguments.callee,
+ false
+ );
+ doReady(doc);
+ },
+ false
+ );
+ win.addEventListener(
+ "load",
+ function() {
+ doReady(doc);
+ },
+ false
+ );
+ }
+ }
+ };
+ })(),
+
+ /**
+ * 动态添加css样式
+ * @method cssRule
+ * @param { String } 节点名称
+ * @grammar UE.utils.cssRule('添加的样式的节点名称',['样式','放到哪个document上'])
+ * @grammar UE.utils.cssRule('body','body{background:#ccc}') => null //给body添加背景颜色
+ * @grammar UE.utils.cssRule('body') =>样式的字符串 //取得key值为body的样式的内容,如果没有找到key值先关的样式将返回空,例如刚才那个背景颜色,将返回 body{background:#ccc}
+ * @grammar UE.utils.cssRule('body',document) => 返回指定key的样式,并且指定是哪个document
+ * @grammar UE.utils.cssRule('body','') =>null //清空给定的key值的背景颜色
+ */
+ cssRule: browser.ie && browser.version != 11
+ ? function(key, style, doc) {
+ var indexList, index;
+ if (
+ style === undefined ||
+ (style && style.nodeType && style.nodeType == 9)
+ ) {
+ //获取样式
+ doc = style && style.nodeType && style.nodeType == 9
+ ? style
+ : doc || document;
+ indexList = doc.indexList || (doc.indexList = {});
+ index = indexList[key];
+ if (index !== undefined) {
+ return doc.styleSheets[index].cssText;
+ }
+ return undefined;
+ }
+ doc = doc || document;
+ indexList = doc.indexList || (doc.indexList = {});
+ index = indexList[key];
+ //清除样式
+ if (style === "") {
+ if (index !== undefined) {
+ doc.styleSheets[index].cssText = "";
+ delete indexList[key];
+ return true;
+ }
+ return false;
+ }
+
+ //添加样式
+ if (index !== undefined) {
+ sheetStyle = doc.styleSheets[index];
+ } else {
+ sheetStyle = doc.createStyleSheet(
+ "",
+ (index = doc.styleSheets.length)
+ );
+ indexList[key] = index;
+ }
+ sheetStyle.cssText = style;
+ }
+ : function(key, style, doc) {
+ var head, node;
+ if (
+ style === undefined ||
+ (style && style.nodeType && style.nodeType == 9)
+ ) {
+ //获取样式
+ doc = style && style.nodeType && style.nodeType == 9
+ ? style
+ : doc || document;
+ node = doc.getElementById(key);
+ return node ? node.innerHTML : undefined;
+ }
+ doc = doc || document;
+ node = doc.getElementById(key);
+
+ //清除样式
+ if (style === "") {
+ if (node) {
+ node.parentNode.removeChild(node);
+ return true;
+ }
+ return false;
+ }
+
+ //添加样式
+ if (node) {
+ node.innerHTML = style;
+ } else {
+ node = doc.createElement("style");
+ node.id = key;
+ node.innerHTML = style;
+ doc.getElementsByTagName("head")[0].appendChild(node);
+ }
+ },
+ sort: function(array, compareFn) {
+ compareFn =
+ compareFn ||
+ function(item1, item2) {
+ return item1.localeCompare(item2);
+ };
+ for (var i = 0, len = array.length; i < len; i++) {
+ for (var j = i, length = array.length; j < length; j++) {
+ if (compareFn(array[i], array[j]) > 0) {
+ var t = array[i];
+ array[i] = array[j];
+ array[j] = t;
+ }
+ }
+ }
+ return array;
+ },
+ serializeParam: function(json) {
+ var strArr = [];
+ for (var i in json) {
+ //忽略默认的几个参数
+ if (i == "method" || i == "timeout" || i == "async") continue;
+ //传递过来的对象和函数不在提交之列
+ if (
+ !(
+ (typeof json[i]).toLowerCase() == "function" ||
+ (typeof json[i]).toLowerCase() == "object"
+ )
+ ) {
+ strArr.push(encodeURIComponent(i) + "=" + encodeURIComponent(json[i]));
+ } else if (utils.isArray(json[i])) {
+ //支持传数组内容
+ for (var j = 0; j < json[i].length; j++) {
+ strArr.push(
+ encodeURIComponent(i) + "[]=" + encodeURIComponent(json[i][j])
+ );
+ }
+ }
+ }
+ return strArr.join("&");
+ },
+ formatUrl: function(url) {
+ var u = url.replace(/&&/g, "&");
+ u = u.replace(/\?&/g, "?");
+ u = u.replace(/&$/g, "");
+ u = u.replace(//g, "#");
+ u = u.replace(/&+/g, "&");
+ return u;
+ },
+ isCrossDomainUrl: function(url) {
+ var a = document.createElement("a");
+ a.href = url;
+ if (browser.ie) {
+ a.href = a.href;
+ }
+ return !(
+ a.protocol == location.protocol &&
+ a.hostname == location.hostname &&
+ (a.port == location.port ||
+ (a.port == "80" && location.port == "") ||
+ (a.port == "" && location.port == "80"))
+ );
+ },
+ clearEmptyAttrs: function(obj) {
+ for (var p in obj) {
+ if (obj[p] === "") {
+ delete obj[p];
+ }
+ }
+ return obj;
+ },
+ str2json: function(s) {
+ if (!utils.isString(s)) return null;
+ if (window.JSON) {
+ return JSON.parse(s);
+ } else {
+ return new Function("return " + utils.trim(s || ""))();
+ }
+ },
+ json2str: (function() {
+ if (window.JSON) {
+ return JSON.stringify;
+ } else {
+ var escapeMap = {
+ "\b": "\\b",
+ "\t": "\\t",
+ "\n": "\\n",
+ "\f": "\\f",
+ "\r": "\\r",
+ '"': '\\"',
+ "\\": "\\\\"
+ };
+
+ function encodeString(source) {
+ if (/["\\\x00-\x1f]/.test(source)) {
+ source = source.replace(/["\\\x00-\x1f]/g, function(match) {
+ var c = escapeMap[match];
+ if (c) {
+ return c;
+ }
+ c = match.charCodeAt();
+ return (
+ "\\u00" + Math.floor(c / 16).toString(16) + (c % 16).toString(16)
+ );
+ });
+ }
+ return '"' + source + '"';
+ }
+
+ function encodeArray(source) {
+ var result = ["["],
+ l = source.length,
+ preComma,
+ i,
+ item;
+
+ for (i = 0; i < l; i++) {
+ item = source[i];
+
+ switch (typeof item) {
+ case "undefined":
+ case "function":
+ case "unknown":
+ break;
+ default:
+ if (preComma) {
+ result.push(",");
+ }
+ result.push(utils.json2str(item));
+ preComma = 1;
+ }
+ }
+ result.push("]");
+ return result.join("");
+ }
+
+ function pad(source) {
+ return source < 10 ? "0" + source : source;
+ }
+
+ function encodeDate(source) {
+ return (
+ '"' +
+ source.getFullYear() +
+ "-" +
+ pad(source.getMonth() + 1) +
+ "-" +
+ pad(source.getDate()) +
+ "T" +
+ pad(source.getHours()) +
+ ":" +
+ pad(source.getMinutes()) +
+ ":" +
+ pad(source.getSeconds()) +
+ '"'
+ );
+ }
+
+ return function(value) {
+ switch (typeof value) {
+ case "undefined":
+ return "undefined";
+
+ case "number":
+ return isFinite(value) ? String(value) : "null";
+
+ case "string":
+ return encodeString(value);
+
+ case "boolean":
+ return String(value);
+
+ default:
+ if (value === null) {
+ return "null";
+ } else if (utils.isArray(value)) {
+ return encodeArray(value);
+ } else if (utils.isDate(value)) {
+ return encodeDate(value);
+ } else {
+ var result = ["{"],
+ encode = utils.json2str,
+ preComma,
+ item;
+
+ for (var key in value) {
+ if (Object.prototype.hasOwnProperty.call(value, key)) {
+ item = value[key];
+ switch (typeof item) {
+ case "undefined":
+ case "unknown":
+ case "function":
+ break;
+ default:
+ if (preComma) {
+ result.push(",");
+ }
+ preComma = 1;
+ result.push(encode(key) + ":" + encode(item));
+ }
+ }
+ }
+ result.push("}");
+ return result.join("");
+ }
+ }
+ };
+ }
+ })(),
+ renderTplstr: function(tpl, data) {
+ return tpl.replace(/\$\{\s*(\w*?)\s*\}/g, function (match, variable) {
+ if (data.hasOwnProperty(variable)) {
+ return data[variable];
+ }
+ });
+ }
+});
+/**
+ * 判断给定的对象是否是字符串
+ * @method isString
+ * @param { * } object 需要判断的对象
+ * @return { Boolean } 给定的对象是否是字符串
+ */
+
+/**
+ * 判断给定的对象是否是数组
+ * @method isArray
+ * @param { * } object 需要判断的对象
+ * @return { Boolean } 给定的对象是否是数组
+ */
+
+/**
+ * 判断给定的对象是否是一个Function
+ * @method isFunction
+ * @param { * } object 需要判断的对象
+ * @return { Boolean } 给定的对象是否是Function
+ */
+
+/**
+ * 判断给定的对象是否是Number
+ * @method isNumber
+ * @param { * } object 需要判断的对象
+ * @return { Boolean } 给定的对象是否是Number
+ */
+
+/**
+ * 判断给定的对象是否是一个正则表达式
+ * @method isRegExp
+ * @param { * } object 需要判断的对象
+ * @return { Boolean } 给定的对象是否是正则表达式
+ */
+
+/**
+ * 判断给定的对象是否是一个普通对象
+ * @method isObject
+ * @param { * } object 需要判断的对象
+ * @return { Boolean } 给定的对象是否是普通对象
+ */
+utils.each(
+ ["String", "Function", "Array", "Number", "RegExp", "Object", "Date"],
+ function(v) {
+ UE.utils["is" + v] = function(obj) {
+ return Object.prototype.toString.apply(obj) == "[object " + v + "]";
+ };
+ }
+);
+
+
+// core/EventBase.js
+/**
+ * UE采用的事件基类
+ * @file
+ * @module UE
+ * @class EventBase
+ * @since 1.2.6.1
+ */
+
+/**
+ * UEditor公用空间,UEditor所有的功能都挂载在该空间下
+ * @unfile
+ * @module UE
+ */
+
+/**
+ * UE采用的事件基类,继承此类的对应类将获取addListener,removeListener,fireEvent方法。
+ * 在UE中,Editor以及所有ui实例都继承了该类,故可以在对应的ui对象以及editor对象上使用上述方法。
+ * @unfile
+ * @module UE
+ * @class EventBase
+ */
+
+/**
+ * 通过此构造器,子类可以继承EventBase获取事件监听的方法
+ * @constructor
+ * @example
+ * ```javascript
+ * UE.EventBase.call(editor);
+ * ```
+ */
+var EventBase = (UE.EventBase = function() {});
+
+EventBase.prototype = {
+ /**
+ * 注册事件监听器
+ * @method addListener
+ * @param { String } types 监听的事件名称,同时监听多个事件使用空格分隔
+ * @param { Function } fn 监听的事件被触发时,会执行该回调函数
+ * @waining 事件被触发时,监听的函数假如返回的值恒等于true,回调函数的队列中后面的函数将不执行
+ * @example
+ * ```javascript
+ * editor.addListener('selectionchange',function(){
+ * console.log("选区已经变化!");
+ * })
+ * editor.addListener('beforegetcontent aftergetcontent',function(type){
+ * if(type == 'beforegetcontent'){
+ * //do something
+ * }else{
+ * //do something
+ * }
+ * console.log(this.getContent) // this是注册的事件的编辑器实例
+ * })
+ * ```
+ * @see UE.EventBase:fireEvent(String)
+ */
+ addListener: function(types, listener) {
+ types = utils.trim(types).split(/\s+/);
+ for (var i = 0, ti; (ti = types[i++]); ) {
+ getListener(this, ti, true).push(listener);
+ }
+ },
+
+ on: function(types, listener) {
+ return this.addListener(types, listener);
+ },
+ off: function(types, listener) {
+ return this.removeListener(types, listener);
+ },
+ trigger: function() {
+ return this.fireEvent.apply(this, arguments);
+ },
+ /**
+ * 移除事件监听器
+ * @method removeListener
+ * @param { String } types 移除的事件名称,同时移除多个事件使用空格分隔
+ * @param { Function } fn 移除监听事件的函数引用
+ * @example
+ * ```javascript
+ * //changeCallback为方法体
+ * editor.removeListener("selectionchange",changeCallback);
+ * ```
+ */
+ removeListener: function(types, listener) {
+ types = utils.trim(types).split(/\s+/);
+ for (var i = 0, ti; (ti = types[i++]); ) {
+ utils.removeItem(getListener(this, ti) || [], listener);
+ }
+ },
+
+ /**
+ * 触发事件
+ * @method fireEvent
+ * @param { String } types 触发的事件名称,同时触发多个事件使用空格分隔
+ * @remind 该方法会触发addListener
+ * @return { * } 返回触发事件的队列中,最后执行的回调函数的返回值
+ * @example
+ * ```javascript
+ * editor.fireEvent("selectionchange");
+ * ```
+ */
+
+ /**
+ * 触发事件
+ * @method fireEvent
+ * @param { String } types 触发的事件名称,同时触发多个事件使用空格分隔
+ * @param { *... } options 可选参数,可以传入一个或多个参数,会传给事件触发的回调函数
+ * @return { * } 返回触发事件的队列中,最后执行的回调函数的返回值
+ * @example
+ * ```javascript
+ *
+ * editor.addListener( "selectionchange", function ( type, arg1, arg2 ) {
+ *
+ * console.log( arg1 + " " + arg2 );
+ *
+ * } );
+ *
+ * //触发selectionchange事件, 会执行上面的事件监听器
+ * //output: Hello World
+ * editor.fireEvent("selectionchange", "Hello", "World");
+ * ```
+ */
+ fireEvent: function() {
+ var types = arguments[0];
+ types = utils.trim(types).split(" ");
+ for (var i = 0, ti; (ti = types[i++]); ) {
+ var listeners = getListener(this, ti),
+ r,
+ t,
+ k;
+ if (listeners) {
+ k = listeners.length;
+ while (k--) {
+ if (!listeners[k]) continue;
+ t = listeners[k].apply(this, arguments);
+ if (t === true) {
+ return t;
+ }
+ if (t !== undefined) {
+ r = t;
+ }
+ }
+ }
+ if ((t = this["on" + ti.toLowerCase()])) {
+ r = t.apply(this, arguments);
+ }
+ }
+ return r;
+ }
+};
+/**
+ * 获得对象所拥有监听类型的所有监听器
+ * @unfile
+ * @module UE
+ * @since 1.2.6.1
+ * @method getListener
+ * @public
+ * @param { Object } obj 查询监听器的对象
+ * @param { String } type 事件类型
+ * @param { Boolean } force 为true且当前所有type类型的侦听器不存在时,创建一个空监听器数组
+ * @return { Array } 监听器数组
+ */
+function getListener(obj, type, force) {
+ var allListeners;
+ type = type.toLowerCase();
+ return (
+ (allListeners =
+ obj.__allListeners || (force && (obj.__allListeners = {}))) &&
+ (allListeners[type] || (force && (allListeners[type] = [])))
+ );
+}
+
+
+// core/dtd.js
+///import editor.js
+///import core/dom/dom.js
+///import core/utils.js
+/**
+ * dtd html语义化的体现类
+ * @constructor
+ * @namespace dtd
+ */
+var dtd = (dom.dtd = (function() {
+ function _(s) {
+ for (var k in s) {
+ s[k.toUpperCase()] = s[k];
+ }
+ return s;
+ }
+ var X = utils.extend2;
+ var A = _({ isindex: 1, fieldset: 1 }),
+ B = _({ input: 1, button: 1, select: 1, textarea: 1, label: 1 }),
+ C = X(_({ a: 1 }), B),
+ D = X({ iframe: 1 }, C),
+ E = _({
+ hr: 1,
+ ul: 1,
+ menu: 1,
+ div: 1,
+ blockquote: 1,
+ noscript: 1,
+ table: 1,
+ center: 1,
+ address: 1,
+ dir: 1,
+ pre: 1,
+ h5: 1,
+ dl: 1,
+ h4: 1,
+ noframes: 1,
+ h6: 1,
+ ol: 1,
+ h1: 1,
+ h3: 1,
+ h2: 1
+ }),
+ F = _({ ins: 1, del: 1, script: 1, style: 1 }),
+ G = X(
+ _({
+ mark: 1,
+ b: 1,
+ acronym: 1,
+ bdo: 1,
+ var: 1,
+ "#": 1,
+ abbr: 1,
+ code: 1,
+ br: 1,
+ i: 1,
+ cite: 1,
+ kbd: 1,
+ u: 1,
+ strike: 1,
+ s: 1,
+ tt: 1,
+ strong: 1,
+ q: 1,
+ samp: 1,
+ em: 1,
+ dfn: 1,
+ span: 1
+ }),
+ F
+ ),
+ H = X(
+ _({
+ sub: 1,
+ img: 1,
+ embed: 1,
+ object: 1,
+ sup: 1,
+ basefont: 1,
+ map: 1,
+ applet: 1,
+ font: 1,
+ big: 1,
+ small: 1
+ }),
+ G
+ ),
+ I = X(_({ p: 1 }), H),
+ J = X(_({ iframe: 1 }), H, B),
+ K = _({
+ img: 1,
+ embed: 1,
+ noscript: 1,
+ br: 1,
+ kbd: 1,
+ center: 1,
+ button: 1,
+ basefont: 1,
+ h5: 1,
+ h4: 1,
+ samp: 1,
+ h6: 1,
+ ol: 1,
+ h1: 1,
+ h3: 1,
+ h2: 1,
+ form: 1,
+ font: 1,
+ "#": 1,
+ select: 1,
+ menu: 1,
+ ins: 1,
+ abbr: 1,
+ label: 1,
+ code: 1,
+ table: 1,
+ script: 1,
+ cite: 1,
+ input: 1,
+ iframe: 1,
+ strong: 1,
+ textarea: 1,
+ noframes: 1,
+ big: 1,
+ small: 1,
+ span: 1,
+ hr: 1,
+ sub: 1,
+ bdo: 1,
+ var: 1,
+ div: 1,
+ object: 1,
+ sup: 1,
+ strike: 1,
+ dir: 1,
+ map: 1,
+ dl: 1,
+ applet: 1,
+ del: 1,
+ isindex: 1,
+ fieldset: 1,
+ ul: 1,
+ b: 1,
+ acronym: 1,
+ a: 1,
+ blockquote: 1,
+ i: 1,
+ u: 1,
+ s: 1,
+ tt: 1,
+ address: 1,
+ q: 1,
+ pre: 1,
+ p: 1,
+ em: 1,
+ dfn: 1
+ }),
+ L = X(_({ a: 0 }), J), //a不能被切开,所以把他
+ M = _({ tr: 1 }),
+ N = _({ "#": 1 }),
+ O = X(_({ param: 1 }), K),
+ P = X(_({ form: 1 }), A, D, E, I),
+ Q = _({ li: 1, ol: 1, ul: 1 }),
+ R = _({ style: 1, script: 1 }),
+ S = _({ base: 1, link: 1, meta: 1, title: 1 }),
+ T = X(S, R),
+ U = _({ head: 1, body: 1 }),
+ V = _({ html: 1 });
+
+ var block = _({
+ address: 1,
+ blockquote: 1,
+ center: 1,
+ dir: 1,
+ div: 1,
+ dl: 1,
+ fieldset: 1,
+ form: 1,
+ h1: 1,
+ h2: 1,
+ h3: 1,
+ h4: 1,
+ h5: 1,
+ h6: 1,
+ hr: 1,
+ isindex: 1,
+ menu: 1,
+ noframes: 1,
+ ol: 1,
+ p: 1,
+ pre: 1,
+ table: 1,
+ ul: 1
+ }),
+ empty = _({
+ area: 1,
+ base: 1,
+ basefont: 1,
+ br: 1,
+ col: 1,
+ command: 1,
+ dialog: 1,
+ embed: 1,
+ hr: 1,
+ img: 1,
+ input: 1,
+ isindex: 1,
+ keygen: 1,
+ link: 1,
+ meta: 1,
+ param: 1,
+ source: 1,
+ track: 1,
+ wbr: 1
+ });
+
+ return _({
+ // $ 表示自定的属性
+
+ // body外的元素列表.
+ $nonBodyContent: X(V, U, S),
+
+ //块结构元素列表
+ $block: block,
+
+ //内联元素列表
+ $inline: L,
+
+ $inlineWithA: X(_({ a: 1 }), L),
+
+ $body: X(_({ script: 1, style: 1 }), block),
+
+ $cdata: _({ script: 1, style: 1 }),
+
+ //自闭和元素
+ $empty: empty,
+
+ //不是自闭合,但不能让range选中里边
+ $nonChild: _({ iframe: 1, textarea: 1 }),
+ //列表元素列表
+ $listItem: _({ dd: 1, dt: 1, li: 1 }),
+
+ //列表根元素列表
+ $list: _({ ul: 1, ol: 1, dl: 1 }),
+
+ //不能认为是空的元素
+ $isNotEmpty: _({
+ table: 1,
+ ul: 1,
+ ol: 1,
+ dl: 1,
+ iframe: 1,
+ area: 1,
+ base: 1,
+ col: 1,
+ hr: 1,
+ img: 1,
+ embed: 1,
+ input: 1,
+ textarea: 1,
+ link: 1,
+ meta: 1,
+ param: 1,
+ h1: 1,
+ h2: 1,
+ h3: 1,
+ h4: 1,
+ h5: 1,
+ h6: 1
+ }),
+
+ //如果没有子节点就可以删除的元素列表,像span,a
+ $removeEmpty: _({
+ a: 1,
+ abbr: 1,
+ acronym: 1,
+ address: 1,
+ b: 1,
+ bdo: 1,
+ big: 1,
+ cite: 1,
+ code: 1,
+ del: 1,
+ dfn: 1,
+ em: 1,
+ font: 1,
+ i: 1,
+ ins: 1,
+ label: 1,
+ kbd: 1,
+ q: 1,
+ s: 1,
+ samp: 1,
+ small: 1,
+ span: 1,
+ strike: 1,
+ strong: 1,
+ sub: 1,
+ sup: 1,
+ tt: 1,
+ u: 1,
+ var: 1
+ }),
+
+ $removeEmptyBlock: _({ p: 1, div: 1 }),
+
+ //在table元素里的元素列表
+ $tableContent: _({
+ caption: 1,
+ col: 1,
+ colgroup: 1,
+ tbody: 1,
+ td: 1,
+ tfoot: 1,
+ th: 1,
+ thead: 1,
+ tr: 1,
+ table: 1
+ }),
+ //不转换的标签
+ $notTransContent: _({ pre: 1, script: 1, style: 1, textarea: 1 }),
+ html: U,
+ head: T,
+ style: N,
+ script: N,
+ body: P,
+ base: {},
+ link: {},
+ meta: {},
+ title: N,
+ col: {},
+ tr: _({ td: 1, th: 1 }),
+ img: {},
+ embed: {},
+ colgroup: _({ thead: 1, col: 1, tbody: 1, tr: 1, tfoot: 1 }),
+ noscript: P,
+ td: P,
+ br: {},
+ th: P,
+ center: P,
+ kbd: L,
+ button: X(I, E),
+ basefont: {},
+ h5: L,
+ h4: L,
+ samp: L,
+ h6: L,
+ ol: Q,
+ h1: L,
+ h3: L,
+ option: N,
+ h2: L,
+ form: X(A, D, E, I),
+ select: _({ optgroup: 1, option: 1 }),
+ font: L,
+ ins: L,
+ menu: Q,
+ abbr: L,
+ label: L,
+ table: _({
+ thead: 1,
+ col: 1,
+ tbody: 1,
+ tr: 1,
+ colgroup: 1,
+ caption: 1,
+ tfoot: 1
+ }),
+ code: L,
+ tfoot: M,
+ cite: L,
+ li: P,
+ input: {},
+ iframe: P,
+ strong: L,
+ textarea: N,
+ noframes: P,
+ big: L,
+ small: L,
+ //trace:
+ span: _({
+ "#": 1,
+ br: 1,
+ b: 1,
+ strong: 1,
+ u: 1,
+ i: 1,
+ em: 1,
+ sub: 1,
+ sup: 1,
+ strike: 1,
+ span: 1
+ }),
+ hr: L,
+ dt: L,
+ sub: L,
+ optgroup: _({ option: 1 }),
+ param: {},
+ bdo: L,
+ var: L,
+ div: P,
+ object: O,
+ sup: L,
+ dd: P,
+ strike: L,
+ area: {},
+ dir: Q,
+ map: X(_({ area: 1, form: 1, p: 1 }), A, F, E),
+ applet: O,
+ dl: _({ dt: 1, dd: 1 }),
+ del: L,
+ isindex: {},
+ fieldset: X(_({ legend: 1 }), K),
+ thead: M,
+ ul: Q,
+ acronym: L,
+ b: L,
+ a: X(_({ a: 1 }), J),
+ blockquote: X(_({ td: 1, tr: 1, tbody: 1, li: 1 }), P),
+ caption: L,
+ i: L,
+ u: L,
+ tbody: M,
+ s: L,
+ address: X(D, I),
+ tt: L,
+ legend: L,
+ q: L,
+ pre: X(G, C),
+ p: X(_({ a: 1 }), L),
+ em: L,
+ dfn: L,
+ mark: L
+ });
+})());
+
+
+// core/domUtils.js
+/**
+ * Dom操作工具包
+ * @file
+ * @module UE.dom.domUtils
+ * @since 1.2.6.1
+ */
+
+/**
+ * Dom操作工具包
+ * @unfile
+ * @module UE.dom.domUtils
+ */
+function getDomNode(node, start, ltr, startFromChild, fn, guard) {
+ var tmpNode = startFromChild && node[start],
+ parent;
+ !tmpNode && (tmpNode = node[ltr]);
+ while (!tmpNode && (parent = (parent || node).parentNode)) {
+ if (parent.tagName == "BODY" || (guard && !guard(parent))) {
+ return null;
+ }
+ tmpNode = parent[ltr];
+ }
+ if (tmpNode && fn && !fn(tmpNode)) {
+ return getDomNode(tmpNode, start, ltr, false, fn);
+ }
+ return tmpNode;
+}
+var attrFix = ie && browser.version < 9
+ ? {
+ tabindex: "tabIndex",
+ readonly: "readOnly",
+ for: "htmlFor",
+ class: "className",
+ maxlength: "maxLength",
+ cellspacing: "cellSpacing",
+ cellpadding: "cellPadding",
+ rowspan: "rowSpan",
+ colspan: "colSpan",
+ usemap: "useMap",
+ frameborder: "frameBorder"
+ }
+ : {
+ tabindex: "tabIndex",
+ readonly: "readOnly"
+ },
+ styleBlock = utils.listToMap([
+ "-webkit-box",
+ "-moz-box",
+ "block",
+ "list-item",
+ "table",
+ "table-row-group",
+ "table-header-group",
+ "table-footer-group",
+ "table-row",
+ "table-column-group",
+ "table-column",
+ "table-cell",
+ "table-caption"
+ ]);
+var domUtils = (dom.domUtils = {
+ //节点常量
+ NODE_ELEMENT: 1,
+ NODE_DOCUMENT: 9,
+ NODE_TEXT: 3,
+ NODE_COMMENT: 8,
+ NODE_DOCUMENT_FRAGMENT: 11,
+
+ //位置关系
+ POSITION_IDENTICAL: 0,
+ POSITION_DISCONNECTED: 1,
+ POSITION_FOLLOWING: 2,
+ POSITION_PRECEDING: 4,
+ POSITION_IS_CONTAINED: 8,
+ POSITION_CONTAINS: 16,
+ //ie6使用其他的会有一段空白出现
+ fillChar: ie && browser.version == "6" ? "\ufeff" : "\u200B",
+ //-------------------------Node部分--------------------------------
+ keys: {
+ /*Backspace*/ 8: 1,
+ /*Delete*/ 46: 1,
+ /*Shift*/ 16: 1,
+ /*Ctrl*/ 17: 1,
+ /*Alt*/ 18: 1,
+ 37: 1,
+ 38: 1,
+ 39: 1,
+ 40: 1,
+ 13: 1 /*enter*/
+ },
+ /**
+ * 获取节点A相对于节点B的位置关系
+ * @method getPosition
+ * @param { Node } nodeA 需要查询位置关系的节点A
+ * @param { Node } nodeB 需要查询位置关系的节点B
+ * @return { Number } 节点A与节点B的关系
+ * @example
+ * ```javascript
+ * //output: 20
+ * var position = UE.dom.domUtils.getPosition( document.documentElement, document.body );
+ *
+ * switch ( position ) {
+ *
+ * //0
+ * case UE.dom.domUtils.POSITION_IDENTICAL:
+ * console.log('元素相同');
+ * break;
+ * //1
+ * case UE.dom.domUtils.POSITION_DISCONNECTED:
+ * console.log('两个节点在不同的文档中');
+ * break;
+ * //2
+ * case UE.dom.domUtils.POSITION_FOLLOWING:
+ * console.log('节点A在节点B之后');
+ * break;
+ * //4
+ * case UE.dom.domUtils.POSITION_PRECEDING;
+ * console.log('节点A在节点B之前');
+ * break;
+ * //8
+ * case UE.dom.domUtils.POSITION_IS_CONTAINED:
+ * console.log('节点A被节点B包含');
+ * break;
+ * case 10:
+ * console.log('节点A被节点B包含且节点A在节点B之后');
+ * break;
+ * //16
+ * case UE.dom.domUtils.POSITION_CONTAINS:
+ * console.log('节点A包含节点B');
+ * break;
+ * case 20:
+ * console.log('节点A包含节点B且节点A在节点B之前');
+ * break;
+ *
+ * }
+ * ```
+ */
+ getPosition: function(nodeA, nodeB) {
+ // 如果两个节点是同一个节点
+ if (nodeA === nodeB) {
+ // domUtils.POSITION_IDENTICAL
+ return 0;
+ }
+ var node,
+ parentsA = [nodeA],
+ parentsB = [nodeB];
+ node = nodeA;
+ while ((node = node.parentNode)) {
+ // 如果nodeB是nodeA的祖先节点
+ if (node === nodeB) {
+ // domUtils.POSITION_IS_CONTAINED + domUtils.POSITION_FOLLOWING
+ return 10;
+ }
+ parentsA.push(node);
+ }
+ node = nodeB;
+ while ((node = node.parentNode)) {
+ // 如果nodeA是nodeB的祖先节点
+ if (node === nodeA) {
+ // domUtils.POSITION_CONTAINS + domUtils.POSITION_PRECEDING
+ return 20;
+ }
+ parentsB.push(node);
+ }
+ parentsA.reverse();
+ parentsB.reverse();
+ if (parentsA[0] !== parentsB[0]) {
+ // domUtils.POSITION_DISCONNECTED
+ return 1;
+ }
+ var i = -1;
+ while ((i++, parentsA[i] === parentsB[i])) {}
+ nodeA = parentsA[i];
+ nodeB = parentsB[i];
+ while ((nodeA = nodeA.nextSibling)) {
+ if (nodeA === nodeB) {
+ // domUtils.POSITION_PRECEDING
+ return 4;
+ }
+ }
+ // domUtils.POSITION_FOLLOWING
+ return 2;
+ },
+
+ /**
+ * 检测节点node在父节点中的索引位置
+ * @method getNodeIndex
+ * @param { Node } node 需要检测的节点对象
+ * @return { Number } 该节点在父节点中的位置
+ * @see UE.dom.domUtils.getNodeIndex(Node,Boolean)
+ */
+
+ /**
+ * 检测节点node在父节点中的索引位置, 根据给定的mergeTextNode参数决定是否要合并多个连续的文本节点为一个节点
+ * @method getNodeIndex
+ * @param { Node } node 需要检测的节点对象
+ * @param { Boolean } mergeTextNode 是否合并多个连续的文本节点为一个节点
+ * @return { Number } 该节点在父节点中的位置
+ * @example
+ * ```javascript
+ *
+ * var node = document.createElement("div");
+ *
+ * node.appendChild( document.createTextNode( "hello" ) );
+ * node.appendChild( document.createTextNode( "world" ) );
+ * node.appendChild( node = document.createElement( "div" ) );
+ *
+ * //output: 2
+ * console.log( UE.dom.domUtils.getNodeIndex( node ) );
+ *
+ * //output: 1
+ * console.log( UE.dom.domUtils.getNodeIndex( node, true ) );
+ *
+ * ```
+ */
+ getNodeIndex: function(node, ignoreTextNode) {
+ var preNode = node,
+ i = 0;
+ while ((preNode = preNode.previousSibling)) {
+ if (ignoreTextNode && preNode.nodeType == 3) {
+ if (preNode.nodeType != preNode.nextSibling.nodeType) {
+ i++;
+ }
+ continue;
+ }
+ i++;
+ }
+ return i;
+ },
+
+ /**
+ * 检测节点node是否在给定的document对象上
+ * @method inDoc
+ * @param { Node } node 需要检测的节点对象
+ * @param { DomDocument } doc 需要检测的document对象
+ * @return { Boolean } 该节点node是否在给定的document的dom树上
+ * @example
+ * ```javascript
+ *
+ * var node = document.createElement("div");
+ *
+ * //output: false
+ * console.log( UE.do.domUtils.inDoc( node, document ) );
+ *
+ * document.body.appendChild( node );
+ *
+ * //output: true
+ * console.log( UE.do.domUtils.inDoc( node, document ) );
+ *
+ * ```
+ */
+ inDoc: function(node, doc) {
+ return domUtils.getPosition(node, doc) == 10;
+ },
+ /**
+ * 根据给定的过滤规则filterFn, 查找符合该过滤规则的node节点的第一个祖先节点,
+ * 查找的起点是给定node节点的父节点。
+ * @method findParent
+ * @param { Node } node 需要查找的节点
+ * @param { Function } filterFn 自定义的过滤方法。
+ * @warning 查找的终点是到body节点为止
+ * @remind 自定义的过滤方法filterFn接受一个Node对象作为参数, 该对象代表当前执行检测的祖先节点。 如果该
+ * 节点满足过滤条件, 则要求返回true, 这时将直接返回该节点作为findParent()的结果, 否则, 请返回false。
+ * @return { Node | Null } 如果找到符合过滤条件的节点, 就返回该节点, 否则返回NULL
+ * @example
+ * ```javascript
+ * var filterNode = UE.dom.domUtils.findParent( document.body.firstChild, function ( node ) {
+ *
+ * //由于查找的终点是body节点, 所以永远也不会匹配当前过滤器的条件, 即这里永远会返回false
+ * return node.tagName === "HTML";
+ *
+ * } );
+ *
+ * //output: true
+ * console.log( filterNode === null );
+ * ```
+ */
+
+ /**
+ * 根据给定的过滤规则filterFn, 查找符合该过滤规则的node节点的第一个祖先节点,
+ * 如果includeSelf的值为true,则查找的起点是给定的节点node, 否则, 起点是node的父节点
+ * @method findParent
+ * @param { Node } node 需要查找的节点
+ * @param { Function } filterFn 自定义的过滤方法。
+ * @param { Boolean } includeSelf 查找过程是否包含自身
+ * @warning 查找的终点是到body节点为止
+ * @remind 自定义的过滤方法filterFn接受一个Node对象作为参数, 该对象代表当前执行检测的祖先节点。 如果该
+ * 节点满足过滤条件, 则要求返回true, 这时将直接返回该节点作为findParent()的结果, 否则, 请返回false。
+ * @remind 如果includeSelf为true, 则过滤器第一次执行时的参数会是节点本身。
+ * 反之, 过滤器第一次执行时的参数将是该节点的父节点。
+ * @return { Node | Null } 如果找到符合过滤条件的节点, 就返回该节点, 否则返回NULL
+ * @example
+ * ```html
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * ```
+ */
+ findParent: function(node, filterFn, includeSelf) {
+ if (node && !domUtils.isBody(node)) {
+ node = includeSelf ? node : node.parentNode;
+ while (node) {
+ if (!filterFn || filterFn(node) || domUtils.isBody(node)) {
+ return filterFn && !filterFn(node) && domUtils.isBody(node)
+ ? null
+ : node;
+ }
+ node = node.parentNode;
+ }
+ }
+ return null;
+ },
+ /**
+ * 查找node的节点名为tagName的第一个祖先节点, 查找的起点是node节点的父节点。
+ * @method findParentByTagName
+ * @param { Node } node 需要查找的节点对象
+ * @param { Array } tagNames 需要查找的父节点的名称数组
+ * @warning 查找的终点是到body节点为止
+ * @return { Node | NULL } 如果找到符合条件的节点, 则返回该节点, 否则返回NULL
+ * @example
+ * ```javascript
+ * var node = UE.dom.domUtils.findParentByTagName( document.getElementsByTagName("div")[0], [ "BODY" ] );
+ * //output: BODY
+ * console.log( node.tagName );
+ * ```
+ */
+
+ /**
+ * 查找node的节点名为tagName的祖先节点, 如果includeSelf的值为true,则查找的起点是给定的节点node,
+ * 否则, 起点是node的父节点。
+ * @method findParentByTagName
+ * @param { Node } node 需要查找的节点对象
+ * @param { Array } tagNames 需要查找的父节点的名称数组
+ * @param { Boolean } includeSelf 查找过程是否包含node节点自身
+ * @warning 查找的终点是到body节点为止
+ * @return { Node | NULL } 如果找到符合条件的节点, 则返回该节点, 否则返回NULL
+ * @example
+ * ```javascript
+ * var queryTarget = document.getElementsByTagName("div")[0];
+ * var node = UE.dom.domUtils.findParentByTagName( queryTarget, [ "DIV" ], true );
+ * //output: true
+ * console.log( queryTarget === node );
+ * ```
+ */
+ findParentByTagName: function(node, tagNames, includeSelf, excludeFn) {
+ tagNames = utils.listToMap(utils.isArray(tagNames) ? tagNames : [tagNames]);
+ return domUtils.findParent(
+ node,
+ function(node) {
+ return tagNames[node.tagName] && !(excludeFn && excludeFn(node));
+ },
+ includeSelf
+ );
+ },
+ /**
+ * 查找节点node的祖先节点集合, 查找的起点是给定节点的父节点,结果集中不包含给定的节点。
+ * @method findParents
+ * @param { Node } node 需要查找的节点对象
+ * @return { Array } 给定节点的祖先节点数组
+ * @grammar UE.dom.domUtils.findParents(node) => Array //返回一个祖先节点数组集合,不包含自身
+ * @grammar UE.dom.domUtils.findParents(node,includeSelf) => Array //返回一个祖先节点数组集合,includeSelf指定是否包含自身
+ * @grammar UE.dom.domUtils.findParents(node,includeSelf,filterFn) => Array //返回一个祖先节点数组集合,filterFn指定过滤条件,返回true的node将被选取
+ * @grammar UE.dom.domUtils.findParents(node,includeSelf,filterFn,closerFirst) => Array //返回一个祖先节点数组集合,closerFirst为true的话,node的直接父亲节点是数组的第0个
+ */
+
+ /**
+ * 查找节点node的祖先节点集合, 如果includeSelf的值为true,
+ * 则返回的结果集中允许出现当前给定的节点, 否则, 该节点不会出现在其结果集中。
+ * @method findParents
+ * @param { Node } node 需要查找的节点对象
+ * @param { Boolean } includeSelf 查找的结果中是否允许包含当前查找的节点对象
+ * @return { Array } 给定节点的祖先节点数组
+ */
+ findParents: function(node, includeSelf, filterFn, closerFirst) {
+ var parents = includeSelf && ((filterFn && filterFn(node)) || !filterFn)
+ ? [node]
+ : [];
+ while ((node = domUtils.findParent(node, filterFn))) {
+ parents.push(node);
+ }
+ return closerFirst ? parents : parents.reverse();
+ },
+
+ /**
+ * 在节点node后面插入新节点newNode
+ * @method insertAfter
+ * @param { Node } node 目标节点
+ * @param { Node } newNode 新插入的节点, 该节点将置于目标节点之后
+ * @return { Node } 新插入的节点
+ */
+ insertAfter: function(node, newNode) {
+ return node.nextSibling
+ ? node.parentNode.insertBefore(newNode, node.nextSibling)
+ : node.parentNode.appendChild(newNode);
+ },
+
+ /**
+ * 删除节点node及其下属的所有节点
+ * @method remove
+ * @param { Node } node 需要删除的节点对象
+ * @return { Node } 返回刚删除的节点对象
+ * @example
+ * ```html
+ *
+ *
+ * ```
+ */
+
+ /**
+ * 删除节点node,并根据keepChildren的值决定是否保留子节点
+ * @method remove
+ * @param { Node } node 需要删除的节点对象
+ * @param { Boolean } keepChildren 是否需要保留子节点
+ * @return { Node } 返回刚删除的节点对象
+ * @example
+ * ```html
+ *
+ *
+ * ```
+ */
+ remove: function(node, keepChildren) {
+ var parent = node.parentNode,
+ child;
+ if (parent) {
+ if (keepChildren && node.hasChildNodes()) {
+ while ((child = node.firstChild)) {
+ parent.insertBefore(child, node);
+ }
+ }
+ parent.removeChild(node);
+ }
+ return node;
+ },
+
+ /**
+ * 取得node节点的下一个兄弟节点, 如果该节点其后没有兄弟节点, 则递归查找其父节点之后的第一个兄弟节点,
+ * 直到找到满足条件的节点或者递归到BODY节点之后才会结束。
+ * @method getNextDomNode
+ * @param { Node } node 需要获取其后的兄弟节点的节点对象
+ * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL
+ * @example
+ * ```html
+ *
+ *
+ *
+ *
+ *
xxx
+ *
+ *
+ * ```
+ * @example
+ * ```html
+ *
+ *
+ *
+ * xxx
+ *
+ *
xxx
+ *
+ *
+ * ```
+ */
+
+ /**
+ * 取得node节点的下一个兄弟节点, 如果startFromChild的值为ture,则先获取其子节点,
+ * 如果有子节点则直接返回第一个子节点;如果没有子节点或者startFromChild的值为false,
+ * 则执行
getNextDomNode(Node node)的查找过程。
+ * @method getNextDomNode
+ * @param { Node } node 需要获取其后的兄弟节点的节点对象
+ * @param { Boolean } startFromChild 查找过程是否从其子节点开始
+ * @return { Node | NULL } 如果找满足条件的节点, 则返回该节点, 否则返回NULL
+ * @see UE.dom.domUtils.getNextDomNode(Node)
+ */
+ getNextDomNode: function(node, startFromChild, filterFn, guard) {
+ return getDomNode(
+ node,
+ "firstChild",
+ "nextSibling",
+ startFromChild,
+ filterFn,
+ guard
+ );
+ },
+ getPreDomNode: function(node, startFromChild, filterFn, guard) {
+ return getDomNode(
+ node,
+ "lastChild",
+ "previousSibling",
+ startFromChild,
+ filterFn,
+ guard
+ );
+ },
+ /**
+ * 检测节点node是否属是UEditor定义的bookmark节点
+ * @method isBookmarkNode
+ * @private
+ * @param { Node } node 需要检测的节点对象
+ * @return { Boolean } 是否是bookmark节点
+ * @example
+ * ```html
+ *
+ *
+ * ```
+ */
+ isBookmarkNode: function(node) {
+ return node.nodeType == 1 && node.id && /^_baidu_bookmark_/i.test(node.id);
+ },
+ /**
+ * 获取节点node所属的window对象
+ * @method getWindow
+ * @param { Node } node 节点对象
+ * @return { Window } 当前节点所属的window对象
+ * @example
+ * ```javascript
+ * //output: true
+ * console.log( UE.dom.domUtils.getWindow( document.body ) === window );
+ * ```
+ */
+ getWindow: function(node) {
+ var doc = node.ownerDocument || node;
+ return doc.defaultView || doc.parentWindow;
+ },
+ /**
+ * 获取离nodeA与nodeB最近的公共的祖先节点
+ * @method getCommonAncestor
+ * @param { Node } nodeA 第一个节点
+ * @param { Node } nodeB 第二个节点
+ * @remind 如果给定的两个节点是同一个节点, 将直接返回该节点。
+ * @return { Node | NULL } 如果未找到公共节点, 返回NULL, 否则返回最近的公共祖先节点。
+ * @example
+ * ```javascript
+ * var commonAncestor = UE.dom.domUtils.getCommonAncestor( document.body, document.body.firstChild );
+ * //output: true
+ * console.log( commonAncestor.tagName.toLowerCase() === 'body' );
+ * ```
+ */
+ getCommonAncestor: function(nodeA, nodeB) {
+ if (nodeA === nodeB) return nodeA;
+ var parentsA = [nodeA],
+ parentsB = [nodeB],
+ parent = nodeA,
+ i = -1;
+ while ((parent = parent.parentNode)) {
+ if (parent === nodeB) {
+ return parent;
+ }
+ parentsA.push(parent);
+ }
+ parent = nodeB;
+ while ((parent = parent.parentNode)) {
+ if (parent === nodeA) return parent;
+ parentsB.push(parent);
+ }
+ parentsA.reverse();
+ parentsB.reverse();
+ while ((i++, parentsA[i] === parentsB[i])) {}
+ return i == 0 ? null : parentsA[i - 1];
+ },
+ /**
+ * 清除node节点左右连续为空的兄弟inline节点
+ * @method clearEmptySibling
+ * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
+ * 则这些兄弟节点将被删除
+ * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext) //ignoreNext指定是否忽略右边空节点
+ * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext,ignorePre) //ignorePre指定是否忽略左边空节点
+ * @example
+ * ```html
+ *
+ *
+ *
+ *
+ *
+ *
xxx
+ *
+ *
+ *
+ * ```
+ */
+
+ /**
+ * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true,
+ * 则忽略对右边兄弟节点的操作。
+ * @method clearEmptySibling
+ * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
+ * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
+ * 则这些兄弟节点将被删除
+ * @see UE.dom.domUtils.clearEmptySibling(Node)
+ */
+
+ /**
+ * 清除node节点左右连续为空的兄弟inline节点, 如果ignoreNext的值为true,
+ * 则忽略对右边兄弟节点的操作, 如果ignorePre的值为true,则忽略对左边兄弟节点的操作。
+ * @method clearEmptySibling
+ * @param { Node } node 执行的节点对象, 如果该节点的左右连续的兄弟节点是空的inline节点,
+ * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
+ * @param { Boolean } ignorePre 是否忽略忽略对左边的兄弟节点的操作
+ * 则这些兄弟节点将被删除
+ * @see UE.dom.domUtils.clearEmptySibling(Node)
+ */
+ clearEmptySibling: function(node, ignoreNext, ignorePre) {
+ function clear(next, dir) {
+ var tmpNode;
+ while (
+ next &&
+ !domUtils.isBookmarkNode(next) &&
+ (domUtils.isEmptyInlineElement(next) ||
+ //这里不能把空格算进来会吧空格干掉,出现文字间的空格丢掉了
+ !new RegExp("[^\t\n\r" + domUtils.fillChar + "]").test(
+ next.nodeValue
+ ))
+ ) {
+ tmpNode = next[dir];
+ domUtils.remove(next);
+ next = tmpNode;
+ }
+ }
+ !ignoreNext && clear(node.nextSibling, "nextSibling");
+ !ignorePre && clear(node.previousSibling, "previousSibling");
+ },
+ /**
+ * 将一个文本节点textNode拆分成两个文本节点,offset指定拆分位置
+ * @method split
+ * @param { Node } textNode 需要拆分的文本节点对象
+ * @param { int } offset 需要拆分的位置, 位置计算从0开始
+ * @return { Node } 拆分后形成的新节点
+ * @example
+ * ```html
+ *
abcdef
+ *
+ * ```
+ */
+ split: function(node, offset) {
+ var doc = node.ownerDocument;
+ if (browser.ie && offset == node.nodeValue.length) {
+ var next = doc.createTextNode("");
+ return domUtils.insertAfter(node, next);
+ }
+ var retval = node.splitText(offset);
+ //ie8下splitText不会跟新childNodes,我们手动触发他的更新
+ if (browser.ie8) {
+ var tmpNode = doc.createTextNode("");
+ domUtils.insertAfter(retval, tmpNode);
+ domUtils.remove(tmpNode);
+ }
+ return retval;
+ },
+
+ /**
+ * 检测文本节点textNode是否为空节点(包括空格、换行、占位符等字符)
+ * @method isWhitespace
+ * @param { Node } node 需要检测的节点对象
+ * @return { Boolean } 检测的节点是否为空
+ * @example
+ * ```html
+ *
+ *
+ *
+ *
+ * ```
+ */
+ isWhitespace: function(node) {
+ return !new RegExp("[^ \t\n\r" + domUtils.fillChar + "]").test(
+ node.nodeValue
+ );
+ },
+ /**
+ * 获取元素element相对于viewport的位置坐标
+ * @method getXY
+ * @param { Node } element 需要计算位置的节点对象
+ * @return { Object } 返回形如{x:left,y:top}的一个key-value映射对象, 其中键x代表水平偏移距离,
+ * y代表垂直偏移距离。
+ *
+ * @example
+ * ```javascript
+ * var location = UE.dom.domUtils.getXY( document.getElementById("test") );
+ * //output: test的坐标为: 12, 24
+ * console.log( 'test的坐标为: ', location.x, ',', location.y );
+ * ```
+ */
+ getXY: function(element) {
+ var x = 0,
+ y = 0;
+ while (element.offsetParent) {
+ y += element.offsetTop;
+ x += element.offsetLeft;
+ element = element.offsetParent;
+ }
+ return { x: x, y: y };
+ },
+ /**
+ * 为元素element绑定原生DOM事件,type为事件类型,handler为处理函数
+ * @method on
+ * @param { Node } element 需要绑定事件的节点对象
+ * @param { String } type 绑定的事件类型
+ * @param { Function } handler 事件处理器
+ * @example
+ * ```javascript
+ * UE.dom.domUtils.on(document.body,"click",function(e){
+ * //e为事件对象,this为被点击元素对戏那个
+ * });
+ * ```
+ */
+
+ /**
+ * 为元素element绑定原生DOM事件,type为事件类型,handler为处理函数
+ * @method on
+ * @param { Node } element 需要绑定事件的节点对象
+ * @param { Array } type 绑定的事件类型数组
+ * @param { Function } handler 事件处理器
+ * @example
+ * ```javascript
+ * UE.dom.domUtils.on(document.body,["click","mousedown"],function(evt){
+ * //evt为事件对象,this为被点击元素对象
+ * });
+ * ```
+ */
+ on: function(element, type, handler) {
+ var types = utils.isArray(type) ? type : utils.trim(type).split(/\s+/),
+ k = types.length;
+ if (k)
+ while (k--) {
+ type = types[k];
+ if (element.addEventListener) {
+ element.addEventListener(type, handler, false);
+ } else {
+ if (!handler._d) {
+ handler._d = {
+ els: []
+ };
+ }
+ var key = type + handler.toString(),
+ index = utils.indexOf(handler._d.els, element);
+ if (!handler._d[key] || index == -1) {
+ if (index == -1) {
+ handler._d.els.push(element);
+ }
+ if (!handler._d[key]) {
+ handler._d[key] = function(evt) {
+ return handler.call(evt.srcElement, evt || window.event);
+ };
+ }
+
+ element.attachEvent("on" + type, handler._d[key]);
+ }
+ }
+ }
+ element = null;
+ },
+ /**
+ * 解除DOM事件绑定
+ * @method un
+ * @param { Node } element 需要解除事件绑定的节点对象
+ * @param { String } type 需要接触绑定的事件类型
+ * @param { Function } handler 对应的事件处理器
+ * @example
+ * ```javascript
+ * UE.dom.domUtils.un(document.body,"click",function(evt){
+ * //evt为事件对象,this为被点击元素对象
+ * });
+ * ```
+ */
+
+ /**
+ * 解除DOM事件绑定
+ * @method un
+ * @param { Node } element 需要解除事件绑定的节点对象
+ * @param { Array } type 需要接触绑定的事件类型数组
+ * @param { Function } handler 对应的事件处理器
+ * @example
+ * ```javascript
+ * UE.dom.domUtils.un(document.body, ["click","mousedown"],function(evt){
+ * //evt为事件对象,this为被点击元素对象
+ * });
+ * ```
+ */
+ un: function(element, type, handler) {
+ var types = utils.isArray(type) ? type : utils.trim(type).split(/\s+/),
+ k = types.length;
+ if (k)
+ while (k--) {
+ type = types[k];
+ if (element.removeEventListener) {
+ element.removeEventListener(type, handler, false);
+ } else {
+ var key = type + handler.toString();
+ try {
+ element.detachEvent(
+ "on" + type,
+ handler._d ? handler._d[key] : handler
+ );
+ } catch (e) {}
+ if (handler._d && handler._d[key]) {
+ var index = utils.indexOf(handler._d.els, element);
+ if (index != -1) {
+ handler._d.els.splice(index, 1);
+ }
+ handler._d.els.length == 0 && delete handler._d[key];
+ }
+ }
+ }
+ },
+
+ /**
+ * 比较节点nodeA与节点nodeB是否具有相同的标签名、属性名以及属性值
+ * @method isSameElement
+ * @param { Node } nodeA 需要比较的节点
+ * @param { Node } nodeB 需要比较的节点
+ * @return { Boolean } 两个节点是否具有相同的标签名、属性名以及属性值
+ * @example
+ * ```html
+ *
ssss
+ *
bbbbb
+ *
ssss
+ *
bbbbb
+ *
+ *
+ * ```
+ */
+ isSameElement: function(nodeA, nodeB) {
+ if (nodeA.tagName != nodeB.tagName) {
+ return false;
+ }
+ var thisAttrs = nodeA.attributes,
+ otherAttrs = nodeB.attributes;
+ if (!ie && thisAttrs.length != otherAttrs.length) {
+ return false;
+ }
+ var attrA,
+ attrB,
+ al = 0,
+ bl = 0;
+ for (var i = 0; (attrA = thisAttrs[i++]); ) {
+ if (attrA.nodeName == "style") {
+ if (attrA.specified) {
+ al++;
+ }
+ if (domUtils.isSameStyle(nodeA, nodeB)) {
+ continue;
+ } else {
+ return false;
+ }
+ }
+ if (ie) {
+ if (attrA.specified) {
+ al++;
+ attrB = otherAttrs.getNamedItem(attrA.nodeName);
+ } else {
+ continue;
+ }
+ } else {
+ attrB = nodeB.attributes[attrA.nodeName];
+ }
+ if (!attrB.specified || attrA.nodeValue != attrB.nodeValue) {
+ return false;
+ }
+ }
+ // 有可能attrB的属性包含了attrA的属性之外还有自己的属性
+ if (ie) {
+ for (i = 0; (attrB = otherAttrs[i++]); ) {
+ if (attrB.specified) {
+ bl++;
+ }
+ }
+ if (al != bl) {
+ return false;
+ }
+ }
+ return true;
+ },
+
+ /**
+ * 判断节点nodeA与节点nodeB的元素的style属性是否一致
+ * @method isSameStyle
+ * @param { Node } nodeA 需要比较的节点
+ * @param { Node } nodeB 需要比较的节点
+ * @return { Boolean } 两个节点是否具有相同的style属性值
+ * @example
+ * ```html
+ *
ssss
+ *
bbbbb
+ *
ssss
+ *
bbbbb
+ *
+ *
+ * ```
+ */
+ isSameStyle: function(nodeA, nodeB) {
+ var styleA = nodeA.style.cssText
+ .replace(/( ?; ?)/g, ";")
+ .replace(/( ?: ?)/g, ":"),
+ styleB = nodeB.style.cssText
+ .replace(/( ?; ?)/g, ";")
+ .replace(/( ?: ?)/g, ":");
+ if (browser.opera) {
+ styleA = nodeA.style;
+ styleB = nodeB.style;
+ if (styleA.length != styleB.length) return false;
+ for (var p in styleA) {
+ if (/^(\d+|csstext)$/i.test(p)) {
+ continue;
+ }
+ if (styleA[p] != styleB[p]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ if (!styleA || !styleB) {
+ return styleA == styleB;
+ }
+ styleA = styleA.split(";");
+ styleB = styleB.split(";");
+ if (styleA.length != styleB.length) {
+ return false;
+ }
+ for (var i = 0, ci; (ci = styleA[i++]); ) {
+ if (utils.indexOf(styleB, ci) == -1) {
+ return false;
+ }
+ }
+ return true;
+ },
+ /**
+ * 检查节点node是否为block元素
+ * @method isBlockElm
+ * @param { Node } node 需要检测的节点对象
+ * @return { Boolean } 是否是block元素节点
+ * @warning 该方法的判断规则如下: 如果该元素原本是block元素, 则不论该元素当前的css样式是什么都会返回true;
+ * 否则,检测该元素的css样式, 如果该元素当前是block元素, 则返回true。 其余情况下都返回false。
+ * @example
+ * ```html
+ *
+ *
+ *
+ *
+ *
+ * ```
+ */
+ isBlockElm: function(node) {
+ return (
+ node.nodeType == 1 &&
+ (dtd.$block[node.tagName] ||
+ styleBlock[domUtils.getComputedStyle(node, "display")]) &&
+ !dtd.$nonChild[node.tagName]
+ );
+ },
+ /**
+ * 检测node节点是否为body节点
+ * @method isBody
+ * @param { Element } node 需要检测的dom元素
+ * @return { Boolean } 给定的元素是否是body元素
+ * @example
+ * ```javascript
+ * //output: true
+ * console.log( UE.dom.domUtils.isBody( document.body ) );
+ * ```
+ */
+ isBody: function(node) {
+ return node && node.nodeType == 1 && node.tagName.toLowerCase() == "body";
+ },
+ /**
+ * 以node节点为分界,将该节点的指定祖先节点parent拆分成两个独立的节点,
+ * 拆分形成的两个节点之间是node节点
+ * @method breakParent
+ * @param { Node } node 作为分界的节点对象
+ * @param { Node } parent 该节点必须是node节点的祖先节点, 且是block节点。
+ * @return { Node } 给定的node分界节点
+ * @example
+ * ```javascript
+ *
+ * var node = document.createElement("span"),
+ * wrapNode = document.createElement( "div" ),
+ * parent = document.createElement("p");
+ *
+ * parent.appendChild( node );
+ * wrapNode.appendChild( parent );
+ *
+ * //拆分前
+ * //output:
+ * console.log( wrapNode.innerHTML );
+ *
+ *
+ * UE.dom.domUtils.breakParent( node, parent );
+ * //拆分后
+ * //output:
+ * console.log( wrapNode.innerHTML );
+ *
+ * ```
+ */
+ breakParent: function(node, parent) {
+ var tmpNode,
+ parentClone = node,
+ clone = node,
+ leftNodes,
+ rightNodes;
+ do {
+ parentClone = parentClone.parentNode;
+ if (leftNodes) {
+ tmpNode = parentClone.cloneNode(false);
+ tmpNode.appendChild(leftNodes);
+ leftNodes = tmpNode;
+ tmpNode = parentClone.cloneNode(false);
+ tmpNode.appendChild(rightNodes);
+ rightNodes = tmpNode;
+ } else {
+ leftNodes = parentClone.cloneNode(false);
+ rightNodes = leftNodes.cloneNode(false);
+ }
+ while ((tmpNode = clone.previousSibling)) {
+ leftNodes.insertBefore(tmpNode, leftNodes.firstChild);
+ }
+ while ((tmpNode = clone.nextSibling)) {
+ rightNodes.appendChild(tmpNode);
+ }
+ clone = parentClone;
+ } while (parent !== parentClone);
+ tmpNode = parent.parentNode;
+ tmpNode.insertBefore(leftNodes, parent);
+ tmpNode.insertBefore(rightNodes, parent);
+ tmpNode.insertBefore(node, rightNodes);
+ domUtils.remove(parent);
+ return node;
+ },
+ /**
+ * 检查节点node是否是空inline节点
+ * @method isEmptyInlineElement
+ * @param { Node } node 需要检测的节点对象
+ * @return { Number } 如果给定的节点是空的inline节点, 则返回1, 否则返回0。
+ * @example
+ * ```html
+ *
=> 1
+ *
=> 1
+ *
=> 1
+ *
xx => 0
+ * ```
+ */
+ isEmptyInlineElement: function(node) {
+ if (node.nodeType != 1 || !dtd.$removeEmpty[node.tagName]) {
+ return 0;
+ }
+ node = node.firstChild;
+ while (node) {
+ //如果是创建的bookmark就跳过
+ if (domUtils.isBookmarkNode(node)) {
+ return 0;
+ }
+ if (
+ (node.nodeType == 1 && !domUtils.isEmptyInlineElement(node)) ||
+ (node.nodeType == 3 && !domUtils.isWhitespace(node))
+ ) {
+ return 0;
+ }
+ node = node.nextSibling;
+ }
+ return 1;
+ },
+
+ /**
+ * 删除node节点下首尾两端的空白文本子节点
+ * @method trimWhiteTextNode
+ * @param { Element } node 需要执行删除操作的元素对象
+ * @example
+ * ```javascript
+ * var node = document.createElement("div");
+ *
+ * node.appendChild( document.createTextNode( "" ) );
+ *
+ * node.appendChild( document.createElement("div") );
+ *
+ * node.appendChild( document.createTextNode( "" ) );
+ *
+ * //3
+ * console.log( node.childNodes.length );
+ *
+ * UE.dom.domUtils.trimWhiteTextNode( node );
+ *
+ * //1
+ * console.log( node.childNodes.length );
+ * ```
+ */
+ trimWhiteTextNode: function(node) {
+ function remove(dir) {
+ var child;
+ while (
+ (child = node[dir]) &&
+ child.nodeType == 3 &&
+ domUtils.isWhitespace(child)
+ ) {
+ node.removeChild(child);
+ }
+ }
+ remove("firstChild");
+ remove("lastChild");
+ },
+
+ /**
+ * 合并node节点下相同的子节点
+ * @name mergeChild
+ * @desc
+ * UE.dom.domUtils.mergeChild(node,tagName) //tagName要合并的子节点的标签
+ * @example
+ *
xxaaxx
+ * ==> UE.dom.domUtils.mergeChild(node,'span')
+ *
xxaaxx
+ */
+ mergeChild: function(node, tagName, attrs) {
+ var list = domUtils.getElementsByTagName(node, node.tagName.toLowerCase());
+ for (var i = 0, ci; (ci = list[i++]); ) {
+ if (!ci.parentNode || domUtils.isBookmarkNode(ci)) {
+ continue;
+ }
+ //span单独处理
+ if (ci.tagName.toLowerCase() == "span") {
+ if (node === ci.parentNode) {
+ domUtils.trimWhiteTextNode(node);
+ if (node.childNodes.length == 1) {
+ node.style.cssText = ci.style.cssText + ";" + node.style.cssText;
+ domUtils.remove(ci, true);
+ continue;
+ }
+ }
+ ci.style.cssText = node.style.cssText + ";" + ci.style.cssText;
+ if (attrs) {
+ var style = attrs.style;
+ if (style) {
+ style = style.split(";");
+ for (var j = 0, s; (s = style[j++]); ) {
+ ci.style[utils.cssStyleToDomStyle(s.split(":")[0])] = s.split(
+ ":"
+ )[1];
+ }
+ }
+ }
+ if (domUtils.isSameStyle(ci, node)) {
+ domUtils.remove(ci, true);
+ }
+ continue;
+ }
+ if (domUtils.isSameElement(node, ci)) {
+ domUtils.remove(ci, true);
+ }
+ }
+ },
+
+ /**
+ * 原生方法getElementsByTagName的封装
+ * @method getElementsByTagName
+ * @param { Node } node 目标节点对象
+ * @param { String } tagName 需要查找的节点的tagName, 多个tagName以空格分割
+ * @return { Array } 符合条件的节点集合
+ */
+ getElementsByTagName: function(node, name, filter) {
+ if (filter && utils.isString(filter)) {
+ var className = filter;
+ filter = function(node) {
+ return domUtils.hasClass(node, className);
+ };
+ }
+ name = utils.trim(name).replace(/[ ]{2,}/g, " ").split(" ");
+ var arr = [];
+ for (var n = 0, ni; (ni = name[n++]); ) {
+ var list = node.getElementsByTagName(ni);
+ for (var i = 0, ci; (ci = list[i++]); ) {
+ if (!filter || filter(ci)) arr.push(ci);
+ }
+ }
+
+ return arr;
+ },
+ /**
+ * 将节点node提取到父节点上
+ * @method mergeToParent
+ * @param { Element } node 需要提取的元素对象
+ * @example
+ * ```html
+ *
+ *
+ *
+ * ```
+ */
+ mergeToParent: function(node) {
+ var parent = node.parentNode;
+ while (parent && dtd.$removeEmpty[parent.tagName]) {
+ if (parent.tagName == node.tagName || parent.tagName == "A") {
+ //针对a标签单独处理
+ domUtils.trimWhiteTextNode(parent);
+ //span需要特殊处理 不处理这样的情况
xxxxxxxxx
+ if (
+ (parent.tagName == "SPAN" && !domUtils.isSameStyle(parent, node)) ||
+ (parent.tagName == "A" && node.tagName == "SPAN")
+ ) {
+ if (parent.childNodes.length > 1 || parent !== node.parentNode) {
+ node.style.cssText =
+ parent.style.cssText + ";" + node.style.cssText;
+ parent = parent.parentNode;
+ continue;
+ } else {
+ parent.style.cssText += ";" + node.style.cssText;
+ //trace:952 a标签要保持下划线
+ if (parent.tagName == "A") {
+ parent.style.textDecoration = "underline";
+ }
+ }
+ }
+ if (parent.tagName != "A") {
+ parent === node.parentNode && domUtils.remove(node, true);
+ break;
+ }
+ }
+ parent = parent.parentNode;
+ }
+ },
+ /**
+ * 合并节点node的左右兄弟节点
+ * @method mergeSibling
+ * @param { Element } node 需要合并的目标节点
+ * @example
+ * ```html
+ *
xxxxoooxxxx
+ *
+ *
+ * ```
+ */
+
+ /**
+ * 合并节点node的左右兄弟节点, 可以根据给定的条件选择是否忽略合并左节点。
+ * @method mergeSibling
+ * @param { Element } node 需要合并的目标节点
+ * @param { Boolean } ignorePre 是否忽略合并左节点
+ * @example
+ * ```html
+ *
xxxxoooxxxx
+ *
+ *
+ * ```
+ */
+
+ /**
+ * 合并节点node的左右兄弟节点,可以根据给定的条件选择是否忽略合并左右节点。
+ * @method mergeSibling
+ * @param { Element } node 需要合并的目标节点
+ * @param { Boolean } ignorePre 是否忽略合并左节点
+ * @param { Boolean } ignoreNext 是否忽略合并右节点
+ * @remind 如果同时忽略左右节点, 则该操作什么也不会做
+ * @example
+ * ```html
+ *
xxxxoooxxxx
+ *
+ *
+ * ```
+ */
+ mergeSibling: function(node, ignorePre, ignoreNext) {
+ function merge(rtl, start, node) {
+ var next;
+ if (
+ (next = node[rtl]) &&
+ !domUtils.isBookmarkNode(next) &&
+ next.nodeType == 1 &&
+ domUtils.isSameElement(node, next)
+ ) {
+ while (next.firstChild) {
+ if (start == "firstChild") {
+ node.insertBefore(next.lastChild, node.firstChild);
+ } else {
+ node.appendChild(next.firstChild);
+ }
+ }
+ domUtils.remove(next);
+ }
+ }
+ !ignorePre && merge("previousSibling", "firstChild", node);
+ !ignoreNext && merge("nextSibling", "lastChild", node);
+ },
+
+ /**
+ * 设置节点node及其子节点不会被选中
+ * @method unSelectable
+ * @param { Element } node 需要执行操作的dom元素
+ * @remind 执行该操作后的节点, 将不能被鼠标选中
+ * @example
+ * ```javascript
+ * UE.dom.domUtils.unSelectable( document.body );
+ * ```
+ */
+ unSelectable: (ie && browser.ie9below) || browser.opera
+ ? function(node) {
+ //for ie9
+ node.onselectstart = function() {
+ return false;
+ };
+ node.onclick = node.onkeyup = node.onkeydown = function() {
+ return false;
+ };
+ node.unselectable = "on";
+ node.setAttribute("unselectable", "on");
+ for (var i = 0, ci; (ci = node.all[i++]); ) {
+ switch (ci.tagName.toLowerCase()) {
+ case "iframe":
+ case "textarea":
+ case "input":
+ case "select":
+ break;
+ default:
+ ci.unselectable = "on";
+ node.setAttribute("unselectable", "on");
+ }
+ }
+ }
+ : function(node) {
+ node.style.MozUserSelect = node.style.webkitUserSelect = node.style.msUserSelect = node.style.KhtmlUserSelect =
+ "none";
+ },
+ /**
+ * 删除节点node上的指定属性名称的属性
+ * @method removeAttributes
+ * @param { Node } node 需要删除属性的节点对象
+ * @param { String } attrNames 可以是空格隔开的多个属性名称,该操作将会依次删除相应的属性
+ * @example
+ * ```html
+ *
+ * xxxxx
+ *
+ *
+ *
+ * ```
+ */
+
+ /**
+ * 删除节点node上的指定属性名称的属性
+ * @method removeAttributes
+ * @param { Node } node 需要删除属性的节点对象
+ * @param { Array } attrNames 需要删除的属性名数组
+ * @example
+ * ```html
+ *
+ * xxxxx
+ *
+ *
+ *
+ * ```
+ */
+ removeAttributes: function(node, attrNames) {
+ attrNames = utils.isArray(attrNames)
+ ? attrNames
+ : utils.trim(attrNames).replace(/[ ]{2,}/g, " ").split(" ");
+ for (var i = 0, ci; (ci = attrNames[i++]); ) {
+ ci = attrFix[ci] || ci;
+ switch (ci) {
+ case "className":
+ node[ci] = "";
+ break;
+ case "style":
+ node.style.cssText = "";
+ var val = node.getAttributeNode("style");
+ !browser.ie && val && node.removeAttributeNode(val);
+ }
+ node.removeAttribute(ci);
+ }
+ },
+ /**
+ * 在doc下创建一个标签名为tag,属性为attrs的元素
+ * @method createElement
+ * @param { DomDocument } doc 新创建的元素属于该document节点创建
+ * @param { String } tagName 需要创建的元素的标签名
+ * @param { Object } attrs 新创建的元素的属性key-value集合
+ * @return { Element } 新创建的元素对象
+ * @example
+ * ```javascript
+ * var ele = UE.dom.domUtils.createElement( document, 'div', {
+ * id: 'test'
+ * } );
+ *
+ * //output: DIV
+ * console.log( ele.tagName );
+ *
+ * //output: test
+ * console.log( ele.id );
+ *
+ * ```
+ */
+ createElement: function(doc, tag, attrs) {
+ return domUtils.setAttributes(doc.createElement(tag), attrs);
+ },
+ /**
+ * 为节点node添加属性attrs,attrs为属性键值对
+ * @method setAttributes
+ * @param { Element } node 需要设置属性的元素对象
+ * @param { Object } attrs 需要设置的属性名-值对
+ * @return { Element } 设置属性的元素对象
+ * @example
+ * ```html
+ *
+ *
+ *
+ *
+ */
+ setAttributes: function(node, attrs) {
+ for (var attr in attrs) {
+ if (attrs.hasOwnProperty(attr)) {
+ var value = attrs[attr];
+ switch (attr) {
+ case "class":
+ //ie下要这样赋值,setAttribute不起作用
+ node.className = value;
+ break;
+ case "style":
+ node.style.cssText = node.style.cssText + ";" + value;
+ break;
+ case "innerHTML":
+ node[attr] = value;
+ break;
+ case "value":
+ node.value = value;
+ break;
+ default:
+ node.setAttribute(attrFix[attr] || attr, value);
+ }
+ }
+ }
+ return node;
+ },
+
+ /**
+ * 获取元素element经过计算后的样式值
+ * @method getComputedStyle
+ * @param { Element } element 需要获取样式的元素对象
+ * @param { String } styleName 需要获取的样式名
+ * @return { String } 获取到的样式值
+ * @example
+ * ```html
+ *
+ *
+ *
+ *
+ *
+ * ```
+ */
+ getComputedStyle: function(element, styleName) {
+ //一下的属性单独处理
+ var pros = "width height top left";
+
+ if (pros.indexOf(styleName) > -1) {
+ return (
+ element[
+ "offset" +
+ styleName.replace(/^\w/, function(s) {
+ return s.toUpperCase();
+ })
+ ] + "px"
+ );
+ }
+ //忽略文本节点
+ if (element.nodeType == 3) {
+ element = element.parentNode;
+ }
+ //ie下font-size若body下定义了font-size,则从currentStyle里会取到这个font-size. 取不到实际值,故此修改.
+ if (
+ browser.ie &&
+ browser.version < 9 &&
+ styleName == "font-size" &&
+ !element.style.fontSize &&
+ !dtd.$empty[element.tagName] &&
+ !dtd.$nonChild[element.tagName]
+ ) {
+ var span = element.ownerDocument.createElement("span");
+ span.style.cssText = "padding:0;border:0;font-family:simsun;";
+ span.innerHTML = ".";
+ element.appendChild(span);
+ var result = span.offsetHeight;
+ element.removeChild(span);
+ span = null;
+ return result + "px";
+ }
+ try {
+ var value = domUtils.getStyle(element, styleName) ||
+ (window.getComputedStyle
+ ? domUtils.getWindow(element).getComputedStyle(element, "").getPropertyValue(styleName)
+ : (element.currentStyle || element.style)[utils.cssStyleToDomStyle(styleName)]);
+ } catch (e) {
+ return "";
+ }
+ return utils.transUnitToPx(utils.fixColor(styleName, value));
+ },
+ /**
+ * 删除元素element指定的className
+ * @method removeClasses
+ * @param { Element } ele 需要删除class的元素节点
+ * @param { String } classNames 需要删除的className, 多个className之间以空格分开
+ * @example
+ * ```html
+ *
xxx
+ *
+ *
+ * ```
+ */
+
+ /**
+ * 删除元素element指定的className
+ * @method removeClasses
+ * @param { Element } ele 需要删除class的元素节点
+ * @param { Array } classNames 需要删除的className数组
+ * @example
+ * ```html
+ *
xxx
+ *
+ *
+ * ```
+ */
+ removeClasses: function(elm, classNames) {
+ classNames = utils.isArray(classNames)
+ ? classNames
+ : utils.trim(classNames).replace(/[ ]{2,}/g, " ").split(" ");
+ for (var i = 0, ci, cls = elm.className; (ci = classNames[i++]); ) {
+ cls = cls.replace(new RegExp("\\b" + ci + "\\b"), "");
+ }
+ cls = utils.trim(cls).replace(/[ ]{2,}/g, " ");
+ if (cls) {
+ elm.className = cls;
+ } else {
+ domUtils.removeAttributes(elm, ["class"]);
+ }
+ },
+ /**
+ * 给元素element添加className
+ * @method addClass
+ * @param { Node } ele 需要增加className的元素
+ * @param { String } classNames 需要添加的className, 多个className之间以空格分割
+ * @remind 相同的类名不会被重复添加
+ * @example
+ * ```html
+ *
+ *
+ *
+ * ```
+ */
+
+ /**
+ * 判断元素element是否包含给定的样式类名className
+ * @method hasClass
+ * @param { Node } ele 需要检测的元素
+ * @param { Array } classNames 需要检测的className数组
+ * @return { Boolean } 元素是否包含所有给定的className
+ * @example
+ * ```html
+ *
+ *
+ *
+ * ```
+ */
+ hasClass: function(element, className) {
+ if (utils.isRegExp(className)) {
+ return className.test(element.className);
+ }
+ className = utils.trim(className).replace(/[ ]{2,}/g, " ").split(" ");
+ for (var i = 0, ci, cls = element.className; (ci = className[i++]); ) {
+ if (!new RegExp("\\b" + ci + "\\b", "i").test(cls)) {
+ return false;
+ }
+ }
+ return i - 1 == className.length;
+ },
+
+ /**
+ * 阻止事件默认行为
+ * @method preventDefault
+ * @param { Event } evt 需要阻止默认行为的事件对象
+ * @example
+ * ```javascript
+ * UE.dom.domUtils.preventDefault( evt );
+ * ```
+ */
+ preventDefault: function(evt) {
+ evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
+ },
+ /**
+ * 删除元素element指定的样式
+ * @method removeStyle
+ * @param { Element } element 需要删除样式的元素
+ * @param { String } styleName 需要删除的样式名
+ * @example
+ * ```html
+ *
+ *
+ *
+ * ```
+ */
+ removeStyle: function(element, name) {
+ if (browser.ie) {
+ //针对color先单独处理一下
+ if (name == "color") {
+ name = "(^|;)" + name;
+ }
+ element.style.cssText = element.style.cssText.replace(
+ new RegExp(name + "[^:]*:[^;]+;?", "ig"),
+ ""
+ );
+ } else {
+ if (element.style.removeProperty) {
+ element.style.removeProperty(name);
+ } else {
+ element.style.removeAttribute(utils.cssStyleToDomStyle(name));
+ }
+ }
+
+ if (!element.style.cssText) {
+ domUtils.removeAttributes(element, ["style"]);
+ }
+ },
+ /**
+ * 获取元素element的style属性的指定值
+ * @method getStyle
+ * @param { Element } element 需要获取属性值的元素
+ * @param { String } styleName 需要获取的style的名称
+ * @warning 该方法仅获取元素style属性中所标明的值
+ * @return { String } 该元素包含指定的style属性值
+ * @example
+ * ```html
+ *
+ *
+ *
+ * ```
+ */
+ getStyle: function(element, name) {
+ var value = element.style[utils.cssStyleToDomStyle(name)];
+ return utils.fixColor(name, value);
+ },
+ /**
+ * 为元素element设置样式属性值
+ * @method setStyle
+ * @param { Element } element 需要设置样式的元素
+ * @param { String } styleName 样式名
+ * @param { String } styleValue 样式值
+ * @example
+ * ```html
+ *
+ *
+ *
+ * ```
+ */
+ setStyle: function(element, name, value) {
+ element.style[utils.cssStyleToDomStyle(name)] = value;
+ if (!utils.trim(element.style.cssText)) {
+ this.removeAttributes(element, "style");
+ }
+ },
+ /**
+ * 为元素element设置多个样式属性值
+ * @method setStyles
+ * @param { Element } element 需要设置样式的元素
+ * @param { Object } styles 样式名值对
+ * @example
+ * ```html
+ *
+ *
+ *
+ * ```
+ */
+ setStyles: function(element, styles) {
+ for (var name in styles) {
+ if (styles.hasOwnProperty(name)) {
+ domUtils.setStyle(element, name, styles[name]);
+ }
+ }
+ },
+ /**
+ * 删除_moz_dirty属性
+ * @private
+ * @method removeDirtyAttr
+ */
+ removeDirtyAttr: function(node) {
+ for (
+ var i = 0, ci, nodes = node.getElementsByTagName("*");
+ (ci = nodes[i++]);
+
+ ) {
+ ci.removeAttribute("_moz_dirty");
+ }
+ node.removeAttribute("_moz_dirty");
+ },
+ /**
+ * 获取子节点的数量
+ * @method getChildCount
+ * @param { Element } node 需要检测的元素
+ * @return { Number } 给定的node元素的子节点数量
+ * @example
+ * ```html
+ *
+ *
+ *
+ *
+ *
+ * ```
+ */
+
+ /**
+ * 根据给定的过滤规则, 获取符合条件的子节点的数量
+ * @method getChildCount
+ * @param { Element } node 需要检测的元素
+ * @param { Function } fn 过滤器, 要求对符合条件的子节点返回true, 反之则要求返回false
+ * @return { Number } 符合过滤条件的node元素的子节点数量
+ * @example
+ * ```html
+ *
+ *
+ *
+ *
+ *
+ * ```
+ */
+ getChildCount: function(node, fn) {
+ var count = 0,
+ first = node.firstChild;
+ fn =
+ fn ||
+ function() {
+ return 1;
+ };
+ while (first) {
+ if (fn(first)) {
+ count++;
+ }
+ first = first.nextSibling;
+ }
+ return count;
+ },
+
+ /**
+ * 判断给定节点是否为空节点
+ * @method isEmptyNode
+ * @param { Node } node 需要检测的节点对象
+ * @return { Boolean } 节点是否为空
+ * @example
+ * ```javascript
+ * UE.dom.domUtils.isEmptyNode( document.body );
+ * ```
+ */
+ isEmptyNode: function(node) {
+ return (
+ !node.firstChild ||
+ domUtils.getChildCount(node, function(node) {
+ return (
+ !domUtils.isBr(node) &&
+ !domUtils.isBookmarkNode(node) &&
+ !domUtils.isWhitespace(node)
+ );
+ }) == 0
+ );
+ },
+ clearSelectedArr: function(nodes) {
+ var node;
+ while ((node = nodes.pop())) {
+ domUtils.removeAttributes(node, ["class"]);
+ }
+ },
+ /**
+ * 将显示区域滚动到指定节点的位置
+ * @method scrollToView
+ * @param {Node} node 节点
+ * @param {window} win window对象
+ * @param {Number} offsetTop 距离上方的偏移量
+ */
+ scrollToView: function(node, win, offsetTop) {
+ var getViewPaneSize = function() {
+ var doc = win.document,
+ mode = doc.compatMode == "CSS1Compat";
+ return {
+ width:
+ (mode ? doc.documentElement.clientWidth : doc.body.clientWidth) || 0,
+ height:
+ (mode ? doc.documentElement.clientHeight : doc.body.clientHeight) || 0
+ };
+ },
+ getScrollPosition = function(win) {
+ if ("pageXOffset" in win) {
+ return {
+ x: win.pageXOffset || 0,
+ y: win.pageYOffset || 0
+ };
+ } else {
+ var doc = win.document;
+ return {
+ x: doc.documentElement.scrollLeft || doc.body.scrollLeft || 0,
+ y: doc.documentElement.scrollTop || doc.body.scrollTop || 0
+ };
+ }
+ };
+ var winHeight = getViewPaneSize().height,
+ offset = winHeight * -1 + offsetTop;
+ offset += node.offsetHeight || 0;
+ var elementPosition = domUtils.getXY(node);
+ offset += elementPosition.y;
+ var currentScroll = getScrollPosition(win).y;
+ // offset += 50;
+ if (offset > currentScroll || offset < currentScroll - winHeight) {
+ win.scrollTo(0, offset + (offset < 0 ? -20 : 20));
+ }
+ },
+ /**
+ * 判断给定节点是否为br
+ * @method isBr
+ * @param { Node } node 需要判断的节点对象
+ * @return { Boolean } 给定的节点是否是br节点
+ */
+ isBr: function(node) {
+ return node.nodeType == 1 && node.tagName == "BR";
+ },
+ /**
+ * 判断给定的节点是否是一个“填充”节点
+ * @private
+ * @method isFillChar
+ * @param { Node } node 需要判断的节点
+ * @param { Boolean } isInStart 是否从节点内容的开始位置匹配
+ * @returns { Boolean } 节点是否是填充节点
+ */
+ isFillChar: function(node, isInStart) {
+ if (node.nodeType != 3) return false;
+ var text = node.nodeValue;
+ if (isInStart) {
+ return new RegExp("^" + domUtils.fillChar).test(text);
+ }
+ return !text.replace(new RegExp(domUtils.fillChar, "g"), "").length;
+ },
+ isStartInblock: function(range) {
+ var tmpRange = range.cloneRange(),
+ flag = 0,
+ start = tmpRange.startContainer,
+ tmp;
+ if (start.nodeType == 1 && start.childNodes[tmpRange.startOffset]) {
+ start = start.childNodes[tmpRange.startOffset];
+ var pre = start.previousSibling;
+ while (pre && domUtils.isFillChar(pre)) {
+ start = pre;
+ pre = pre.previousSibling;
+ }
+ }
+ if (this.isFillChar(start, true) && tmpRange.startOffset == 1) {
+ tmpRange.setStartBefore(start);
+ start = tmpRange.startContainer;
+ }
+
+ while (start && domUtils.isFillChar(start)) {
+ tmp = start;
+ start = start.previousSibling;
+ }
+ if (tmp) {
+ tmpRange.setStartBefore(tmp);
+ start = tmpRange.startContainer;
+ }
+ if (
+ start.nodeType == 1 &&
+ domUtils.isEmptyNode(start) &&
+ tmpRange.startOffset == 1
+ ) {
+ tmpRange.setStart(start, 0).collapse(true);
+ }
+ while (!tmpRange.startOffset) {
+ start = tmpRange.startContainer;
+ if (domUtils.isBlockElm(start) || domUtils.isBody(start)) {
+ flag = 1;
+ break;
+ }
+ var pre = tmpRange.startContainer.previousSibling,
+ tmpNode;
+ if (!pre) {
+ tmpRange.setStartBefore(tmpRange.startContainer);
+ } else {
+ while (pre && domUtils.isFillChar(pre)) {
+ tmpNode = pre;
+ pre = pre.previousSibling;
+ }
+ if (tmpNode) {
+ tmpRange.setStartBefore(tmpNode);
+ } else {
+ tmpRange.setStartBefore(tmpRange.startContainer);
+ }
+ }
+ }
+ return flag && !domUtils.isBody(tmpRange.startContainer) ? 1 : 0;
+ },
+
+ /**
+ * 判断给定的元素是否是一个空元素
+ * @method isEmptyBlock
+ * @param { Element } node 需要判断的元素
+ * @return { Boolean } 是否是空元素
+ * @example
+ * ```html
+ *
+ *
+ *
+ * ```
+ */
+
+ /**
+ * 根据指定的判断规则判断给定的元素是否是一个空元素
+ * @method isEmptyBlock
+ * @param { Element } node 需要判断的元素
+ * @param { RegExp } reg 对内容执行判断的正则表达式对象
+ * @return { Boolean } 是否是空元素
+ */
+ isEmptyBlock: function(node, reg) {
+ if (node.nodeType != 1) return 0;
+ reg = reg || new RegExp("[ \xa0\t\r\n" + domUtils.fillChar + "]", "g");
+
+ if (
+ node[browser.ie ? "innerText" : "textContent"].replace(reg, "").length > 0
+ ) {
+ return 0;
+ }
+ for (var n in dtd.$isNotEmpty) {
+ if (node.getElementsByTagName(n).length) {
+ return 0;
+ }
+ }
+ return 1;
+ },
+
+ /**
+ * 移动元素使得该元素的位置移动指定的偏移量的距离
+ * @method setViewportOffset
+ * @param { Element } element 需要设置偏移量的元素
+ * @param { Object } offset 偏移量, 形如{ left: 100, top: 50 }的一个键值对, 表示该元素将在
+ * 现有的位置上向水平方向偏移offset.left的距离, 在竖直方向上偏移
+ * offset.top的距离
+ * @example
+ * ```html
+ *
+ *
+ *
+ * ```
+ */
+ setViewportOffset: function(element, offset) {
+ var left = parseInt(element.style.left) | 0;
+ var top = parseInt(element.style.top) | 0;
+ var rect = element.getBoundingClientRect();
+ var offsetLeft = offset.left - rect.left;
+ var offsetTop = offset.top - rect.top;
+ if (offsetLeft) {
+ element.style.left = left + offsetLeft + "px";
+ }
+ if (offsetTop) {
+ element.style.top = top + offsetTop + "px";
+ }
+ },
+
+ /**
+ * 用“填充字符”填充节点
+ * @method fillNode
+ * @private
+ * @param { DomDocument } doc 填充的节点所在的docment对象
+ * @param { Node } node 需要填充的节点对象
+ * @example
+ * ```html
+ *
+ *
+ *
+ * ```
+ */
+ fillNode: function(doc, node) {
+ var tmpNode = browser.ie
+ ? doc.createTextNode(domUtils.fillChar)
+ : doc.createElement("br");
+ node.innerHTML = "";
+ node.appendChild(tmpNode);
+ },
+
+ /**
+ * 把节点src的所有子节点追加到另一个节点tag上去
+ * @method moveChild
+ * @param { Node } src 源节点, 该节点下的所有子节点将被移除
+ * @param { Node } tag 目标节点, 从源节点移除的子节点将被追加到该节点下
+ * @example
+ * ```html
+ *
+ *
+ *
+ *
+ *
+ *
+ * ```
+ */
+
+ /**
+ * 把节点src的所有子节点移动到另一个节点tag上去, 可以通过dir参数控制附加的行为是“追加”还是“插入顶部”
+ * @method moveChild
+ * @param { Node } src 源节点, 该节点下的所有子节点将被移除
+ * @param { Node } tag 目标节点, 从源节点移除的子节点将被附加到该节点下
+ * @param { Boolean } dir 附加方式, 如果为true, 则附加进去的节点将被放到目标节点的顶部, 反之,则放到末尾
+ * @example
+ * ```html
+ *
+ *
+ *
+ *
+ *
+ *
+ * ```
+ */
+ moveChild: function(src, tag, dir) {
+ while (src.firstChild) {
+ if (dir && tag.firstChild) {
+ tag.insertBefore(src.lastChild, tag.firstChild);
+ } else {
+ tag.appendChild(src.firstChild);
+ }
+ }
+ },
+
+ /**
+ * 判断节点的标签上是否不存在任何属性
+ * @method hasNoAttributes
+ * @private
+ * @param { Node } node 需要检测的节点对象
+ * @return { Boolean } 节点是否不包含任何属性
+ * @example
+ * ```html
+ *
xxxx
+ *
+ *
+ * ```
+ */
+ hasNoAttributes: function(node) {
+ return browser.ie
+ ? /^<\w+\s*?>/.test(node.outerHTML)
+ : node.attributes.length == 0;
+ },
+
+ /**
+ * 检测节点是否是UEditor所使用的辅助节点
+ * @method isCustomeNode
+ * @private
+ * @param { Node } node 需要检测的节点
+ * @remind 辅助节点是指编辑器要完成工作临时添加的节点, 在输出的时候将会从编辑器内移除, 不会影响最终的结果。
+ * @return { Boolean } 给定的节点是否是一个辅助节点
+ */
+ isCustomeNode: function(node) {
+ return node.nodeType == 1 && node.getAttribute("_ue_custom_node_");
+ },
+
+ /**
+ * 检测节点的标签是否是给定的标签
+ * @method isTagNode
+ * @param { Node } node 需要检测的节点对象
+ * @param { String } tagName 标签
+ * @return { Boolean } 节点的标签是否是给定的标签
+ * @example
+ * ```html
+ *
+ *
+ *
+ * ```
+ */
+ isTagNode: function(node, tagNames) {
+ return (
+ node.nodeType == 1 &&
+ new RegExp("\\b" + node.tagName + "\\b", "i").test(tagNames)
+ );
+ },
+
+ /**
+ * 给定一个节点数组,在通过指定的过滤器过滤后, 获取其中满足过滤条件的第一个节点
+ * @method filterNodeList
+ * @param { Array } nodeList 需要过滤的节点数组
+ * @param { Function } fn 过滤器, 对符合条件的节点, 执行结果返回true, 反之则返回false
+ * @return { Node | NULL } 如果找到符合过滤条件的节点, 则返回该节点, 否则返回NULL
+ * @example
+ * ```javascript
+ * var divNodes = document.getElementsByTagName("div");
+ * divNodes = [].slice.call( divNodes, 0 );
+ *
+ * //output: null
+ * console.log( UE.dom.domUtils.filterNodeList( divNodes, function ( node ) {
+ * return node.tagName.toLowerCase() !== 'div';
+ * } ) );
+ * ```
+ */
+
+ /**
+ * 给定一个节点数组nodeList和一组标签名tagNames, 获取其中能够匹配标签名的节点集合中的第一个节点
+ * @method filterNodeList
+ * @param { Array } nodeList 需要过滤的节点数组
+ * @param { String } tagNames 需要匹配的标签名, 多个标签名之间用空格分割
+ * @return { Node | NULL } 如果找到标签名匹配的节点, 则返回该节点, 否则返回NULL
+ * @example
+ * ```javascript
+ * var divNodes = document.getElementsByTagName("div");
+ * divNodes = [].slice.call( divNodes, 0 );
+ *
+ * //output: null
+ * console.log( UE.dom.domUtils.filterNodeList( divNodes, 'a span' ) );
+ * ```
+ */
+
+ /**
+ * 给定一个节点数组,在通过指定的过滤器过滤后, 如果参数forAll为true, 则会返回所有满足过滤
+ * 条件的节点集合, 否则, 返回满足条件的节点集合中的第一个节点
+ * @method filterNodeList
+ * @param { Array } nodeList 需要过滤的节点数组
+ * @param { Function } fn 过滤器, 对符合条件的节点, 执行结果返回true, 反之则返回false
+ * @param { Boolean } forAll 是否返回整个节点数组, 如果该参数为false, 则返回节点集合中的第一个节点
+ * @return { Array | Node | NULL } 如果找到符合过滤条件的节点, 则根据参数forAll的值决定返回满足
+ * 过滤条件的节点数组或第一个节点, 否则返回NULL
+ * @example
+ * ```javascript
+ * var divNodes = document.getElementsByTagName("div");
+ * divNodes = [].slice.call( divNodes, 0 );
+ *
+ * //output: 3(假定有3个div)
+ * console.log( divNodes.length );
+ *
+ * var nodes = UE.dom.domUtils.filterNodeList( divNodes, function ( node ) {
+ * return node.tagName.toLowerCase() === 'div';
+ * }, true );
+ *
+ * //output: 3
+ * console.log( nodes.length );
+ *
+ * var node = UE.dom.domUtils.filterNodeList( divNodes, function ( node ) {
+ * return node.tagName.toLowerCase() === 'div';
+ * }, false );
+ *
+ * //output: div
+ * console.log( node.nodeName );
+ * ```
+ */
+ filterNodeList: function(nodelist, filter, forAll) {
+ var results = [];
+ if (!utils.isFunction(filter)) {
+ var str = filter;
+ filter = function(n) {
+ return (
+ utils.indexOf(
+ utils.isArray(str) ? str : str.split(" "),
+ n.tagName.toLowerCase()
+ ) != -1
+ );
+ };
+ }
+ utils.each(nodelist, function(n) {
+ filter(n) && results.push(n);
+ });
+ return results.length == 0
+ ? null
+ : results.length == 1 || !forAll ? results[0] : results;
+ },
+
+ /**
+ * 查询给定的range选区是否在给定的node节点内,且在该节点的最末尾
+ * @method isInNodeEndBoundary
+ * @param { UE.dom.Range } rng 需要判断的range对象, 该对象的startContainer不能为NULL
+ * @param node 需要检测的节点对象
+ * @return { Number } 如果给定的选取range对象是在node内部的最末端, 则返回1, 否则返回0
+ */
+ isInNodeEndBoundary: function(rng, node) {
+ var start = rng.startContainer;
+ if (start.nodeType == 3 && rng.startOffset != start.nodeValue.length) {
+ return 0;
+ }
+ if (start.nodeType == 1 && rng.startOffset != start.childNodes.length) {
+ return 0;
+ }
+ while (start !== node) {
+ if (start.nextSibling) {
+ return 0;
+ }
+ start = start.parentNode;
+ }
+ return 1;
+ },
+ isBoundaryNode: function(node, dir) {
+ var tmp;
+ while (!domUtils.isBody(node)) {
+ tmp = node;
+ node = node.parentNode;
+ if (tmp !== node[dir]) {
+ return false;
+ }
+ }
+ return true;
+ },
+ fillHtml: browser.ie11below ? " " : "
"
+});
+var fillCharReg = new RegExp(domUtils.fillChar, "g");
+
+
+// core/Range.js
+/**
+ * Range封装
+ * @file
+ * @module UE.dom
+ * @class Range
+ * @since 1.2.6.1
+ */
+
+/**
+ * dom操作封装
+ * @unfile
+ * @module UE.dom
+ */
+
+/**
+ * Range实现类,本类是UEditor底层核心类,封装不同浏览器之间的Range操作。
+ * @unfile
+ * @module UE.dom
+ * @class Range
+ */
+
+;(function() {
+ var guid = 0,
+ fillChar = domUtils.fillChar,
+ fillData;
+
+ /**
+ * 更新range的collapse状态
+ * @param {Range} range range对象
+ */
+ function updateCollapse(range) {
+ range.collapsed =
+ range.startContainer &&
+ range.endContainer &&
+ range.startContainer === range.endContainer &&
+ range.startOffset == range.endOffset;
+ }
+
+ function selectOneNode(rng) {
+ return (
+ !rng.collapsed &&
+ rng.startContainer.nodeType == 1 &&
+ rng.startContainer === rng.endContainer &&
+ rng.endOffset - rng.startOffset == 1
+ );
+ }
+ function setEndPoint(toStart, node, offset, range) {
+ //如果node是自闭合标签要处理
+ if (
+ node.nodeType == 1 &&
+ (dtd.$empty[node.tagName] || dtd.$nonChild[node.tagName])
+ ) {
+ offset = domUtils.getNodeIndex(node) + (toStart ? 0 : 1);
+ node = node.parentNode;
+ }
+ if (toStart) {
+ range.startContainer = node;
+ range.startOffset = offset;
+ if (!range.endContainer) {
+ range.collapse(true);
+ }
+ } else {
+ range.endContainer = node;
+ range.endOffset = offset;
+ if (!range.startContainer) {
+ range.collapse(false);
+ }
+ }
+ updateCollapse(range);
+ return range;
+ }
+
+ function execContentsAction(range, action) {
+ //调整边界
+ //range.includeBookmark();
+ var start = range.startContainer,
+ end = range.endContainer,
+ startOffset = range.startOffset,
+ endOffset = range.endOffset,
+ doc = range.document,
+ frag = doc.createDocumentFragment(),
+ tmpStart,
+ tmpEnd;
+ if (start.nodeType == 1) {
+ start =
+ start.childNodes[startOffset] ||
+ (tmpStart = start.appendChild(doc.createTextNode("")));
+ }
+ if (end.nodeType == 1) {
+ end =
+ end.childNodes[endOffset] ||
+ (tmpEnd = end.appendChild(doc.createTextNode("")));
+ }
+ if (start === end && start.nodeType == 3) {
+ frag.appendChild(
+ doc.createTextNode(
+ start.substringData(startOffset, endOffset - startOffset)
+ )
+ );
+ //is not clone
+ if (action) {
+ start.deleteData(startOffset, endOffset - startOffset);
+ range.collapse(true);
+ }
+ return frag;
+ }
+ var current,
+ currentLevel,
+ clone = frag,
+ startParents = domUtils.findParents(start, true),
+ endParents = domUtils.findParents(end, true);
+ for (var i = 0; startParents[i] == endParents[i]; ) {
+ i++;
+ }
+ for (var j = i, si; (si = startParents[j]); j++) {
+ current = si.nextSibling;
+ if (si == start) {
+ if (!tmpStart) {
+ if (range.startContainer.nodeType == 3) {
+ clone.appendChild(
+ doc.createTextNode(start.nodeValue.slice(startOffset))
+ );
+ //is not clone
+ if (action) {
+ start.deleteData(
+ startOffset,
+ start.nodeValue.length - startOffset
+ );
+ }
+ } else {
+ clone.appendChild(!action ? start.cloneNode(true) : start);
+ }
+ }
+ } else {
+ currentLevel = si.cloneNode(false);
+ clone.appendChild(currentLevel);
+ }
+ while (current) {
+ if (current === end || current === endParents[j]) {
+ break;
+ }
+ si = current.nextSibling;
+ clone.appendChild(!action ? current.cloneNode(true) : current);
+ current = si;
+ }
+ clone = currentLevel;
+ }
+ clone = frag;
+ if (!startParents[i]) {
+ clone.appendChild(startParents[i - 1].cloneNode(false));
+ clone = clone.firstChild;
+ }
+ for (var j = i, ei; (ei = endParents[j]); j++) {
+ current = ei.previousSibling;
+ if (ei == end) {
+ if (!tmpEnd && range.endContainer.nodeType == 3) {
+ clone.appendChild(
+ doc.createTextNode(end.substringData(0, endOffset))
+ );
+ //is not clone
+ if (action) {
+ end.deleteData(0, endOffset);
+ }
+ }
+ } else {
+ currentLevel = ei.cloneNode(false);
+ clone.appendChild(currentLevel);
+ }
+ //如果两端同级,右边第一次已经被开始做了
+ if (j != i || !startParents[i]) {
+ while (current) {
+ if (current === start) {
+ break;
+ }
+ ei = current.previousSibling;
+ clone.insertBefore(
+ !action ? current.cloneNode(true) : current,
+ clone.firstChild
+ );
+ current = ei;
+ }
+ }
+ clone = currentLevel;
+ }
+ if (action) {
+ range
+ .setStartBefore(
+ !endParents[i]
+ ? endParents[i - 1]
+ : !startParents[i] ? startParents[i - 1] : endParents[i]
+ )
+ .collapse(true);
+ }
+ tmpStart && domUtils.remove(tmpStart);
+ tmpEnd && domUtils.remove(tmpEnd);
+ return frag;
+ }
+
+ /**
+ * 创建一个跟document绑定的空的Range实例
+ * @constructor
+ * @param { Document } document 新建的选区所属的文档对象
+ */
+
+ /**
+ * @property { Node } startContainer 当前Range的开始边界的容器节点, 可以是一个元素节点或者是文本节点
+ */
+
+ /**
+ * @property { Node } startOffset 当前Range的开始边界容器节点的偏移量, 如果是元素节点,
+ * 该值就是childNodes中的第几个节点, 如果是文本节点就是文本内容的第几个字符
+ */
+
+ /**
+ * @property { Node } endContainer 当前Range的结束边界的容器节点, 可以是一个元素节点或者是文本节点
+ */
+
+ /**
+ * @property { Node } endOffset 当前Range的结束边界容器节点的偏移量, 如果是元素节点,
+ * 该值就是childNodes中的第几个节点, 如果是文本节点就是文本内容的第几个字符
+ */
+
+ /**
+ * @property { Boolean } collapsed 当前Range是否闭合
+ * @default true
+ * @remind Range是闭合的时候, startContainer === endContainer && startOffset === endOffset
+ */
+
+ /**
+ * @property { Document } document 当前Range所属的Document对象
+ * @remind 不同range的的document属性可以是不同的
+ */
+ var Range = (dom.Range = function(document) {
+ var me = this;
+ me.startContainer = me.startOffset = me.endContainer = me.endOffset = null;
+ me.document = document;
+ me.collapsed = true;
+ });
+
+ /**
+ * 删除fillData
+ * @param doc
+ * @param excludeNode
+ */
+ function removeFillData(doc, excludeNode) {
+ try {
+ if (fillData && domUtils.inDoc(fillData, doc)) {
+ if (!fillData.nodeValue.replace(fillCharReg, "").length) {
+ var tmpNode = fillData.parentNode;
+ domUtils.remove(fillData);
+ while (
+ tmpNode &&
+ domUtils.isEmptyInlineElement(tmpNode) &&
+ //safari的contains有bug
+ (browser.safari
+ ? !(
+ domUtils.getPosition(tmpNode, excludeNode) &
+ domUtils.POSITION_CONTAINS
+ )
+ : !tmpNode.contains(excludeNode))
+ ) {
+ fillData = tmpNode.parentNode;
+ domUtils.remove(tmpNode);
+ tmpNode = fillData;
+ }
+ } else {
+ fillData.nodeValue = fillData.nodeValue.replace(fillCharReg, "");
+ }
+ }
+ } catch (e) {}
+ }
+
+ /**
+ * @param node
+ * @param dir
+ */
+ function mergeSibling(node, dir) {
+ var tmpNode;
+ node = node[dir];
+ while (node && domUtils.isFillChar(node)) {
+ tmpNode = node[dir];
+ domUtils.remove(node);
+ node = tmpNode;
+ }
+ }
+
+ Range.prototype = {
+ /**
+ * 克隆选区的内容到一个DocumentFragment里
+ * @method cloneContents
+ * @return { DocumentFragment | NULL } 如果选区是闭合的将返回null, 否则, 返回包含所clone内容的DocumentFragment元素
+ * @example
+ * ```html
+ *
+ *
+ *
xx[xxx]x
+ *
+ *
+ *
+ * ```
+ */
+ cloneContents: function() {
+ return this.collapsed ? null : execContentsAction(this, 0);
+ },
+
+ /**
+ * 删除当前选区范围中的所有内容
+ * @method deleteContents
+ * @remind 执行完该操作后, 当前Range对象变成了闭合状态
+ * @return { UE.dom.Range } 当前操作的Range对象
+ * @example
+ * ```html
+ *
+ *
+ *
xx[xxx]x
+ *
+ *
+ *
+ * ```
+ */
+ deleteContents: function() {
+ var txt;
+ if (!this.collapsed) {
+ execContentsAction(this, 1);
+ }
+ if (browser.webkit) {
+ txt = this.startContainer;
+ if (txt.nodeType == 3 && !txt.nodeValue.length) {
+ this.setStartBefore(txt).collapse(true);
+ domUtils.remove(txt);
+ }
+ }
+ return this;
+ },
+
+ /**
+ * 将当前选区的内容提取到一个DocumentFragment里
+ * @method extractContents
+ * @remind 执行该操作后, 选区将变成闭合状态
+ * @warning 执行该操作后, 原来选区所选中的内容将从dom树上剥离出来
+ * @return { DocumentFragment } 返回包含所提取内容的DocumentFragment对象
+ * @example
+ * ```html
+ *
+ *
+ *
xx[xxx]x
+ *
+ *
+ *
+ */
+ extractContents: function() {
+ return this.collapsed ? null : execContentsAction(this, 2);
+ },
+
+ /**
+ * 设置Range的开始容器节点和偏移量
+ * @method setStart
+ * @remind 如果给定的节点是元素节点,那么offset指的是其子元素中索引为offset的元素,
+ * 如果是文本节点,那么offset指的是其文本内容的第offset个字符
+ * @remind 如果提供的容器节点是一个不能包含子元素的节点, 则该选区的开始容器将被设置
+ * 为该节点的父节点, 此时, 其距离开始容器的偏移量也变成了该节点在其父节点
+ * 中的索引
+ * @param { Node } node 将被设为当前选区开始边界容器的节点对象
+ * @param { int } offset 选区的开始位置偏移量
+ * @return { UE.dom.Range } 当前range对象
+ * @example
+ * ```html
+ *
+ *
xxxxxxxxxxxxx[xxx]
+ *
+ *
+ * ```
+ * @example
+ * ```html
+ *
+ *
xxx
[xx]x
+ *
+ *
+ * ```
+ */
+ setStart: function(node, offset) {
+ return setEndPoint(true, node, offset, this);
+ },
+
+ /**
+ * 设置Range的结束容器和偏移量
+ * @method setEnd
+ * @param { Node } node 作为当前选区结束边界容器的节点对象
+ * @param { int } offset 结束边界的偏移量
+ * @see UE.dom.Range:setStart(Node,int)
+ * @return { UE.dom.Range } 当前range对象
+ */
+ setEnd: function(node, offset) {
+ return setEndPoint(false, node, offset, this);
+ },
+
+ /**
+ * 将Range开始位置设置到node节点之后
+ * @method setStartAfter
+ * @remind 该操作将会把给定节点的父节点作为range的开始容器, 且偏移量是该节点在其父节点中的位置索引+1
+ * @param { Node } node 选区的开始边界将紧接着该节点之后
+ * @return { UE.dom.Range } 当前range对象
+ * @example
+ * ```html
+ *
+ *
xxxxxxx[xxxx]
+ *
+ *
+ * ```
+ */
+ setStartAfter: function(node) {
+ return this.setStart(node.parentNode, domUtils.getNodeIndex(node) + 1);
+ },
+
+ /**
+ * 将Range开始位置设置到node节点之前
+ * @method setStartBefore
+ * @remind 该操作将会把给定节点的父节点作为range的开始容器, 且偏移量是该节点在其父节点中的位置索引
+ * @param { Node } node 新的选区开始位置在该节点之前
+ * @see UE.dom.Range:setStartAfter(Node)
+ * @return { UE.dom.Range } 当前range对象
+ */
+ setStartBefore: function(node) {
+ return this.setStart(node.parentNode, domUtils.getNodeIndex(node));
+ },
+
+ /**
+ * 将Range结束位置设置到node节点之后
+ * @method setEndAfter
+ * @remind 该操作将会把给定节点的父节点作为range的结束容器, 且偏移量是该节点在其父节点中的位置索引+1
+ * @param { Node } node 目标节点
+ * @see UE.dom.Range:setStartAfter(Node)
+ * @return { UE.dom.Range } 当前range对象
+ * @example
+ * ```html
+ *
+ *
[xxxxxxx]xxxx
+ *
+ *
+ * ```
+ */
+ setEndAfter: function(node) {
+ return this.setEnd(node.parentNode, domUtils.getNodeIndex(node) + 1);
+ },
+
+ /**
+ * 将Range结束位置设置到node节点之前
+ * @method setEndBefore
+ * @remind 该操作将会把给定节点的父节点作为range的结束容器, 且偏移量是该节点在其父节点中的位置索引
+ * @param { Node } node 目标节点
+ * @see UE.dom.Range:setEndAfter(Node)
+ * @return { UE.dom.Range } 当前range对象
+ */
+ setEndBefore: function(node) {
+ return this.setEnd(node.parentNode, domUtils.getNodeIndex(node));
+ },
+
+ /**
+ * 设置Range的开始位置到node节点内的第一个子节点之前
+ * @method setStartAtFirst
+ * @remind 选区的开始容器将变成给定的节点, 且偏移量为0
+ * @remind 如果给定的节点是元素节点, 则该节点必须是允许包含子节点的元素。
+ * @param { Node } node 目标节点
+ * @see UE.dom.Range:setStartBefore(Node)
+ * @return { UE.dom.Range } 当前range对象
+ * @example
+ * ```html
+ *
+ *
xxxxx[xx]xxxx
+ *
+ *
+ * ```
+ */
+ setStartAtFirst: function(node) {
+ return this.setStart(node, 0);
+ },
+
+ /**
+ * 设置Range的开始位置到node节点内的最后一个节点之后
+ * @method setStartAtLast
+ * @remind 选区的开始容器将变成给定的节点, 且偏移量为该节点的子节点数
+ * @remind 如果给定的节点是元素节点, 则该节点必须是允许包含子节点的元素。
+ * @param { Node } node 目标节点
+ * @see UE.dom.Range:setStartAtFirst(Node)
+ * @return { UE.dom.Range } 当前range对象
+ */
+ setStartAtLast: function(node) {
+ return this.setStart(
+ node,
+ node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length
+ );
+ },
+
+ /**
+ * 设置Range的结束位置到node节点内的第一个节点之前
+ * @method setEndAtFirst
+ * @param { Node } node 目标节点
+ * @remind 选区的结束容器将变成给定的节点, 且偏移量为0
+ * @remind node必须是一个元素节点, 且必须是允许包含子节点的元素。
+ * @see UE.dom.Range:setStartAtFirst(Node)
+ * @return { UE.dom.Range } 当前range对象
+ */
+ setEndAtFirst: function(node) {
+ return this.setEnd(node, 0);
+ },
+
+ /**
+ * 设置Range的结束位置到node节点内的最后一个节点之后
+ * @method setEndAtLast
+ * @param { Node } node 目标节点
+ * @remind 选区的结束容器将变成给定的节点, 且偏移量为该节点的子节点数量
+ * @remind node必须是一个元素节点, 且必须是允许包含子节点的元素。
+ * @see UE.dom.Range:setStartAtFirst(Node)
+ * @return { UE.dom.Range } 当前range对象
+ */
+ setEndAtLast: function(node) {
+ return this.setEnd(
+ node,
+ node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length
+ );
+ },
+
+ /**
+ * 选中给定节点
+ * @method selectNode
+ * @remind 此时, 选区的开始容器和结束容器都是该节点的父节点, 其startOffset是该节点在父节点中的位置索引,
+ * 而endOffset为startOffset+1
+ * @param { Node } node 需要选中的节点
+ * @return { UE.dom.Range } 当前range对象,此时的range仅包含当前给定的节点对象
+ * @example
+ * ```html
+ *
+ *
xxxxx[xx]xxxx
+ *
+ *
+ * ```
+ */
+ selectNode: function(node) {
+ return this.setStartBefore(node).setEndAfter(node);
+ },
+
+ /**
+ * 选中给定节点内部的所有节点
+ * @method selectNodeContents
+ * @remind 此时, 选区的开始容器和结束容器都是该节点, 其startOffset为0,
+ * 而endOffset是该节点的子节点数。
+ * @param { Node } node 目标节点, 当前range将包含该节点内的所有节点
+ * @return { UE.dom.Range } 当前range对象, 此时range仅包含给定节点的所有子节点
+ * @example
+ * ```html
+ *
+ *
xxxxx[xx]xxxx
+ *
+ *
+ * ```
+ */
+ selectNodeContents: function(node) {
+ return this.setStart(node, 0).setEndAtLast(node);
+ },
+
+ /**
+ * clone当前Range对象
+ * @method cloneRange
+ * @remind 返回的range是一个全新的range对象, 其内部所有属性与当前被clone的range相同。
+ * @return { UE.dom.Range } 当前range对象的一个副本
+ */
+ cloneRange: function() {
+ var me = this;
+ return new Range(me.document)
+ .setStart(me.startContainer, me.startOffset)
+ .setEnd(me.endContainer, me.endOffset);
+ },
+
+ /**
+ * 向当前选区的结束处闭合选区
+ * @method collapse
+ * @return { UE.dom.Range } 当前range对象
+ * @example
+ * ```html
+ *
+ *
xxxxx[xx]xxxx
+ *
+ *
+ * ```
+ */
+
+ /**
+ * 闭合当前选区,根据给定的toStart参数项决定是向当前选区开始处闭合还是向结束处闭合,
+ * 如果toStart的值为true,则向开始位置闭合, 反之,向结束位置闭合。
+ * @method collapse
+ * @param { Boolean } toStart 是否向选区开始处闭合
+ * @return { UE.dom.Range } 当前range对象,此时range对象处于闭合状态
+ * @see UE.dom.Range:collapse()
+ * @example
+ * ```html
+ *
+ *
xxxxx[xx]xxxx
+ *
+ *
+ * ```
+ */
+ collapse: function(toStart) {
+ var me = this;
+ if (toStart) {
+ me.endContainer = me.startContainer;
+ me.endOffset = me.startOffset;
+ } else {
+ me.startContainer = me.endContainer;
+ me.startOffset = me.endOffset;
+ }
+ me.collapsed = true;
+ return me;
+ },
+
+ /**
+ * 调整range的开始位置和结束位置,使其"收缩"到最小的位置
+ * @method shrinkBoundary
+ * @return { UE.dom.Range } 当前range对象
+ * @example
+ * ```html
+ *
xxxx[xxxxx] =>
xxxx[xxxxx]
+ * ```
+ *
+ * @example
+ * ```html
+ *
+ *
x[xx]xxx
+ *
+ *
+ * ```
+ *
+ * @example
+ * ```html
+ * [
xxxxxxxxxxx] =>
[xxxxxxxxxxx]
+ * ```
+ */
+
+ /**
+ * 调整range的开始位置和结束位置,使其"收缩"到最小的位置,
+ * 如果ignoreEnd的值为true,则忽略对结束位置的调整
+ * @method shrinkBoundary
+ * @param { Boolean } ignoreEnd 是否忽略对结束位置的调整
+ * @return { UE.dom.Range } 当前range对象
+ * @see UE.dom.domUtils.Range:shrinkBoundary()
+ */
+ shrinkBoundary: function(ignoreEnd) {
+ var me = this,
+ child,
+ collapsed = me.collapsed;
+ function check(node) {
+ return (
+ node.nodeType == 1 &&
+ !domUtils.isBookmarkNode(node) &&
+ !dtd.$empty[node.tagName] &&
+ !dtd.$nonChild[node.tagName]
+ );
+ }
+ while (
+ me.startContainer.nodeType == 1 && //是element
+ (child = me.startContainer.childNodes[me.startOffset]) && //子节点也是element
+ check(child)
+ ) {
+ me.setStart(child, 0);
+ }
+ if (collapsed) {
+ return me.collapse(true);
+ }
+ if (!ignoreEnd) {
+ while (
+ me.endContainer.nodeType == 1 && //是element
+ me.endOffset > 0 && //如果是空元素就退出 endOffset=0那么endOffst-1为负值,childNodes[endOffset]报错
+ (child = me.endContainer.childNodes[me.endOffset - 1]) && //子节点也是element
+ check(child)
+ ) {
+ me.setEnd(child, child.childNodes.length);
+ }
+ }
+ return me;
+ },
+
+ /**
+ * 获取离当前选区内包含的所有节点最近的公共祖先节点,
+ * @method getCommonAncestor
+ * @remind 返回的公共祖先节点一定不是range自身的容器节点, 但有可能是一个文本节点
+ * @return { Node } 当前range对象内所有节点的公共祖先节点
+ * @example
+ * ```html
+ * //选区示例
+ *
xxxx[xxx]xxxxxx
+ *
+ * ```
+ */
+
+ /**
+ * 获取当前选区所包含的所有节点的公共祖先节点, 可以根据给定的参数 includeSelf 决定获取到
+ * 的公共祖先节点是否可以是当前选区的startContainer或endContainer节点, 如果 includeSelf
+ * 的取值为true, 则返回的节点可以是自身的容器节点, 否则, 则不能是容器节点
+ * @method getCommonAncestor
+ * @param { Boolean } includeSelf 是否允许获取到的公共祖先节点是当前range对象的容器节点
+ * @return { Node } 当前range对象内所有节点的公共祖先节点
+ * @see UE.dom.Range:getCommonAncestor()
+ * @example
+ * ```html
+ *
+ *
+ *
+ *
xxxxxxxxx[xxx]xxxxxxxx
+ *
+ *
+ *
+ *
+ * ```
+ */
+
+ /**
+ * 获取当前选区所包含的所有节点的公共祖先节点, 可以根据给定的参数 includeSelf 决定获取到
+ * 的公共祖先节点是否可以是当前选区的startContainer或endContainer节点, 如果 includeSelf
+ * 的取值为true, 则返回的节点可以是自身的容器节点, 否则, 则不能是容器节点; 同时可以根据
+ * ignoreTextNode 参数的取值决定是否忽略类型为文本节点的祖先节点。
+ * @method getCommonAncestor
+ * @param { Boolean } includeSelf 是否允许获取到的公共祖先节点是当前range对象的容器节点
+ * @param { Boolean } ignoreTextNode 获取祖先节点的过程中是否忽略类型为文本节点的祖先节点
+ * @return { Node } 当前range对象内所有节点的公共祖先节点
+ * @see UE.dom.Range:getCommonAncestor()
+ * @see UE.dom.Range:getCommonAncestor(Boolean)
+ * @example
+ * ```html
+ *
+ *
+ *
+ *
xxxxxxxx[x]xxxxxxxxxxx
+ *
+ *
+ *
+ *
+ * ```
+ */
+ getCommonAncestor: function(includeSelf, ignoreTextNode) {
+ var me = this,
+ start = me.startContainer,
+ end = me.endContainer;
+ if (start === end) {
+ if (includeSelf && selectOneNode(this)) {
+ start = start.childNodes[me.startOffset];
+ if (start.nodeType == 1) return start;
+ }
+ //只有在上来就相等的情况下才会出现是文本的情况
+ return ignoreTextNode && start.nodeType == 3 ? start.parentNode : start;
+ }
+ return domUtils.getCommonAncestor(start, end);
+ },
+
+ /**
+ * 调整当前Range的开始和结束边界容器,如果是容器节点是文本节点,就调整到包含该文本节点的父节点上
+ * @method trimBoundary
+ * @remind 该操作有可能会引起文本节点被切开
+ * @return { UE.dom.Range } 当前range对象
+ * @example
+ * ```html
+ *
+ * //选区示例
+ *
xxx[xxxxx]xxx
+ *
+ *
+ * ```
+ */
+
+ /**
+ * 调整当前Range的开始和结束边界容器,如果是容器节点是文本节点,就调整到包含该文本节点的父节点上,
+ * 可以根据 ignoreEnd 参数的值决定是否调整对结束边界的调整
+ * @method trimBoundary
+ * @param { Boolean } ignoreEnd 是否忽略对结束边界的调整
+ * @return { UE.dom.Range } 当前range对象
+ * @example
+ * ```html
+ *
+ * //选区示例
+ *
xxx[xxxxx]xxx
+ *
+ *
+ * ```
+ */
+ trimBoundary: function(ignoreEnd) {
+ this.txtToElmBoundary();
+ var start = this.startContainer,
+ offset = this.startOffset,
+ collapsed = this.collapsed,
+ end = this.endContainer;
+ if (start.nodeType == 3) {
+ if (offset == 0) {
+ this.setStartBefore(start);
+ } else {
+ if (offset >= start.nodeValue.length) {
+ this.setStartAfter(start);
+ } else {
+ var textNode = domUtils.split(start, offset);
+ //跟新结束边界
+ if (start === end) {
+ this.setEnd(textNode, this.endOffset - offset);
+ } else if (start.parentNode === end) {
+ this.endOffset += 1;
+ }
+ this.setStartBefore(textNode);
+ }
+ }
+ if (collapsed) {
+ return this.collapse(true);
+ }
+ }
+ if (!ignoreEnd) {
+ offset = this.endOffset;
+ end = this.endContainer;
+ if (end.nodeType == 3) {
+ if (offset == 0) {
+ this.setEndBefore(end);
+ } else {
+ offset < end.nodeValue.length && domUtils.split(end, offset);
+ this.setEndAfter(end);
+ }
+ }
+ }
+ return this;
+ },
+
+ /**
+ * 如果选区在文本的边界上,就扩展选区到文本的父节点上, 如果当前选区是闭合的, 则什么也不做
+ * @method txtToElmBoundary
+ * @remind 该操作不会修改dom节点
+ * @return { UE.dom.Range } 当前range对象
+ */
+
+ /**
+ * 如果选区在文本的边界上,就扩展选区到文本的父节点上, 如果当前选区是闭合的, 则根据参数项
+ * ignoreCollapsed 的值决定是否执行该调整
+ * @method txtToElmBoundary
+ * @param { Boolean } ignoreCollapsed 是否忽略选区的闭合状态, 如果该参数取值为true, 则
+ * 不论选区是否闭合, 都会执行该操作, 反之, 则不会对闭合的选区执行该操作
+ * @return { UE.dom.Range } 当前range对象
+ */
+ txtToElmBoundary: function(ignoreCollapsed) {
+ function adjust(r, c) {
+ var container = r[c + "Container"],
+ offset = r[c + "Offset"];
+ if (container.nodeType == 3) {
+ if (!offset) {
+ r[
+ "set" +
+ c.replace(/(\w)/, function(a) {
+ return a.toUpperCase();
+ }) +
+ "Before"
+ ](container);
+ } else if (offset >= container.nodeValue.length) {
+ r[
+ "set" +
+ c.replace(/(\w)/, function(a) {
+ return a.toUpperCase();
+ }) +
+ "After"
+ ](container);
+ }
+ }
+ }
+
+ if (ignoreCollapsed || !this.collapsed) {
+ adjust(this, "start");
+ adjust(this, "end");
+ }
+ return this;
+ },
+
+ /**
+ * 在当前选区的开始位置前插入节点,新插入的节点会被该range包含
+ * @method insertNode
+ * @param { Node } node 需要插入的节点
+ * @remind 插入的节点可以是一个DocumentFragment依次插入多个节点
+ * @return { UE.dom.Range } 当前range对象
+ */
+ insertNode: function(node) {
+ var first = node,
+ length = 1;
+ if (node.nodeType == 11) {
+ first = node.firstChild;
+ length = node.childNodes.length;
+ }
+ this.trimBoundary(true);
+ var start = this.startContainer,
+ offset = this.startOffset;
+ var nextNode = start.childNodes[offset];
+ if (nextNode) {
+ start.insertBefore(node, nextNode);
+ } else {
+ start.appendChild(node);
+ }
+ if (first.parentNode === this.endContainer) {
+ this.endOffset = this.endOffset + length;
+ }
+ return this.setStartBefore(first);
+ },
+
+ /**
+ * 闭合选区到当前选区的开始位置, 并且定位光标到闭合后的位置
+ * @method setCursor
+ * @return { UE.dom.Range } 当前range对象
+ * @see UE.dom.Range:collapse()
+ */
+
+ /**
+ * 闭合选区,可以根据参数toEnd的值控制选区是向前闭合还是向后闭合, 并且定位光标到闭合后的位置。
+ * @method setCursor
+ * @param { Boolean } toEnd 是否向后闭合, 如果为true, 则闭合选区时, 将向结束容器方向闭合,
+ * 反之,则向开始容器方向闭合
+ * @return { UE.dom.Range } 当前range对象
+ * @see UE.dom.Range:collapse(Boolean)
+ */
+ setCursor: function(toEnd, noFillData) {
+ return this.collapse(!toEnd).select(noFillData);
+ },
+
+ /**
+ * 创建当前range的一个书签,记录下当前range的位置,方便当dom树改变时,还能找回原来的选区位置
+ * @method createBookmark
+ * @param { Boolean } serialize 控制返回的标记位置是对当前位置的引用还是ID,如果该值为true,则
+ * 返回标记位置的ID, 反之则返回标记位置节点的引用
+ * @return { Object } 返回一个书签记录键值对, 其包含的key有: start => 开始标记的ID或者引用,
+ * end => 结束标记的ID或引用, id => 当前标记的类型, 如果为true,则表示
+ * 返回的记录的类型为ID, 反之则为引用
+ */
+ createBookmark: function(serialize, same) {
+ var endNode,
+ startNode = this.document.createElement("span");
+ startNode.style.cssText = "display:none;line-height:0px;";
+ startNode.appendChild(this.document.createTextNode("\u200D"));
+ startNode.id = "_baidu_bookmark_start_" + (same ? "" : guid++);
+
+ if (!this.collapsed) {
+ endNode = startNode.cloneNode(true);
+ endNode.id = "_baidu_bookmark_end_" + (same ? "" : guid++);
+ }
+ this.insertNode(startNode);
+ if (endNode) {
+ this.collapse().insertNode(endNode).setEndBefore(endNode);
+ }
+ this.setStartAfter(startNode);
+ return {
+ start: serialize ? startNode.id : startNode,
+ end: endNode ? (serialize ? endNode.id : endNode) : null,
+ id: serialize
+ };
+ },
+
+ /**
+ * 调整当前range的边界到书签位置,并删除该书签对象所标记的位置内的节点
+ * @method moveToBookmark
+ * @param { BookMark } bookmark createBookmark所创建的标签对象
+ * @return { UE.dom.Range } 当前range对象
+ * @see UE.dom.Range:createBookmark(Boolean)
+ */
+ moveToBookmark: function(bookmark) {
+ var start = bookmark.id
+ ? this.document.getElementById(bookmark.start)
+ : bookmark.start,
+ end = bookmark.end && bookmark.id
+ ? this.document.getElementById(bookmark.end)
+ : bookmark.end;
+ this.setStartBefore(start);
+ domUtils.remove(start);
+ if (end) {
+ this.setEndBefore(end);
+ domUtils.remove(end);
+ } else {
+ this.collapse(true);
+ }
+ return this;
+ },
+
+ /**
+ * 调整range的边界,使其"放大"到最近的父节点
+ * @method enlarge
+ * @remind 会引起选区的变化
+ * @return { UE.dom.Range } 当前range对象
+ */
+
+ /**
+ * 调整range的边界,使其"放大"到最近的父节点,根据参数 toBlock 的取值, 可以
+ * 要求扩大之后的父节点是block节点
+ * @method enlarge
+ * @param { Boolean } toBlock 是否要求扩大之后的父节点必须是block节点
+ * @return { UE.dom.Range } 当前range对象
+ */
+ enlarge: function(toBlock, stopFn) {
+ var isBody = domUtils.isBody,
+ pre,
+ node,
+ tmp = this.document.createTextNode("");
+ if (toBlock) {
+ node = this.startContainer;
+ if (node.nodeType == 1) {
+ if (node.childNodes[this.startOffset]) {
+ pre = node = node.childNodes[this.startOffset];
+ } else {
+ node.appendChild(tmp);
+ pre = node = tmp;
+ }
+ } else {
+ pre = node;
+ }
+ while (1) {
+ if (domUtils.isBlockElm(node)) {
+ node = pre;
+ while ((pre = node.previousSibling) && !domUtils.isBlockElm(pre)) {
+ node = pre;
+ }
+ this.setStartBefore(node);
+ break;
+ }
+ pre = node;
+ node = node.parentNode;
+ }
+ node = this.endContainer;
+ if (node.nodeType == 1) {
+ if ((pre = node.childNodes[this.endOffset])) {
+ node.insertBefore(tmp, pre);
+ } else {
+ node.appendChild(tmp);
+ }
+ pre = node = tmp;
+ } else {
+ pre = node;
+ }
+ while (1) {
+ if (domUtils.isBlockElm(node)) {
+ node = pre;
+ while ((pre = node.nextSibling) && !domUtils.isBlockElm(pre)) {
+ node = pre;
+ }
+ this.setEndAfter(node);
+ break;
+ }
+ pre = node;
+ node = node.parentNode;
+ }
+ if (tmp.parentNode === this.endContainer) {
+ this.endOffset--;
+ }
+ domUtils.remove(tmp);
+ }
+
+ // 扩展边界到最大
+ if (!this.collapsed) {
+ while (this.startOffset == 0) {
+ if (stopFn && stopFn(this.startContainer)) {
+ break;
+ }
+ if (isBody(this.startContainer)) {
+ break;
+ }
+ this.setStartBefore(this.startContainer);
+ }
+ while (
+ this.endOffset ==
+ (this.endContainer.nodeType == 1
+ ? this.endContainer.childNodes.length
+ : this.endContainer.nodeValue.length)
+ ) {
+ if (stopFn && stopFn(this.endContainer)) {
+ break;
+ }
+ if (isBody(this.endContainer)) {
+ break;
+ }
+ this.setEndAfter(this.endContainer);
+ }
+ }
+ return this;
+ },
+ enlargeToBlockElm: function(ignoreEnd) {
+ while (!domUtils.isBlockElm(this.startContainer)) {
+ this.setStartBefore(this.startContainer);
+ }
+ if (!ignoreEnd) {
+ while (!domUtils.isBlockElm(this.endContainer)) {
+ this.setEndAfter(this.endContainer);
+ }
+ }
+ return this;
+ },
+ /**
+ * 调整Range的边界,使其"缩小"到最合适的位置
+ * @method adjustmentBoundary
+ * @return { UE.dom.Range } 当前range对象
+ * @see UE.dom.Range:shrinkBoundary()
+ */
+ adjustmentBoundary: function() {
+ if (!this.collapsed) {
+ while (
+ !domUtils.isBody(this.startContainer) &&
+ this.startOffset ==
+ this.startContainer[
+ this.startContainer.nodeType == 3 ? "nodeValue" : "childNodes"
+ ].length &&
+ this.startContainer[
+ this.startContainer.nodeType == 3 ? "nodeValue" : "childNodes"
+ ].length
+ ) {
+ this.setStartAfter(this.startContainer);
+ }
+ while (
+ !domUtils.isBody(this.endContainer) &&
+ !this.endOffset &&
+ this.endContainer[
+ this.endContainer.nodeType == 3 ? "nodeValue" : "childNodes"
+ ].length
+ ) {
+ this.setEndBefore(this.endContainer);
+ }
+ }
+ return this;
+ },
+
+ /**
+ * 给range选区中的内容添加给定的inline标签
+ * @method applyInlineStyle
+ * @param { String } tagName 需要添加的标签名
+ * @example
+ * ```html
+ *
xxxx[xxxx]x
==> range.applyInlineStyle("strong") ==>
xxxx[xxxx]x
+ * ```
+ */
+
+ /**
+ * 给range选区中的内容添加给定的inline标签, 并且为标签附加上一些初始化属性。
+ * @method applyInlineStyle
+ * @param { String } tagName 需要添加的标签名
+ * @param { Object } attrs 跟随新添加的标签的属性
+ * @return { UE.dom.Range } 当前选区
+ * @example
+ * ```html
+ *
xxxx[xxxx]x
+ *
+ * ==>
+ *
+ *
+ * range.applyInlineStyle("strong",{"style":"font-size:12px"})
+ *
+ * ==>
+ *
+ *
xxxx[xxxx]x
+ * ```
+ */
+ applyInlineStyle: function(tagName, attrs, list) {
+ if (this.collapsed) return this;
+ this.trimBoundary()
+ .enlarge(false, function(node) {
+ return node.nodeType == 1 && domUtils.isBlockElm(node);
+ })
+ .adjustmentBoundary();
+ var bookmark = this.createBookmark(),
+ end = bookmark.end,
+ filterFn = function(node) {
+ return node.nodeType == 1
+ ? node.tagName.toLowerCase() != "br"
+ : !domUtils.isWhitespace(node);
+ },
+ current = domUtils.getNextDomNode(bookmark.start, false, filterFn),
+ node,
+ pre,
+ range = this.cloneRange();
+ while (
+ current &&
+ domUtils.getPosition(current, end) & domUtils.POSITION_PRECEDING
+ ) {
+ if (current.nodeType == 3 || dtd[tagName][current.tagName]) {
+ range.setStartBefore(current);
+ node = current;
+ while (
+ node &&
+ (node.nodeType == 3 || dtd[tagName][node.tagName]) &&
+ node !== end
+ ) {
+ pre = node;
+ node = domUtils.getNextDomNode(
+ node,
+ node.nodeType == 1,
+ null,
+ function(parent) {
+ return dtd[tagName][parent.tagName];
+ }
+ );
+ }
+ var frag = range.setEndAfter(pre).extractContents(),
+ elm;
+ if (list && list.length > 0) {
+ var level, top;
+ top = level = list[0].cloneNode(false);
+ for (var i = 1, ci; (ci = list[i++]); ) {
+ level.appendChild(ci.cloneNode(false));
+ level = level.firstChild;
+ }
+ elm = level;
+ } else {
+ elm = range.document.createElement(tagName);
+ }
+ if (attrs) {
+ domUtils.setAttributes(elm, attrs);
+ }
+ elm.appendChild(frag);
+ //针对嵌套span的全局样式指定,做容错处理
+ if (elm.tagName == "SPAN" && attrs && attrs.style) {
+ utils.each(elm.getElementsByTagName("span"), function(s) {
+ s.style.cssText = s.style.cssText + ";" + attrs.style;
+ });
+ }
+ range.insertNode(list ? top : elm);
+ //处理下滑线在a上的情况
+ var aNode;
+ if (
+ tagName == "span" &&
+ attrs.style &&
+ /text\-decoration/.test(attrs.style) &&
+ (aNode = domUtils.findParentByTagName(elm, "a", true))
+ ) {
+ domUtils.setAttributes(aNode, attrs);
+ domUtils.remove(elm, true);
+ elm = aNode;
+ } else {
+ domUtils.mergeSibling(elm);
+ domUtils.clearEmptySibling(elm);
+ }
+ //去除子节点相同的
+ domUtils.mergeChild(elm, attrs);
+ current = domUtils.getNextDomNode(elm, false, filterFn);
+ domUtils.mergeToParent(elm);
+ if (node === end) {
+ break;
+ }
+ } else {
+ current = domUtils.getNextDomNode(current, true, filterFn);
+ }
+ }
+ return this.moveToBookmark(bookmark);
+ },
+
+ /**
+ * 移除当前选区内指定的inline标签,但保留其中的内容
+ * @method removeInlineStyle
+ * @param { String } tagName 需要移除的标签名
+ * @return { UE.dom.Range } 当前的range对象
+ * @example
+ * ```html
+ * xx[x
xxxyyyzz]z => range.removeInlineStyle(["em"]) => xx[x
xxxyyyzz]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",
+ 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);
+ }
+ },
+ /**
+ * 该方法用于设置placeholder
+ * @method setPlaceholder
+ * @param { String } placeholder 编辑器的placeholder文案
+ * @example
+ * ```javascript
+ * editor.setPlaceholder('请输入内容');
+ * ```
+ */
+ setPlaceholder: function(){
+
+ function contentChange(){
+ var localHtml = this.getPlainTxt();
+ if(!localHtml.trim()){
+ UE.dom.domUtils.addClass( this.body, 'empty' );
+ }else{
+ UE.dom.domUtils.removeClasses( this.body, 'empty' );
+ }
+ }
+
+ return function(placeholder){
+ var _editor = this;
+
+ _editor.ready(function () {
+ contentChange.call(_editor);
+ _editor.body.setAttribute('placeholder', placeholder);
+ });
+ _editor.removeListener('keyup contentchange', contentChange);
+ _editor.addListener('keyup contentchange', contentChange);
+ }
+ }(),
+
+ /**
+ * 该方法是提供给插件里面使用,设置配置项默认值
+ * @method setOpt
+ * @warning 三处设置配置项的优先级: 实例化时传入参数 > setOpt()设置 > config文件里设置
+ * @warning 该方法仅供编辑器插件内部和编辑器初始化时调用,其他地方不能调用。
+ * @param { String } key 编辑器的可接受的选项名称
+ * @param { * } val 该选项可接受的值
+ * @example
+ * ```javascript
+ * editor.setOpt( 'initContent', '欢迎使用编辑器' );
+ * ```
+ */
+
+ /**
+ * 该方法是提供给插件里面使用,以{key:value}集合的方式设置插件内用到的配置项默认值
+ * @method setOpt
+ * @warning 三处设置配置项的优先级: 实例化时传入参数 > setOpt()设置 > config文件里设置
+ * @warning 该方法仅供编辑器插件内部和编辑器初始化时调用,其他地方不能调用。
+ * @param { Object } options 将要设置的选项的键值对对象
+ * @example
+ * ```javascript
+ * editor.setOpt( {
+ * 'initContent': '欢迎使用编辑器'
+ * } );
+ * ```
+ */
+ setOpt: function(key, val) {
+ var obj = {};
+ if (utils.isString(key)) {
+ obj[key] = val;
+ } else {
+ obj = key;
+ }
+ utils.extend(this.options, obj, true);
+ },
+ getOpt: function(key) {
+ return this.options[key];
+ },
+ /**
+ * 销毁编辑器实例,使用textarea代替
+ * @method destroy
+ * @example
+ * ```javascript
+ * editor.destroy();
+ * ```
+ */
+ destroy: function() {
+ var me = this;
+ me.fireEvent("destroy");
+ var container = me.container.parentNode;
+ var textarea = me.textarea;
+ if (!textarea) {
+ textarea = document.createElement("textarea");
+ container.parentNode.insertBefore(textarea, container);
+ } else {
+ textarea.style.display = "";
+ }
+
+ textarea.style.width = me.iframe.offsetWidth + "px";
+ textarea.style.height = me.iframe.offsetHeight + "px";
+ textarea.value = me.getContent();
+ textarea.id = me.key;
+ container.innerHTML = "";
+ domUtils.remove(container);
+ var key = me.key;
+ //trace:2004
+ for (var p in me) {
+ if (me.hasOwnProperty(p)) {
+ delete this[p];
+ }
+ }
+ UE.delEditor(key);
+ },
+
+ /**
+ * 渲染编辑器的DOM到指定容器
+ * @method render
+ * @param { String } containerId 指定一个容器ID
+ * @remind 执行该方法,会触发ready事件
+ * @warning 必须且只能调用一次
+ */
+
+ /**
+ * 渲染编辑器的DOM到指定容器
+ * @method render
+ * @param { Element } containerDom 直接指定容器对象
+ * @remind 执行该方法,会触发ready事件
+ * @warning 必须且只能调用一次
+ */
+ render: function(container) {
+ var me = this,
+ options = me.options,
+ getStyleValue = function(attr) {
+ return parseInt(domUtils.getComputedStyle(container, attr));
+ };
+ if (utils.isString(container)) {
+ container = document.getElementById(container);
+ }
+ if (container) {
+ if (options.initialFrameWidth) {
+ options.minFrameWidth = options.initialFrameWidth;
+ } else {
+ options.minFrameWidth = options.initialFrameWidth =
+ container.offsetWidth;
+ }
+ if (options.initialFrameHeight) {
+ options.minFrameHeight = options.initialFrameHeight;
+ } else {
+ options.initialFrameHeight = options.minFrameHeight =
+ container.offsetHeight;
+ }
+
+ container.style.width = /%$/.test(options.initialFrameWidth)
+ ? "100%"
+ : options.initialFrameWidth -
+ getStyleValue("padding-left") -
+ getStyleValue("padding-right") +
+ "px";
+ container.style.height = /%$/.test(options.initialFrameHeight)
+ ? "100%"
+ : options.initialFrameHeight -
+ getStyleValue("padding-top") -
+ getStyleValue("padding-bottom") +
+ "px";
+
+ container.style.zIndex = options.zIndex;
+
+ var html =
+ (ie && browser.version < 9 ? "" : "") +
+ "" +
+ "" +
+ "" +
+ (options.iframeCssUrl
+ ? ""
+ : "") +
+ (options.initialStyle
+ ? ""
+ : "") +
+ "" +
+ "" +
+ "" +
+ (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);
+ });
+ } else {
+ domUtils.on(form, "submit", function() {
+ setValue(this, me);
+ });
+ }
+ break;
+ }
+ }
+ if (options.initialContent) {
+ if (options.autoClearinitialContent) {
+ var oldExecCommand = me.execCommand;
+ me.execCommand = function() {
+ me.fireEvent("firstBeforeExecCommand");
+ return oldExecCommand.apply(me, arguments);
+ };
+ this._setDefaultContent(options.initialContent);
+ } else this.setContent(options.initialContent, false, true);
+ }
+
+ //编辑器不能为空内容
+
+ if (domUtils.isEmptyNode(me.body)) {
+ me.body.innerHTML = "" + (browser.ie ? "" : "
") + "
";
+ }
+ //如果要求focus, 就把光标定位到内容开始
+ if (options.focus) {
+ setTimeout(function() {
+ me.focus(me.options.focusInEnd);
+ //如果自动清除开着,就不需要做selectionchange;
+ !me.options.autoClearinitialContent && me._selectionChange();
+ }, 0);
+ }
+ if (!me.container) {
+ me.container = this.iframe.parentNode;
+ }
+ if (options.fullscreen && me.ui) {
+ me.ui.setFullScreen(true);
+ }
+
+ try {
+ me.document.execCommand("2D-position", false, false);
+ } catch (e) {}
+ try {
+ me.document.execCommand("enableInlineTableEditing", false, false);
+ } catch (e) {}
+ try {
+ me.document.execCommand("enableObjectResizing", false, false);
+ } catch (e) {}
+
+ //挂接快捷键
+ me._bindshortcutKeys();
+ me.isReady = 1;
+ me.fireEvent("ready");
+ options.onready && options.onready.call(me);
+ if (!browser.ie9below) {
+ domUtils.on(me.window, ["blur", "focus"], function(e) {
+ //chrome下会出现alt+tab切换时,导致选区位置不对
+ if (e.type == "blur") {
+ me._bakRange = me.selection.getRange();
+ try {
+ me._bakNativeRange = me.selection.getNative().getRangeAt(0);
+ me.selection.getNative().removeAllRanges();
+ } catch (e) {
+ me._bakNativeRange = null;
+ }
+ } else {
+ try {
+ me._bakRange && me._bakRange.select();
+ } catch (e) {}
+ }
+ });
+ }
+ //trace:1518 ff3.6body不够寛,会导致点击空白处无法获得焦点
+ if (browser.gecko && browser.version <= 10902) {
+ //修复ff3.6初始化进来,不能点击获得焦点
+ me.body.contentEditable = false;
+ setTimeout(function() {
+ me.body.contentEditable = true;
+ }, 100);
+ setInterval(function() {
+ me.body.style.height = me.iframe.offsetHeight - 20 + "px";
+ }, 100);
+ }
+
+ !options.isShow && me.setHide();
+ options.readonly && me.setDisabled();
+ },
+
+ /**
+ * 同步数据到编辑器所在的form
+ * 从编辑器的容器节点向上查找form元素,若找到,就同步编辑内容到找到的form里,为提交数据做准备,主要用于是手动提交的情况
+ * 后台取得数据的键值,使用你容器上的name属性,如果没有就使用参数里的textarea项
+ * @method sync
+ * @example
+ * ```javascript
+ * editor.sync();
+ * form.sumbit(); //form变量已经指向了form元素
+ * ```
+ */
+
+ /**
+ * 根据传入的formId,在页面上查找要同步数据的表单,若找到,就同步编辑内容到找到的form里,为提交数据做准备
+ * 后台取得数据的键值,该键值默认使用给定的编辑器容器的name属性,如果没有name属性则使用参数项里给定的“textarea”项
+ * @method sync
+ * @param { String } formID 指定一个要同步数据的form的id,编辑器的数据会同步到你指定form下
+ */
+ sync: function(formId) {
+ var me = this,
+ form = formId
+ ? document.getElementById(formId)
+ : domUtils.findParent(
+ me.iframe.parentNode,
+ function(node) {
+ return node.tagName == "FORM";
+ },
+ true
+ );
+ form && setValue(form, me);
+ },
+
+ /**
+ * 设置编辑器高度
+ * @method setHeight
+ * @remind 当配置项autoHeightEnabled为真时,该方法无效
+ * @param { Number } number 设置的高度值,纯数值,不带单位
+ * @example
+ * ```javascript
+ * editor.setHeight(number);
+ * ```
+ */
+ setHeight: function(height, notSetHeight) {
+ if (height !== parseInt(this.iframe.parentNode.style.height)) {
+ this.iframe.parentNode.style.height = height + "px";
+ }
+ !notSetHeight &&
+ (this.options.minFrameHeight = this.options.initialFrameHeight = height);
+ this.body.style.height = height + "px";
+ !notSetHeight && this.trigger("setHeight");
+ },
+
+ /**
+ * 为编辑器的编辑命令提供快捷键
+ * 这个接口是为插件扩展提供的接口,主要是为新添加的插件,如果需要添加快捷键,所提供的接口
+ * @method addshortcutkey
+ * @param { Object } keyset 命令名和快捷键键值对对象,多个按钮的快捷键用“+”分隔
+ * @example
+ * ```javascript
+ * editor.addshortcutkey({
+ * "Bold" : "ctrl+66",//^B
+ * "Italic" : "ctrl+73", //^I
+ * });
+ * ```
+ */
+ /**
+ * 这个接口是为插件扩展提供的接口,主要是为新添加的插件,如果需要添加快捷键,所提供的接口
+ * @method addshortcutkey
+ * @param { String } cmd 触发快捷键时,响应的命令
+ * @param { String } keys 快捷键的字符串,多个按钮用“+”分隔
+ * @example
+ * ```javascript
+ * editor.addshortcutkey("Underline", "ctrl+85"); //^U
+ * ```
+ */
+ addshortcutkey: function(cmd, keys) {
+ var obj = {};
+ if (keys) {
+ obj[cmd] = keys;
+ } else {
+ obj = cmd;
+ }
+ utils.extend(this.shortcutkeys, obj);
+ },
+
+ /**
+ * 对编辑器设置keydown事件监听,绑定快捷键和命令,当快捷键组合触发成功,会响应对应的命令
+ * @method _bindshortcutKeys
+ * @private
+ */
+ _bindshortcutKeys: function() {
+ var me = this,
+ shortcutkeys = this.shortcutkeys;
+ me.addListener("keydown", function(type, e) {
+ var keyCode = e.keyCode || e.which;
+ for (var i in shortcutkeys) {
+ var tmp = shortcutkeys[i].split(",");
+ for (var t = 0, ti; (ti = tmp[t++]); ) {
+ ti = ti.split(":");
+ var key = ti[0],
+ param = ti[1];
+ if (
+ /^(ctrl)(\+shift)?\+(\d+)$/.test(key.toLowerCase()) ||
+ /^(\d+)$/.test(key)
+ ) {
+ if (
+ ((RegExp.$1 == "ctrl" ? e.ctrlKey || e.metaKey : 0) &&
+ (RegExp.$2 != "" ? e[RegExp.$2.slice(1) + "Key"] : 1) &&
+ keyCode == RegExp.$3) ||
+ keyCode == RegExp.$1
+ ) {
+ if (me.queryCommandState(i, param) != -1)
+ me.execCommand(i, param);
+ domUtils.preventDefault(e);
+ }
+ }
+ }
+ }
+ });
+ },
+
+ /**
+ * 获取编辑器的内容
+ * @method getContent
+ * @warning 该方法获取到的是经过编辑器内置的过滤规则进行过滤后得到的内容
+ * @return { String } 编辑器的内容字符串, 如果编辑器的内容为空,或者是空的标签内容(如:”<p><br/></p>“), 则返回空字符串
+ * @example
+ * ```javascript
+ * //编辑器html内容: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>/gi, "\n")
+ .replace(/
/gi, "\n")
+ .replace(/<[^>/]+>/g, "")
+ .replace(/(\n)?<\/([^>]+)>/g, function(a, b, c) {
+ return dtd.$block[c] ? "\n" : b ? b : "";
+ });
+ //取出来的空格会有c2a0会变成乱码,处理这种情况\u00a0
+ return html
+ .replace(reg, "")
+ .replace(/\u00a0/g, " ")
+ .replace(/ /g, " ");
+ },
+
+ /**
+ * 获取编辑器中的纯文本内容,没有段落格式
+ * @method getContentTxt
+ * @return { String } 编辑器不带段落格式的纯文本内容字符串
+ * @example
+ * ```javascript
+ * //编辑器html内容: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 ? " " : "
") +
+ "
" +
+ this.body.innerHTML;
+ } else {
+ var p = me.document.createElement("p");
+ while (child) {
+ while (
+ child &&
+ (child.nodeType == 3 ||
+ (child.nodeType == 1 &&
+ dtd.p[child.tagName] &&
+ !dtd.$cdata[child.tagName]))
+ ) {
+ tmpNode = child.nextSibling;
+ p.appendChild(child);
+ child = tmpNode;
+ }
+ if (p.firstChild) {
+ if (!child) {
+ me.body.appendChild(p);
+ break;
+ } else {
+ child.parentNode.insertBefore(p, child);
+ p = me.document.createElement("p");
+ }
+ }
+ child = child.nextSibling;
+ }
+ }
+ }
+ me.fireEvent("aftersetcontent");
+ me.fireEvent("contentchange");
+
+ !notFireSelectionchange && me._selectionChange();
+ //清除保存的选区
+ me._bakRange = me._bakIERange = me._bakNativeRange = null;
+ //trace:1742 setContent后gecko能得到焦点问题
+ var geckoSel;
+ if (browser.gecko && (geckoSel = this.selection.getNative())) {
+ geckoSel.removeAllRanges();
+ }
+ if (me.options.autoSyncData) {
+ me.form && setValue(me.form, me);
+ }
+ },
+
+ /**
+ * 让编辑器获得焦点,默认focus到编辑器头部
+ * @method focus
+ * @example
+ * ```javascript
+ * editor.focus()
+ * ```
+ */
+
+ /**
+ * 让编辑器获得焦点,toEnd确定focus位置
+ * @method focus
+ * @param { Boolean } toEnd 默认focus到编辑器头部,toEnd为true时focus到内容尾部
+ * @example
+ * ```javascript
+ * editor.focus(true)
+ * ```
+ */
+ focus: function(toEnd) {
+ try {
+ var me = this,
+ rng = me.selection.getRange();
+ if (toEnd) {
+ var node = me.body.lastChild;
+ if (node && node.nodeType == 1 && !dtd.$empty[node.tagName]) {
+ if (domUtils.isEmptyBlock(node)) {
+ rng.setStartAtFirst(node);
+ } else {
+ rng.setStartAtLast(node);
+ }
+ rng.collapse(true);
+ }
+ rng.setCursor(true);
+ } else {
+ if (
+ !rng.collapsed &&
+ domUtils.isBody(rng.startContainer) &&
+ rng.startOffset == 0
+ ) {
+ var node = me.body.firstChild;
+ if (node && node.nodeType == 1 && !dtd.$empty[node.tagName]) {
+ rng.setStartAtFirst(node).collapse(true);
+ }
+ }
+
+ rng.select(true);
+ }
+ this.fireEvent("focus selectionchange");
+ } catch (e) {}
+ },
+ isFocus: function() {
+ return this.selection.isFocus();
+ },
+ blur: function() {
+ var sel = this.selection.getNative();
+ if (sel.empty && browser.ie) {
+ var nativeRng = document.body.createTextRange();
+ nativeRng.moveToElementText(document.body);
+ nativeRng.collapse(true);
+ nativeRng.select();
+ sel.empty();
+ } else {
+ sel.removeAllRanges();
+ }
+
+ //this.fireEvent('blur selectionchange');
+ },
+ /**
+ * 初始化UE事件及部分事件代理
+ * @method _initEvents
+ * @private
+ */
+ _initEvents: function() {
+ var me = this,
+ doc = me.document,
+ win = me.window;
+ me._proxyDomEvent = utils.bind(me._proxyDomEvent, me);
+ domUtils.on(
+ doc,
+ [
+ "click",
+ "contextmenu",
+ "mousedown",
+ "keydown",
+ "keyup",
+ "keypress",
+ "mouseup",
+ "mouseover",
+ "mouseout",
+ "selectstart"
+ ],
+ me._proxyDomEvent
+ );
+ domUtils.on(win, ["focus", "blur"], me._proxyDomEvent);
+ domUtils.on(me.body, "drop", function(e) {
+ //阻止ff下默认的弹出新页面打开图片
+ if (browser.gecko && e.stopPropagation) {
+ e.stopPropagation();
+ }
+ me.fireEvent("contentchange");
+ });
+ domUtils.on(doc, ["mouseup", "keydown"], function(evt) {
+ //特殊键不触发selectionchange
+ if (
+ evt.type == "keydown" &&
+ (evt.ctrlKey || evt.metaKey || evt.shiftKey || evt.altKey)
+ ) {
+ return;
+ }
+ if (evt.button == 2) return;
+ me._selectionChange(250, evt);
+ });
+ },
+ /**
+ * 触发事件代理
+ * @method _proxyDomEvent
+ * @private
+ * @return { * } fireEvent的返回值
+ * @see UE.EventBase:fireEvent(String)
+ */
+ _proxyDomEvent: function(evt) {
+ if (
+ this.fireEvent("before" + evt.type.replace(/^on/, "").toLowerCase()) ===
+ false
+ ) {
+ return false;
+ }
+ if (this.fireEvent(evt.type.replace(/^on/, ""), evt) === false) {
+ return false;
+ }
+ return this.fireEvent(
+ "after" + evt.type.replace(/^on/, "").toLowerCase()
+ );
+ },
+ /**
+ * 变化选区
+ * @method _selectionChange
+ * @private
+ */
+ _selectionChange: function(delay, evt) {
+ var me = this;
+ //有光标才做selectionchange 为了解决未focus时点击source不能触发更改工具栏状态的问题(source命令notNeedUndo=1)
+ // if ( !me.selection.isFocus() ){
+ // return;
+ // }
+
+ var hackForMouseUp = false;
+ var mouseX, mouseY;
+ if (browser.ie && browser.version < 9 && evt && evt.type == "mouseup") {
+ var range = this.selection.getRange();
+ if (!range.collapsed) {
+ hackForMouseUp = true;
+ mouseX = evt.clientX;
+ mouseY = evt.clientY;
+ }
+ }
+ clearTimeout(_selectionChangeTimer);
+ _selectionChangeTimer = setTimeout(function() {
+ if (!me.selection || !me.selection.getNative()) {
+ return;
+ }
+ //修复一个IE下的bug: 鼠标点击一段已选择的文本中间时,可能在mouseup后的一段时间内取到的range是在selection的type为None下的错误值.
+ //IE下如果用户是拖拽一段已选择文本,则不会触发mouseup事件,所以这里的特殊处理不会对其有影响
+ var ieRange;
+ if (hackForMouseUp && me.selection.getNative().type == "None") {
+ ieRange = me.document.body.createTextRange();
+ try {
+ ieRange.moveToPoint(mouseX, mouseY);
+ } catch (ex) {
+ ieRange = null;
+ }
+ }
+ var bakGetIERange;
+ if (ieRange) {
+ bakGetIERange = me.selection.getIERange;
+ me.selection.getIERange = function() {
+ return ieRange;
+ };
+ }
+ me.selection.cache();
+ if (bakGetIERange) {
+ me.selection.getIERange = bakGetIERange;
+ }
+ if (me.selection._cachedRange && me.selection._cachedStartElement) {
+ me.fireEvent("beforeselectionchange");
+ // 第二个参数causeByUi为true代表由用户交互造成的selectionchange.
+ me.fireEvent("selectionchange", !!evt);
+ me.fireEvent("afterselectionchange");
+ me.selection.clear();
+ }
+ }, delay || 50);
+ },
+
+ /**
+ * 执行编辑命令
+ * @method _callCmdFn
+ * @private
+ * @param { String } fnName 函数名称
+ * @param { * } args 传给命令函数的参数
+ * @return { * } 返回命令函数运行的返回值
+ */
+ _callCmdFn: function(fnName, args) {
+ var cmdName = args[0].toLowerCase(),
+ cmd,
+ cmdFn;
+ cmd = this.commands[cmdName] || UE.commands[cmdName];
+ cmdFn = cmd && cmd[fnName];
+ //没有querycommandstate或者没有command的都默认返回0
+ if ((!cmd || !cmdFn) && fnName == "queryCommandState") {
+ return 0;
+ } else if (cmdFn) {
+ return cmdFn.apply(this, args);
+ }
+ },
+
+ /**
+ * 执行编辑命令cmdName,完成富文本编辑效果
+ * @method execCommand
+ * @param { String } cmdName 需要执行的命令
+ * @remind 具体命令的使用请参考命令列表
+ * @return { * } 返回命令函数运行的返回值
+ * @example
+ * ```javascript
+ * editor.execCommand(cmdName);
+ * ```
+ */
+ execCommand: function(cmdName) {
+ cmdName = cmdName.toLowerCase();
+ var me = this;
+ var result;
+ var cmd = me.commands[cmdName] || UE.commands[cmdName];
+ if (!cmd || !cmd.execCommand) {
+ return null;
+ }
+ if (!cmd.notNeedUndo && !me.__hasEnterExecCommand) {
+ me.__hasEnterExecCommand = true;
+ if (me.queryCommandState.apply(me, arguments) != -1) {
+ me.fireEvent("saveScene");
+ me.fireEvent.apply(
+ me,
+ ["beforeexeccommand", cmdName].concat(arguments)
+ );
+ result = this._callCmdFn("execCommand", arguments);
+ //保存场景时,做了内容对比,再看是否进行contentchange触发,这里多触发了一次,去掉
+ // (!cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange');
+ me.fireEvent.apply(
+ me,
+ ["afterexeccommand", cmdName].concat(arguments)
+ );
+ me.fireEvent("saveScene");
+ }
+ me.__hasEnterExecCommand = false;
+ } else {
+ result = this._callCmdFn("execCommand", arguments);
+ !me.__hasEnterExecCommand &&
+ !cmd.ignoreContentChange &&
+ !me._ignoreContentChange &&
+ me.fireEvent("contentchange");
+ }
+ !me.__hasEnterExecCommand &&
+ !cmd.ignoreContentChange &&
+ !me._ignoreContentChange &&
+ me._selectionChange();
+ return result;
+ },
+
+ /**
+ * 根据传入的command命令,查选编辑器当前的选区,返回命令的状态
+ * @method queryCommandState
+ * @param { String } cmdName 需要查询的命令名称
+ * @remind 具体命令的使用请参考命令列表
+ * @return { Number } number 返回放前命令的状态,返回值三种情况:(-1|0|1)
+ * @example
+ * ```javascript
+ * editor.queryCommandState(cmdName) => (-1|0|1)
+ * ```
+ * @see COMMAND.LIST
+ */
+ queryCommandState: function(cmdName) {
+ return this._callCmdFn("queryCommandState", arguments);
+ },
+
+ /**
+ * 根据传入的command命令,查选编辑器当前的选区,根据命令返回相关的值
+ * @method queryCommandValue
+ * @param { String } cmdName 需要查询的命令名称
+ * @remind 具体命令的使用请参考命令列表
+ * @remind 只有部分插件有此方法
+ * @return { * } 返回每个命令特定的当前状态值
+ * @grammar editor.queryCommandValue(cmdName) => {*}
+ * @see COMMAND.LIST
+ */
+ queryCommandValue: function(cmdName) {
+ return this._callCmdFn("queryCommandValue", arguments);
+ },
+
+ /**
+ * 检查编辑区域中是否有内容
+ * @method hasContents
+ * @remind 默认有文本内容,或者有以下节点都不认为是空
+ * table,ul,ol,dl,iframe,area,base,col,hr,img,embed,input,link,meta,param
+ * @return { Boolean } 检查有内容返回true,否则返回false
+ * @example
+ * ```javascript
+ * editor.hasContents()
+ * ```
+ */
+
+ /**
+ * 检查编辑区域中是否有内容,若包含参数tags中的节点类型,直接返回true
+ * @method hasContents
+ * @param { Array } tags 传入数组判断时用到的节点类型
+ * @return { Boolean } 若文档中包含tags数组里对应的tag,返回true,否则返回false
+ * @example
+ * ```javascript
+ * editor.hasContents(['span']);
+ * ```
+ */
+ hasContents: function(tags) {
+ if (tags) {
+ for (var i = 0, ci; (ci = tags[i++]); ) {
+ if (this.document.getElementsByTagName(ci).length > 0) {
+ return true;
+ }
+ }
+ }
+ if (!domUtils.isEmptyBlock(this.body)) {
+ return true;
+ }
+ //随时添加,定义的特殊标签如果存在,不能认为是空
+ tags = ["div"];
+ for (i = 0; (ci = tags[i++]); ) {
+ var nodes = domUtils.getElementsByTagName(this.document, ci);
+ for (var n = 0, cn; (cn = nodes[n++]); ) {
+ if (domUtils.isCustomeNode(cn)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ },
+
+ /**
+ * 重置编辑器,可用来做多个tab使用同一个编辑器实例
+ * @method reset
+ * @remind 此方法会清空编辑器内容,清空回退列表,会触发reset事件
+ * @example
+ * ```javascript
+ * editor.reset()
+ * ```
+ */
+ reset: function() {
+ this.fireEvent("reset");
+ },
+
+ /**
+ * 设置当前编辑区域可以编辑
+ * @method setEnabled
+ * @example
+ * ```javascript
+ * editor.setEnabled()
+ * ```
+ */
+ setEnabled: function() {
+ var me = this,
+ range;
+ if (me.body.contentEditable == "false") {
+ me.body.contentEditable = true;
+ range = me.selection.getRange();
+ //有可能内容丢失了
+ try {
+ range.moveToBookmark(me.lastBk);
+ delete me.lastBk;
+ } catch (e) {
+ range.setStartAtFirst(me.body).collapse(true);
+ }
+ range.select(true);
+ if (me.bkqueryCommandState) {
+ me.queryCommandState = me.bkqueryCommandState;
+ delete me.bkqueryCommandState;
+ }
+ if (me.bkqueryCommandValue) {
+ me.queryCommandValue = me.bkqueryCommandValue;
+ delete me.bkqueryCommandValue;
+ }
+ me.fireEvent("selectionchange");
+ }
+ },
+ enable: function() {
+ return this.setEnabled();
+ },
+
+ /** 设置当前编辑区域不可编辑
+ * @method setDisabled
+ */
+
+ /** 设置当前编辑区域不可编辑,except中的命令除外
+ * @method setDisabled
+ * @param { String } except 例外命令的字符串
+ * @remind 即使设置了disable,此处配置的例外命令仍然可以执行
+ * @example
+ * ```javascript
+ * editor.setDisabled('bold'); //禁用工具栏中除加粗之外的所有功能
+ * ```
+ */
+
+ /** 设置当前编辑区域不可编辑,except中的命令除外
+ * @method setDisabled
+ * @param { Array } except 例外命令的字符串数组,数组中的命令仍然可以执行
+ * @remind 即使设置了disable,此处配置的例外命令仍然可以执行
+ * @example
+ * ```javascript
+ * editor.setDisabled(['bold','insertimage']); //禁用工具栏中除加粗和插入图片之外的所有功能
+ * ```
+ */
+ setDisabled: function(except) {
+ var me = this;
+ except = except ? (utils.isArray(except) ? except : [except]) : [];
+ if (me.body.contentEditable == "true") {
+ if (!me.lastBk) {
+ me.lastBk = me.selection.getRange().createBookmark(true);
+ }
+ me.body.contentEditable = false;
+ me.bkqueryCommandState = me.queryCommandState;
+ me.bkqueryCommandValue = me.queryCommandValue;
+ me.queryCommandState = function(type) {
+ if (utils.indexOf(except, type) != -1) {
+ return me.bkqueryCommandState.apply(me, arguments);
+ }
+ return -1;
+ };
+ me.queryCommandValue = function(type) {
+ if (utils.indexOf(except, type) != -1) {
+ return me.bkqueryCommandValue.apply(me, arguments);
+ }
+ return null;
+ };
+ me.fireEvent("selectionchange");
+ }
+ },
+ disable: function(except) {
+ return this.setDisabled(except);
+ },
+
+ /**
+ * 设置默认内容
+ * @method _setDefaultContent
+ * @private
+ * @param { String } cont 要存入的内容
+ */
+ _setDefaultContent: (function() {
+ function clear() {
+ var me = this;
+ if (me.document.getElementById("initContent")) {
+ me.body.innerHTML = "" + (ie ? "" : "
") + "
";
+ me.removeListener("firstBeforeExecCommand focus", clear);
+ setTimeout(function() {
+ me.focus();
+ me._selectionChange();
+ }, 0);
+ }
+ }
+
+ return function(cont) {
+ var me = this;
+ me.body.innerHTML = '' + cont + "
";
+
+ me.addListener("firstBeforeExecCommand focus", clear);
+ };
+ })(),
+
+ /**
+ * 显示编辑器
+ * @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 "";
+ } */
+
+ if (serverUrl) {
+ serverUrl = serverUrl + "?";
+ 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",
+ 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 + "i18n/",
+ 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" : "",
+ onsuccess: function(r) {
+ try {
+ var config = isJsonp ? r : eval("(" + r.responseText + ")");
+ utils.extend(me.options, config);
+ 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,
+ 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;
+
+ 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 (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: 'neditor'
+ * },
+ *
+ * //请求成功后的回调, 该回调接受当前的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(/]*>[\s\S]*?.<\/v:shape>/gi, function(str) {
+ //opera能自己解析出image所这里直接返回空
+ if (browser.opera) {
+ return "";
+ }
+ try {
+ //有可能是bitmap占为图,无用,直接过滤掉,主要体现在粘贴excel表格中
+ if (/Bitmap/i.test(str)) {
+ return "";
+ }
+ var width = str.match(/width:([ \d.]*p[tx])/i)[1],
+ height = str.match(/height:([ \d.]*p[tx])/i)[1],
+ src = str.match(/src=\s*"([^"]*)"/i)[1];
+ return (
+ '
'
+ );
+ } catch (e) {
+ return "";
+ }
+ })
+ //针对wps添加的多余标签处理
+ .replace(/<\/?div[^>]*>/g, "")
+ //去掉多余的属性
+ .replace(/v:\w+=(["']?)[^'"]+\1/g, "")
+ .replace(
+ /<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|xml|meta|link|style|\w+:\w+)(?=[\s\/>]))[^>]*>/gi,
+ ""
+ )
+ .replace(
+ /]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi,
+ "
$1
"
+ )
+ //去掉多余的属性
+ .replace(/\s+(class|lang|align)\s*=\s*(['"]?)([\w-]+)\2/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 (!/]/.test(html)) {
+ return UE.htmlparser(html).children[0];
+ } else {
+ return new uNode({
+ type: "element",
+ children: [],
+ tagName: html
+ });
+ }
+ };
+ uNode.createText = function(data, noTrans) {
+ return new UE.uNode({
+ type: "text",
+ data: noTrans ? data : utils.unhtml(data || "")
+ });
+ };
+ function nodeToHtml(node, arr, formatter, current) {
+ switch (node.type) {
+ case "root":
+ for (var i = 0, ci; (ci = node.children[i++]); ) {
+ //插入新行
+ if (
+ formatter &&
+ ci.type == "element" &&
+ !dtd.$inlineWithA[ci.tagName] &&
+ i > 1
+ ) {
+ insertLine(arr, current, true);
+ insertIndent(arr, current);
+ }
+ nodeToHtml(ci, arr, formatter, current);
+ }
+ break;
+ case "text":
+ isText(node, arr);
+ break;
+ case "element":
+ isElement(node, arr, formatter, current);
+ break;
+ case "comment":
+ isComment(node, arr, formatter);
+ }
+ return arr;
+ }
+
+ function isText(node, arr) {
+ if (node.parentNode.tagName == "pre") {
+ //源码模式下输入html标签,不能做转换处理,直接输出
+ arr.push(node.data);
+ } else {
+ arr.push(
+ notTransTagName[node.parentNode.tagName]
+ ? utils.html(node.data)
+ : node.data.replace(/[ ]{2}/g, " ")
+ );
+ }
+ }
+
+ function isElement(node, arr, formatter, current) {
+ var attrhtml = "";
+ if (node.attrs) {
+ attrhtml = [];
+ var attrs = node.attrs;
+ for (var a in attrs) {
+ //这里就针对
+ //'
+ //这里边的\"做转换,要不用innerHTML直接被截断了,属性src
+ //有可能做的不够
+ attrhtml.push(
+ a +
+ (attrs[a] !== undefined
+ ? '="' +
+ (notTransAttrs[a]
+ ? utils.html(attrs[a]).replace(/["]/g, function(a) {
+ return """;
+ })
+ : utils.unhtml(attrs[a])) +
+ '"'
+ : "")
+ );
+ }
+ attrhtml = attrhtml.join(" ");
+ }
+ arr.push(
+ "<" +
+ node.tagName +
+ (attrhtml ? " " + attrhtml : "") +
+ (dtd.$empty[node.tagName] ? "/" : "") +
+ ">"
+ );
+ //插入新行
+ if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != "pre") {
+ if (node.children && node.children.length) {
+ current = insertLine(arr, current, true);
+ insertIndent(arr, current);
+ }
+ }
+ if (node.children && node.children.length) {
+ for (var i = 0, ci; (ci = node.children[i++]); ) {
+ if (
+ formatter &&
+ ci.type == "element" &&
+ !dtd.$inlineWithA[ci.tagName] &&
+ i > 1
+ ) {
+ insertLine(arr, current);
+ insertIndent(arr, current);
+ }
+ nodeToHtml(ci, arr, formatter, current);
+ }
+ }
+ if (!dtd.$empty[node.tagName]) {
+ if (
+ formatter &&
+ !dtd.$inlineWithA[node.tagName] &&
+ node.tagName != "pre"
+ ) {
+ if (node.children && node.children.length) {
+ current = insertLine(arr, current);
+ insertIndent(arr, current);
+ }
+ }
+ arr.push("" + node.tagName + ">");
+ }
+ }
+
+ function isComment(node, arr) {
+ arr.push("");
+ }
+
+ function getNodeById(root, id) {
+ var node;
+ if (root.type == "element" && root.getAttr("id") == id) {
+ return root;
+ }
+ if (root.children && root.children.length) {
+ for (var i = 0, ci; (ci = root.children[i++]); ) {
+ if ((node = getNodeById(ci, id))) {
+ return node;
+ }
+ }
+ }
+ }
+
+ function getNodesByTagName(node, tagName, arr) {
+ if (node.type == "element" && node.tagName == tagName) {
+ arr.push(node);
+ }
+ if (node.children && node.children.length) {
+ for (var i = 0, ci; (ci = node.children[i++]); ) {
+ getNodesByTagName(ci, tagName, arr);
+ }
+ }
+ }
+ function nodeTraversal(root, fn) {
+ if (root.children && root.children.length) {
+ for (var i = 0, ci; (ci = root.children[i]); ) {
+ nodeTraversal(ci, fn);
+ //ci被替换的情况,这里就不再走 fn了
+ if (ci.parentNode) {
+ if (ci.children && ci.children.length) {
+ fn(ci);
+ }
+ if (ci.parentNode) i++;
+ }
+ }
+ } else {
+ fn(root);
+ }
+ }
+ uNode.prototype = {
+ /**
+ * 当前节点对象,转换成html文本
+ * @method toHtml
+ * @return { String } 返回转换后的html字符串
+ * @example
+ * ```javascript
+ * node.toHtml();
+ * ```
+ */
+
+ /**
+ * 当前节点对象,转换成html文本
+ * @method toHtml
+ * @param { Boolean } formatter 是否格式化返回值
+ * @return { String } 返回转换后的html字符串
+ * @example
+ * ```javascript
+ * node.toHtml( true );
+ * ```
+ */
+ toHtml: function(formatter) {
+ var arr = [];
+ nodeToHtml(this, arr, formatter, 0);
+ return arr.join("");
+ },
+
+ /**
+ * 获取节点的html内容
+ * @method innerHTML
+ * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
+ * @return { String } 返回节点的html内容
+ * @example
+ * ```javascript
+ * var htmlstr = node.innerHTML();
+ * ```
+ */
+
+ /**
+ * 设置节点的html内容
+ * @method innerHTML
+ * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
+ * @param { String } htmlstr 传入要设置的html内容
+ * @return { UE.uNode } 返回节点本身
+ * @example
+ * ```javascript
+ * node.innerHTML('text');
+ * ```
+ */
+ innerHTML: function(htmlstr) {
+ if (this.type != "element" || dtd.$empty[this.tagName]) {
+ return this;
+ }
+ if (utils.isString(htmlstr)) {
+ if (this.children) {
+ for (var i = 0, ci; (ci = this.children[i++]); ) {
+ ci.parentNode = null;
+ }
+ }
+ this.children = [];
+ var tmpRoot = UE.htmlparser(htmlstr);
+ for (var i = 0, ci; (ci = tmpRoot.children[i++]); ) {
+ this.children.push(ci);
+ ci.parentNode = this;
+ }
+ return this;
+ } else {
+ var tmpRoot = new UE.uNode({
+ type: "root",
+ children: this.children
+ });
+ return tmpRoot.toHtml();
+ }
+ },
+
+ /**
+ * 获取节点的纯文本内容
+ * @method innerText
+ * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
+ * @return { String } 返回节点的存文本内容
+ * @example
+ * ```javascript
+ * var textStr = node.innerText();
+ * ```
+ */
+
+ /**
+ * 设置节点的纯文本内容
+ * @method innerText
+ * @warning 假如节点的type不是'element',或节点的标签名称不在dtd列表里,直接返回当前节点
+ * @param { String } textStr 传入要设置的文本内容
+ * @return { UE.uNode } 返回节点本身
+ * @example
+ * ```javascript
+ * node.innerText('text');
+ * ```
+ */
+ innerText: function(textStr, noTrans) {
+ if (this.type != "element" || dtd.$empty[this.tagName]) {
+ return this;
+ }
+ if (textStr) {
+ if (this.children) {
+ for (var i = 0, ci; (ci = this.children[i++]); ) {
+ ci.parentNode = null;
+ }
+ }
+ this.children = [];
+ this.appendChild(uNode.createText(textStr, noTrans));
+ return this;
+ } else {
+ return this.toHtml().replace(/<[^>]+>/g, "");
+ }
+ },
+
+ /**
+ * 获取当前对象的data属性
+ * @method getData
+ * @return { Object } 若节点的type值是elemenet,返回空字符串,否则返回节点的data属性
+ * @example
+ * ```javascript
+ * node.getData();
+ * ```
+ */
+ getData: function() {
+ if (this.type == "element") return "";
+ return this.data;
+ },
+
+ /**
+ * 获取当前节点下的第一个子节点
+ * @method firstChild
+ * @return { UE.uNode } 返回第一个子节点
+ * @example
+ * ```javascript
+ * node.firstChild(); //返回第一个子节点
+ * ```
+ */
+ firstChild: function() {
+ // if (this.type != 'element' || dtd.$empty[this.tagName]) {
+ // return this;
+ // }
+ return this.children ? this.children[0] : null;
+ },
+
+ /**
+ * 获取当前节点下的最后一个子节点
+ * @method lastChild
+ * @return { UE.uNode } 返回最后一个子节点
+ * @example
+ * ```javascript
+ * node.lastChild(); //返回最后一个子节点
+ * ```
+ */
+ lastChild: function() {
+ // if (this.type != 'element' || dtd.$empty[this.tagName] ) {
+ // return this;
+ // }
+ return this.children ? this.children[this.children.length - 1] : null;
+ },
+
+ /**
+ * 获取和当前节点有相同父亲节点的前一个节点
+ * @method previousSibling
+ * @return { UE.uNode } 返回前一个节点
+ * @example
+ * ```javascript
+ * node.children[2].previousSibling(); //返回子节点node.children[1]
+ * ```
+ */
+ previousSibling: function() {
+ var parent = this.parentNode;
+ for (var i = 0, ci; (ci = parent.children[i]); i++) {
+ if (ci === this) {
+ return i == 0 ? null : parent.children[i - 1];
+ }
+ }
+ },
+
+ /**
+ * 获取和当前节点有相同父亲节点的后一个节点
+ * @method nextSibling
+ * @return { UE.uNode } 返回后一个节点,找不到返回null
+ * @example
+ * ```javascript
+ * node.children[2].nextSibling(); //如果有,返回子节点node.children[3]
+ * ```
+ */
+ nextSibling: function() {
+ var parent = this.parentNode;
+ for (var i = 0, ci; (ci = parent.children[i++]); ) {
+ if (ci === this) {
+ return parent.children[i];
+ }
+ }
+ },
+
+ /**
+ * 用新的节点替换当前节点
+ * @method replaceChild
+ * @param { UE.uNode } target 要替换成该节点参数
+ * @param { UE.uNode } source 要被替换掉的节点
+ * @return { UE.uNode } 返回替换之后的节点对象
+ * @example
+ * ```javascript
+ * node.replaceChild(newNode, childNode); //用newNode替换childNode,childNode是node的子节点
+ * ```
+ */
+ replaceChild: function(target, source) {
+ if (this.children) {
+ if (target.parentNode) {
+ target.parentNode.removeChild(target);
+ }
+ for (var i = 0, ci; (ci = this.children[i]); i++) {
+ if (ci === source) {
+ this.children.splice(i, 1, target);
+ source.parentNode = null;
+ target.parentNode = this;
+ return target;
+ }
+ }
+ }
+ },
+
+ /**
+ * 在节点的子节点列表最后位置插入一个节点
+ * @method appendChild
+ * @param { UE.uNode } node 要插入的节点
+ * @return { UE.uNode } 返回刚插入的子节点
+ * @example
+ * ```javascript
+ * node.appendChild( newNode ); //在node内插入子节点newNode
+ * ```
+ */
+ appendChild: function(node) {
+ if (
+ this.type == "root" ||
+ (this.type == "element" && !dtd.$empty[this.tagName])
+ ) {
+ if (!this.children) {
+ this.children = [];
+ }
+ if (node.parentNode) {
+ node.parentNode.removeChild(node);
+ }
+ for (var i = 0, ci; (ci = this.children[i]); i++) {
+ if (ci === node) {
+ this.children.splice(i, 1);
+ break;
+ }
+ }
+ this.children.push(node);
+ node.parentNode = this;
+ return node;
+ }
+ },
+
+ /**
+ * 在传入节点的前面插入一个节点
+ * @method insertBefore
+ * @param { UE.uNode } target 要插入的节点
+ * @param { UE.uNode } source 在该参数节点前面插入
+ * @return { UE.uNode } 返回刚插入的子节点
+ * @example
+ * ```javascript
+ * node.parentNode.insertBefore(newNode, node); //在node节点后面插入newNode
+ * ```
+ */
+ insertBefore: function(target, source) {
+ if (this.children) {
+ if (target.parentNode) {
+ target.parentNode.removeChild(target);
+ }
+ for (var i = 0, ci; (ci = this.children[i]); i++) {
+ if (ci === source) {
+ this.children.splice(i, 0, target);
+ target.parentNode = this;
+ return target;
+ }
+ }
+ }
+ },
+
+ /**
+ * 在传入节点的后面插入一个节点
+ * @method insertAfter
+ * @param { UE.uNode } target 要插入的节点
+ * @param { UE.uNode } source 在该参数节点后面插入
+ * @return { UE.uNode } 返回刚插入的子节点
+ * @example
+ * ```javascript
+ * node.parentNode.insertAfter(newNode, node); //在node节点后面插入newNode
+ * ```
+ */
+ insertAfter: function(target, source) {
+ if (this.children) {
+ if (target.parentNode) {
+ target.parentNode.removeChild(target);
+ }
+ for (var i = 0, ci; (ci = this.children[i]); i++) {
+ if (ci === source) {
+ this.children.splice(i + 1, 0, target);
+ target.parentNode = this;
+ return target;
+ }
+ }
+ }
+ },
+
+ /**
+ * 从当前节点的子节点列表中,移除节点
+ * @method removeChild
+ * @param { UE.uNode } node 要移除的节点引用
+ * @param { Boolean } keepChildren 是否保留移除节点的子节点,若传入true,自动把移除节点的子节点插入到移除的位置
+ * @return { * } 返回刚移除的子节点
+ * @example
+ * ```javascript
+ * node.removeChild(childNode,true); //在node的子节点列表中移除child节点,并且吧child的子节点插入到移除的位置
+ * ```
+ */
+ removeChild: function(node, keepChildren) {
+ if (this.children) {
+ for (var i = 0, ci; (ci = this.children[i]); i++) {
+ if (ci === node) {
+ this.children.splice(i, 1);
+ ci.parentNode = null;
+ if (keepChildren && ci.children && ci.children.length) {
+ for (var j = 0, cj; (cj = ci.children[j]); j++) {
+ this.children.splice(i + j, 0, cj);
+ cj.parentNode = this;
+ }
+ }
+ return ci;
+ }
+ }
+ }
+ },
+
+ /**
+ * 获取当前节点所代表的元素属性,即获取attrs对象下的属性值
+ * @method getAttr
+ * @param { String } attrName 要获取的属性名称
+ * @return { * } 返回attrs对象下的属性值
+ * @example
+ * ```javascript
+ * node.getAttr('title');
+ * ```
+ */
+ getAttr: function(attrName) {
+ return this.attrs && this.attrs[attrName.toLowerCase()];
+ },
+
+ /**
+ * 设置当前节点所代表的元素属性,即设置attrs对象下的属性值
+ * @method setAttr
+ * @param { String } attrName 要设置的属性名称
+ * @param { * } attrVal 要设置的属性值,类型视设置的属性而定
+ * @return { * } 返回attrs对象下的属性值
+ * @example
+ * ```javascript
+ * node.setAttr('title','标题');
+ * ```
+ */
+ setAttr: function(attrName, attrVal) {
+ if (!attrName) {
+ delete this.attrs;
+ return;
+ }
+ if (!this.attrs) {
+ this.attrs = {};
+ }
+ if (utils.isObject(attrName)) {
+ for (var a in attrName) {
+ if (!attrName[a]) {
+ delete this.attrs[a];
+ } else {
+ this.attrs[a.toLowerCase()] = attrName[a];
+ }
+ }
+ } else {
+ if (!attrVal) {
+ delete this.attrs[attrName];
+ } else {
+ this.attrs[attrName.toLowerCase()] = attrVal;
+ }
+ }
+ },
+
+ /**
+ * 获取当前节点在父节点下的位置索引
+ * @method getIndex
+ * @return { Number } 返回索引数值,如果没有父节点,返回-1
+ * @example
+ * ```javascript
+ * node.getIndex();
+ * ```
+ */
+ getIndex: function() {
+ var parent = this.parentNode;
+ for (var i = 0, ci; (ci = parent.children[i]); i++) {
+ if (ci === this) {
+ return i;
+ }
+ }
+ return -1;
+ },
+
+ /**
+ * 在当前节点下,根据id查找节点
+ * @method getNodeById
+ * @param { String } id 要查找的id
+ * @return { UE.uNode } 返回找到的节点
+ * @example
+ * ```javascript
+ * node.getNodeById('textId');
+ * ```
+ */
+ getNodeById: function(id) {
+ var node;
+ if (this.children && this.children.length) {
+ for (var i = 0, ci; (ci = this.children[i++]); ) {
+ if ((node = getNodeById(ci, id))) {
+ return node;
+ }
+ }
+ }
+ },
+
+ /**
+ * 在当前节点下,根据元素名称查找节点列表
+ * @method getNodesByTagName
+ * @param { String } tagNames 要查找的元素名称
+ * @return { Array } 返回找到的节点列表
+ * @example
+ * ```javascript
+ * node.getNodesByTagName('span');
+ * ```
+ */
+ getNodesByTagName: function(tagNames) {
+ tagNames = utils.trim(tagNames).replace(/[ ]{2,}/g, " ").split(" ");
+ var arr = [],
+ me = this;
+ utils.each(tagNames, function(tagName) {
+ if (me.children && me.children.length) {
+ for (var i = 0, ci; (ci = me.children[i++]); ) {
+ getNodesByTagName(ci, tagName, arr);
+ }
+ }
+ });
+ return arr;
+ },
+
+ /**
+ * 根据样式名称,获取节点的样式值
+ * @method getStyle
+ * @param { String } name 要获取的样式名称
+ * @return { String } 返回样式值
+ * @example
+ * ```javascript
+ * node.getStyle('font-size');
+ * ```
+ */
+ getStyle: function(name) {
+ var cssStyle = this.getAttr("style");
+ if (!cssStyle) {
+ return "";
+ }
+ var reg = new RegExp("(^|;)\\s*" + name + ":([^;]+)", "i");
+ var match = cssStyle.match(reg);
+ if (match && match[0]) {
+ return match[2];
+ }
+ return "";
+ },
+
+ /**
+ * 给节点设置样式
+ * @method setStyle
+ * @param { String } name 要设置的的样式名称
+ * @param { String } val 要设置的的样值
+ * @example
+ * ```javascript
+ * node.setStyle('font-size', '12px');
+ * ```
+ */
+ setStyle: function(name, val) {
+ function exec(name, val) {
+ var reg = new RegExp("(^|;)\\s*" + name + ":([^;]+;?)", "gi");
+ cssStyle = cssStyle.replace(reg, "$1");
+ if (val) {
+ cssStyle = name + ":" + utils.unhtml(val) + ";" + cssStyle;
+ }
+ }
+
+ var cssStyle = this.getAttr("style");
+ if (!cssStyle) {
+ cssStyle = "";
+ }
+ if (utils.isObject(name)) {
+ for (var a in name) {
+ exec(a, name[a]);
+ }
+ } else {
+ exec(name, val);
+ }
+ this.setAttr("style", utils.trim(cssStyle));
+ },
+
+ /**
+ * 传入一个函数,递归遍历当前节点下的所有节点
+ * @method traversal
+ * @param { Function } fn 遍历到节点的时,传入节点作为参数,运行此函数
+ * @example
+ * ```javascript
+ * traversal(node, function(){
+ * console.log(node.type);
+ * });
+ * ```
+ */
+ traversal: function(fn) {
+ if (this.children && this.children.length) {
+ nodeTraversal(this, fn);
+ }
+ return this;
+ }
+ };
+})();
+
+
+// core/htmlparser.js
+/**
+ * html字符串转换成uNode节点
+ * @file
+ * @module UE
+ * @since 1.2.6.1
+ */
+
+/**
+ * UEditor公用空间,UEditor所有的功能都挂载在该空间下
+ * @unfile
+ * @module UE
+ */
+
+/**
+ * html字符串转换成uNode节点的静态方法
+ * @method htmlparser
+ * @param { String } htmlstr 要转换的html代码
+ * @param { Boolean } ignoreBlank 若设置为true,转换的时候忽略\n\r\t等空白字符
+ * @return { uNode } 给定的html片段转换形成的uNode对象
+ * @example
+ * ```javascript
+ * var root = UE.htmlparser('htmlparser
', true);
+ * ```
+ */
+
+var htmlparser = (UE.htmlparser = function(htmlstr, ignoreBlank) {
+ //todo 原来的方式 [^"'<>\/] 有\/就不能配对上 这样的标签了
+ //先去掉了,加上的原因忘了,这里先记录
+ //var re_tag = /<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\s\/<>]+)\s*((?:(?:"[^"]*")|(?:'[^']*')|[^"'<>])*)\/?>))/g,
+ //以上的正则表达式无法匹配:
+ //修改为如下正则表达式:
+ var re_tag = /<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\/\s>]+)((?:\s+[\w\-:.]+(?:\s*=\s*?(?:(?:"[^"]*")|(?:'[^']*')|[^\s"'\/>]+))?)*)[\S\s]*?(\/?)>))/g,
+ re_attr = /([\w\-:.]+)(?:(?:\s*=\s*(?:(?:"([^"]*)")|(?:'([^']*)')|([^\s>]+)))|(?=\s|$))/g;
+
+ //ie下取得的html可能会有\n存在,要去掉,在处理replace(/[\t\r\n]*/g,'');代码高量的\n不能去除
+ var allowEmptyTags = {
+ b: 1,
+ code: 1,
+ i: 1,
+ u: 1,
+ strike: 1,
+ s: 1,
+ tt: 1,
+ strong: 1,
+ q: 1,
+ samp: 1,
+ em: 1,
+ span: 1,
+ sub: 1,
+ img: 1,
+ sup: 1,
+ font: 1,
+ big: 1,
+ small: 1,
+ iframe: 1,
+ a: 1,
+ br: 1,
+ pre: 1
+ };
+ htmlstr = htmlstr.replace(new RegExp(domUtils.fillChar, "g"), "");
+ if (!ignoreBlank) {
+ htmlstr = htmlstr.replace(
+ new RegExp(
+ "[\\r\\t\\n" +
+ (ignoreBlank ? "" : " ") +
+ "]*?(\\w+)\\s*(?:[^>]*)>[\\r\\t\\n" +
+ (ignoreBlank ? "" : " ") +
+ "]*",
+ "g"
+ ),
+ function(a, b) {
+ //br暂时单独处理
+ if (b && allowEmptyTags[b.toLowerCase()]) {
+ return a.replace(/(^[\n\r]+)|([\n\r]+$)/g, "");
+ }
+ return a
+ .replace(new RegExp("^[\\r\\n" + (ignoreBlank ? "" : " ") + "]+"), "")
+ .replace(
+ new RegExp("[\\r\\n" + (ignoreBlank ? "" : " ") + "]+$"),
+ ""
+ );
+ }
+ );
+ }
+
+ var notTransAttrs = {
+ href: 1,
+ src: 1
+ };
+
+ var uNode = UE.uNode,
+ needParentNode = {
+ td: "tr",
+ tr: ["tbody", "thead", "tfoot"],
+ tbody: "table",
+ th: "tr",
+ thead: "table",
+ tfoot: "table",
+ caption: "table",
+ li: ["ul", "ol"],
+ dt: "dl",
+ dd: "dl",
+ option: "select"
+ },
+ needChild = {
+ ol: "li",
+ ul: "li"
+ };
+
+ function text(parent, data) {
+ if (needChild[parent.tagName]) {
+ var tmpNode = uNode.createElement(needChild[parent.tagName]);
+ parent.appendChild(tmpNode);
+ tmpNode.appendChild(uNode.createText(data));
+ parent = tmpNode;
+ } else {
+ parent.appendChild(uNode.createText(data));
+ }
+ }
+
+ function element(parent, tagName, htmlattr) {
+ var needParentTag;
+ if ((needParentTag = needParentNode[tagName])) {
+ var tmpParent = parent,
+ hasParent;
+ while (tmpParent.type != "root") {
+ if (
+ utils.isArray(needParentTag)
+ ? utils.indexOf(needParentTag, tmpParent.tagName) != -1
+ : needParentTag == tmpParent.tagName
+ ) {
+ parent = tmpParent;
+ hasParent = true;
+ break;
+ }
+ tmpParent = tmpParent.parentNode;
+ }
+ if (!hasParent) {
+ parent = element(
+ parent,
+ utils.isArray(needParentTag) ? needParentTag[0] : needParentTag
+ );
+ }
+ }
+ //按dtd处理嵌套
+ // if(parent.type != 'root' && !dtd[parent.tagName][tagName])
+ // parent = parent.parentNode;
+ var elm = new uNode({
+ parentNode: parent,
+ type: "element",
+ tagName: tagName.toLowerCase(),
+ //是自闭合的处理一下
+ children: dtd.$empty[tagName] ? null : []
+ });
+ //如果属性存在,处理属性
+ if (htmlattr) {
+ var attrs = {},
+ match;
+ while ((match = re_attr.exec(htmlattr))) {
+ attrs[match[1].toLowerCase()] = notTransAttrs[match[1].toLowerCase()]
+ ? match[2] || match[3] || match[4]
+ : utils.unhtml(match[2] || match[3] || match[4]);
+ }
+ elm.attrs = attrs;
+ }
+ //trace:3970
+ // //如果parent下不能放elm
+ // if(dtd.$inline[parent.tagName] && dtd.$block[elm.tagName] && !dtd[parent.tagName][elm.tagName]){
+ // parent = parent.parentNode;
+ // elm.parentNode = parent;
+ // }
+ parent.children.push(elm);
+ //如果是自闭合节点返回父亲节点
+ return dtd.$empty[tagName] ? parent : elm;
+ }
+
+ function comment(parent, data) {
+ parent.children.push(
+ new uNode({
+ type: "comment",
+ data: data,
+ parentNode: parent
+ })
+ );
+ }
+
+ var match,
+ currentIndex = 0,
+ nextIndex = 0;
+ //设置根节点
+ var root = new uNode({
+ type: "root",
+ children: []
+ });
+ var currentParent = root;
+
+ while ((match = re_tag.exec(htmlstr))) {
+ currentIndex = match.index;
+ try {
+ if (currentIndex > nextIndex) {
+ //text node
+ text(currentParent, htmlstr.slice(nextIndex, currentIndex));
+ }
+ if (match[3]) {
+ if (dtd.$cdata[currentParent.tagName]) {
+ text(currentParent, match[0]);
+ } else {
+ //start tag
+ currentParent = element(
+ currentParent,
+ match[3].toLowerCase(),
+ match[4]
+ );
+ }
+ } else if (match[1]) {
+ if (currentParent.type != "root") {
+ if (dtd.$cdata[currentParent.tagName] && !dtd.$cdata[match[1]]) {
+ text(currentParent, match[0]);
+ } else {
+ var tmpParent = currentParent;
+ while (
+ currentParent.type == "element" &&
+ currentParent.tagName != match[1].toLowerCase()
+ ) {
+ currentParent = currentParent.parentNode;
+ if (currentParent.type == "root") {
+ currentParent = tmpParent;
+ throw "break";
+ }
+ }
+ //end tag
+ currentParent = currentParent.parentNode;
+ }
+ }
+ } else if (match[2]) {
+ //comment
+ comment(currentParent, match[2]);
+ }
+ } catch (e) {}
+
+ nextIndex = re_tag.lastIndex;
+ }
+ //如果结束是文本,就有可能丢掉,所以这里手动判断一下
+ //例如 sdfsdfsdfsdfsdfsdfsdf
+ if (nextIndex < htmlstr.length) {
+ text(currentParent, htmlstr.slice(nextIndex));
+ }
+ return root;
+});
+
+
+// core/filternode.js
+/**
+ * UE过滤节点的静态方法
+ * @file
+ */
+
+/**
+ * UEditor公用空间,UEditor所有的功能都挂载在该空间下
+ * @module UE
+ */
+
+/**
+ * 根据传入节点和过滤规则过滤相应节点
+ * @module UE
+ * @since 1.2.6.1
+ * @method filterNode
+ * @param { Object } root 指定root节点
+ * @param { Object } rules 过滤规则json对象
+ * @example
+ * ```javascript
+ * UE.filterNode(root,editor.options.filterRules);
+ * ```
+ */
+var filterNode = (UE.filterNode = (function() {
+ function filterNode(node, rules) {
+ switch (node.type) {
+ case "text":
+ break;
+ case "element":
+ var val;
+ if ((val = rules[node.tagName])) {
+ if (val === "-") {
+ node.parentNode.removeChild(node);
+ } else if (utils.isFunction(val)) {
+ var parentNode = node.parentNode,
+ index = node.getIndex();
+ val(node);
+ if (node.parentNode) {
+ if (node.children) {
+ for (var i = 0, ci; (ci = node.children[i]); ) {
+ filterNode(ci, rules);
+ if (ci.parentNode) {
+ i++;
+ }
+ }
+ }
+ } else {
+ for (var i = index, ci; (ci = parentNode.children[i]); ) {
+ filterNode(ci, rules);
+ if (ci.parentNode) {
+ i++;
+ }
+ }
+ }
+ } else {
+ var attrs = val["$"];
+ if (attrs && node.attrs) {
+ var tmpAttrs = {},
+ tmpVal;
+ for (var a in attrs) {
+ tmpVal = node.getAttr(a);
+ //todo 只先对style单独处理
+ if (a == "style" && utils.isArray(attrs[a])) {
+ var tmpCssStyle = [];
+ utils.each(attrs[a], function(v) {
+ var tmp;
+ if ((tmp = node.getStyle(v))) {
+ tmpCssStyle.push(v + ":" + tmp);
+ }
+ });
+ tmpVal = tmpCssStyle.join(";");
+ }
+ if (tmpVal) {
+ tmpAttrs[a] = tmpVal;
+ }
+ }
+ node.attrs = tmpAttrs;
+ }
+ if (node.children) {
+ for (var i = 0, ci; (ci = node.children[i]); ) {
+ filterNode(ci, rules);
+ if (ci.parentNode) {
+ i++;
+ }
+ }
+ }
+ }
+ } else {
+ //如果不在名单里扣出子节点并删除该节点,cdata除外
+ if (dtd.$cdata[node.tagName]) {
+ node.parentNode.removeChild(node);
+ } else {
+ var parentNode = node.parentNode,
+ index = node.getIndex();
+ node.parentNode.removeChild(node, true);
+ for (var i = index, ci; (ci = parentNode.children[i]); ) {
+ filterNode(ci, rules);
+ if (ci.parentNode) {
+ i++;
+ }
+ }
+ }
+ }
+ break;
+ case "comment":
+ node.parentNode.removeChild(node);
+ }
+ }
+ return function(root, rules) {
+ if (utils.isEmptyObject(rules)) {
+ return root;
+ }
+ var val;
+ if ((val = rules["-"])) {
+ utils.each(val.split(" "), function(k) {
+ rules[k] = "-";
+ });
+ }
+ for (var i = 0, ci; (ci = root.children[i]); ) {
+ filterNode(ci, rules);
+ if (ci.parentNode) {
+ i++;
+ }
+ }
+ return root;
+ };
+})());
+
+
+// core/plugin.js
+/**
+ * Created with JetBrains PhpStorm.
+ * User: campaign
+ * Date: 10/8/13
+ * Time: 6:15 PM
+ * To change this template use File | Settings | File Templates.
+ */
+UE.plugin = (function() {
+ var _plugins = {};
+ return {
+ register: function(pluginName, fn, oldOptionName, afterDisabled) {
+ if (oldOptionName && utils.isFunction(oldOptionName)) {
+ afterDisabled = oldOptionName;
+ oldOptionName = null;
+ }
+ _plugins[pluginName] = {
+ optionName: oldOptionName || pluginName,
+ execFn: fn,
+ //当插件被禁用时执行
+ afterDisabled: afterDisabled
+ };
+ },
+ load: function(editor) {
+ utils.each(_plugins, function(plugin) {
+ var _export = plugin.execFn.call(editor);
+ if (editor.options[plugin.optionName] !== false) {
+ if (_export) {
+ //后边需要再做扩展
+ utils.each(_export, function(v, k) {
+ switch (k.toLowerCase()) {
+ case "shortcutkey":
+ editor.addshortcutkey(v);
+ break;
+ case "bindevents":
+ utils.each(v, function(fn, eventName) {
+ editor.addListener(eventName, fn);
+ });
+ break;
+ case "bindmultievents":
+ utils.each(utils.isArray(v) ? v : [v], function(event) {
+ var types = utils.trim(event.type).split(/\s+/);
+ utils.each(types, function(eventName) {
+ editor.addListener(eventName, event.handler);
+ });
+ });
+ break;
+ case "commands":
+ utils.each(v, function(execFn, execName) {
+ editor.commands[execName] = execFn;
+ });
+ break;
+ case "outputrule":
+ editor.addOutputRule(v);
+ break;
+ case "inputrule":
+ editor.addInputRule(v);
+ break;
+ case "defaultoptions":
+ editor.setOpt(v);
+ }
+ });
+ }
+ } else if (plugin.afterDisabled) {
+ plugin.afterDisabled.call(editor);
+ }
+ });
+ //向下兼容
+ utils.each(UE.plugins, function(plugin) {
+ plugin.call(editor);
+ });
+ },
+ run: function(pluginName, editor) {
+ var plugin = _plugins[pluginName];
+ if (plugin) {
+ plugin.exeFn.call(editor);
+ }
+ }
+ };
+})();
+
+
+// core/keymap.js
+var keymap = (UE.keymap = {
+ Backspace: 8,
+ Tab: 9,
+ Enter: 13,
+
+ Shift: 16,
+ Control: 17,
+ Alt: 18,
+ CapsLock: 20,
+
+ Esc: 27,
+
+ Spacebar: 32,
+
+ PageUp: 33,
+ PageDown: 34,
+ End: 35,
+ Home: 36,
+
+ Left: 37,
+ Up: 38,
+ Right: 39,
+ Down: 40,
+
+ Insert: 45,
+
+ Del: 46,
+
+ NumLock: 144,
+
+ Cmd: 91,
+
+ "=": 187,
+ "-": 189,
+
+ b: 66,
+ i: 73,
+ //回退
+ z: 90,
+ y: 89,
+ //粘贴
+ v: 86,
+ x: 88,
+
+ s: 83,
+
+ n: 78
+});
+
+
+// core/localstorage.js
+//存储媒介封装
+var LocalStorage = (UE.LocalStorage = (function() {
+ var storage = window.localStorage || getUserData() || null,
+ LOCAL_FILE = "localStorage";
+
+ return {
+ saveLocalData: function(key, data) {
+ if (storage && data) {
+ storage.setItem(key, data);
+ return true;
+ }
+
+ return false;
+ },
+
+ getLocalData: function(key) {
+ if (storage) {
+ return storage.getItem(key);
+ }
+
+ return null;
+ },
+
+ removeItem: function(key) {
+ storage && storage.removeItem(key);
+ }
+ };
+
+ function getUserData() {
+ var container = document.createElement("div");
+ container.style.display = "none";
+
+ if (!container.addBehavior) {
+ return null;
+ }
+
+ container.addBehavior("#default#userdata");
+
+ return {
+ getItem: function(key) {
+ var result = null;
+
+ try {
+ document.body.appendChild(container);
+ container.load(LOCAL_FILE);
+ result = container.getAttribute(key);
+ document.body.removeChild(container);
+ } catch (e) {}
+
+ return result;
+ },
+
+ setItem: function(key, value) {
+ document.body.appendChild(container);
+ container.setAttribute(key, value);
+ container.save(LOCAL_FILE);
+ document.body.removeChild(container);
+ },
+
+ //// 暂时没有用到
+ //clear: function () {
+ //
+ // var expiresTime = new Date();
+ // expiresTime.setFullYear(expiresTime.getFullYear() - 1);
+ // document.body.appendChild(container);
+ // container.expires = expiresTime.toUTCString();
+ // container.save(LOCAL_FILE);
+ // document.body.removeChild(container);
+ //
+ //},
+
+ removeItem: function(key) {
+ document.body.appendChild(container);
+ container.removeAttribute(key);
+ container.save(LOCAL_FILE);
+ document.body.removeChild(container);
+ }
+ };
+ }
+})());
+
+;(function() {
+ var ROOTKEY = "ueditor_preference";
+
+ UE.Editor.prototype.setPreferences = function(key, value) {
+ var obj = {};
+ if (utils.isString(key)) {
+ obj[key] = value;
+ } else {
+ obj = key;
+ }
+ var data = LocalStorage.getLocalData(ROOTKEY);
+ if (data && (data = utils.str2json(data))) {
+ utils.extend(data, obj);
+ } else {
+ data = obj;
+ }
+ data && LocalStorage.saveLocalData(ROOTKEY, utils.json2str(data));
+ };
+
+ UE.Editor.prototype.getPreferences = function(key) {
+ var data = LocalStorage.getLocalData(ROOTKEY);
+ if (data && (data = utils.str2json(data))) {
+ return key ? data[key] : data;
+ }
+ return null;
+ };
+
+ UE.Editor.prototype.removePreferences = function(key) {
+ var data = LocalStorage.getLocalData(ROOTKEY);
+ if (data && (data = utils.str2json(data))) {
+ data[key] = undefined;
+ delete data[key];
+ }
+ data && LocalStorage.saveLocalData(ROOTKEY, utils.json2str(data));
+ };
+})();
+
+
+// plugins/defaultfilter.js
+///import core
+///plugin 编辑器默认的过滤转换机制
+
+UE.plugins["defaultfilter"] = function() {
+ var me = this;
+ me.setOpt({
+ allowDivTransToP: true,
+ disabledTableInTable: true,
+ rgb2Hex: true
+ });
+ //默认的过滤处理
+ //进入编辑器的内容处理
+ me.addInputRule(function(root) {
+ var allowDivTransToP = this.options.allowDivTransToP;
+ var val;
+ function tdParent(node) {
+ while (node && node.type == "element") {
+ if (node.tagName == "td") {
+ return true;
+ }
+ node = node.parentNode;
+ }
+ return false;
+ }
+ //进行默认的处理
+ root.traversal(function(node) {
+ if (node.type == "element") {
+ if (
+ !dtd.$cdata[node.tagName] &&
+ me.options.autoClearEmptyNode &&
+ dtd.$inline[node.tagName] &&
+ !dtd.$empty[node.tagName] &&
+ (!node.attrs || utils.isEmptyObject(node.attrs))
+ ) {
+ if (!node.firstChild()) node.parentNode.removeChild(node);
+ else if (
+ node.tagName == "span" &&
+ (!node.attrs || utils.isEmptyObject(node.attrs))
+ ) {
+ node.parentNode.removeChild(node, true);
+ }
+ return;
+ }
+ switch (node.tagName) {
+ case "style":
+ case "script":
+ node.setAttr({
+ cdata_tag: node.tagName,
+ cdata_data: node.innerHTML() || "",
+ _ue_custom_node_: "true"
+ });
+ node.tagName = "div";
+ node.innerHTML("");
+ break;
+ case "a":
+ if ((val = node.getAttr("href"))) {
+ node.setAttr("_href", val);
+ }
+ break;
+ case "img":
+ //todo base64暂时去掉,后边做远程图片上传后,干掉这个
+ if ((val = node.getAttr("src"))) {
+ if (/^data:/.test(val)) {
+ node.parentNode.removeChild(node);
+ break;
+ }
+ }
+ node.setAttr("_src", node.getAttr("src"));
+ break;
+ case "span":
+ if (browser.webkit && (val = node.getStyle("white-space"))) {
+ if (/nowrap|normal/.test(val)) {
+ node.setStyle("white-space", "");
+ if (
+ me.options.autoClearEmptyNode &&
+ utils.isEmptyObject(node.attrs)
+ ) {
+ node.parentNode.removeChild(node, true);
+ }
+ }
+ }
+ val = node.getAttr("id");
+ if (val && /^_baidu_bookmark_/i.test(val)) {
+ node.parentNode.removeChild(node);
+ }
+ break;
+ case "p":
+ if ((val = node.getAttr("align"))) {
+ node.setAttr("align");
+ node.setStyle("text-align", val);
+ }
+ //trace:3431
+ // var cssStyle = node.getAttr('style');
+ // if (cssStyle) {
+ // cssStyle = cssStyle.replace(/(margin|padding)[^;]+/g, '');
+ // node.setAttr('style', cssStyle)
+ //
+ // }
+ //p标签不允许嵌套
+ utils.each(node.children, function(n) {
+ if (n.type == "element" && n.tagName == "p") {
+ var next = n.nextSibling();
+ node.parentNode.insertAfter(n, node);
+ var last = n;
+ while (next) {
+ var tmp = next.nextSibling();
+ node.parentNode.insertAfter(next, last);
+ last = next;
+ next = tmp;
+ }
+ return false;
+ }
+ });
+ if (!node.firstChild()) {
+ node.innerHTML(browser.ie ? " " : " ");
+ }
+ 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 ? "" : " ") + " ";
+ range.setStart(me.body.firstChild, 0).collapse(true);
+ }
+ }
+ }
+ !range.collapsed && range.deleteContents();
+ if (range.startContainer.nodeType == 1) {
+ var child = range.startContainer.childNodes[range.startOffset],
+ pre;
+ if (
+ child &&
+ domUtils.isBlockElm(child) &&
+ (pre = child.previousSibling) &&
+ domUtils.isBlockElm(pre)
+ ) {
+ range.setEnd(pre, pre.childNodes.length).collapse();
+ while (child.firstChild) {
+ pre.appendChild(child.firstChild);
+ }
+ domUtils.remove(child);
+ }
+ }
+ }
+
+ var child,
+ parent,
+ pre,
+ tmp,
+ hadBreak = 0,
+ nextNode;
+ //如果当前位置选中了fillchar要干掉,要不会产生空行
+ if (range.inFillChar()) {
+ child = range.startContainer;
+ if (domUtils.isFillChar(child)) {
+ range.setStartBefore(child).collapse(true);
+ domUtils.remove(child);
+ } else if (domUtils.isFillChar(child, true)) {
+ child.nodeValue = child.nodeValue.replace(fillCharReg, "");
+ range.startOffset--;
+ range.collapsed && range.collapse(true);
+ }
+ }
+ //列表单独处理
+ var li = domUtils.findParentByTagName(range.startContainer, "li", true);
+ if (li) {
+ var next, last;
+ while ((child = div.firstChild)) {
+ //针对hr单独处理一下先
+ while (
+ child &&
+ (child.nodeType == 3 ||
+ !domUtils.isBlockElm(child) ||
+ child.tagName == "HR")
+ ) {
+ next = child.nextSibling;
+ range.insertNode(child).collapse();
+ last = child;
+ child = next;
+ }
+ if (child) {
+ if (/^(ol|ul)$/i.test(child.tagName)) {
+ while (child.firstChild) {
+ last = child.firstChild;
+ domUtils.insertAfter(li, child.firstChild);
+ li = li.nextSibling;
+ }
+ domUtils.remove(child);
+ } else {
+ var tmpLi;
+ next = child.nextSibling;
+ tmpLi = me.document.createElement("li");
+ domUtils.insertAfter(li, tmpLi);
+ tmpLi.appendChild(child);
+ last = child;
+ child = next;
+ li = tmpLi;
+ }
+ }
+ }
+ li = domUtils.findParentByTagName(range.startContainer, "li", true);
+ if (domUtils.isEmptyBlock(li)) {
+ domUtils.remove(li);
+ }
+ if (last) {
+ range.setStartAfter(last).collapse(true).select(true);
+ }
+ } else {
+ while ((child = div.firstChild)) {
+ if (hadBreak) {
+ var p = me.document.createElement("p");
+ while (child && (child.nodeType == 3 || !dtd.$block[child.tagName])) {
+ nextNode = child.nextSibling;
+ p.appendChild(child);
+ child = nextNode;
+ }
+ if (p.firstChild) {
+ child = p;
+ }
+ }
+ range.insertNode(child);
+ nextNode = child.nextSibling;
+ if (
+ !hadBreak &&
+ child.nodeType == domUtils.NODE_ELEMENT &&
+ domUtils.isBlockElm(child)
+ ) {
+ parent = domUtils.findParent(child, function(node) {
+ return domUtils.isBlockElm(node);
+ });
+ if (
+ parent &&
+ parent.tagName.toLowerCase() != "body" &&
+ !(
+ dtd[parent.tagName][child.nodeName] && child.parentNode === parent
+ )
+ ) {
+ if (!dtd[parent.tagName][child.nodeName]) {
+ pre = parent;
+ } else {
+ tmp = child.parentNode;
+ while (tmp !== parent) {
+ pre = tmp;
+ tmp = tmp.parentNode;
+ }
+ }
+
+ domUtils.breakParent(child, pre || tmp);
+ //去掉break后前一个多余的节点 |<[p> ==> |
+ 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, //合并空行
+ removeClass: true, //去掉冗余的class
+ removeEmptyline: false, //去掉空行
+ textAlign: "left", //段落的排版方式,可以是 left,right,center,justify 去掉这个属性表示不执行排版
+ imageBlockLine: "center", //图片的浮动方式,独占一行剧中,左右浮动,默认: center,left,right,none 去掉这个属性表示不执行排版
+ 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") {
+ var 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.getElementsByClassName("_img_parent_tmp")[0];
+ tmpNode.removeAttribute("class");
+ 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("word_img")
+ ) {
+ 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 =
+ ' ";
+ if (ci["floatStyle"] == "center") {
+ str = '' + str + " ";
+ }
+ html.push(str);
+ } else {
+ for (var i = 0; (ci = opt[i++]); ) {
+ str =
+ " ";
+ html.push(str);
+ }
+ }
+
+ me.execCommand("insertHtml", html.join(""));
+ }
+
+ me.fireEvent("afterinsertimage", opt);
+ }
+};
+
+
+// plugins/justify.js
+/**
+ * 段落格式
+ * @file
+ * @since 1.2.6.1
+ */
+
+/**
+ * 段落对齐方式
+ * @command justify
+ * @method execCommand
+ * @param { String } cmd 命令字符串
+ * @param { String } align 对齐方式:left => 居左,right => 居右,center => 居中,justify => 两端对齐
+ * @example
+ * ```javascript
+ * editor.execCommand( 'justify', 'center' );
+ * ```
+ */
+/**
+ * 如果选区所在位置是段落区域,返回当前段落对齐方式
+ * @command justify
+ * @method queryCommandValue
+ * @param { String } cmd 命令字符串
+ * @return { String } 返回段落对齐方式
+ * @example
+ * ```javascript
+ * editor.queryCommandValue( 'justify' );
+ * ```
+ */
+
+UE.plugins["justify"] = function() {
+ var me = this,
+ block = domUtils.isBlockElm,
+ defaultValue = {
+ left: 1,
+ right: 1,
+ center: 1,
+ justify: 1
+ },
+ doJustify = function(range, style) {
+ var bookmark = range.createBookmark(),
+ filterFn = function(node) {
+ return node.nodeType == 1
+ ? node.tagName.toLowerCase() != "br" &&
+ !domUtils.isBookmarkNode(node)
+ : !domUtils.isWhitespace(node);
+ };
+
+ range.enlarge(true);
+ var bookmark2 = range.createBookmark(),
+ current = domUtils.getNextDomNode(bookmark2.start, false, filterFn),
+ tmpRange = range.cloneRange(),
+ tmpNode;
+ while (
+ current &&
+ !(
+ domUtils.getPosition(current, bookmark2.end) &
+ domUtils.POSITION_FOLLOWING
+ )
+ ) {
+ if (current.nodeType == 3 || !block(current)) {
+ tmpRange.setStartBefore(current);
+ while (current && current !== bookmark2.end && !block(current)) {
+ tmpNode = current;
+ current = domUtils.getNextDomNode(current, false, null, function(
+ node
+ ) {
+ return !block(node);
+ });
+ }
+ tmpRange.setEndAfter(tmpNode);
+ var common = tmpRange.getCommonAncestor();
+ if (!domUtils.isBody(common) && block(common)) {
+ domUtils.setStyles(
+ common,
+ utils.isString(style) ? { "text-align": style } : style
+ );
+ current = common;
+ } else {
+ var p = range.document.createElement("p");
+ domUtils.setStyles(
+ p,
+ utils.isString(style) ? { "text-align": style } : style
+ );
+ var frag = tmpRange.extractContents();
+ p.appendChild(frag);
+ tmpRange.insertNode(p);
+ current = p;
+ }
+ current = domUtils.getNextDomNode(current, false, filterFn);
+ } else {
+ current = domUtils.getNextDomNode(current, true, filterFn);
+ }
+ }
+ return range.moveToBookmark(bookmark2).moveToBookmark(bookmark);
+ };
+
+ UE.commands["justify"] = {
+ execCommand: function(cmdName, align) {
+ var range = this.selection.getRange(),
+ txt;
+
+ //闭合时单独处理
+ if (range.collapsed) {
+ txt = this.document.createTextNode("p");
+ range.insertNode(txt);
+ }
+ doJustify(range, align);
+ if (txt) {
+ range.setStartBefore(txt).collapse(true);
+ domUtils.remove(txt);
+ }
+
+ range.select();
+
+ return true;
+ },
+ queryCommandValue: function() {
+ var startNode = this.selection.getStart(),
+ value = domUtils.getComputedStyle(startNode, "text-align");
+ return defaultValue ? value : "left";
+ },
+ queryCommandState: function() {
+ var start = this.selection.getStart(),
+ cell =
+ start &&
+ domUtils.findParentByTagName(start, ["td", "th", "caption"], true);
+
+ return cell ? -1 : 0;
+ }
+ };
+};
+
+
+// plugins/font.js
+/**
+ * 字体颜色,背景色,字号,字体,下划线,删除线
+ * @file
+ * @since 1.2.6.1
+ */
+
+/**
+ * 字体颜色
+ * @command forecolor
+ * @method execCommand
+ * @param { String } cmd 命令字符串
+ * @param { String } value 色值(必须十六进制)
+ * @example
+ * ```javascript
+ * editor.execCommand( 'forecolor', '#000' );
+ * ```
+ */
+/**
+ * 返回选区字体颜色
+ * @command forecolor
+ * @method queryCommandValue
+ * @param { String } cmd 命令字符串
+ * @return { String } 返回字体颜色
+ * @example
+ * ```javascript
+ * editor.queryCommandValue( 'forecolor' );
+ * ```
+ */
+
+/**
+ * 字体背景颜色
+ * @command backcolor
+ * @method execCommand
+ * @param { String } cmd 命令字符串
+ * @param { String } value 色值(必须十六进制)
+ * @example
+ * ```javascript
+ * editor.execCommand( 'backcolor', '#000' );
+ * ```
+ */
+/**
+ * 返回选区字体颜色
+ * @command backcolor
+ * @method queryCommandValue
+ * @param { String } cmd 命令字符串
+ * @return { String } 返回字体背景颜色
+ * @example
+ * ```javascript
+ * editor.queryCommandValue( 'backcolor' );
+ * ```
+ */
+
+/**
+ * 字体大小
+ * @command fontsize
+ * @method execCommand
+ * @param { String } cmd 命令字符串
+ * @param { String } value 字体大小
+ * @example
+ * ```javascript
+ * editor.execCommand( 'fontsize', '14px' );
+ * ```
+ */
+/**
+ * 返回选区字体大小
+ * @command fontsize
+ * @method queryCommandValue
+ * @param { String } cmd 命令字符串
+ * @return { String } 返回字体大小
+ * @example
+ * ```javascript
+ * editor.queryCommandValue( 'fontsize' );
+ * ```
+ */
+
+/**
+ * 字体样式
+ * @command fontfamily
+ * @method execCommand
+ * @param { String } cmd 命令字符串
+ * @param { String } value 字体样式
+ * @example
+ * ```javascript
+ * editor.execCommand( 'fontfamily', '微软雅黑' );
+ * ```
+ */
+/**
+ * 返回选区字体样式
+ * @command fontfamily
+ * @method queryCommandValue
+ * @param { String } cmd 命令字符串
+ * @return { String } 返回字体样式
+ * @example
+ * ```javascript
+ * editor.queryCommandValue( 'fontfamily' );
+ * ```
+ */
+
+/**
+ * 字体下划线,与删除线互斥
+ * @command underline
+ * @method execCommand
+ * @param { String } cmd 命令字符串
+ * @example
+ * ```javascript
+ * editor.execCommand( 'underline' );
+ * ```
+ */
+
+/**
+ * 字体删除线,与下划线互斥
+ * @command strikethrough
+ * @method execCommand
+ * @param { String } cmd 命令字符串
+ * @example
+ * ```javascript
+ * editor.execCommand( 'strikethrough' );
+ * ```
+ */
+
+/**
+ * 字体边框
+ * @command fontborder
+ * @method execCommand
+ * @param { String } cmd 命令字符串
+ * @example
+ * ```javascript
+ * editor.execCommand( 'fontborder' );
+ * ```
+ */
+
+UE.plugins["font"] = function() {
+ var me = this,
+ fonts = {
+ forecolor: "color",
+ backcolor: "background-color",
+ fontsize: "font-size",
+ fontfamily: "font-family",
+ underline: "text-decoration",
+ strikethrough: "text-decoration",
+ fontborder: "border"
+ },
+ needCmd = { underline: 1, strikethrough: 1, fontborder: 1 },
+ needSetChild = {
+ forecolor: "color",
+ backcolor: "background-color",
+ fontsize: "font-size",
+ fontfamily: "font-family"
+ };
+ me.setOpt({
+ fontfamily: [
+ { name: "songti", val: "宋体,SimSun" },
+ { name: "yahei", val: "微软雅黑,Microsoft YaHei" },
+ { name: "kaiti", val: "楷体,楷体_GB2312, SimKai" },
+ { name: "heiti", val: "黑体, SimHei" },
+ { name: "lishu", val: "隶书, SimLi" },
+ { name: "andaleMono", val: "andale mono" },
+ { name: "arial", val: "arial, helvetica,sans-serif" },
+ { name: "arialBlack", val: "arial black,avant garde" },
+ { name: "comicSansMs", val: "comic sans ms" },
+ { name: "impact", val: "impact,chicago" },
+ { name: "timesNewRoman", val: "times new roman" }
+ ],
+ fontsize: [10, 11, 12, 14, 16, 18, 20, 24, 36]
+ });
+
+ function mergeWithParent(node) {
+ var parent;
+ while ((parent = node.parentNode)) {
+ if (
+ parent.tagName == "SPAN" &&
+ domUtils.getChildCount(parent, function(child) {
+ return !domUtils.isBookmarkNode(child) && !domUtils.isBr(child);
+ }) == 1
+ ) {
+ parent.style.cssText += node.style.cssText;
+ domUtils.remove(node, true);
+ node = parent;
+ } else {
+ break;
+ }
+ }
+ }
+ function mergeChild(rng, cmdName, value) {
+ if (needSetChild[cmdName]) {
+ rng.adjustmentBoundary();
+ if (!rng.collapsed && rng.startContainer.nodeType == 1) {
+ rng.traversal(function(node){
+ var start;
+ if(domUtils.isTagNode(node,'span')){
+ start = node;
+ }else{
+ start = domUtils.getElementsByTagName(node,'span')[0];
+ }
+ if (start && domUtils.isTagNode(start, "span")) {
+ var bk = rng.createBookmark();
+ utils.each(domUtils.getElementsByTagName(start, "span"), function(
+ span
+ ) {
+ if (!span.parentNode || domUtils.isBookmarkNode(span)) return;
+ if (
+ cmdName == "backcolor" &&
+ domUtils
+ .getComputedStyle(span, "background-color")
+ .toLowerCase() === value
+ ) {
+ return;
+ }
+ domUtils.removeStyle(span, needSetChild[cmdName]);
+ if (span.style.cssText.replace(/^\s+$/, "").length == 0) {
+ domUtils.remove(span, true);
+ }
+ });
+ rng.moveToBookmark(bk);
+ }
+ });
+ }
+ }
+ }
+ function mergesibling(rng, cmdName, value) {
+ var collapsed = rng.collapsed,
+ bk = rng.createBookmark(),
+ common;
+ if (collapsed) {
+ common = bk.start.parentNode;
+ while (dtd.$inline[common.tagName]) {
+ common = common.parentNode;
+ }
+ } else {
+ common = domUtils.getCommonAncestor(bk.start, bk.end);
+ }
+ utils.each(domUtils.getElementsByTagName(common, "span"), function(span) {
+ if (!span.parentNode || domUtils.isBookmarkNode(span)) return;
+ if (/\s*border\s*:\s*none;?\s*/i.test(span.style.cssText)) {
+ if (/^\s*border\s*:\s*none;?\s*$/.test(span.style.cssText)) {
+ domUtils.remove(span, true);
+ } else {
+ domUtils.removeStyle(span, "border");
+ }
+ return;
+ }
+ if (
+ /border/i.test(span.style.cssText) &&
+ span.parentNode.tagName == "SPAN" &&
+ /border/i.test(span.parentNode.style.cssText)
+ ) {
+ span.style.cssText = span.style.cssText.replace(
+ /border[^:]*:[^;]+;?/gi,
+ ""
+ );
+ }
+ if (!(cmdName == "fontborder" && value == "none")) {
+ var next = span.nextSibling;
+ while (next && next.nodeType == 1 && next.tagName == "SPAN") {
+ if (domUtils.isBookmarkNode(next) && cmdName == "fontborder") {
+ span.appendChild(next);
+ next = span.nextSibling;
+ continue;
+ }
+ if (next.style.cssText == span.style.cssText) {
+ domUtils.moveChild(next, span);
+ domUtils.remove(next);
+ }
+ if (span.nextSibling === next) break;
+ next = span.nextSibling;
+ }
+ }
+
+ mergeWithParent(span);
+ if (browser.ie && browser.version > 8) {
+ //拷贝父亲们的特别的属性,这里只做背景颜色的处理
+ var parent = domUtils.findParent(span, function(n) {
+ return (
+ n.tagName == "SPAN" && /background-color/.test(n.style.cssText)
+ );
+ });
+ if (parent && !/background-color/.test(span.style.cssText)) {
+ span.style.backgroundColor = parent.style.backgroundColor;
+ }
+ }
+ });
+ rng.moveToBookmark(bk);
+ mergeChild(rng, cmdName, value);
+ }
+
+ me.addInputRule(function(root) {
+ utils.each(root.getNodesByTagName("u s del font strike"), function(node) {
+ if (node.tagName == "font") {
+ var cssStyle = [];
+ for (var p in node.attrs) {
+ switch (p) {
+ case "size":
+ cssStyle.push(
+ "font-size:" +
+ ({
+ "1": "10",
+ "2": "12",
+ "3": "16",
+ "4": "18",
+ "5": "24",
+ "6": "32",
+ "7": "48"
+ }[node.attrs[p]] || node.attrs[p]) +
+ "px"
+ );
+ break;
+ case "color":
+ cssStyle.push("color:" + node.attrs[p]);
+ break;
+ case "face":
+ cssStyle.push("font-family:" + node.attrs[p]);
+ break;
+ case "style":
+ cssStyle.push(node.attrs[p]);
+ }
+ }
+ node.attrs = {
+ style: cssStyle.join(";")
+ };
+ } else {
+ var val = node.tagName == "u" ? "underline" : "line-through";
+ node.attrs = {
+ style: (node.getAttr("style") || "") + "text-decoration:" + val + ";"
+ };
+ }
+ node.tagName = "span";
+ });
+ // utils.each(root.getNodesByTagName('span'), function (node) {
+ // var val;
+ // if(val = node.getAttr('class')){
+ // if(/fontstrikethrough/.test(val)){
+ // node.setStyle('text-decoration','line-through');
+ // if(node.attrs['class']){
+ // node.attrs['class'] = node.attrs['class'].replace(/fontstrikethrough/,'');
+ // }else{
+ // node.setAttr('class')
+ // }
+ // }
+ // if(/fontborder/.test(val)){
+ // node.setStyle('border','1px solid #000');
+ // if(node.attrs['class']){
+ // node.attrs['class'] = node.attrs['class'].replace(/fontborder/,'');
+ // }else{
+ // node.setAttr('class')
+ // }
+ // }
+ // }
+ // });
+ });
+ // me.addOutputRule(function(root){
+ // utils.each(root.getNodesByTagName('span'), function (node) {
+ // var val;
+ // if(val = node.getStyle('text-decoration')){
+ // if(/line-through/.test(val)){
+ // if(node.attrs['class']){
+ // node.attrs['class'] += ' fontstrikethrough';
+ // }else{
+ // node.setAttr('class','fontstrikethrough')
+ // }
+ // }
+ //
+ // node.setStyle('text-decoration')
+ // }
+ // if(val = node.getStyle('border')){
+ // if(/1px/.test(val) && /solid/.test(val)){
+ // if(node.attrs['class']){
+ // node.attrs['class'] += ' fontborder';
+ //
+ // }else{
+ // node.setAttr('class','fontborder')
+ // }
+ // }
+ // node.setStyle('border')
+ //
+ // }
+ // });
+ // });
+ for (var p in fonts) {
+ (function(cmd, style) {
+ UE.commands[cmd] = {
+ execCommand: function(cmdName, value) {
+ value =
+ value ||
+ (this.queryCommandState(cmdName)
+ ? "none"
+ : cmdName == "underline"
+ ? "underline"
+ : cmdName == "fontborder" ? "1px solid #000" : "line-through");
+ var me = this,
+ range = this.selection.getRange(),
+ text;
+
+ if (value == "default") {
+ if (range.collapsed) {
+ text = me.document.createTextNode("font");
+ range.insertNode(text).select();
+ }
+ me.execCommand("removeFormat", "span,a", style);
+ if (text) {
+ range.setStartBefore(text).collapse(true);
+ domUtils.remove(text);
+ }
+ mergesibling(range, cmdName, value);
+ range.select();
+ } else {
+ if (!range.collapsed) {
+ if (needCmd[cmd] && me.queryCommandValue(cmd)) {
+ me.execCommand("removeFormat", "span,a", style);
+ }
+ range = me.selection.getRange();
+
+ range.applyInlineStyle("span", { style: style + ":" + value });
+ mergesibling(range, cmdName, value);
+ range.select();
+ } else {
+ var span = domUtils.findParentByTagName(
+ range.startContainer,
+ "span",
+ true
+ );
+ text = me.document.createTextNode("font");
+ if (
+ span &&
+ !span.children.length &&
+ !span[browser.ie ? "innerText" : "textContent"].replace(
+ fillCharReg,
+ ""
+ ).length
+ ) {
+ //for ie hack when enter
+ range.insertNode(text);
+ if (needCmd[cmd]) {
+ range.selectNode(text).select();
+ me.execCommand("removeFormat", "span,a", style, null);
+
+ span = domUtils.findParentByTagName(text, "span", true);
+ range.setStartBefore(text);
+ }
+ span && (span.style.cssText += ";" + style + ":" + value);
+ range.collapse(true).select();
+ } else {
+ range.insertNode(text);
+ range.selectNode(text).select();
+ span = range.document.createElement("span");
+
+ if (needCmd[cmd]) {
+ //a标签内的不处理跳过
+ if (domUtils.findParentByTagName(text, "a", true)) {
+ range.setStartBefore(text).setCursor();
+ domUtils.remove(text);
+ return;
+ }
+ me.execCommand("removeFormat", "span,a", style);
+ }
+
+ span.style.cssText = style + ":" + value;
+
+ text.parentNode.insertBefore(span, text);
+ //修复,span套span 但样式不继承的问题
+ if (!browser.ie || (browser.ie && browser.version == 9)) {
+ var spanParent = span.parentNode;
+ while (!domUtils.isBlockElm(spanParent)) {
+ if (spanParent.tagName == "SPAN") {
+ //opera合并style不会加入";"
+ span.style.cssText =
+ spanParent.style.cssText + ";" + span.style.cssText;
+ }
+ spanParent = spanParent.parentNode;
+ }
+ }
+
+ if (opera) {
+ setTimeout(function() {
+ range.setStart(span, 0).collapse(true);
+ mergesibling(range, cmdName, value);
+ range.select();
+ });
+ } else {
+ range.setStart(span, 0).collapse(true);
+ mergesibling(range, cmdName, value);
+ range.select();
+ }
+
+ //trace:981
+ //domUtils.mergeToParent(span)
+ }
+ domUtils.remove(text);
+ }
+ }
+ return true;
+ },
+ queryCommandValue: function(cmdName) {
+ var startNode = this.selection.getStart();
+
+ //trace:946
+ if (cmdName == "underline" || cmdName == "strikethrough") {
+ var tmpNode = startNode,
+ value;
+ while (
+ tmpNode &&
+ !domUtils.isBlockElm(tmpNode) &&
+ !domUtils.isBody(tmpNode)
+ ) {
+ if (tmpNode.nodeType == 1) {
+ value = domUtils.getComputedStyle(tmpNode, style);
+ if (value != "none") {
+ return value;
+ }
+ }
+
+ tmpNode = tmpNode.parentNode;
+ }
+ return "none";
+ }
+ if (cmdName == "fontborder") {
+ var tmp = startNode,
+ val;
+ while (tmp && dtd.$inline[tmp.tagName]) {
+ if ((val = domUtils.getComputedStyle(tmp, "border"))) {
+ if (/1px/.test(val) && /solid/.test(val)) {
+ return val;
+ }
+ }
+ tmp = tmp.parentNode;
+ }
+ return "";
+ }
+
+ if (cmdName == "FontSize") {
+ var styleVal = domUtils.getComputedStyle(startNode, style),
+ tmp = /^([\d\.]+)(\w+)$/.exec(styleVal);
+
+ if (tmp) {
+ return Math.floor(tmp[1]) + tmp[2];
+ }
+
+ return styleVal;
+ }
+
+ return domUtils.getComputedStyle(startNode, style);
+ },
+ queryCommandState: function(cmdName) {
+ if (!needCmd[cmdName]) return 0;
+ var val = this.queryCommandValue(cmdName);
+ if (cmdName == "fontborder") {
+ return /1px/.test(val) && /solid/.test(val);
+ } else {
+ return cmdName == "underline"
+ ? /underline/.test(val)
+ : /line\-through/.test(val);
+ }
+ }
+ };
+ })(p, fonts[p]);
+ }
+};
+
+
+// plugins/link.js
+/**
+ * 超链接
+ * @file
+ * @since 1.2.6.1
+ */
+
+/**
+ * 插入超链接
+ * @command link
+ * @method execCommand
+ * @param { String } cmd 命令字符串
+ * @param { Object } options 设置自定义属性,例如:url、title、target
+ * @example
+ * ```javascript
+ * editor.execCommand( 'link', '{
+ * url:'neditor.baidu.com',
+ * title:'neditor',
+ * target:'_blank'
+ * }' );
+ * ```
+ */
+/**
+ * 返回当前选中的第一个超链接节点
+ * @command link
+ * @method queryCommandValue
+ * @param { String } cmd 命令字符串
+ * @return { Element } 超链接节点
+ * @example
+ * ```javascript
+ * editor.queryCommandValue( 'link' );
+ * ```
+ */
+
+/**
+ * 取消超链接
+ * @command unlink
+ * @method execCommand
+ * @param { String } cmd 命令字符串
+ * @example
+ * ```javascript
+ * editor.execCommand( 'unlink');
+ * ```
+ */
+
+UE.plugins["link"] = function() {
+ function optimize(range) {
+ var start = range.startContainer,
+ end = range.endContainer;
+
+ if ((start = domUtils.findParentByTagName(start, "a", true))) {
+ range.setStartBefore(start);
+ }
+ if ((end = domUtils.findParentByTagName(end, "a", true))) {
+ range.setEndAfter(end);
+ }
+ }
+
+ UE.commands["unlink"] = {
+ execCommand: function() {
+ var range = this.selection.getRange(),
+ bookmark;
+ if (
+ range.collapsed &&
+ !domUtils.findParentByTagName(range.startContainer, "a", true)
+ ) {
+ return;
+ }
+ bookmark = range.createBookmark();
+ optimize(range);
+ range.removeInlineStyle("a").moveToBookmark(bookmark).select();
+ },
+ queryCommandState: function() {
+ return !this.highlight && this.queryCommandValue("link") ? 0 : -1;
+ }
+ };
+ function doLink(range, opt, me) {
+ var rngClone = range.cloneRange(),
+ link = me.queryCommandValue("link");
+ optimize((range = range.adjustmentBoundary()));
+ var start = range.startContainer;
+ if (start.nodeType == 1 && link) {
+ start = start.childNodes[range.startOffset];
+ if (
+ start &&
+ start.nodeType == 1 &&
+ start.tagName == "A" &&
+ /^(?:https?|ftp|file)\s*:\s*\/\//.test(
+ start[browser.ie ? "innerText" : "textContent"]
+ )
+ ) {
+ start[browser.ie ? "innerText" : "textContent"] = utils.html(
+ opt.textValue || opt.href
+ );
+ }
+ }
+ if (!rngClone.collapsed || link) {
+ range.removeInlineStyle("a");
+ rngClone = range.cloneRange();
+ }
+
+ if (rngClone.collapsed) {
+ var a = range.document.createElement("a"),
+ text = "";
+ if (opt.textValue) {
+ text = utils.html(opt.textValue);
+ delete opt.textValue;
+ } else {
+ text = utils.html(opt.href);
+ }
+ domUtils.setAttributes(a, opt);
+ start = domUtils.findParentByTagName(rngClone.startContainer, "a", true);
+ if (start && domUtils.isInNodeEndBoundary(rngClone, start)) {
+ range.setStartAfter(start).collapse(true);
+ }
+ a[browser.ie ? "innerText" : "textContent"] = text;
+ range.insertNode(a).selectNode(a);
+ } else {
+ range.applyInlineStyle("a", opt);
+ }
+ }
+ UE.commands["link"] = {
+ execCommand: function(cmdName, opt) {
+ var range;
+ opt._href && (opt._href = utils.unhtml(opt._href, /[<">]/g));
+ opt.href && (opt.href = utils.unhtml(opt.href, /[<">]/g));
+ opt.textValue && (opt.textValue = utils.unhtml(opt.textValue, /[<">]/g));
+ doLink((range = this.selection.getRange()), opt, this);
+ //闭合都不加占位符,如果加了会在a后边多个占位符节点,导致a是图片背景组成的列表,出现空白问题
+ range.collapse().select(true);
+ },
+ queryCommandValue: function() {
+ var range = this.selection.getRange(),
+ node;
+ if (range.collapsed) {
+ // node = this.selection.getStart();
+ //在ie下getstart()取值偏上了
+ node = range.startContainer;
+ node = node.nodeType == 1 ? node : node.parentNode;
+
+ if (
+ node &&
+ (node = domUtils.findParentByTagName(node, "a", true)) &&
+ !domUtils.isInNodeEndBoundary(range, node)
+ ) {
+ return node;
+ }
+ } else {
+ //trace:1111 如果是xx startContainer是p就会找不到a
+ range.shrinkBoundary();
+ var start = range.startContainer.nodeType == 3 ||
+ !range.startContainer.childNodes[range.startOffset]
+ ? range.startContainer
+ : range.startContainer.childNodes[range.startOffset],
+ end = range.endContainer.nodeType == 3 || range.endOffset == 0
+ ? range.endContainer
+ : range.endContainer.childNodes[range.endOffset - 1],
+ common = range.getCommonAncestor();
+ node = domUtils.findParentByTagName(common, "a", true);
+ if (!node && common.nodeType == 1) {
+ var as = common.getElementsByTagName("a"),
+ ps,
+ pe;
+
+ for (var i = 0, ci; (ci = as[i++]); ) {
+ (ps = domUtils.getPosition(ci, start)), (pe = domUtils.getPosition(
+ ci,
+ end
+ ));
+ if (
+ (ps & domUtils.POSITION_FOLLOWING ||
+ ps & domUtils.POSITION_CONTAINS) &&
+ (pe & domUtils.POSITION_PRECEDING ||
+ pe & domUtils.POSITION_CONTAINS)
+ ) {
+ node = ci;
+ break;
+ }
+ }
+ }
+ return node;
+ }
+ },
+ queryCommandState: function() {
+ //判断如果是视频的话连接不可用
+ //fix 853
+ var img = this.selection.getRange().getClosedNode(),
+ flag =
+ img &&
+ (img.className == "edui-faked-video" ||
+ img.className.indexOf("edui-upload-video") != -1);
+ return flag ? -1 : 0;
+ }
+ };
+};
+
+
+// plugins/iframe.js
+///import core
+///import plugins\inserthtml.js
+///commands 插入框架
+///commandsName InsertFrame
+///commandsTitle 插入Iframe
+///commandsDialog dialogs\insertframe
+
+UE.plugins["insertframe"] = function() {
+ var me = this;
+ function deleteIframe() {
+ me._iframe && delete me._iframe;
+ }
+
+ me.addListener("selectionchange", function() {
+ deleteIframe();
+ });
+};
+
+
+// plugins/scrawl.js
+///import core
+///commands 涂鸦
+///commandsName Scrawl
+///commandsTitle 涂鸦
+///commandsDialog dialogs\scrawl
+UE.commands["scrawl"] = {
+ queryCommandState: function() {
+ return browser.ie && browser.version <= 8 ? -1 : 0;
+ }
+};
+
+
+// plugins/removeformat.js
+/**
+ * 清除格式
+ * @file
+ * @since 1.2.6.1
+ */
+
+/**
+ * 清除文字样式
+ * @command removeformat
+ * @method execCommand
+ * @param { String } cmd 命令字符串
+ * @param {String} tags 以逗号隔开的标签。如:strong
+ * @param {String} style 样式如:color
+ * @param {String} attrs 属性如:width
+ * @example
+ * ```javascript
+ * editor.execCommand( 'removeformat', 'strong','color','width' );
+ * ```
+ */
+
+UE.plugins["removeformat"] = function() {
+ var me = this;
+ me.setOpt({
+ removeFormatTags:
+ "b,big,code,del,dfn,em,font,i,ins,kbd,q,samp,small,span,strike,strong,sub,sup,tt,u,var",
+ removeFormatAttributes: "class,style,lang,width,height,align,hspace,valign"
+ });
+ me.commands["removeformat"] = {
+ execCommand: function(cmdName, tags, style, attrs, notIncludeA) {
+ var tagReg = new RegExp(
+ "^(?:" +
+ (tags || this.options.removeFormatTags).replace(/,/g, "|") +
+ ")$",
+ "i"
+ ),
+ removeFormatAttributes = style
+ ? []
+ : (attrs || this.options.removeFormatAttributes).split(","),
+ range = new dom.Range(this.document),
+ bookmark,
+ node,
+ parent,
+ filter = function(node) {
+ return node.nodeType == 1;
+ };
+
+ function isRedundantSpan(node) {
+ if (node.nodeType == 3 || node.tagName.toLowerCase() != "span") {
+ return 0;
+ }
+ if (browser.ie) {
+ //ie 下判断实效,所以只能简单用style来判断
+ //return node.style.cssText == '' ? 1 : 0;
+ var attrs = node.attributes;
+ if (attrs.length) {
+ for (var i = 0, l = attrs.length; i < l; i++) {
+ if (attrs[i].specified) {
+ return 0;
+ }
+ }
+ return 1;
+ }
+ }
+ return !node.attributes.length;
+ }
+ function doRemove(range) {
+ var bookmark1 = range.createBookmark();
+ if (range.collapsed) {
+ range.enlarge(true);
+ }
+
+ //不能把a标签切了
+ if (!notIncludeA) {
+ var aNode = domUtils.findParentByTagName(
+ range.startContainer,
+ "a",
+ true
+ );
+ if (aNode) {
+ range.setStartBefore(aNode);
+ }
+
+ aNode = domUtils.findParentByTagName(range.endContainer, "a", true);
+ if (aNode) {
+ range.setEndAfter(aNode);
+ }
+ }
+
+ bookmark = range.createBookmark();
+
+ node = bookmark.start;
+
+ //切开始
+ while ((parent = node.parentNode) && !domUtils.isBlockElm(parent)) {
+ domUtils.breakParent(node, parent);
+
+ domUtils.clearEmptySibling(node);
+ }
+ if (bookmark.end) {
+ //切结束
+ node = bookmark.end;
+ while ((parent = node.parentNode) && !domUtils.isBlockElm(parent)) {
+ domUtils.breakParent(node, parent);
+ domUtils.clearEmptySibling(node);
+ }
+
+ //开始去除样式
+ var current = domUtils.getNextDomNode(bookmark.start, false, filter),
+ next;
+ while (current) {
+ if (current == bookmark.end) {
+ break;
+ }
+
+ next = domUtils.getNextDomNode(current, true, filter);
+
+ if (
+ !dtd.$empty[current.tagName.toLowerCase()] &&
+ !domUtils.isBookmarkNode(current)
+ ) {
+ if (tagReg.test(current.tagName)) {
+ if (style) {
+ domUtils.removeStyle(current, style);
+ if (isRedundantSpan(current) && style != "text-decoration") {
+ domUtils.remove(current, true);
+ }
+ } else {
+ domUtils.remove(current, true);
+ }
+ } else {
+ //trace:939 不能把list上的样式去掉
+ if (
+ !dtd.$tableContent[current.tagName] &&
+ !dtd.$list[current.tagName]
+ ) {
+ domUtils.removeAttributes(current, removeFormatAttributes);
+ if (isRedundantSpan(current)) {
+ domUtils.remove(current, true);
+ }
+ }
+ }
+ }
+ current = next;
+ }
+ }
+ //trace:1035
+ //trace:1096 不能把td上的样式去掉,比如边框
+ var pN = bookmark.start.parentNode;
+ if (
+ domUtils.isBlockElm(pN) &&
+ !dtd.$tableContent[pN.tagName] &&
+ !dtd.$list[pN.tagName]
+ ) {
+ domUtils.removeAttributes(pN, removeFormatAttributes);
+ }
+ pN = bookmark.end.parentNode;
+ if (
+ bookmark.end &&
+ domUtils.isBlockElm(pN) &&
+ !dtd.$tableContent[pN.tagName] &&
+ !dtd.$list[pN.tagName]
+ ) {
+ domUtils.removeAttributes(pN, removeFormatAttributes);
+ }
+ range.moveToBookmark(bookmark).moveToBookmark(bookmark1);
+ //清除冗余的代码
+ var node = range.startContainer,
+ tmp,
+ collapsed = range.collapsed;
+ while (
+ node.nodeType == 1 &&
+ domUtils.isEmptyNode(node) &&
+ dtd.$removeEmpty[node.tagName]
+ ) {
+ tmp = node.parentNode;
+ range.setStartBefore(node);
+ //trace:937
+ //更新结束边界
+ if (range.startContainer === range.endContainer) {
+ range.endOffset--;
+ }
+ domUtils.remove(node);
+ node = tmp;
+ }
+
+ if (!collapsed) {
+ node = range.endContainer;
+ while (
+ node.nodeType == 1 &&
+ domUtils.isEmptyNode(node) &&
+ dtd.$removeEmpty[node.tagName]
+ ) {
+ tmp = node.parentNode;
+ range.setEndBefore(node);
+ domUtils.remove(node);
+
+ node = tmp;
+ }
+ }
+ }
+
+ range = this.selection.getRange();
+ doRemove(range);
+ range.select();
+ }
+ };
+};
+
+
+// plugins/blockquote.js
+/**
+ * 添加引用
+ * @file
+ * @since 1.2.6.1
+ */
+
+/**
+ * 添加引用
+ * @command blockquote
+ * @method execCommand
+ * @param { String } cmd 命令字符串
+ * @example
+ * ```javascript
+ * editor.execCommand( 'blockquote' );
+ * ```
+ */
+
+/**
+ * 添加引用
+ * @command blockquote
+ * @method execCommand
+ * @param { String } cmd 命令字符串
+ * @param { Object } attrs 节点属性
+ * @example
+ * ```javascript
+ * editor.execCommand( 'blockquote',{
+ * style: "color: red;"
+ * } );
+ * ```
+ */
+
+UE.plugins["blockquote"] = function() {
+ var me = this;
+ function getObj(editor) {
+ return domUtils.filterNodeList(
+ editor.selection.getStartElementPath(),
+ "blockquote"
+ );
+ }
+ me.commands["blockquote"] = {
+ execCommand: function(cmdName, attrs) {
+ var range = this.selection.getRange(),
+ obj = getObj(this),
+ blockquote = dtd.blockquote,
+ bookmark = range.createBookmark();
+
+ if (obj) {
+ var start = range.startContainer,
+ startBlock = domUtils.isBlockElm(start)
+ ? start
+ : domUtils.findParent(start, function(node) {
+ return domUtils.isBlockElm(node);
+ }),
+ end = range.endContainer,
+ endBlock = domUtils.isBlockElm(end)
+ ? end
+ : domUtils.findParent(end, function(node) {
+ return domUtils.isBlockElm(node);
+ });
+
+ //处理一下li
+ startBlock =
+ domUtils.findParentByTagName(startBlock, "li", true) || startBlock;
+ endBlock =
+ domUtils.findParentByTagName(endBlock, "li", true) || endBlock;
+
+ if (
+ startBlock.tagName == "LI" ||
+ startBlock.tagName == "TD" ||
+ startBlock === obj ||
+ domUtils.isBody(startBlock)
+ ) {
+ domUtils.remove(obj, true);
+ } else {
+ domUtils.breakParent(startBlock, obj);
+ }
+
+ if (startBlock !== endBlock) {
+ obj = domUtils.findParentByTagName(endBlock, "blockquote");
+ if (obj) {
+ if (
+ endBlock.tagName == "LI" ||
+ endBlock.tagName == "TD" ||
+ domUtils.isBody(endBlock)
+ ) {
+ obj.parentNode && domUtils.remove(obj, true);
+ } else {
+ domUtils.breakParent(endBlock, obj);
+ }
+ }
+ }
+
+ var blockquotes = domUtils.getElementsByTagName(
+ this.document,
+ "blockquote"
+ );
+ for (var i = 0, bi; (bi = blockquotes[i++]); ) {
+ if (!bi.childNodes.length) {
+ domUtils.remove(bi);
+ } else if (
+ domUtils.getPosition(bi, startBlock) &
+ domUtils.POSITION_FOLLOWING &&
+ domUtils.getPosition(bi, endBlock) & domUtils.POSITION_PRECEDING
+ ) {
+ domUtils.remove(bi, true);
+ }
+ }
+ } else {
+ var tmpRange = range.cloneRange(),
+ node = tmpRange.startContainer.nodeType == 1
+ ? tmpRange.startContainer
+ : tmpRange.startContainer.parentNode,
+ preNode = node,
+ doEnd = 1;
+
+ //调整开始
+ while (1) {
+ if (domUtils.isBody(node)) {
+ if (preNode !== node) {
+ if (range.collapsed) {
+ tmpRange.selectNode(preNode);
+ doEnd = 0;
+ } else {
+ tmpRange.setStartBefore(preNode);
+ }
+ } else {
+ tmpRange.setStart(node, 0);
+ }
+
+ break;
+ }
+ if (!blockquote[node.tagName]) {
+ if (range.collapsed) {
+ tmpRange.selectNode(preNode);
+ } else {
+ tmpRange.setStartBefore(preNode);
+ }
+ break;
+ }
+
+ preNode = node;
+ node = node.parentNode;
+ }
+
+ //调整结束
+ if (doEnd) {
+ preNode = node = node = tmpRange.endContainer.nodeType == 1
+ ? tmpRange.endContainer
+ : tmpRange.endContainer.parentNode;
+ while (1) {
+ if (domUtils.isBody(node)) {
+ if (preNode !== node) {
+ tmpRange.setEndAfter(preNode);
+ } else {
+ tmpRange.setEnd(node, node.childNodes.length);
+ }
+
+ break;
+ }
+ if (!blockquote[node.tagName]) {
+ tmpRange.setEndAfter(preNode);
+ break;
+ }
+
+ preNode = node;
+ node = node.parentNode;
+ }
+ }
+
+ node = range.document.createElement("blockquote");
+ domUtils.setAttributes(node, attrs);
+ node.appendChild(tmpRange.extractContents());
+ tmpRange.insertNode(node);
+ //去除重复的
+ var childs = domUtils.getElementsByTagName(node, "blockquote");
+ for (var i = 0, ci; (ci = childs[i++]); ) {
+ if (ci.parentNode) {
+ domUtils.remove(ci, true);
+ }
+ }
+ }
+ range.moveToBookmark(bookmark).select();
+ },
+ queryCommandState: function() {
+ return getObj(this) ? 1 : 0;
+ }
+ };
+};
+
+
+// plugins/convertcase.js
+/**
+ * 大小写转换
+ * @file
+ * @since 1.2.6.1
+ */
+
+/**
+ * 把选区内文本变大写,与“tolowercase”命令互斥
+ * @command touppercase
+ * @method execCommand
+ * @param { String } cmd 命令字符串
+ * @example
+ * ```javascript
+ * editor.execCommand( 'touppercase' );
+ * ```
+ */
+
+/**
+ * 把选区内文本变小写,与“touppercase”命令互斥
+ * @command tolowercase
+ * @method execCommand
+ * @param { String } cmd 命令字符串
+ * @example
+ * ```javascript
+ * editor.execCommand( 'tolowercase' );
+ * ```
+ */
+UE.commands["touppercase"] = UE.commands["tolowercase"] = {
+ execCommand: function(cmd) {
+ var me = this;
+ var rng = me.selection.getRange();
+ if (rng.collapsed) {
+ return rng;
+ }
+ var bk = rng.createBookmark(),
+ bkEnd = bk.end,
+ filterFn = function(node) {
+ return !domUtils.isBr(node) && !domUtils.isWhitespace(node);
+ },
+ curNode = domUtils.getNextDomNode(bk.start, false, filterFn);
+ while (
+ curNode &&
+ domUtils.getPosition(curNode, bkEnd) & domUtils.POSITION_PRECEDING
+ ) {
+ if (curNode.nodeType == 3) {
+ curNode.nodeValue = curNode.nodeValue[
+ cmd == "touppercase" ? "toUpperCase" : "toLowerCase"
+ ]();
+ }
+ curNode = domUtils.getNextDomNode(curNode, true, filterFn);
+ if (curNode === bkEnd) {
+ break;
+ }
+ }
+ rng.moveToBookmark(bk).select();
+ }
+};
+
+
+// plugins/indent.js
+/**
+ * 首行缩进
+ * @file
+ * @since 1.2.6.1
+ */
+
+/**
+ * 缩进
+ * @command indent
+ * @method execCommand
+ * @param { String } cmd 命令字符串
+ * @example
+ * ```javascript
+ * editor.execCommand( 'indent' );
+ * ```
+ */
+UE.commands["indent"] = {
+ execCommand: function() {
+ var me = this,
+ value = me.queryCommandState("indent")
+ ? "0em"
+ : me.options.indentValue || "2em";
+ me.execCommand("Paragraph", "p", { style: "text-indent:" + value });
+ },
+ queryCommandState: function() {
+ var pN = domUtils.filterNodeList(
+ this.selection.getStartElementPath(),
+ "p h1 h2 h3 h4 h5 h6"
+ );
+ return pN && pN.style.textIndent && parseInt(pN.style.textIndent) ? 1 : 0;
+ }
+};
+
+
+// plugins/print.js
+/**
+ * 打印
+ * @file
+ * @since 1.2.6.1
+ */
+
+/**
+ * 打印
+ * @command print
+ * @method execCommand
+ * @param { String } cmd 命令字符串
+ * @example
+ * ```javascript
+ * editor.execCommand( 'print' );
+ * ```
+ */
+UE.commands["print"] = {
+ execCommand: function() {
+ this.window.print();
+ },
+ notNeedUndo: 1
+};
+
+
+// plugins/preview.js
+/**
+ * 预览
+ * @file
+ * @since 1.2.6.1
+ */
+
+/**
+ * 预览
+ * @command preview
+ * @method execCommand
+ * @param { String } cmd 命令字符串
+ * @example
+ * ```javascript
+ * editor.execCommand( 'preview' );
+ * ```
+ */
+UE.commands["preview"] = {
+ execCommand: function() {
+ var w = window.open("", "_blank", ""),
+ d = w.document;
+ d.open();
+ d.write(
+ '" +
+ 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, 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.ready(function() {
+ utils.cssRule(
+ "pre",
+ "pre{margin:.5em 0;padding:.4em .6em;border-radius:8px;background:#f8f8f8;}",
+ me.document
+ );
+ });
+ 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 ? "" : " ") + " ";
+ range.setStart(me.body.firstChild, 0).setCursor(false, true);
+ }
+ setTimeout(function() {
+ me.fireEvent("clearDoc");
+ }, 0);
+ }
+};
+
+
+// plugins/anchor.js
+/**
+ * 锚点插件,为UEditor提供插入锚点支持
+ * @file
+ * @since 1.2.6.1
+ */
+UE.plugin.register("anchor", function () {
+ var me = this;
+ return {
+ bindEvents: {
+ ready: function () {
+ utils.cssRule(
+ "anchor",
+ ".anchorclass{background: url('" +
+ this.options.themePath +
+ this.options.theme +
+ "/images/anchor.gif') no-repeat scroll left center transparent;cursor: auto;display: inline-block;height: 16px;width: 16px;}",
+ this.document
+ );
+ }
+ },
+ outputRule: function (root) {
+ utils.each(root.getNodesByTagName("img"), function (a) {
+ var val;
+ if ((val = a.getAttr("anchorname"))) {
+ a.tagName = "a";
+ a.setAttr({
+ anchorname: "",
+ name: val,
+ class: ""
+ });
+ }
+ });
+ },
+ inputRule: function (root) {
+ utils.each(root.getNodesByTagName("a"), function (a) {
+ var val;
+ if ((val = a.getAttr("name")) && !a.getAttr("href")) {
+ //过滤掉word冗余标签
+ //_Toc\d+有可能勿命中
+ if (/^\_Toc\d+$/.test(val)) {
+ a.parentNode.removeChild(a);
+ return;
+ }
+ a.tagName = "img";
+ a.setAttr({
+ anchorname: a.getAttr("name"),
+ class: "anchorclass"
+ });
+ a.setAttr("name");
+ }
+ });
+ },
+ commands: {
+ /**
+ * 插入锚点
+ * @command anchor
+ * @method execCommand
+ * @param { String } cmd 命令字符串
+ * @param { String } name 锚点名称字符串
+ * @example
+ * ```javascript
+ * //editor 是编辑器实例
+ * editor.execCommand('anchor', 'anchor1');
+ * ```
+ */
+ anchor: {
+ execCommand: function (cmd, name) {
+ var range = this.selection.getRange(),
+ img = range.getClosedNode();
+
+ if (img && img.getAttribute("anchorname")) {
+ if (name) {
+ img.setAttribute("anchorname", name);
+ } else {
+ range.setStartBefore(img).setCursor();
+ domUtils.remove(img);
+ }
+ } else {
+ if (name) {
+ //只在选区的开始插入
+ var anchor = utils.renderTplstr(' ', {
+ name: name
+ });
+ me.execCommand("inserthtml", anchor, true);
+ }
+ }
+ }
+ }
+ }
+ };
+});
+
+
+// plugins/wordcount.js
+///import core
+///commands 字数统计
+///commandsName WordCount,wordCount
+///commandsTitle 字数统计
+/*
+ * Created by JetBrains WebStorm.
+ * User: taoqili
+ * Date: 11-9-7
+ * Time: 下午8:18
+ * To change this template use File | Settings | File Templates.
+ */
+
+UE.plugins["wordcount"] = function() {
+ var me = this;
+ me.setOpt("wordCount", true);
+ me.addListener("contentchange", function() {
+ me.fireEvent("wordcount");
+ });
+ var timer;
+ me.addListener("ready", function() {
+ var me = this;
+ domUtils.on(me.body, "keyup", function(evt) {
+ var code = evt.keyCode || evt.which,
+ //忽略的按键,ctr,alt,shift,方向键
+ ignores = {
+ "16": 1,
+ "18": 1,
+ "20": 1,
+ "37": 1,
+ "38": 1,
+ "39": 1,
+ "40": 1
+ };
+ if (code in ignores) return;
+ clearTimeout(timer);
+ timer = setTimeout(function() {
+ me.fireEvent("wordcount");
+ }, 200);
+ });
+ });
+};
+
+
+// plugins/pagebreak.js
+/**
+ * 分页功能插件
+ * @file
+ * @since 1.2.6.1
+ */
+UE.plugins["pagebreak"] = function() {
+ var me = this,
+ notBreakTags = ["td"];
+ me.setOpt("pageBreakTag", "_ueditor_page_break_tag_");
+
+ function fillNode(node) {
+ if (domUtils.isEmptyBlock(node)) {
+ var firstChild = node.firstChild,
+ tmpNode;
+
+ while (
+ firstChild &&
+ firstChild.nodeType == 1 &&
+ domUtils.isEmptyBlock(firstChild)
+ ) {
+ tmpNode = firstChild;
+ firstChild = firstChild.firstChild;
+ }
+ !tmpNode && (tmpNode = node);
+ domUtils.fillNode(me.document, tmpNode);
+ }
+ }
+ //分页符样式添加
+
+ me.ready(function() {
+ utils.cssRule(
+ "pagebreak",
+ ".pagebreak{display:block;clear:both !important;cursor:default !important;width: 100% !important;margin:0;}",
+ me.document
+ );
+ });
+ function isHr(node) {
+ return (
+ node &&
+ node.nodeType == 1 &&
+ node.tagName == "HR" &&
+ node.className == "pagebreak"
+ );
+ }
+ me.addInputRule(function(root) {
+ root.traversal(function(node) {
+ if (node.type == "text" && node.data == me.options.pageBreakTag) {
+ var hr = UE.uNode.createElement(
+ ' '
+ );
+ 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 = [];
+ 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("word_img");
+ 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("word_img")) {
+ 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/notadd/images/spacer.gif";
+ if (attrs["src"] && /^(?:(file:\/+))/.test(attrs["src"])) {
+ img.setAttr({
+ width: attrs.width,
+ height: attrs.height,
+ alt: attrs.alt,
+ word_img: 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/dragdrop.js
+UE.plugins["dragdrop"] = function() {
+ var me = this;
+ me.ready(function() {
+ domUtils.on(this.body, "dragend", function() {
+ var rng = me.selection.getRange();
+ var node = rng.getClosedNode() || me.selection.getStart();
+
+ if (node && node.tagName == "IMG") {
+ var pre = node.previousSibling,
+ next;
+ while ((next = node.nextSibling)) {
+ if (
+ next.nodeType == 1 &&
+ next.tagName == "SPAN" &&
+ !next.firstChild
+ ) {
+ domUtils.remove(next);
+ } else {
+ break;
+ }
+ }
+
+ if (
+ ((pre && pre.nodeType == 1 && !domUtils.isEmptyBlock(pre)) || !pre) &&
+ (!next || (next && !domUtils.isEmptyBlock(next)))
+ ) {
+ if (pre && pre.tagName == "P" && !domUtils.isEmptyBlock(pre)) {
+ pre.appendChild(node);
+ domUtils.moveChild(next, pre);
+ domUtils.remove(next);
+ } else if (
+ next &&
+ next.tagName == "P" &&
+ !domUtils.isEmptyBlock(next)
+ ) {
+ next.insertBefore(node, next.firstChild);
+ }
+
+ if (pre && pre.tagName == "P" && domUtils.isEmptyBlock(pre)) {
+ domUtils.remove(pre);
+ }
+ if (next && next.tagName == "P" && domUtils.isEmptyBlock(next)) {
+ domUtils.remove(next);
+ }
+ rng.selectNode(node).select();
+ me.fireEvent("saveScene");
+ }
+ }
+ });
+ });
+ me.addListener("keyup", function(type, evt) {
+ var keyCode = evt.keyCode || evt.which;
+ if (keyCode == 13) {
+ var rng = me.selection.getRange(),
+ node;
+ if (
+ (node = domUtils.findParentByTagName(rng.startContainer, "p", true))
+ ) {
+ if (domUtils.getComputedStyle(node, "text-align") == "center") {
+ domUtils.removeStyle(node, "text-align");
+ }
+ }
+ }
+ });
+};
+
+
+// plugins/undo.js
+/**
+ * undo redo
+ * @file
+ * @since 1.2.6.1
+ */
+
+/**
+ * 撤销上一次执行的命令
+ * @command undo
+ * @method execCommand
+ * @param { String } cmd 命令字符串
+ * @example
+ * ```javascript
+ * editor.execCommand( 'undo' );
+ * ```
+ */
+
+/**
+ * 重做上一次执行的命令
+ * @command redo
+ * @method execCommand
+ * @param { String } cmd 命令字符串
+ * @example
+ * ```javascript
+ * editor.execCommand( 'redo' );
+ * ```
+ */
+
+UE.plugins["undo"] = function() {
+ var saveSceneTimer;
+ var me = this,
+ maxUndoCount = me.options.maxUndoCount || 20,
+ maxInputCount = me.options.maxInputCount || 20,
+ fillchar = new RegExp(domUtils.fillChar + "|", "gi"); // ie会产生多余的
+ var noNeedFillCharTags = {
+ ol: 1,
+ ul: 1,
+ table: 1,
+ tbody: 1,
+ tr: 1,
+ body: 1
+ };
+ var orgState = me.options.autoClearEmptyNode;
+ function compareAddr(indexA, indexB) {
+ if (indexA.length != indexB.length) return 0;
+ for (var i = 0, l = indexA.length; i < l; i++) {
+ if (indexA[i] != indexB[i]) return 0;
+ }
+ return 1;
+ }
+
+ function compareRangeAddress(rngAddrA, rngAddrB) {
+ if (rngAddrA.collapsed != rngAddrB.collapsed) {
+ return 0;
+ }
+ if (
+ !compareAddr(rngAddrA.startAddress, rngAddrB.startAddress) ||
+ !compareAddr(rngAddrA.endAddress, rngAddrB.endAddress)
+ ) {
+ return 0;
+ }
+ return 1;
+ }
+
+ function UndoManager() {
+ this.list = [];
+ this.index = 0;
+ this.hasUndo = false;
+ this.hasRedo = false;
+ this.undo = function() {
+ if (this.hasUndo) {
+ if (!this.list[this.index - 1] && this.list.length == 1) {
+ this.reset();
+ return;
+ }
+ while (
+ this.list[this.index].content == this.list[this.index - 1].content
+ ) {
+ this.index--;
+ if (this.index == 0) {
+ return this.restore(0);
+ }
+ }
+ this.restore(--this.index);
+ }
+ };
+ this.redo = function() {
+ if (this.hasRedo) {
+ while (
+ this.list[this.index].content == this.list[this.index + 1].content
+ ) {
+ this.index++;
+ if (this.index == this.list.length - 1) {
+ return this.restore(this.index);
+ }
+ }
+ this.restore(++this.index);
+ }
+ };
+
+ this.restore = function() {
+ var me = this.editor;
+ var scene = this.list[this.index];
+ var root = UE.htmlparser(scene.content.replace(fillchar, ""));
+ me.options.autoClearEmptyNode = false;
+ me.filterInputRule(root);
+ me.options.autoClearEmptyNode = orgState;
+ //trace:873
+ //去掉展位符
+ me.document.body.innerHTML = root.toHtml();
+ me.fireEvent("afterscencerestore");
+ //处理undo后空格不展位的问题
+ if (browser.ie) {
+ utils.each(
+ domUtils.getElementsByTagName(me.document, "td th caption p"),
+ function(node) {
+ if (domUtils.isEmptyNode(node)) {
+ domUtils.fillNode(me.document, node);
+ }
+ }
+ );
+ }
+
+ try {
+ var rng = new dom.Range(me.document).moveToAddress(scene.address);
+ rng.select(
+ noNeedFillCharTags[rng.startContainer.nodeName.toLowerCase()]
+ );
+ } catch (e) {}
+
+ this.update();
+ this.clearKey();
+ //不能把自己reset了
+ me.fireEvent("reset", true);
+ };
+
+ this.getScene = function() {
+ var me = this.editor;
+ var rng = me.selection.getRange(),
+ rngAddress = rng.createAddress(false, true);
+ me.fireEvent("beforegetscene");
+ var root = UE.htmlparser(me.body.innerHTML);
+ me.options.autoClearEmptyNode = false;
+ me.filterOutputRule(root);
+ me.options.autoClearEmptyNode = orgState;
+ var cont = root.toHtml();
+ //trace:3461
+ //这个会引起回退时导致空格丢失的情况
+ // browser.ie && (cont = cont.replace(/> <').replace(/\s*\s*/g, '>'));
+ me.fireEvent("aftergetscene");
+
+ return {
+ address: rngAddress,
+ content: cont
+ };
+ };
+ this.save = function(notCompareRange, notSetCursor) {
+ clearTimeout(saveSceneTimer);
+ var currentScene = this.getScene(notSetCursor),
+ lastScene = this.list[this.index];
+
+ if (lastScene && lastScene.content != currentScene.content) {
+ me.trigger("contentchange");
+ }
+ //内容相同位置相同不存
+ if (
+ lastScene &&
+ lastScene.content == currentScene.content &&
+ (notCompareRange
+ ? 1
+ : compareRangeAddress(lastScene.address, currentScene.address))
+ ) {
+ return;
+ }
+ this.list = this.list.slice(0, this.index + 1);
+ this.list.push(currentScene);
+ //如果大于最大数量了,就把最前的剔除
+ if (this.list.length > maxUndoCount) {
+ this.list.shift();
+ }
+ this.index = this.list.length - 1;
+ this.clearKey();
+ //跟新undo/redo状态
+ this.update();
+ };
+ this.update = function() {
+ this.hasRedo = !!this.list[this.index + 1];
+ this.hasUndo = !!this.list[this.index - 1];
+ };
+ this.reset = function() {
+ this.list = [];
+ this.index = 0;
+ this.hasUndo = false;
+ this.hasRedo = false;
+ this.clearKey();
+ };
+ this.clearKey = function() {
+ keycont = 0;
+ lastKeyCode = null;
+ };
+ }
+
+ me.undoManger = new UndoManager();
+ me.undoManger.editor = me;
+ function saveScene() {
+ this.undoManger.save();
+ }
+
+ me.addListener("saveScene", function() {
+ var args = Array.prototype.splice.call(arguments, 1);
+ this.undoManger.save.apply(this.undoManger, args);
+ });
+
+ // me.addListener('beforeexeccommand', saveScene);
+ // me.addListener('afterexeccommand', saveScene);
+
+ me.addListener("reset", function(type, exclude) {
+ if (!exclude) {
+ this.undoManger.reset();
+ }
+ });
+ me.commands["redo"] = me.commands["undo"] = {
+ execCommand: function(cmdName) {
+ this.undoManger[cmdName]();
+ },
+ queryCommandState: function(cmdName) {
+ return this.undoManger[
+ "has" + (cmdName.toLowerCase() == "undo" ? "Undo" : "Redo")
+ ]
+ ? 0
+ : -1;
+ },
+ notNeedUndo: 1
+ };
+
+ var keys = {
+ // /*Backspace*/ 8:1, /*Delete*/ 46:1,
+ /*Shift*/ 16: 1,
+ /*Ctrl*/ 17: 1,
+ /*Alt*/ 18: 1,
+ 37: 1,
+ 38: 1,
+ 39: 1,
+ 40: 1
+ },
+ keycont = 0,
+ lastKeyCode;
+ //输入法状态下不计算字符数
+ var inputType = false;
+ me.addListener("ready", function() {
+ domUtils.on(this.body, "compositionstart", function() {
+ inputType = true;
+ });
+ domUtils.on(this.body, "compositionend", function() {
+ inputType = false;
+ });
+ });
+ //快捷键
+ me.addshortcutkey({
+ Undo: "ctrl+90", //undo
+ Redo: "ctrl+89" //redo
+ });
+ var isCollapsed = true;
+ me.addListener("keydown", function(type, evt) {
+ var me = this;
+ var keyCode = evt.keyCode || evt.which;
+ if (
+ !keys[keyCode] &&
+ !evt.ctrlKey &&
+ !evt.metaKey &&
+ !evt.shiftKey &&
+ !evt.altKey
+ ) {
+ if (inputType) return;
+
+ if (!me.selection.getRange().collapsed) {
+ me.undoManger.save(false, true);
+ isCollapsed = false;
+ return;
+ }
+ if (me.undoManger.list.length == 0) {
+ me.undoManger.save(true);
+ }
+ clearTimeout(saveSceneTimer);
+ function save(cont) {
+ cont.undoManger.save(false, true);
+ cont.fireEvent("selectionchange");
+ }
+ saveSceneTimer = setTimeout(function() {
+ if (inputType) {
+ var interalTimer = setInterval(function() {
+ if (!inputType) {
+ save(me);
+ clearInterval(interalTimer);
+ }
+ }, 300);
+ return;
+ }
+ save(me);
+ }, 200);
+
+ lastKeyCode = keyCode;
+ keycont++;
+ if (keycont >= maxInputCount) {
+ save(me);
+ }
+ }
+ });
+ me.addListener("keyup", function(type, evt) {
+ var keyCode = evt.keyCode || evt.which;
+ if (
+ !keys[keyCode] &&
+ !evt.ctrlKey &&
+ !evt.metaKey &&
+ !evt.shiftKey &&
+ !evt.altKey
+ ) {
+ if (inputType) return;
+ if (!isCollapsed) {
+ this.undoManger.save(false, true);
+ isCollapsed = true;
+ }
+ }
+ });
+ //扩展实例,添加关闭和开启命令undo
+ me.stopCmdUndo = function() {
+ me.__hasEnterExecCommand = true;
+ };
+ me.startCmdUndo = function() {
+ me.__hasEnterExecCommand = false;
+ };
+};
+
+
+// plugins/copy.js
+UE.plugin.register("copy", function() {
+ var me = this;
+
+ function initZeroClipboard() {
+ ZeroClipboard.config({
+ debug: false,
+ swfPath:
+ me.options.UEDITOR_HOME_URL +
+ "third-party/zeroclipboard/ZeroClipboard.swf"
+ });
+
+ var client = (me.zeroclipboard = new ZeroClipboard());
+
+ // 复制内容
+ client.on("copy", function(e) {
+ var client = e.client,
+ rng = me.selection.getRange(),
+ div = document.createElement("div");
+
+ div.appendChild(rng.cloneContents());
+ client.setText(div.innerText || div.textContent);
+ client.setHtml(div.innerHTML);
+ rng.select();
+ });
+ // hover事件传递到target
+ client.on("mouseover mouseout", function(e) {
+ var target = e.target;
+ if (target) {
+ if (e.type == "mouseover") {
+ domUtils.addClass(target, "edui-state-hover");
+ } else if (e.type == "mouseout") {
+ domUtils.removeClasses(target, "edui-state-hover");
+ }
+ }
+ });
+ // flash加载不成功
+ client.on("wrongflash noflash", function() {
+ ZeroClipboard.destroy();
+ });
+
+ // 触发事件
+ me.fireEvent("zeroclipboardready", client);
+ }
+
+ return {
+ bindEvents: {
+ ready: function() {
+ if (!browser.ie) {
+ if (window.ZeroClipboard) {
+ initZeroClipboard();
+ } else {
+ utils.loadFile(
+ document,
+ {
+ src:
+ me.options.UEDITOR_HOME_URL +
+ "third-party/zeroclipboard/ZeroClipboard.js",
+ tag: "script",
+ type: "text/javascript",
+ defer: "defer"
+ },
+ function() {
+ initZeroClipboard();
+ }
+ );
+ }
+ }
+ }
+ },
+ commands: {
+ copy: {
+ execCommand: function(cmd) {
+ if (!me.document.execCommand("copy")) {
+ alert(me.getLang("copymsg"));
+ }
+ }
+ }
+ }
+ };
+});
+
+
+// plugins/paste.js
+///import core
+///import plugins/inserthtml.js
+///import plugins/undo.js
+///import plugins/serialize.js
+///commands 粘贴
+///commandsName PastePlain
+///commandsTitle 纯文本粘贴模式
+/**
+ * @description 粘贴
+ * @author zhanyi
+ */
+UE.plugins["paste"] = function() {
+ function getClipboardData(callback) {
+ var doc = this.document;
+ if (doc.getElementById("baidu_pastebin")) {
+ return;
+ }
+ var range = this.selection.getRange(),
+ bk = range.createBookmark(),
+ //创建剪贴的容器div
+ pastebin = doc.createElement("div");
+ pastebin.id = "baidu_pastebin";
+ // Safari 要求div必须有内容,才能粘贴内容进来
+ browser.webkit &&
+ pastebin.appendChild(
+ doc.createTextNode(domUtils.fillChar + domUtils.fillChar)
+ );
+ doc.body.appendChild(pastebin);
+ //trace:717 隐藏的span不能得到top
+ //bk.start.innerHTML = ' ';
+ bk.start.style.display = "";
+ pastebin.style.cssText =
+ "position:absolute;width:1px;height:1px;overflow:hidden;left:-1000px;white-space:nowrap;top:" +
+ //要在现在光标平行的位置加入,否则会出现跳动的问题
+ domUtils.getXY(bk.start).y +
+ "px";
+
+ range.selectNodeContents(pastebin).select(true);
+
+ setTimeout(function() {
+ if (browser.webkit) {
+ for (
+ var i = 0, pastebins = doc.querySelectorAll("#baidu_pastebin"), pi;
+ (pi = pastebins[i++]);
+
+ ) {
+ if (domUtils.isEmptyNode(pi)) {
+ domUtils.remove(pi);
+ } else {
+ pastebin = pi;
+ break;
+ }
+ }
+ }
+ try {
+ pastebin.parentNode.removeChild(pastebin);
+ } catch (e) {}
+ range.moveToBookmark(bk).select(true);
+ callback(pastebin);
+ }, 0);
+ }
+
+ var me = this;
+
+ me.setOpt({
+ retainOnlyLabelPasted: false
+ });
+
+ var txtContent, htmlContent, address;
+
+ function getPureHtml(html) {
+ return html.replace(/<(\/?)([\w\-]+)([^>]*)>/gi, function(
+ a,
+ b,
+ tagName,
+ attrs
+ ) {
+ tagName = tagName.toLowerCase();
+ if ({ img: 1 }[tagName]) {
+ return a;
+ }
+ attrs = attrs.replace(
+ /([\w\-]*?)\s*=\s*(("([^"]*)")|('([^']*)')|([^\s>]+))/gi,
+ function(str, atr, val) {
+ if (
+ {
+ src: 1,
+ href: 1,
+ name: 1
+ }[atr.toLowerCase()]
+ ) {
+ return atr + "=" + val + " ";
+ }
+ return "";
+ }
+ );
+ if (
+ {
+ span: 1,
+ div: 1
+ }[tagName]
+ ) {
+ return "";
+ } else {
+ return "<" + b + tagName + " " + utils.trim(attrs) + ">";
+ }
+ });
+ }
+ function filter(div) {
+ var html;
+ if (div.firstChild) {
+ //去掉cut中添加的边界值
+ var nodes = domUtils.getElementsByTagName(div, "span");
+ for (var i = 0, ni; (ni = nodes[i++]); ) {
+ if (ni.id == "_baidu_cut_start" || ni.id == "_baidu_cut_end") {
+ domUtils.remove(ni);
+ }
+ }
+
+ if (browser.webkit) {
+ var brs = div.querySelectorAll("div br");
+ for (var i = 0, bi; (bi = brs[i++]); ) {
+ var pN = bi.parentNode;
+ if (pN.tagName == "DIV" && pN.childNodes.length == 1) {
+ pN.innerHTML = "
";
+ domUtils.remove(pN);
+ }
+ }
+ var divs = div.querySelectorAll("#baidu_pastebin");
+ for (var i = 0, di; (di = divs[i++]); ) {
+ var tmpP = me.document.createElement("p");
+ di.parentNode.insertBefore(tmpP, di);
+ while (di.firstChild) {
+ tmpP.appendChild(di.firstChild);
+ }
+ domUtils.remove(di);
+ }
+
+ var metas = div.querySelectorAll("meta");
+ for (var i = 0, ci; (ci = metas[i++]); ) {
+ domUtils.remove(ci);
+ }
+
+ var brs = div.querySelectorAll("br");
+ for (i = 0; (ci = brs[i++]); ) {
+ if (/^apple-/i.test(ci.className)) {
+ domUtils.remove(ci);
+ }
+ }
+ }
+ if (browser.gecko) {
+ var dirtyNodes = div.querySelectorAll("[_moz_dirty]");
+ for (i = 0; (ci = dirtyNodes[i++]); ) {
+ ci.removeAttribute("_moz_dirty");
+ }
+ }
+ if (!browser.ie) {
+ var spans = div.querySelectorAll("span.Apple-style-span");
+ for (var i = 0, ci; (ci = spans[i++]); ) {
+ domUtils.remove(ci, true);
+ }
+ }
+
+ //ie下使用innerHTML会产生多余的\r\n字符,也会产生 这里过滤掉
+ html = div.innerHTML; //.replace(/>(?:(\s| )*?)<');
+
+ //过滤word粘贴过来的冗余属性
+ html = UE.filterWord(html);
+ //取消了忽略空白的第二个参数,粘贴过来的有些是有空白的,会被套上相关的标签
+ var root = UE.htmlparser(html);
+ //如果给了过滤规则就先进行过滤
+ if (me.options.filterRules) {
+ UE.filterNode(root, me.options.filterRules);
+ }
+ //执行默认的处理
+ me.filterInputRule(root);
+ //针对chrome的处理
+ if (browser.webkit) {
+ var br = root.lastChild();
+ if (br && br.type == "element" && br.tagName == "br") {
+ root.removeChild(br);
+ }
+ utils.each(me.body.querySelectorAll("div"), function(node) {
+ if (domUtils.isEmptyBlock(node)) {
+ domUtils.remove(node, true);
+ }
+ });
+ }
+ html = { html: root.toHtml() };
+ me.fireEvent("beforepaste", html, root);
+ //抢了默认的粘贴,那后边的内容就不执行了,比如表格粘贴
+ if (!html.html) {
+ return;
+ }
+ root = UE.htmlparser(html.html, true);
+ //如果开启了纯文本模式
+ if (me.queryCommandState("pasteplain") === 1) {
+ me.execCommand(
+ "insertHtml",
+ UE.filterNode(root, me.options.filterTxtRules).toHtml(),
+ true
+ );
+ } else {
+ //文本模式
+ UE.filterNode(root, me.options.filterTxtRules);
+ txtContent = root.toHtml();
+ //完全模式
+ htmlContent = html.html;
+
+ address = me.selection.getRange().createAddress(true);
+ me.execCommand(
+ "insertHtml",
+ me.getOpt("retainOnlyLabelPasted") === true
+ ? getPureHtml(htmlContent)
+ : htmlContent,
+ true
+ );
+ }
+ me.fireEvent("afterpaste", html);
+ }
+ }
+
+ me.addListener("pasteTransfer", function(cmd, plainType) {
+ if (address && txtContent && htmlContent && txtContent != htmlContent) {
+ var range = me.selection.getRange();
+ range.moveToAddress(address, true);
+
+ if (!range.collapsed) {
+ while (!domUtils.isBody(range.startContainer)) {
+ var start = range.startContainer;
+ if (start.nodeType == 1) {
+ start = start.childNodes[range.startOffset];
+ if (!start) {
+ range.setStartBefore(range.startContainer);
+ continue;
+ }
+ var pre = start.previousSibling;
+
+ if (
+ pre &&
+ pre.nodeType == 3 &&
+ new RegExp("^[\n\r\t " + domUtils.fillChar + "]*$").test(
+ pre.nodeValue
+ )
+ ) {
+ range.setStartBefore(pre);
+ }
+ }
+ if (range.startOffset == 0) {
+ range.setStartBefore(range.startContainer);
+ } else {
+ break;
+ }
+ }
+ while (!domUtils.isBody(range.endContainer)) {
+ var end = range.endContainer;
+ if (end.nodeType == 1) {
+ end = end.childNodes[range.endOffset];
+ if (!end) {
+ range.setEndAfter(range.endContainer);
+ continue;
+ }
+ var next = end.nextSibling;
+ if (
+ next &&
+ next.nodeType == 3 &&
+ new RegExp("^[\n\r\t" + domUtils.fillChar + "]*$").test(
+ next.nodeValue
+ )
+ ) {
+ range.setEndAfter(next);
+ }
+ }
+ if (
+ range.endOffset ==
+ range.endContainer[
+ range.endContainer.nodeType == 3 ? "nodeValue" : "childNodes"
+ ].length
+ ) {
+ range.setEndAfter(range.endContainer);
+ } else {
+ break;
+ }
+ }
+ }
+
+ range.deleteContents();
+ range.select(true);
+ me.__hasEnterExecCommand = true;
+ var html = htmlContent;
+ if (plainType === 2) {
+ html = getPureHtml(html);
+ } else if (plainType) {
+ html = txtContent;
+ }
+ me.execCommand("inserthtml", html, true);
+ me.__hasEnterExecCommand = false;
+ var rng = me.selection.getRange();
+ while (
+ !domUtils.isBody(rng.startContainer) &&
+ !rng.startOffset &&
+ rng.startContainer[
+ rng.startContainer.nodeType == 3 ? "nodeValue" : "childNodes"
+ ].length
+ ) {
+ rng.setStartBefore(rng.startContainer);
+ }
+ var tmpAddress = rng.createAddress(true);
+ address.endAddress = tmpAddress.startAddress;
+ }
+ });
+
+ me.addListener("ready", function() {
+ domUtils.on(me.body, "cut", function() {
+ var range = me.selection.getRange();
+ if (!range.collapsed && me.undoManger) {
+ if (me.undoManger.list.length < 1) me.undoManger.save();
+ setTimeout(function() {
+ me.undoManger.save();
+ });
+ }
+ });
+
+ //ie下beforepaste在点击右键时也会触发,所以用监控键盘才处理
+ domUtils.on(
+ me.body,
+ browser.ie || browser.opera ? "keydown" : "paste",
+ function(e) {
+ if (
+ (browser.ie || browser.opera) &&
+ ((!e.ctrlKey && !e.metaKey) || e.keyCode != "86")
+ ) {
+ return;
+ }
+ getClipboardData.call(me, function(div) {
+ filter(div);
+ });
+ }
+ );
+ });
+
+ me.commands["paste"] = {
+ execCommand: function(cmd) {
+ if (browser.ie) {
+ getClipboardData.call(me, function(div) {
+ filter(div);
+ });
+ me.document.execCommand("paste");
+ } else {
+ alert(me.getLang("pastemsg"));
+ }
+ }
+ };
+};
+
+
+// plugins/puretxtpaste.js
+/**
+ * 纯文本粘贴插件
+ * @file
+ * @since 1.2.6.1
+ */
+
+UE.plugins["pasteplain"] = function() {
+ var me = this;
+ me.setOpt({
+ pasteplain: false,
+ filterTxtRules: (function() {
+ function transP(node) {
+ node.tagName = "p";
+ node.setStyle();
+ }
+ function removeNode(node) {
+ node.parentNode.removeChild(node, true);
+ }
+ return {
+ //直接删除及其字节点内容
+ "-": "script style object iframe embed input select",
+ p: { $: {} },
+ br: { $: {} },
+ div: function(node) {
+ var tmpNode,
+ p = UE.uNode.createElement("p");
+ while ((tmpNode = node.firstChild())) {
+ if (tmpNode.type == "text" || !UE.dom.dtd.$block[tmpNode.tagName]) {
+ p.appendChild(tmpNode);
+ } else {
+ if (p.firstChild()) {
+ node.parentNode.insertBefore(p, node);
+ p = UE.uNode.createElement("p");
+ } else {
+ node.parentNode.insertBefore(tmpNode, node);
+ }
+ }
+ }
+ if (p.firstChild()) {
+ node.parentNode.insertBefore(p, node);
+ }
+ node.parentNode.removeChild(node);
+ },
+ ol: removeNode,
+ ul: removeNode,
+ dl: removeNode,
+ dt: removeNode,
+ dd: removeNode,
+ li: removeNode,
+ caption: transP,
+ th: transP,
+ tr: transP,
+ h1: transP,
+ h2: transP,
+ h3: transP,
+ h4: transP,
+ h5: transP,
+ h6: transP,
+ td: function(node) {
+ //没有内容的td直接删掉
+ var txt = !!node.innerText();
+ if (txt) {
+ node.parentNode.insertAfter(
+ UE.uNode.createText(" "),
+ node
+ );
+ }
+ node.parentNode.removeChild(node, node.innerText());
+ }
+ };
+ })()
+ });
+ //暂时这里支持一下老版本的属性
+ var pasteplain = me.options.pasteplain;
+
+ /**
+ * 启用或取消纯文本粘贴模式
+ * @command pasteplain
+ * @method execCommand
+ * @param { String } cmd 命令字符串
+ * @example
+ * ```javascript
+ * editor.queryCommandState( 'pasteplain' );
+ * ```
+ */
+
+ /**
+ * 查询当前是否处于纯文本粘贴模式
+ * @command pasteplain
+ * @method queryCommandState
+ * @param { String } cmd 命令字符串
+ * @return { int } 如果处于纯文本模式,返回1,否则,返回0
+ * @example
+ * ```javascript
+ * editor.queryCommandState( 'pasteplain' );
+ * ```
+ */
+ me.commands["pasteplain"] = {
+ queryCommandState: function() {
+ return pasteplain ? 1 : 0;
+ },
+ execCommand: function() {
+ pasteplain = !pasteplain | 0;
+ },
+ notNeedUndo: 1
+ };
+};
+
+
+// plugins/list.js
+/**
+ * 有序列表,无序列表插件
+ * @file
+ * @since 1.2.6.1
+ */
+
+UE.plugins["list"] = function() {
+ var me = this,
+ notExchange = {
+ TD: 1,
+ PRE: 1,
+ BLOCKQUOTE: 1
+ };
+ var customStyle = {
+ cn: "cn-1-",
+ cn1: "cn-2-",
+ cn2: "cn-3-",
+ num: "num-1-",
+ num1: "num-2-",
+ num2: "num-3-",
+ dash: "dash",
+ dot: "dot"
+ };
+
+ me.setOpt({
+ autoTransWordToList: false,
+ insertorderedlist: {
+ num: "",
+ num1: "",
+ num2: "",
+ cn: "",
+ cn1: "",
+ cn2: "",
+ decimal: "",
+ "lower-alpha": "",
+ "lower-roman": "",
+ "upper-alpha": "",
+ "upper-roman": ""
+ },
+ insertunorderedlist: {
+ circle: "",
+ disc: "",
+ square: "",
+ dash: "",
+ dot: ""
+ },
+ listDefaultPaddingLeft: "30",
+ listiconpath: me.options.UEDITOR_HOME_URL + "themes/ueditor-list/",
+ maxListLevel: -1, //-1不限制
+ disablePInList: false
+ });
+ function listToArray(list) {
+ var arr = [];
+ for (var p in list) {
+ arr.push(p);
+ }
+ return arr;
+ }
+ var listStyle = {
+ OL: listToArray(me.options.insertorderedlist),
+ UL: listToArray(me.options.insertunorderedlist)
+ };
+ var liiconpath = me.options.listiconpath;
+
+ //根据用户配置,调整customStyle
+ for (var s in customStyle) {
+ if (
+ !me.options.insertorderedlist.hasOwnProperty(s) &&
+ !me.options.insertunorderedlist.hasOwnProperty(s)
+ ) {
+ delete customStyle[s];
+ }
+ }
+
+ me.ready(function() {
+ var customCss = [];
+ for (var p in customStyle) {
+ if (p == "dash" || p == "dot") {
+ customCss.push(
+ "li.list-" +
+ customStyle[p] +
+ "{background-image:url(" +
+ liiconpath +
+ customStyle[p] +
+ ".gif)}"
+ );
+ customCss.push(
+ "ul.custom_" +
+ p +
+ "{list-style:none;}ul.custom_" +
+ p +
+ " li{background-position:0 3px;background-repeat:no-repeat}"
+ );
+ } else {
+ for (var i = 0; i < 99; i++) {
+ customCss.push(
+ "li.list-" +
+ customStyle[p] +
+ i +
+ "{background-image:url(" +
+ liiconpath +
+ "list-" +
+ customStyle[p] +
+ i +
+ ".gif)}"
+ );
+ }
+ customCss.push(
+ "ol.custom_" +
+ p +
+ "{list-style:none;}ol.custom_" +
+ p +
+ " li{background-position:0 3px;background-repeat:no-repeat}"
+ );
+ }
+ switch (p) {
+ case "cn":
+ customCss.push("li.list-" + p + "-paddingleft-1{padding-left:25px}");
+ customCss.push("li.list-" + p + "-paddingleft-2{padding-left:40px}");
+ customCss.push("li.list-" + p + "-paddingleft-3{padding-left:55px}");
+ break;
+ case "cn1":
+ customCss.push("li.list-" + p + "-paddingleft-1{padding-left:30px}");
+ customCss.push("li.list-" + p + "-paddingleft-2{padding-left:40px}");
+ customCss.push("li.list-" + p + "-paddingleft-3{padding-left:55px}");
+ break;
+ case "cn2":
+ customCss.push("li.list-" + p + "-paddingleft-1{padding-left:40px}");
+ customCss.push("li.list-" + p + "-paddingleft-2{padding-left:55px}");
+ customCss.push("li.list-" + p + "-paddingleft-3{padding-left:68px}");
+ break;
+ case "num":
+ case "num1":
+ customCss.push("li.list-" + p + "-paddingleft-1{padding-left:25px}");
+ break;
+ case "num2":
+ customCss.push("li.list-" + p + "-paddingleft-1{padding-left:35px}");
+ customCss.push("li.list-" + p + "-paddingleft-2{padding-left:40px}");
+ break;
+ case "dash":
+ customCss.push("li.list-" + p + "-paddingleft{padding-left:35px}");
+ break;
+ case "dot":
+ customCss.push("li.list-" + p + "-paddingleft{padding-left:20px}");
+ }
+ }
+ customCss.push(".list-paddingleft-1{padding-left:0}");
+ customCss.push(
+ ".list-paddingleft-2{padding-left:" +
+ me.options.listDefaultPaddingLeft +
+ "px}"
+ );
+ customCss.push(
+ ".list-paddingleft-3{padding-left:" +
+ me.options.listDefaultPaddingLeft * 2 +
+ "px}"
+ );
+ //如果不给宽度会在自定应样式里出现滚动条
+ utils.cssRule(
+ "list",
+ "ol,ul{margin:0;pading:0;" +
+ (browser.ie ? "" : "width:95%") +
+ "}li{clear:both;}" +
+ customCss.join("\n"),
+ me.document
+ );
+ });
+ //单独处理剪切的问题
+ me.ready(function() {
+ domUtils.on(me.body, "cut", function() {
+ setTimeout(function() {
+ var rng = me.selection.getRange(),
+ li;
+ //trace:3416
+ if (!rng.collapsed) {
+ if (
+ (li = domUtils.findParentByTagName(rng.startContainer, "li", true))
+ ) {
+ if (!li.nextSibling && domUtils.isEmptyBlock(li)) {
+ var pn = li.parentNode,
+ node;
+ if ((node = pn.previousSibling)) {
+ domUtils.remove(pn);
+ rng.setStartAtLast(node).collapse(true);
+ rng.select(true);
+ } else if ((node = pn.nextSibling)) {
+ domUtils.remove(pn);
+ rng.setStartAtFirst(node).collapse(true);
+ rng.select(true);
+ } else {
+ var tmpNode = me.document.createElement("p");
+ domUtils.fillNode(me.document, tmpNode);
+ pn.parentNode.insertBefore(tmpNode, pn);
+ domUtils.remove(pn);
+ rng.setStart(tmpNode, 0).collapse(true);
+ rng.select(true);
+ }
+ }
+ }
+ }
+ });
+ });
+ });
+
+ function getStyle(node) {
+ var cls = node.className;
+ if (domUtils.hasClass(node, /custom_/)) {
+ return cls.match(/custom_(\w+)/)[1];
+ }
+ return domUtils.getStyle(node, "list-style-type");
+ }
+
+ me.addListener("beforepaste", function(type, html) {
+ var me = this,
+ rng = me.selection.getRange(),
+ li;
+ var root = UE.htmlparser(html.html, true);
+ if ((li = domUtils.findParentByTagName(rng.startContainer, "li", true))) {
+ var list = li.parentNode,
+ tagName = list.tagName == "OL" ? "ul" : "ol";
+ utils.each(root.getNodesByTagName(tagName), function(n) {
+ n.tagName = list.tagName;
+ n.setAttr();
+ if (n.parentNode === root) {
+ type = getStyle(list) || (list.tagName == "OL" ? "decimal" : "disc");
+ } else {
+ var className = n.parentNode.getAttr("class");
+ if (className && /custom_/.test(className)) {
+ type = className.match(/custom_(\w+)/)[1];
+ } else {
+ type = n.parentNode.getStyle("list-style-type");
+ }
+ if (!type) {
+ type = list.tagName == "OL" ? "decimal" : "disc";
+ }
+ }
+ var index = utils.indexOf(listStyle[list.tagName], type);
+ if (n.parentNode !== root)
+ index = index + 1 == listStyle[list.tagName].length ? 0 : index + 1;
+ var currentStyle = listStyle[list.tagName][index];
+ if (customStyle[currentStyle]) {
+ n.setAttr("class", "custom_" + currentStyle);
+ } else {
+ n.setStyle("list-style-type", currentStyle);
+ }
+ });
+ }
+
+ html.html = root.toHtml();
+ });
+ //导出时,去掉p标签
+ me.getOpt("disablePInList") === true &&
+ me.addOutputRule(function(root) {
+ utils.each(root.getNodesByTagName("li"), function(li) {
+ var newChildrens = [],
+ index = 0;
+ utils.each(li.children, function(n) {
+ if (n.tagName == "p") {
+ var tmpNode;
+ while ((tmpNode = n.children.pop())) {
+ newChildrens.splice(index, 0, tmpNode);
+ tmpNode.parentNode = li;
+ lastNode = tmpNode;
+ }
+ tmpNode = newChildrens[newChildrens.length - 1];
+ if (
+ !tmpNode ||
+ tmpNode.type != "element" ||
+ tmpNode.tagName != "br"
+ ) {
+ var br = UE.uNode.createElement("br");
+ br.parentNode = li;
+ newChildrens.push(br);
+ }
+
+ index = newChildrens.length;
+ }
+ });
+ if (newChildrens.length) {
+ li.children = newChildrens;
+ }
+ });
+ });
+ //进入编辑器的li要套p标签
+ me.addInputRule(function(root) {
+ utils.each(root.getNodesByTagName("li"), function(li) {
+ var tmpP = UE.uNode.createElement("p");
+ for (var i = 0, ci; (ci = li.children[i]); ) {
+ if (ci.type == "text" || dtd.p[ci.tagName]) {
+ tmpP.appendChild(ci);
+ } else {
+ if (tmpP.firstChild()) {
+ li.insertBefore(tmpP, ci);
+ tmpP = UE.uNode.createElement("p");
+ i = i + 2;
+ } else {
+ i++;
+ }
+ }
+ }
+ if ((tmpP.firstChild() && !tmpP.parentNode) || !li.firstChild()) {
+ li.appendChild(tmpP);
+ }
+ //trace:3357
+ //p不能为空
+ if (!tmpP.firstChild()) {
+ tmpP.innerHTML(browser.ie ? " " : " ");
+ }
+ //去掉末尾的空白
+ 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是否是空的节点,如果是
类型的空节点,干掉p标签防止它占位
+ if (domUtils.isEmptyBlock(pre)) {
+ pre.innerHTML = "";
+ }
+ domUtils.moveChild(li, pre);
+ range.setStartBefore(span).collapse(true).select(true);
+
+ domUtils.remove(span);
+ }
+ } else {
+ if (domUtils.isEmptyNode(li)) {
+ var p = me.document.createElement("p");
+ pre.appendChild(p);
+ range.setStart(p, 0).setCursor();
+ // range.setEnd(pre, pre.childNodes.length).shrinkBoundary().collapse().select(true);
+ } else {
+ range
+ .setEnd(pre, pre.childNodes.length)
+ .collapse()
+ .select(true);
+ while (li.firstChild) {
+ pre.appendChild(li.firstChild);
+ }
+ }
+ }
+ domUtils.remove(li);
+ me.fireEvent("contentchange");
+ me.fireEvent("saveScene");
+ domUtils.preventDefault(evt);
+ return;
+ }
+ //trace:980
+
+ if (li && !li.previousSibling) {
+ var parentList = li.parentNode;
+ var bk = range.createBookmark();
+ if (domUtils.isTagNode(parentList.parentNode, "ol ul")) {
+ parentList.parentNode.insertBefore(li, parentList);
+ if (domUtils.isEmptyNode(parentList)) {
+ domUtils.remove(parentList);
+ }
+ } else {
+ while (li.firstChild) {
+ parentList.parentNode.insertBefore(li.firstChild, parentList);
+ }
+
+ domUtils.remove(li);
+ if (domUtils.isEmptyNode(parentList)) {
+ domUtils.remove(parentList);
+ }
+ }
+ range.moveToBookmark(bk).setCursor(false, true);
+ me.fireEvent("contentchange");
+ me.fireEvent("saveScene");
+ domUtils.preventDefault(evt);
+ return;
+ }
+ }
+ }
+ }
+ });
+
+ me.addListener("keyup", function(type, evt) {
+ var keyCode = evt.keyCode || evt.which;
+ if (keyCode == 8) {
+ var rng = me.selection.getRange(),
+ list;
+ if (
+ (list = domUtils.findParentByTagName(
+ rng.startContainer,
+ ["ol", "ul"],
+ true
+ ))
+ ) {
+ adjustList(
+ list,
+ list.tagName.toLowerCase(),
+ getStyle(list) || domUtils.getComputedStyle(list, "list-style-type"),
+ true
+ );
+ }
+ }
+ });
+ //处理tab键
+ me.addListener("tabkeydown", function() {
+ var range = me.selection.getRange();
+
+ //控制级数
+ function checkLevel(li) {
+ if (me.options.maxListLevel != -1) {
+ var level = li.parentNode,
+ levelNum = 0;
+ while (/[ou]l/i.test(level.tagName)) {
+ levelNum++;
+ level = level.parentNode;
+ }
+ if (levelNum >= me.options.maxListLevel) {
+ return true;
+ }
+ }
+ }
+ //只以开始为准
+ //todo 后续改进
+ var li = domUtils.findParentByTagName(range.startContainer, "li", true);
+ if (li) {
+ var bk;
+ if (range.collapsed) {
+ if (checkLevel(li)) return true;
+ var parentLi = li.parentNode,
+ list = me.document.createElement(parentLi.tagName),
+ index = utils.indexOf(
+ listStyle[list.tagName],
+ getStyle(parentLi) ||
+ domUtils.getComputedStyle(parentLi, "list-style-type")
+ );
+ index = index + 1 == listStyle[list.tagName].length ? 0 : index + 1;
+ var currentStyle = listStyle[list.tagName][index];
+ setListStyle(list, currentStyle);
+ if (domUtils.isStartInblock(range)) {
+ me.fireEvent("saveScene");
+ bk = range.createBookmark();
+ parentLi.insertBefore(list, li);
+ list.appendChild(li);
+ adjustList(list, list.tagName.toLowerCase(), currentStyle);
+ me.fireEvent("contentchange");
+ range.moveToBookmark(bk).select(true);
+ return true;
+ }
+ } else {
+ me.fireEvent("saveScene");
+ bk = range.createBookmark();
+ for (
+ var i = 0, closeList, parents = domUtils.findParents(li), ci;
+ (ci = parents[i++]);
+
+ ) {
+ if (domUtils.isTagNode(ci, "ol ul")) {
+ closeList = ci;
+ break;
+ }
+ }
+ var current = li;
+ if (bk.end) {
+ while (
+ current &&
+ !(
+ domUtils.getPosition(current, bk.end) &
+ domUtils.POSITION_FOLLOWING
+ )
+ ) {
+ if (checkLevel(current)) {
+ current = domUtils.getNextDomNode(current, false, null, function(
+ node
+ ) {
+ return node !== closeList;
+ });
+ continue;
+ }
+ var parentLi = current.parentNode,
+ list = me.document.createElement(parentLi.tagName),
+ index = utils.indexOf(
+ listStyle[list.tagName],
+ getStyle(parentLi) ||
+ domUtils.getComputedStyle(parentLi, "list-style-type")
+ );
+ var currentIndex = index + 1 == listStyle[list.tagName].length
+ ? 0
+ : index + 1;
+ var currentStyle = listStyle[list.tagName][currentIndex];
+ setListStyle(list, currentStyle);
+ parentLi.insertBefore(list, current);
+ while (
+ current &&
+ !(
+ domUtils.getPosition(current, bk.end) &
+ domUtils.POSITION_FOLLOWING
+ )
+ ) {
+ li = current.nextSibling;
+ list.appendChild(current);
+ if (!li || domUtils.isTagNode(li, "ol ul")) {
+ if (li) {
+ while ((li = li.firstChild)) {
+ if (li.tagName == "LI") {
+ break;
+ }
+ }
+ } else {
+ li = domUtils.getNextDomNode(current, false, null, function(
+ node
+ ) {
+ return node !== closeList;
+ });
+ }
+ break;
+ }
+ current = li;
+ }
+ adjustList(list, list.tagName.toLowerCase(), currentStyle);
+ current = li;
+ }
+ }
+ me.fireEvent("contentchange");
+ range.moveToBookmark(bk).select();
+ return true;
+ }
+ }
+ });
+ function getLi(start) {
+ while (start && !domUtils.isBody(start)) {
+ if (start.nodeName == "TABLE") {
+ return null;
+ }
+ if (start.nodeName == "LI") {
+ return start;
+ }
+ start = start.parentNode;
+ }
+ }
+
+ /**
+ * 有序列表,与“insertunorderedlist”命令互斥
+ * @command insertorderedlist
+ * @method execCommand
+ * @param { String } command 命令字符串
+ * @param { String } style 插入的有序列表类型,值为:decimal,lower-alpha,lower-roman,upper-alpha,upper-roman,cn,cn1,cn2,num,num1,num2
+ * @example
+ * ```javascript
+ * editor.execCommand( 'insertorderedlist','decimal');
+ * ```
+ */
+ /**
+ * 查询当前选区内容是否有序列表
+ * @command insertorderedlist
+ * @method queryCommandState
+ * @param { String } cmd 命令字符串
+ * @return { int } 如果当前选区是有序列表返回1,否则返回0
+ * @example
+ * ```javascript
+ * editor.queryCommandState( 'insertorderedlist' );
+ * ```
+ */
+ /**
+ * 查询当前选区内容是否有序列表
+ * @command insertorderedlist
+ * @method queryCommandValue
+ * @param { String } cmd 命令字符串
+ * @return { String } 返回当前有序列表的类型,值为null或decimal,lower-alpha,lower-roman,upper-alpha,upper-roman,cn,cn1,cn2,num,num1,num2
+ * @example
+ * ```javascript
+ * editor.queryCommandValue( 'insertorderedlist' );
+ * ```
+ */
+
+ /**
+ * 无序列表,与“insertorderedlist”命令互斥
+ * @command insertunorderedlist
+ * @method execCommand
+ * @param { String } command 命令字符串
+ * @param { String } style 插入的无序列表类型,值为:circle,disc,square,dash,dot
+ * @example
+ * ```javascript
+ * editor.execCommand( 'insertunorderedlist','circle');
+ * ```
+ */
+ /**
+ * 查询当前是否有word文档粘贴进来的图片
+ * @command insertunorderedlist
+ * @method insertunorderedlist
+ * @param { String } command 命令字符串
+ * @return { int } 如果当前选区是无序列表返回1,否则返回0
+ * @example
+ * ```javascript
+ * editor.queryCommandState( 'insertunorderedlist' );
+ * ```
+ */
+ /**
+ * 查询当前选区内容是否有序列表
+ * @command insertunorderedlist
+ * @method queryCommandValue
+ * @param { String } command 命令字符串
+ * @return { String } 返回当前无序列表的类型,值为null或circle,disc,square,dash,dot
+ * @example
+ * ```javascript
+ * editor.queryCommandValue( 'insertunorderedlist' );
+ * ```
+ */
+
+ me.commands["insertorderedlist"] = me.commands["insertunorderedlist"] = {
+ execCommand: function(command, style) {
+ if (!style) {
+ style = command.toLowerCase() == "insertorderedlist"
+ ? "decimal"
+ : "disc";
+ }
+ var me = this,
+ range = this.selection.getRange(),
+ filterFn = function(node) {
+ return node.nodeType == 1
+ ? node.tagName.toLowerCase() != "br"
+ : !domUtils.isWhitespace(node);
+ },
+ tag = command.toLowerCase() == "insertorderedlist" ? "ol" : "ul",
+ frag = me.document.createDocumentFragment();
+ //去掉是因为会出现选到末尾,导致adjustmentBoundary缩到ol/ul的位置
+ //range.shrinkBoundary();//.adjustmentBoundary();
+ range.adjustmentBoundary().shrinkBoundary();
+ var bko = range.createBookmark(true),
+ start = getLi(me.document.getElementById(bko.start)),
+ modifyStart = 0,
+ end = getLi(me.document.getElementById(bko.end)),
+ modifyEnd = 0,
+ startParent,
+ endParent,
+ list,
+ tmp;
+
+ if (start || end) {
+ start && (startParent = start.parentNode);
+ if (!bko.end) {
+ end = start;
+ }
+ end && (endParent = end.parentNode);
+
+ if (startParent === endParent) {
+ while (start !== end) {
+ tmp = start;
+ start = start.nextSibling;
+ if (!domUtils.isBlockElm(tmp.firstChild)) {
+ var p = me.document.createElement("p");
+ while (tmp.firstChild) {
+ p.appendChild(tmp.firstChild);
+ }
+ tmp.appendChild(p);
+ }
+ frag.appendChild(tmp);
+ }
+ tmp = me.document.createElement("span");
+ startParent.insertBefore(tmp, end);
+ if (!domUtils.isBlockElm(end.firstChild)) {
+ p = me.document.createElement("p");
+ while (end.firstChild) {
+ p.appendChild(end.firstChild);
+ }
+ end.appendChild(p);
+ }
+ frag.appendChild(end);
+ domUtils.breakParent(tmp, startParent);
+ if (domUtils.isEmptyNode(tmp.previousSibling)) {
+ domUtils.remove(tmp.previousSibling);
+ }
+ if (domUtils.isEmptyNode(tmp.nextSibling)) {
+ domUtils.remove(tmp.nextSibling);
+ }
+ var nodeStyle =
+ getStyle(startParent) ||
+ domUtils.getComputedStyle(startParent, "list-style-type") ||
+ (command.toLowerCase() == "insertorderedlist" ? "decimal" : "disc");
+ if (startParent.tagName.toLowerCase() == tag && nodeStyle == style) {
+ for (
+ var i = 0, ci, tmpFrag = me.document.createDocumentFragment();
+ (ci = frag.firstChild);
+
+ ) {
+ if (domUtils.isTagNode(ci, "ol ul")) {
+ // 删除时,子列表不处理
+ // utils.each(domUtils.getElementsByTagName(ci,'li'),function(li){
+ // while(li.firstChild){
+ // tmpFrag.appendChild(li.firstChild);
+ // }
+ //
+ // });
+ tmpFrag.appendChild(ci);
+ } else {
+ while (ci.firstChild) {
+ tmpFrag.appendChild(ci.firstChild);
+ domUtils.remove(ci);
+ }
+ }
+ }
+ tmp.parentNode.insertBefore(tmpFrag, tmp);
+ } else {
+ list = me.document.createElement(tag);
+ setListStyle(list, style);
+ list.appendChild(frag);
+ tmp.parentNode.insertBefore(list, tmp);
+ }
+
+ domUtils.remove(tmp);
+ list && adjustList(list, tag, style);
+ range.moveToBookmark(bko).select();
+ return;
+ }
+ //开始
+ if (start) {
+ while (start) {
+ tmp = start.nextSibling;
+ if (domUtils.isTagNode(start, "ol ul")) {
+ frag.appendChild(start);
+ } else {
+ var tmpfrag = me.document.createDocumentFragment(),
+ hasBlock = 0;
+ while (start.firstChild) {
+ if (domUtils.isBlockElm(start.firstChild)) {
+ hasBlock = 1;
+ }
+ tmpfrag.appendChild(start.firstChild);
+ }
+ if (!hasBlock) {
+ var tmpP = me.document.createElement("p");
+ tmpP.appendChild(tmpfrag);
+ frag.appendChild(tmpP);
+ } else {
+ frag.appendChild(tmpfrag);
+ }
+ domUtils.remove(start);
+ }
+
+ start = tmp;
+ }
+ startParent.parentNode.insertBefore(frag, startParent.nextSibling);
+ if (domUtils.isEmptyNode(startParent)) {
+ range.setStartBefore(startParent);
+ domUtils.remove(startParent);
+ } else {
+ range.setStartAfter(startParent);
+ }
+ modifyStart = 1;
+ }
+
+ if (end && domUtils.inDoc(endParent, me.document)) {
+ //结束
+ start = endParent.firstChild;
+ while (start && start !== end) {
+ tmp = start.nextSibling;
+ if (domUtils.isTagNode(start, "ol ul")) {
+ frag.appendChild(start);
+ } else {
+ tmpfrag = me.document.createDocumentFragment();
+ hasBlock = 0;
+ while (start.firstChild) {
+ if (domUtils.isBlockElm(start.firstChild)) {
+ hasBlock = 1;
+ }
+ tmpfrag.appendChild(start.firstChild);
+ }
+ if (!hasBlock) {
+ tmpP = me.document.createElement("p");
+ tmpP.appendChild(tmpfrag);
+ frag.appendChild(tmpP);
+ } else {
+ frag.appendChild(tmpfrag);
+ }
+ domUtils.remove(start);
+ }
+ start = tmp;
+ }
+ var tmpDiv = domUtils.createElement(me.document, "div", {
+ tmpDiv: 1
+ });
+ domUtils.moveChild(end, tmpDiv);
+
+ frag.appendChild(tmpDiv);
+ domUtils.remove(end);
+ endParent.parentNode.insertBefore(frag, endParent);
+ range.setEndBefore(endParent);
+ if (domUtils.isEmptyNode(endParent)) {
+ domUtils.remove(endParent);
+ }
+
+ modifyEnd = 1;
+ }
+ }
+
+ if (!modifyStart) {
+ range.setStartBefore(me.document.getElementById(bko.start));
+ }
+ if (bko.end && !modifyEnd) {
+ range.setEndAfter(me.document.getElementById(bko.end));
+ }
+ range.enlarge(true, function(node) {
+ return notExchange[node.tagName];
+ });
+
+ frag = me.document.createDocumentFragment();
+
+ var bk = range.createBookmark(),
+ current = domUtils.getNextDomNode(bk.start, false, filterFn),
+ tmpRange = range.cloneRange(),
+ tmpNode,
+ block = domUtils.isBlockElm;
+
+ while (
+ current &&
+ current !== bk.end &&
+ domUtils.getPosition(current, bk.end) & domUtils.POSITION_PRECEDING
+ ) {
+ if (current.nodeType == 3 || dtd.li[current.tagName]) {
+ if (current.nodeType == 1 && dtd.$list[current.tagName]) {
+ while (current.firstChild) {
+ frag.appendChild(current.firstChild);
+ }
+ tmpNode = domUtils.getNextDomNode(current, false, filterFn);
+ domUtils.remove(current);
+ current = tmpNode;
+ continue;
+ }
+ tmpNode = current;
+ tmpRange.setStartBefore(current);
+
+ while (
+ current &&
+ current !== bk.end &&
+ (!block(current) || domUtils.isBookmarkNode(current))
+ ) {
+ tmpNode = current;
+ current = domUtils.getNextDomNode(current, false, null, function(
+ node
+ ) {
+ return !notExchange[node.tagName];
+ });
+ }
+
+ if (current && block(current)) {
+ tmp = domUtils.getNextDomNode(tmpNode, false, filterFn);
+ if (tmp && domUtils.isBookmarkNode(tmp)) {
+ current = domUtils.getNextDomNode(tmp, false, filterFn);
+ tmpNode = tmp;
+ }
+ }
+ tmpRange.setEndAfter(tmpNode);
+
+ current = domUtils.getNextDomNode(tmpNode, false, filterFn);
+
+ var li = range.document.createElement("li");
+
+ li.appendChild(tmpRange.extractContents());
+ if (domUtils.isEmptyNode(li)) {
+ var tmpNode = range.document.createElement("p");
+ while (li.firstChild) {
+ tmpNode.appendChild(li.firstChild);
+ }
+ li.appendChild(tmpNode);
+ }
+ frag.appendChild(li);
+ } else {
+ current = domUtils.getNextDomNode(current, true, filterFn);
+ }
+ }
+ range.moveToBookmark(bk).collapse(true);
+ list = me.document.createElement(tag);
+ setListStyle(list, style);
+ list.appendChild(frag);
+ range.insertNode(list);
+ //当前list上下看能否合并
+ adjustList(list, tag, style);
+ //去掉冗余的tmpDiv
+ for (
+ var i = 0, ci, tmpDivs = domUtils.getElementsByTagName(list, "div");
+ (ci = tmpDivs[i++]);
+
+ ) {
+ if (ci.getAttribute("tmpDiv")) {
+ domUtils.remove(ci, true);
+ }
+ }
+ range.moveToBookmark(bko).select();
+ },
+ queryCommandState: function(command) {
+ var tag = command.toLowerCase() == "insertorderedlist" ? "ol" : "ul";
+ var path = this.selection.getStartElementPath();
+ for (var i = 0, ci; (ci = path[i++]); ) {
+ if (ci.nodeName == "TABLE") {
+ return 0;
+ }
+ if (tag == ci.nodeName.toLowerCase()) {
+ return 1;
+ }
+ }
+ return 0;
+ },
+ queryCommandValue: function(command) {
+ var tag = command.toLowerCase() == "insertorderedlist" ? "ol" : "ul";
+ var path = this.selection.getStartElementPath(),
+ node;
+ for (var i = 0, ci; (ci = path[i++]); ) {
+ if (ci.nodeName == "TABLE") {
+ node = null;
+ break;
+ }
+ if (tag == ci.nodeName.toLowerCase()) {
+ node = ci;
+ break;
+ }
+ }
+ return node
+ ? getStyle(node) || domUtils.getComputedStyle(node, "list-style-type")
+ : null;
+ }
+ };
+};
+
+
+// plugins/source.js
+/**
+ * 源码编辑插件
+ * @file
+ * @since 1.2.6.1
+ */
+
+;(function() {
+ var sourceEditors = {
+ textarea: function(editor, holder) {
+ var textarea = holder.ownerDocument.createElement("textarea");
+ textarea.style.cssText =
+ "position:absolute;resize:none;width:100%;height:100%;border:0;padding:0;margin:0;overflow-y:auto;";
+ // todo: IE下只有onresize属性可用... 很纠结
+ if (browser.ie && browser.version < 8) {
+ textarea.style.width = holder.offsetWidth + "px";
+ textarea.style.height = holder.offsetHeight + "px";
+ holder.onresize = function() {
+ textarea.style.width = holder.offsetWidth + "px";
+ textarea.style.height = holder.offsetHeight + "px";
+ };
+ }
+ holder.appendChild(textarea);
+ return {
+ setContent: function(content) {
+ textarea.value = content;
+ },
+ getContent: function() {
+ return textarea.value;
+ },
+ select: function() {
+ var range;
+ if (browser.ie) {
+ range = textarea.createTextRange();
+ range.collapse(true);
+ range.select();
+ } else {
+ //todo: chrome下无法设置焦点
+ textarea.setSelectionRange(0, 0);
+ textarea.focus();
+ }
+ },
+ dispose: function() {
+ holder.removeChild(textarea);
+ // todo
+ holder.onresize = null;
+ textarea = null;
+ holder = null;
+ },
+ focus: function (){
+ textarea.focus();
+ },
+ blur: function (){
+ textarea.blur();
+ }
+ };
+ },
+ codemirror: function(editor, holder) {
+ var codeEditor = window.CodeMirror(holder, {
+ mode: "text/html",
+ tabMode: "indent",
+ lineNumbers: true,
+ lineWrapping: true
+ });
+ var dom = codeEditor.getWrapperElement();
+ dom.style.cssText =
+ 'position:absolute;left:0;top:0;width:100%;height:100%;font-family:consolas,"Courier new",monospace;font-size:13px;';
+ codeEditor.getScrollerElement().style.cssText =
+ "position:absolute;left:0;top:0;width:100%;height:100%;";
+ codeEditor.refresh();
+ return {
+ getCodeMirror: function() {
+ return codeEditor;
+ },
+ setContent: function(content) {
+ codeEditor.setValue(content);
+ },
+ getContent: function() {
+ return codeEditor.getValue();
+ },
+ select: function() {
+ codeEditor.focus();
+ },
+ dispose: function() {
+ holder.removeChild(dom);
+ dom = null;
+ codeEditor = null;
+ },
+ focus: function (){
+ codeEditor.focus();
+ },
+ blur: function (){
+ // codeEditor.blur();
+ // since codemirror not support blur()
+ codeEditor.setOption('readOnly', true);
+ codeEditor.setOption('readOnly', false);
+ }
+ };
+ }
+ };
+
+ UE.plugins["source"] = function() {
+ var me = this;
+ var opt = this.options;
+ var sourceMode = false;
+ var sourceEditor;
+ var orgSetContent;
+ var orgFocus;
+ var orgBlur;
+ opt.sourceEditor = browser.ie
+ ? "textarea"
+ : opt.sourceEditor || "codemirror";
+
+ me.setOpt({
+ sourceEditorFirst: false
+ });
+ function createSourceEditor(holder) {
+ return sourceEditors[
+ opt.sourceEditor == "codemirror" && window.CodeMirror
+ ? "codemirror"
+ : "textarea"
+ ](me, holder);
+ }
+
+ var bakCssText;
+ //解决在源码模式下getContent不能得到最新的内容问题
+ var oldGetContent, bakAddress;
+
+ /**
+ * 切换源码模式和编辑模式
+ * @command source
+ * @method execCommand
+ * @param { String } cmd 命令字符串
+ * @example
+ * ```javascript
+ * editor.execCommand( 'source');
+ * ```
+ */
+
+ /**
+ * 查询当前编辑区域的状态是源码模式还是可视化模式
+ * @command source
+ * @method queryCommandState
+ * @param { String } cmd 命令字符串
+ * @return { int } 如果当前是源码编辑模式,返回1,否则返回0
+ * @example
+ * ```javascript
+ * editor.queryCommandState( 'source' );
+ * ```
+ */
+
+ me.commands["source"] = {
+ execCommand: function() {
+ sourceMode = !sourceMode;
+ if (sourceMode) {
+ bakAddress = me.selection.getRange().createAddress(false, true);
+ me.undoManger && me.undoManger.save(true);
+ if (browser.gecko) {
+ me.body.contentEditable = false;
+ }
+
+ bakCssText = me.iframe.style.cssText;
+ me.iframe.style.cssText +=
+ "position:absolute;left:-32768px;top:-32768px;";
+
+ me.fireEvent("beforegetcontent");
+ var root = UE.htmlparser(me.body.innerHTML);
+ me.filterOutputRule(root);
+ root.traversal(function(node) {
+ if (node.type == "element") {
+ switch (node.tagName) {
+ case "td":
+ case "th":
+ case "caption":
+ if (node.children && node.children.length == 1) {
+ if (node.firstChild().tagName == "br") {
+ node.removeChild(node.firstChild());
+ }
+ }
+ break;
+ case "pre":
+ node.innerText(node.innerText().replace(/ /g, " "));
+ }
+ }
+ });
+
+ me.fireEvent("aftergetcontent");
+
+ var content = root.toHtml(true);
+
+ sourceEditor = createSourceEditor(me.iframe.parentNode);
+
+ sourceEditor.setContent(content);
+
+ orgSetContent = me.setContent;
+
+ me.setContent = function(html) {
+ //这里暂时不触发事件,防止报错
+ var root = UE.htmlparser(html);
+ me.filterInputRule(root);
+ html = root.toHtml();
+ sourceEditor.setContent(html);
+ };
+
+ setTimeout(function() {
+ sourceEditor.select();
+ me.addListener("fullscreenchanged", function() {
+ try {
+ sourceEditor.getCodeMirror().refresh();
+ } catch (e) {}
+ });
+ });
+
+ //重置getContent,源码模式下取值也能是最新的数据
+ oldGetContent = me.getContent;
+ me.getContent = function() {
+ return (
+ sourceEditor.getContent() ||
+ "" + (browser.ie ? "" : " ") + " "
+ );
+ };
+
+ orgFocus = me.focus;
+ orgBlur = me.blur;
+
+ me.focus = function(){
+ sourceEditor.focus();
+ };
+
+ me.blur = function(){
+ orgBlur.call(me);
+ sourceEditor.blur();
+ };
+ } else {
+ me.iframe.style.cssText = bakCssText;
+ var cont =
+ sourceEditor.getContent() ||
+ "" + (browser.ie ? "" : " ") + " ";
+ //处理掉block节点前后的空格,有可能会误命中,暂时不考虑
+ cont = cont.replace(
+ new RegExp("[\\r\\t\\n ]*?(\\w+)\\s*(?:[^>]*)>", "g"),
+ function(a, b) {
+ if (b && !dtd.$inlineWithA[b.toLowerCase()]) {
+ return a.replace(/(^[\n\r\t ]*)|([\n\r\t ]*$)/g, "");
+ }
+ return a.replace(/(^[\n\r\t]*)|([\n\r\t]*$)/g, "");
+ }
+ );
+
+ me.setContent = orgSetContent;
+
+ me.setContent(cont);
+ sourceEditor.dispose();
+ sourceEditor = null;
+ //还原getContent方法
+ me.getContent = oldGetContent;
+ me.focus = orgFocus;
+ me.blur = orgBlur;
+ var first = me.body.firstChild;
+ //trace:1106 都删除空了,下边会报错,所以补充一个p占位
+ if (!first) {
+ me.body.innerHTML = "" + (browser.ie ? "" : " ") + " ";
+ first = me.body.firstChild;
+ }
+
+ //要在ifm为显示时ff才能取到selection,否则报错
+ //这里不能比较位置了
+ me.undoManger && me.undoManger.save(true);
+
+ if (browser.gecko) {
+ var input = document.createElement("input");
+ input.style.cssText = "position:absolute;left:0;top:-32768px";
+
+ document.body.appendChild(input);
+
+ me.body.contentEditable = false;
+ setTimeout(function() {
+ domUtils.setViewportOffset(input, { left: -32768, top: 0 });
+ input.focus();
+ setTimeout(function() {
+ me.body.contentEditable = true;
+ me.selection.getRange().moveToAddress(bakAddress).select(true);
+ domUtils.remove(input);
+ });
+ });
+ } else {
+ //ie下有可能报错,比如在代码顶头的情况
+ try {
+ me.selection.getRange().moveToAddress(bakAddress).select(true);
+ } catch (e) {}
+ }
+ }
+ this.fireEvent("sourcemodechanged", sourceMode);
+ },
+ queryCommandState: function() {
+ return sourceMode | 0;
+ },
+ notNeedUndo: 1
+ };
+ var oldQueryCommandState = me.queryCommandState;
+
+ me.queryCommandState = function(cmdName) {
+ cmdName = cmdName.toLowerCase();
+ if (sourceMode) {
+ //源码模式下可以开启的命令
+ return cmdName in
+ {
+ source: 1,
+ fullscreen: 1
+ }
+ ? 1
+ : -1;
+ }
+ return oldQueryCommandState.apply(this, arguments);
+ };
+
+ if (opt.sourceEditor == "codemirror") {
+ me.addListener("ready", function() {
+ utils.loadFile(
+ document,
+ {
+ src:
+ opt.codeMirrorJsUrl ||
+ opt.UEDITOR_HOME_URL + "third-party/codemirror/codemirror.js",
+ tag: "script",
+ type: "text/javascript",
+ defer: "defer"
+ },
+ function() {
+ if (opt.sourceEditorFirst) {
+ setTimeout(function() {
+ me.execCommand("source");
+ }, 0);
+ }
+ }
+ );
+ utils.loadFile(document, {
+ tag: "link",
+ rel: "stylesheet",
+ type: "text/css",
+ href:
+ opt.codeMirrorCssUrl ||
+ opt.UEDITOR_HOME_URL + "third-party/codemirror/codemirror.css"
+ });
+ });
+ }
+ };
+})();
+
+
+// plugins/enterkey.js
+///import core
+///import plugins/undo.js
+///commands 设置回车标签p或br
+///commandsName EnterKey
+///commandsTitle 设置回车标签p或br
+/**
+ * @description 处理回车
+ * @author zhanyi
+ */
+UE.plugins["enterkey"] = function() {
+ var hTag,
+ me = this,
+ tag = me.options.enterTag;
+ me.addListener("keyup", function(type, evt) {
+ var keyCode = evt.keyCode || evt.which;
+ if (keyCode == 13) {
+ var range = me.selection.getRange(),
+ start = range.startContainer,
+ doSave;
+
+ //修正在h1-h6里边回车后不能嵌套p的问题
+ if (!browser.ie) {
+ if (/h\d/i.test(hTag)) {
+ if (browser.gecko) {
+ var h = domUtils.findParentByTagName(
+ start,
+ [
+ "h1",
+ "h2",
+ "h3",
+ "h4",
+ "h5",
+ "h6",
+ "blockquote",
+ "caption",
+ "table"
+ ],
+ true
+ );
+ if (!h) {
+ me.document.execCommand("formatBlock", false, "");
+ 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]
+ if (rng.endContainer && rng.endContainer.nodeType == 1) {
+ tmpNode = rng.endContainer.childNodes[rng.endOffset];
+ if (tmpNode && domUtils.isBr(tmpNode)) {
+ rng.setEndAfter(tmpNode);
+ }
+ }
+ if (rng.startOffset == 0) {
+ tmpNode = rng.startContainer;
+ if (domUtils.isBoundaryNode(tmpNode, "firstChild")) {
+ tmpNode = rng.endContainer;
+ if (
+ rng.endOffset ==
+ (tmpNode.nodeType == 3
+ ? tmpNode.nodeValue.length
+ : tmpNode.childNodes.length) &&
+ domUtils.isBoundaryNode(tmpNode, "lastChild")
+ ) {
+ me.fireEvent("saveScene");
+ me.body.innerHTML = " " + (browser.ie ? "" : " ") + " ";
+ rng.setStart(me.body.firstChild, 0).setCursor(false, true);
+ me._selectionChange();
+ return;
+ }
+ }
+ }
+ }
+
+ //处理backspace
+ if (keyCode == keymap.Backspace) {
+ rng = me.selection.getRange();
+ collapsed = rng.collapsed;
+ if (me.fireEvent("delkeydown", evt)) {
+ return;
+ }
+ var start, end;
+ //避免按两次删除才能生效的问题
+ if (rng.collapsed && rng.inFillChar()) {
+ start = rng.startContainer;
+
+ if (domUtils.isFillChar(start)) {
+ rng.setStartBefore(start).shrinkBoundary(true).collapse(true);
+ domUtils.remove(start);
+ } else {
+ start.nodeValue = start.nodeValue.replace(
+ new RegExp("^" + domUtils.fillChar),
+ ""
+ );
+ rng.startOffset--;
+ rng.collapse(true).select(true);
+ }
+ }
+
+ //解决选中control元素不能删除的问题
+ if ((start = rng.getClosedNode())) {
+ me.fireEvent("saveScene");
+ rng.setStartBefore(start);
+ domUtils.remove(start);
+ rng.setCursor();
+ me.fireEvent("saveScene");
+ domUtils.preventDefault(evt);
+ return;
+ }
+ //阻止在table上的删除
+ if (!browser.ie) {
+ start = domUtils.findParentByTagName(rng.startContainer, "table", true);
+ end = domUtils.findParentByTagName(rng.endContainer, "table", true);
+ if ((start && !end) || (!start && end) || start !== end) {
+ evt.preventDefault();
+ return;
+ }
+ }
+ }
+ //处理tab键的逻辑
+ if (keyCode == keymap.Tab) {
+ //不处理以下标签
+ var excludeTagNameForTabKey = {
+ ol: 1,
+ ul: 1,
+ table: 1
+ };
+ //处理组件里的tab按下事件
+ if (me.fireEvent("tabkeydown", evt)) {
+ domUtils.preventDefault(evt);
+ return;
+ }
+ var range = me.selection.getRange();
+ me.fireEvent("saveScene");
+ for (
+ var i = 0,
+ txt = "",
+ tabSize = me.options.tabSize || 4,
+ tabNode = me.options.tabNode || " ";
+ i < tabSize;
+ i++
+ ) {
+ txt += tabNode;
+ }
+ var span = me.document.createElement("span");
+ span.innerHTML = txt + domUtils.fillChar;
+ if (range.collapsed) {
+ range.insertNode(span.cloneNode(true).firstChild).setCursor(true);
+ } else {
+ var filterFn = function(node) {
+ return (
+ domUtils.isBlockElm(node) &&
+ !excludeTagNameForTabKey[node.tagName.toLowerCase()]
+ );
+ };
+ //普通的情况
+ start = domUtils.findParent(range.startContainer, filterFn, true);
+ end = domUtils.findParent(range.endContainer, filterFn, true);
+ if (start && end && start === end) {
+ range.deleteContents();
+ range.insertNode(span.cloneNode(true).firstChild).setCursor(true);
+ } else {
+ var bookmark = range.createBookmark();
+ range.enlarge(true);
+ var bookmark2 = range.createBookmark(),
+ current = domUtils.getNextDomNode(bookmark2.start, false, filterFn);
+ while (
+ current &&
+ !(
+ domUtils.getPosition(current, bookmark2.end) &
+ domUtils.POSITION_FOLLOWING
+ )
+ ) {
+ current.insertBefore(
+ span.cloneNode(true).firstChild,
+ current.firstChild
+ );
+ current = domUtils.getNextDomNode(current, false, filterFn);
+ }
+ range.moveToBookmark(bookmark2).moveToBookmark(bookmark).select();
+ }
+ }
+ domUtils.preventDefault(evt);
+ }
+ //trace:1634
+ //ff的del键在容器空的时候,也会删除
+ if (browser.gecko && keyCode == 46) {
+ range = me.selection.getRange();
+ if (range.collapsed) {
+ start = range.startContainer;
+ if (domUtils.isEmptyBlock(start)) {
+ var parent = start.parentNode;
+ while (
+ domUtils.getChildCount(parent) == 1 &&
+ !domUtils.isBody(parent)
+ ) {
+ start = parent;
+ parent = parent.parentNode;
+ }
+ if (start === parent.lastChild) evt.preventDefault();
+ return;
+ }
+ }
+ }
+
+ /* 修复在编辑区域快捷键 (Mac:meta+alt+I; Win:ctrl+shift+I) 打不开 chrome 控制台的问题 */
+ browser.chrome &&
+ me.on("keydown", function(type, e) {
+ var keyCode = e.keyCode || e.which;
+ if (
+ ((e.metaKey && e.altKey) || (e.ctrlKey && e.shiftKey)) &&
+ keyCode == 73
+ ) {
+ return true;
+ }
+ });
+ });
+ me.addListener("keyup", function(type, evt) {
+ var keyCode = evt.keyCode || evt.which,
+ rng,
+ me = this;
+ if (keyCode == keymap.Backspace) {
+ if (me.fireEvent("delkeyup")) {
+ return;
+ }
+ rng = me.selection.getRange();
+ if (rng.collapsed) {
+ var tmpNode,
+ autoClearTagName = ["h1", "h2", "h3", "h4", "h5", "h6"];
+ if (
+ (tmpNode = domUtils.findParentByTagName(
+ rng.startContainer,
+ autoClearTagName,
+ true
+ ))
+ ) {
+ if (domUtils.isEmptyBlock(tmpNode)) {
+ var pre = tmpNode.previousSibling;
+ if (pre && pre.nodeName != "TABLE") {
+ domUtils.remove(tmpNode);
+ rng.setStartAtLast(pre).setCursor(false, true);
+ return;
+ } else {
+ var next = tmpNode.nextSibling;
+ if (next && next.nodeName != "TABLE") {
+ domUtils.remove(tmpNode);
+ rng.setStartAtFirst(next).setCursor(false, true);
+ return;
+ }
+ }
+ }
+ }
+ //处理当删除到body时,要重新给p标签展位
+ if (domUtils.isBody(rng.startContainer)) {
+ var tmpNode = domUtils.createElement(me.document, "p", {
+ innerHTML: browser.ie ? domUtils.fillChar : " "
+ });
+ 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,
+ pressMouseLeft = e.buttons === undefined ? e.which === 1 : e.buttons === 1;
+ 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 && pressMouseLeft) {
+ 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);
+
+ var doc = me.editor.document;
+ domUtils.setStyles(resizer, {
+ width: target.width + "px",
+ height: target.height + "px",
+ left:
+ iframePos.x +
+ imgPos.x -
+ (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0) -
+ editorPos.x -
+ parseInt(resizer.style.borderLeftWidth) +
+ "px",
+ top:
+ iframePos.y +
+ imgPos.y -
+ (doc.documentElement.scrollTop || doc.body.scrollTop || 0) -
+ 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.className.indexOf("edui-faked-music") != -1 ||
+ 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) {
+ 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;
+ }
+
+ 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);
+ //适用于在DIV scrollbox中滚动,但页面不滚动的浮动toolbar
+ var scrollBox = document.getElementById("scrollBox");
+ if (scrollBox) {
+ domUtils.un(scrollBox, ['scroll','resize'], 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 = me.ui.getDom("iframeholder").offsetHeight + "px";
+ if (LteIE6) {
+ fixIE6FixedPos();
+ }
+ domUtils.on(window, ["scroll", "resize"], updateFloating);
+ me.addListener("keydown", defer_updateFloating);
+ //适用于在DIV scrollbox中滚动,但页面不滚动的浮动toolbar
+ var scrollBox = document.getElementById("scrollBox");
+ if (scrollBox) {
+ domUtils.on(scrollBox, ['scroll','resize'], 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 "image":
+ str =
+ " ';
+ break;
+ case "embed":
+ str =
+ ' |