mindoc/static/wangEditor/js/wangEditor.js

8585 lines
260 KiB
Go
Raw Normal View History

(function (factory) {
if (typeof window.define === 'function') {
if (window.define.amd) {
// AMD模式
window.define('wangEditor', ["jquery"], factory);
} else if (window.define.cmd) {
// CMD模式
window.define(function (require, exports, module) {
return factory;
});
} else {
// 全局模式
factory(window.jQuery);
}
} else if (typeof module === "object" && typeof module.exports === "object") {
// commonjs
// 引用 css —— webapck
require('../css/wangEditor.css');
module.exports = factory(
// 传入 jquery ,支持使用 npm 方式或者自己定义jquery的路径
require('jquery')
);
} else {
// 全局模式
factory(window.jQuery);
}
})(function($){
// 验证是否引用jquery
if (!$ || !$.fn || !$.fn.jquery) {
alert('wangEditor.jsjQuery使 wangEditor');
return;
}
// 定义扩展函数
var _e = function (fn) {
var E = window.wangEditor;
if (E) {
// 执行传入的函数
fn(E, $);
}
};
// 定义构造函数
(function (window, $) {
if (window.wangEditor) {
// 重复引用
alert(' wangEditor.js wangEditor.min.js ');
return;
}
// 编辑器(整体)构造函数
var E = function (elem) {
// 支持 id 和 element 两种形式
if (typeof elem === 'string') {
elem = '#' + elem;
}
// ---------------获取基本节点------------------
var $elem = $(elem);
if ($elem.length !== 1) {
return;
}
var nodeName = $elem[0].nodeName;
if (nodeName !== 'TEXTAREA' && nodeName !== 'DIV') {
// 只能是 textarea 和 div ,其他类型的元素不行
return;
}
this.valueNodeName = nodeName.toLowerCase();
this.$valueContainer = $elem;
// 记录 elem 的 prev 和 parent最后渲染 editor 要用到)
this.$prev = $elem.prev();
this.$parent = $elem.parent();
// ------------------初始化------------------
this.init();
};
E.fn = E.prototype;
E.$body = $('body');
E.$document = $(document);
E.$window = $(window);
E.userAgent = navigator.userAgent;
E.getComputedStyle = window.getComputedStyle;
E.w3cRange = typeof document.createRange === 'function';
E.hostname = location.hostname.toLowerCase();
E.websiteHost = 'wangeditor.github.io|www.wangeditor.com|wangeditor.coding.me';
E.isOnWebsite = E.websiteHost.indexOf(E.hostname) >= 0;
E.docsite = 'http://www.kancloud.cn/wangfupeng/wangeditor2/113961';
// 暴露给全局对象
window.wangEditor = E;
// 注册 plugin 事件,用于用户自定义插件
// 用户在引用 wangEditor.js 之后,还可以通过 E.plugin() 注入自定义函数,
// 该函数将会在 editor.create() 方法的最后一步执行
E.plugin = function (fn) {
if (!E._plugins) {
E._plugins = [];
}
if (typeof fn === 'function') {
E._plugins.push(fn);
}
};
})(window, $);
// editor 绑定事件
_e(function (E, $) {
E.fn.init = function () {
// 初始化 editor 默认配置
this.initDefaultConfig();
// 增加container
this.addEditorContainer();
// 增加编辑区域
this.addTxt();
// 增加menuContainer
this.addMenuContainer();
// 初始化菜单集合
this.menus = {};
// 初始化commandHooks
this.commandHooks();
};
});
// editor api
_e(function (E, $) {
// 预定义 ready 事件
E.fn.ready = function (fn) {
if (!this.readyFns) {
this.readyFns = [];
}
this.readyFns.push(fn);
};
// 处理ready事件
E.fn.readyHeadler = function () {
var fns = this.readyFns;
while (fns.length) {
fns.shift().call(this);
}
};
// 更新内容到 $valueContainer
E.fn.updateValue = function () {
var editor = this;
var $valueContainer = editor.$valueContainer;
var $txt = editor.txt.$txt;
if ($valueContainer === $txt) {
// 传入生成编辑器的div即是编辑区域
return;
}
var value = $txt.html();
$valueContainer.val(value);
};
// 获取初始化的内容
E.fn.getInitValue = function () {
var editor = this;
var $valueContainer = editor.$valueContainer;
var currentValue = '';
var nodeName = editor.valueNodeName;
if (nodeName === 'div') {
currentValue = $valueContainer.html();
} else if (nodeName === 'textarea') {
currentValue = $valueContainer.val();
}
return currentValue;
};
// 触发菜单updatestyle
E.fn.updateMenuStyle = function () {
var menus = this.menus;
$.each(menus, function (k, menu) {
menu.updateSelected();
});
};
// 除了传入的 menuIds其他全部启用
E.fn.enableMenusExcept = function (menuIds) {
if (this._disabled) {
// 编辑器处于禁用状态,则不执行改操作
return;
}
// menuIds参数支持数组和字符串
menuIds = menuIds || [];
if (typeof menuIds === 'string') {
menuIds = [menuIds];
}
$.each(this.menus, function (k, menu) {
if (menuIds.indexOf(k) >= 0) {
return;
}
menu.disabled(false);
});
};
// 除了传入的 menuIds其他全部禁用
E.fn.disableMenusExcept = function (menuIds) {
if (this._disabled) {
// 编辑器处于禁用状态,则不执行改操作
return;
}
// menuIds参数支持数组和字符串
menuIds = menuIds || [];
if (typeof menuIds === 'string') {
menuIds = [menuIds];
}
$.each(this.menus, function (k, menu) {
if (menuIds.indexOf(k) >= 0) {
return;
}
menu.disabled(true);
});
};
// 隐藏所有 dropPanel droplist modal
E.fn.hideDropPanelAndModal = function () {
var menus = this.menus;
$.each(menus, function (k, menu) {
var m = menu.dropPanel || menu.dropList || menu.modal;
if (m && m.hide) {
m.hide();
}
});
};
});
// selection range API
_e(function (E, $) {
// 用到 w3c range 的函数,如果检测到浏览器不支持 w3c range则赋值为空函数
var ieRange = !E.w3cRange;
function emptyFn() {}
// 设置或读取当前的range
E.fn.currentRange = function (cr){
if (cr) {
this._rangeData = cr;
} else {
return this._rangeData;
}
};
// 将当前选区折叠
E.fn.collapseRange = function (range, opt) {
// opt 参数说明:'start'-折叠到开始; 'end'-折叠到结束
opt = opt || 'end';
opt = opt === 'start' ? true : false;
range = range || this.currentRange();
if (range) {
// 合并,保存
range.collapse(opt);
this.currentRange(range);
}
};
// 获取选区的文字
E.fn.getRangeText = ieRange ? emptyFn : function (range) {
range = range || this.currentRange();
if (!range) {
return;
}
return range.toString();
};
// 获取选区对应的DOM对象
E.fn.getRangeElem = ieRange ? emptyFn : function (range) {
range = range || this.currentRange();
var dom = range.commonAncestorContainer;
if (dom.nodeType === 1) {
return dom;
} else {
return dom.parentNode;
}
};
// 选区内容是否为空?
E.fn.isRangeEmpty = ieRange ? emptyFn : function (range) {
range = range || this.currentRange();
if (range && range.startContainer) {
if (range.startContainer === range.endContainer) {
if (range.startOffset === range.endOffset) {
return true;
}
}
}
return false;
};
// 保存选区数据
E.fn.saveSelection = ieRange ? emptyFn : function (range) {
var self = this,
_parentElem,
selection,
txt = self.txt.$txt.get(0);
if (range) {
_parentElem = range.commonAncestorContainer;
} else {
selection = document.getSelection();
if (selection.getRangeAt && selection.rangeCount) {
range = document.getSelection().getRangeAt(0);
_parentElem = range.commonAncestorContainer;
}
}
// 确定父元素一定要包含在编辑器区域内
if (_parentElem && ($.contains(txt, _parentElem) || txt === _parentElem) ) {
// 保存选择区域
self.currentRange(range);
}
};
// 恢复选中区域
E.fn.restoreSelection = ieRange ? emptyFn : function (range) {
var selection;
range = range || this.currentRange();
if (!range) {
return;
}
// 使用 try catch 来防止 IE 某些情况报错
try {
selection = document.getSelection();
selection.removeAllRanges();
selection.addRange(range);
} catch (ex) {
E.error(' editor.restoreSelection IE使');
}
};
// 根据elem恢复选区
E.fn.restoreSelectionByElem = ieRange ? emptyFn : function (elem, opt) {
// opt参数说明'start'-折叠到开始,'end'-折叠到结束,'all'-全部选中
if (!elem) {
return;
}
opt = opt || 'end'; // 默认为折叠到结束
// 根据elem获取选区
this.setRangeByElem(elem);
// 根据 opt 折叠选区
if (opt === 'start') {
this.collapseRange(this.currentRange(), 'start');
}
if (opt === 'end') {
this.collapseRange(this.currentRange(), 'end');
}
// 恢复选区
this.restoreSelection();
};
// 初始化选区
E.fn.initSelection = ieRange ? emptyFn : function () {
var editor = this;
if( editor.currentRange() ){
//如果currentRange有值则不用再初始化
return;
}
var range;
var $txt = editor.txt.$txt;
var $firstChild = $txt.children().first();
if ($firstChild.length) {
editor.restoreSelectionByElem($firstChild.get(0));
}
};
// 根据元素创建选区
E.fn.setRangeByElem = ieRange ? emptyFn : function (elem) {
var editor = this;
var txtElem = editor.txt.$txt.get(0);
if (!elem || !$.contains(txtElem, elem)) {
return;
}
// 找到elem的第一个 textNode 和 最后一个 textNode
var firstTextNode = elem.firstChild;
while (firstTextNode) {
if (firstTextNode.nodeType === 3) {
break;
}
// 继续向下
firstTextNode = firstTextNode.firstChild;
}
var lastTextNode = elem.lastChild;
while (lastTextNode) {
if (lastTextNode.nodeType === 3) {
break;
}
// 继续向下
lastTextNode = lastTextNode.lastChild;
}
var range = document.createRange();
if (firstTextNode && lastTextNode) {
// 说明 elem 有内容,能取到子元素
range.setStart(firstTextNode, 0);
range.setEnd(lastTextNode, lastTextNode.textContent.length);
} else {
// 说明 elem 无内容
range.setStart(elem, 0);
range.setEnd(elem, 0);
}
// 保存选区
editor.saveSelection(range);
};
});
// selection range API - IE8及以下
_e(function (E, $) {
if (E.w3cRange) {
// 说明支持 W3C 的range方法
return;
}
// -----------------IE8时需要重写以下方法-------------------
// 获取选区的文字
E.fn.getRangeText = function (range) {
range = range || this.currentRange();
if (!range) {
return;
}
return range.text;
};
// 获取选区对应的DOM对象
E.fn.getRangeElem = function (range) {
range = range || this.currentRange();
if (!range) {
return;
}
var dom = range.parentElement();
if (dom.nodeType === 1) {
return dom;
} else {
return dom.parentNode;
}
};
// 选区内容是否为空?
E.fn.isRangeEmpty = function (range) {
range = range || this.currentRange();
if (range && range.text) {
return false;
}
return true;
};
// 保存选区数据
E.fn.saveSelection = function (range) {
var self = this,
_parentElem,
selection,
txt = self.txt.$txt.get(0);
if (range) {
_parentElem = range.parentElement();
} else {
range = document.selection.createRange();
if(typeof range.parentElement === 'undefined'){
//IE6、7中insertImage后会执行此处
//由于找不到range.parentElement所以干脆将_parentElem赋值为null
_parentElem = null;
}else{
_parentElem = range.parentElement();
}
}
// 确定父元素一定要包含在编辑器区域内
if (_parentElem && ($.contains(txt, _parentElem) || txt === _parentElem) ) {
// 保存选择区域
self.currentRange(range);
}
};
// 恢复选中区域
E.fn.restoreSelection = function (currentRange){
var editor = this,
selection,
range;
currentRange = currentRange || editor.currentRange();
if(!currentRange){
return;
}
range = document.selection.createRange();
try {
// 此处plupload上传上传图片时IE8-会报一个『参数无效』的错误
range.setEndPoint('EndToEnd', currentRange);
} catch (ex) {
}
if(currentRange.text.length === 0){
try {
// IE8 插入表情会报错
range.collapse(false);
} catch (ex) {
}
}else{
range.setEndPoint('StartToStart', currentRange);
}
range.select();
};
});
// editor command hooks
_e(function (E, $) {
E.fn.commandHooks = function () {
var editor = this;
var commandHooks = {};
// insertHtml
commandHooks.insertHtml = function (html) {
var $elem = $(html);
var rangeElem = editor.getRangeElem();
var targetElem;
targetElem = editor.getLegalTags(rangeElem);
if (!targetElem) {
return;
}
$(targetElem).after($elem);
};
// 保存到对象
editor.commandHooks = commandHooks;
};
});
// editor command API
_e(function (E, $) {
// 基本命令
E.fn.command = function (e, commandName, commandValue, callback) {
var editor = this;
var hooks;
function commandFn() {
if (!commandName) {
return;
}
if (editor.queryCommandSupported(commandName)) {
// 默认命令
document.execCommand(commandName, false, commandValue);
} else {
// hooks 命令
hooks = editor.commandHooks;
if (commandName in hooks) {
hooks[commandName](commandValue);
}
}
}
this.customCommand(e, commandFn, callback);
};
// 针对一个elem对象执行基础命令
E.fn.commandForElem = function (elemOpt, e, commandName, commandValue, callback) {
// 取得查询elem的查询条件和验证函数
var selector;
var check;
if (typeof elemOpt === 'string') {
selector = elemOpt;
} else {
selector = elemOpt.selector;
check = elemOpt.check;
}
// 查询elem
var rangeElem = this.getRangeElem();
rangeElem = this.getSelfOrParentByName(rangeElem, selector, check);
// 根据elem设置range
if (rangeElem) {
this.setRangeByElem(rangeElem);
}
// 然后执行基础命令
this.command(e, commandName, commandValue, callback);
};
// 自定义命令
E.fn.customCommand = function (e, commandFn, callback) {
var editor = this;
var range = editor.currentRange();
if (!range) {
// 目前没有选区,则无法执行命令
e && e.preventDefault();
return;
}
// 记录内容,以便撤销(执行命令之前就要记录)
editor.undoRecord();
// 恢复选区(有 range 参数)
this.restoreSelection(range);
// 执行命令事件
commandFn.call(editor);
// 保存选区无参数要从浏览器直接获取range信息
this.saveSelection();
// 重新恢复选区无参数要取得刚刚从浏览器得到的range信息
this.restoreSelection();
// 执行 callback
if (callback && typeof callback === 'function') {
callback.call(editor);
}
// 最后插入空行
editor.txt.insertEmptyP();
// 包裹暴露的img和text
editor.txt.wrapImgAndText();
// 更新内容
editor.updateValue();
// 更新菜单样式
editor.updateMenuStyle();
// 隐藏 dropPanel dropList modal 设置 200ms 间隔
function hidePanelAndModal() {
editor.hideDropPanelAndModal();
}
setTimeout(hidePanelAndModal, 200);
if (e) {
e.preventDefault();
}
};
// 封装 document.queryCommandValue 函数
// IE8 直接执行偶尔会报错,因此直接用 try catch 封装一下
E.fn.queryCommandValue = function (commandName) {
var result = '';
try {
result = document.queryCommandValue(commandName);
} catch (ex) {
}
return result;
};
// 封装 document.queryCommandState 函数
// IE8 直接执行偶尔会报错,因此直接用 try catch 封装一下
E.fn.queryCommandState = function (commandName) {
var result = false;
try {
result = document.queryCommandState(commandName);
} catch (ex) {
}
return result;
};
// 封装 document.queryCommandSupported 函数
E.fn.queryCommandSupported = function (commandName) {
var result = false;
try {
result = document.queryCommandSupported(commandName);
} catch (ex) {
}
return result;
};
});
// dom selector
_e(function (E, $) {
var matchesSelector;
// matchesSelector hook
function _matchesSelectorForIE(selector) {
var elem = this;
var $elems = $(selector);
var result = false;
// 用jquery查找 selector 所有对象,如果其中有一个和传入 elem 相同,则证明 elem 符合 selector
$elems.each(function () {
if (this === elem) {
result = true;
return false;
}
});
return result;
}
// 从当前的elem往上去查找合法标签 如 p head table blockquote ul ol 等
E.fn.getLegalTags = function (elem) {
var legalTags = this.config.legalTags;
if (!legalTags) {
E.error(' legalTags ');
return;
}
return this.getSelfOrParentByName(elem, legalTags);
};
// 根据条件,查询自身或者父元素,符合即返回
E.fn.getSelfOrParentByName = function (elem, selector, check) {
if (!elem || !selector) {
return;
}
if (!matchesSelector) {
// 定义 matchesSelector 函数
matchesSelector = elem.webkitMatchesSelector ||
elem.mozMatchesSelector ||
elem.oMatchesSelector ||
elem.matchesSelector;
}
if (!matchesSelector) {
// 如果浏览器本身不支持 matchesSelector 则使用自定义的hook
matchesSelector = _matchesSelectorForIE;
}
var txt = this.txt.$txt.get(0);
while (elem && txt !== elem && $.contains(txt, elem)) {
if (matchesSelector.call(elem, selector)) {
// 符合 selector 查询条件
if (!check) {
// 没有 check 验证函数,直接返回即可
return elem;
}
if (check(elem)) {
// 如果有 check 验证函数,还需 check 函数的确认
return elem;
}
}
// 如果上一步没经过验证,则将跳转到父元素
elem = elem.parentNode;
}
return;
};
});
// undo redo
_e(function (E, $) {
var length = 20; // 缓存的最大长度
function _getRedoList(editor) {
if (editor._redoList == null) {
editor._redoList = [];
}
return editor._redoList;
}
function _getUndoList(editor) {
if (editor._undoList == null) {
editor._undoList = [];
}
return editor._undoList;
}
// 数据处理
function _handle(editor, data, type) {
// var range = data.range;
// var range2 = range.cloneRange && range.cloneRange();
var val = data.val;
var html = editor.txt.$txt.html();
if(val == null) {
return;
}
if (val === html) {
if (type === 'redo') {
editor.redo();
return;
} else if (type === 'undo') {
editor.undo();
return;
} else {
return;
}
}
// 保存数据
editor.txt.$txt.html(val);
// 更新数据到textarea有必要的话
editor.updateValue();
// onchange 事件
if (editor.onchange && typeof editor.onchange === 'function') {
editor.onchange.call(editor);
}
// ?????
// 注释:$txt 被重新赋值之后range会被重置cloneRange() 也不好使
// // 重置选区
// if (range2) {
// editor.restoreSelection(range2);
// }
}
// 记录
E.fn.undoRecord = function () {
var editor = this;
var $txt = editor.txt.$txt;
var val = $txt.html();
var undoList = _getUndoList(editor);
var redoList = _getRedoList(editor);
var currentVal = undoList.length ? undoList[0] : '';
if (val === currentVal.val) {
return;
}
// 清空 redolist
if (redoList.length) {
redoList = [];
}
// 添加数据到 undoList
undoList.unshift({
range: editor.currentRange(), // 将当前的range也记录下
val: val
});
// 限制 undoList 长度
if (undoList.length > length) {
undoList.pop();
}
};
// undo 操作
E.fn.undo = function () {
var editor = this;
var undoList = _getUndoList(editor);
var redoList = _getRedoList(editor);
if (!undoList.length) {
return;
}
// 取出 undolist 第一个值,加入 redolist
var data = undoList.shift();
redoList.unshift(data);
// 并修改编辑器的内容
_handle(this, data, 'undo');
};
// redo 操作
E.fn.redo = function () {
var editor = this;
var undoList = _getUndoList(editor);
var redoList = _getRedoList(editor);
if (!redoList.length) {
return;
}
// 取出 redolist 第一个值,加入 undolist
var data = redoList.shift();
undoList.unshift(data);
// 并修改编辑器的内容
_handle(this, data, 'redo');
};
});
// 暴露给用户的 API
_e(function (E, $) {
// 创建编辑器
E.fn.create = function () {
var editor = this;
// 检查 E.$body 是否有值
// 如果在 body 之前引用了 js 文件body 尚未加载,可能没有值
if (!E.$body || E.$body.length === 0) {
E.$body = $('body');
E.$document = $(document);
E.$window = $(window);
}
// 执行 addMenus 之前:
// 1. 允许用户修改 editor.UI 自定义配置UI
// 2. 允许用户通过修改 editor.menus 来自定义配置菜单
// 因此要在 create 时执行,而不是 init
editor.addMenus();
// 渲染
editor.renderMenus();
editor.renderMenuContainer();
editor.renderTxt();
editor.renderEditorContainer();
// 绑定事件
editor.eventMenus();
editor.eventMenuContainer();
editor.eventTxt();
// 处理ready事件
editor.readyHeadler();
// 初始化选区
editor.initSelection();
// $txt 快捷方式
editor.$txt = editor.txt.$txt;
// 执行用户自定义事件,通过 E.ready() 添加
var _plugins = E._plugins;
if (_plugins && _plugins.length) {
$.each(_plugins, function (k, val) {
val.call(editor);
});
}
};
// 禁用编辑器
E.fn.disable = function () {
this.txt.$txt.removeAttr('contenteditable');
this.disableMenusExcept();
// 先禁用,再记录状态
this._disabled = true;
};
// 启用编辑器
E.fn.enable = function () {
// 先解除状态记录,再启用
this._disabled = false;
this.txt.$txt.attr('contenteditable', 'true');
this.enableMenusExcept();
};
// 销毁编辑器
E.fn.destroy = function () {
var self = this;
var $valueContainer = self.$valueContainer;
var $editorContainer = self.$editorContainer;
var valueNodeName = self.valueNodeName;
if (valueNodeName === 'div') {
// div 生成的编辑器
$valueContainer.removeAttr('contenteditable');
$editorContainer.after($valueContainer);
$editorContainer.hide();
} else {
// textarea 生成的编辑器
$valueContainer.show();
$editorContainer.hide();
}
};
// 撤销 销毁编辑器
E.fn.undestroy = function () {
var self = this;
var $valueContainer = self.$valueContainer;
var $editorContainer = self.$editorContainer;
var $menuContainer = self.menuContainer.$menuContainer;
var valueNodeName = self.valueNodeName;
if (valueNodeName === 'div') {
// div 生成的编辑器
$valueContainer.attr('contenteditable', 'true');
$menuContainer.after($valueContainer);
$editorContainer.show();
} else {
// textarea 生成的编辑器
$valueContainer.hide();
$editorContainer.show();
}
};
// 清空内容的快捷方式
E.fn.clear = function () {
var editor = this;
var $txt = editor.txt.$txt;
$txt.html('<p><br></p>');
editor.restoreSelectionByElem($txt.find('p').get(0));
};
});
// menuContainer 构造函数
_e(function (E, $) {
// 定义构造函数
var MenuContainer = function (editor) {
this.editor = editor;
this.init();
};
MenuContainer.fn = MenuContainer.prototype;
// 暴露给 E 即 window.wangEditor
E.MenuContainer = MenuContainer;
});
// MenuContainer.fn bind fn
_e(function (E, $) {
var MenuContainer = E.MenuContainer;
// 初始化
MenuContainer.fn.init = function () {
var self = this;
var $menuContainer = $('<div class="wangEditor-menu-container clearfix"></div>');
self.$menuContainer = $menuContainer;
// change shadow
self.changeShadow();
};
// 编辑区域滚动时增加shadow
MenuContainer.fn.changeShadow = function () {
var $menuContainer = this.$menuContainer;
var editor = this.editor;
var $txt = editor.txt.$txt;
$txt.on('scroll', function () {
if ($txt.scrollTop() > 10) {
$menuContainer.addClass('wangEditor-menu-shadow');
} else {
$menuContainer.removeClass('wangEditor-menu-shadow');
}
});
};
});
// MenuContainer.fn API
_e(function (E, $) {
var MenuContainer = E.MenuContainer;
MenuContainer.fn.render = function () {
var $menuContainer = this.$menuContainer;
var $editorContainer = this.editor.$editorContainer;
$editorContainer.append($menuContainer);
};
// 获取菜单栏的高度
MenuContainer.fn.height = function () {
var $menuContainer = this.$menuContainer;
return $menuContainer.height();
};
// 添加菜单
MenuContainer.fn.appendMenu = function (groupIdx, menu) {
// 判断是否需要新增一个菜单组
this._addGroup(groupIdx);
// 增加菜单(返回 $menuItem
return this._addOneMenu(menu);
};
MenuContainer.fn._addGroup = function (groupIdx) {
var $menuContainer = this.$menuContainer;
var $menuGroup;
if (!this.$currentGroup || this.currentGroupIdx !== groupIdx) {
$menuGroup = $('<div class="menu-group clearfix"></div>');
$menuContainer.append($menuGroup);
this.$currentGroup = $menuGroup;
this.currentGroupIdx = groupIdx;
}
};
MenuContainer.fn._addOneMenu = function (menu) {
var $menuNormal = menu.$domNormal;
var $menuSelected = menu.$domSelected;
var $menuGroup = this.$currentGroup;
var $item = $('<div class="menu-item clearfix"></div>');
$menuSelected.hide();
$item.append($menuNormal).append($menuSelected);
$menuGroup.append($item);
return $item;
};
});
// menu 构造函数
_e(function (E, $) {
// 定义构造函数
var Menu = function (opt) {
this.editor = opt.editor;
this.id = opt.id;
this.title = opt.title;
this.$domNormal = opt.$domNormal;
this.$domSelected = opt.$domSelected || opt.$domNormal;
// document.execCommand 的参数
this.commandName = opt.commandName;
this.commandValue = opt.commandValue;
this.commandNameSelected = opt.commandNameSelected || opt.commandName;
this.commandValueSelected = opt.commandValueSelected || opt.commandValue;
};
Menu.fn = Menu.prototype;
// 暴露给 E 即 window.wangEditor
E.Menu = Menu;
});
// Menu.fn 初始化绑定的事件
_e(function (E, $) {
var Menu = E.Menu;
// 初始化UI
Menu.fn.initUI = function () {
var editor = this.editor;
var uiConfig = editor.UI.menus;
var menuId = this.id;
var menuUI = uiConfig[menuId];
if (this.$domNormal && this.$domSelected) {
// 自定义的菜单中,已经传入了 $dom 无需从配置文件中查找生成
return;
}
if (menuUI == null) {
E.warn('editor.UI "' + menuId + '" UI');
// 必须写成 uiConfig['default'];
// 写成 uiConfig.default IE8会报错
menuUI = uiConfig['default'];
}
// 正常状态
this.$domNormal = $(menuUI.normal);
// 选中状态
if (/^\./.test(menuUI.selected)) {
// 增加一个样式
this.$domSelected = this.$domNormal.clone().addClass(menuUI.selected.slice(1));
} else {
// 一个新的dom对象
this.$domSelected = $(menuUI.selected);
}
};
});
// Menu.fn API
_e(function (E, $) {
var Menu = E.Menu;
// 渲染菜单
Menu.fn.render = function (groupIdx) {
// 渲染UI
this.initUI();
var editor = this.editor;
var menuContainer = editor.menuContainer;
var $menuItem = menuContainer.appendMenu(groupIdx, this);
var onRender = this.onRender;
// 渲染tip
this._renderTip($menuItem);
// 执行 onRender 函数
if (onRender && typeof onRender === 'function') {
onRender.call(this);
}
};
Menu.fn._renderTip = function ($menuItem) {
var self = this;
var editor = self.editor;
var title = self.title;
var $tip = $('<div class="menu-tip"></div>');
// var $triangle = $('<i class="tip-triangle"></i>'); // 小三角
// 计算 tip 宽度
var $tempDiv;
if (!self.tipWidth) {
// 设置一个纯透明的 pabsolute;top:-10000px;不会显示在内容区域)
// 内容赋值为 title 为了计算tip宽度
$tempDiv = $('<p style="opacity:0; filter:Alpha(opacity=0); position:absolute;top:-10000px;">' + title + '</p>');
// 先添加到body计算完再 remove
E.$body.append($tempDiv);
editor.ready(function () {
var editor = this;
var titleWidth = $tempDiv.outerWidth() + 5; // 多出 5px 的冗余
var currentWidth = $tip.outerWidth();
var currentMarginLeft = parseFloat($tip.css('margin-left'), 10);
// 计算完,拿到数据,则弃用
$tempDiv.remove();
$tempDiv = null;
// 重新设置样式
$tip.css({
width: titleWidth,
'margin-left': currentMarginLeft + (currentWidth - titleWidth)/2
});
// 存储
self.tipWidth = titleWidth;
});
}
// $tip.append($triangle);
$tip.append(title);
$menuItem.append($tip);
function show() {
$tip.show();
}
function hide() {
$tip.hide();
}
var timeoutId;
$menuItem.find('a').on('mouseenter', function (e) {
if (!self.active() && !self.disabled()) {
timeoutId = setTimeout(show, 200);
}
}).on('mouseleave', function (e) {
timeoutId && clearTimeout(timeoutId);
hide();
}).on('click', hide);
};
// 绑定事件
Menu.fn.bindEvent = function () {
var self = this;
var $domNormal = self.$domNormal;
var $domSelected = self.$domSelected;
// 试图获取该菜单定义的事件未selected没有则自己定义
var clickEvent = self.clickEvent;
if (!clickEvent) {
clickEvent = function (e) {
// -----------dropPanel dropList modal-----------
var dropObj = self.dropPanel || self.dropList || self.modal;
if (dropObj && dropObj.show) {
if (dropObj.isShowing) {
dropObj.hide();
} else {
dropObj.show();
}
return;
}
// -----------command-----------
var editor = self.editor;
var commandName;
var commandValue;
var selected = self.selected;
if (selected) {
commandName = self.commandNameSelected;
commandValue = self.commandValueSelected;
} else {
commandName = self.commandName;
commandValue = self.commandValue;
}
if (commandName) {
// 执行命令
editor.command(e, commandName, commandValue);
} else {
// 提示
E.warn(' "' + self.id + '" click');
e.preventDefault();
}
};
}
// 获取菜单定义的selected情况下的点击事件
var clickEventSelected = self.clickEventSelected || clickEvent;
// 将事件绑定到菜单dom上
$domNormal.click(function (e) {
if (!self.disabled()) {
clickEvent.call(self, e);
self.updateSelected();
}
e.preventDefault();
});
$domSelected.click(function (e) {
if (!self.disabled()) {
clickEventSelected.call(self, e);
self.updateSelected();
}
e.preventDefault();
});
};
// 更新选中状态
Menu.fn.updateSelected = function () {
var self = this;
var editor = self.editor;
// 试图获取用户自定义的判断事件
var updateSelectedEvent = self.updateSelectedEvent;
if (!updateSelectedEvent) {
// 用户未自定义,则设置默认值
updateSelectedEvent = function () {
var self = this;
var editor = self.editor;
var commandName = self.commandName;
var commandValue = self.commandValue;
if (commandValue) {
if (editor.queryCommandValue(commandName).toLowerCase() === commandValue.toLowerCase()) {
return true;
}
} else if (editor.queryCommandState(commandName)) {
return true;
}
return false;
};
}
// 获取结果
var result = updateSelectedEvent.call(self);
result = !!result;
// 存储结果、显示效果
self.changeSelectedState(result);
};
// 切换选中状态、显示效果
Menu.fn.changeSelectedState = function (state) {
var self = this;
var selected = self.selected;
if (state != null && typeof state === 'boolean') {
if (selected === state) {
// 计算结果和当前的状态一样
return;
}
// 存储结果
self.selected = state;
// 切换菜单的显示
if (state) {
// 选中
self.$domNormal.hide();
self.$domSelected.show();
} else {
// 未选中
self.$domNormal.show();
self.$domSelected.hide();
}
} // if
};
// 点击菜单,显示了 dropPanel modal 时,菜单的状态
Menu.fn.active = function (active) {
if (active == null) {
return this._activeState;
}
this._activeState = active;
};
Menu.fn.activeStyle = function (active) {
var selected = this.selected;
var $dom = this.$domNormal;
var $domSelected = this.$domSelected;
if (active) {
$dom.addClass('active');
$domSelected.addClass('active');
} else {
$dom.removeClass('active');
$domSelected.removeClass('active');
}
// 记录状态 menu hover 时会取状态用
this.active(active);
};
// 菜单的启用和禁用
Menu.fn.disabled = function (opt) {
// 参数为空,取值
if (opt == null) {
return !!this._disabled;
}
if (this._disabled === opt) {
// 要设置的参数值和当前参数只一样,无需再次设置
return;
}
var $dom = this.$domNormal;
var $domSelected = this.$domSelected;
// 设置样式
if (opt) {
$dom.addClass('disable');
$domSelected.addClass('disable');
} else {
$dom.removeClass('disable');
$domSelected.removeClass('disable');
}
// 存储
this._disabled = opt;
};
});
// dropList 构造函数
_e(function (E, $) {
// 定义构造函数
var DropList = function (editor, menu, opt) {
this.editor = editor;
this.menu = menu;
// list 的数据源,格式 {'commandValue': 'title', ...}
this.data = opt.data;
// 要为每个item自定义的模板
this.tpl = opt.tpl;
// 为了执行 editor.commandForElem 而传入的elem查询方式
this.selectorForELemCommand = opt.selectorForELemCommand;
// 执行事件前后的钩子
this.beforeEvent = opt.beforeEvent;
this.afterEvent = opt.afterEvent;
// 初始化
this.init();
};
DropList.fn = DropList.prototype;
// 暴露给 E 即 window.wangEditor
E.DropList = DropList;
});
// dropList fn bind
_e(function (E, $) {
var DropList = E.DropList;
// init
DropList.fn.init = function () {
var self = this;
// 生成dom对象
self.initDOM();
// 绑定command事件
self.bindEvent();
// 声明隐藏的事件
self.initHideEvent();
};
// 初始化dom结构
DropList.fn.initDOM = function () {
var self = this;
var data = self.data;
var tpl = self.tpl || '<span>{#title}</span>';
var $list = $('<div class="wangEditor-drop-list clearfix"></div>');
var itemContent;
var $item;
$.each(data, function (commandValue, title) {
itemContent = tpl.replace(/{#commandValue}/ig, commandValue).replace(/{#title}/ig, title);
$item = $('<a href="#" commandValue="' + commandValue + '"></a>');
$item.append(itemContent);
$list.append($item);
});
self.$list = $list;
};
// 绑定事件
DropList.fn.bindEvent = function () {
var self = this;
var editor = self.editor;
var menu = self.menu;
var commandName = menu.commandName;
var selectorForELemCommand = self.selectorForELemCommand;
var $list = self.$list;
// 执行事件前后的钩子函数
var beforeEvent = self.beforeEvent;
var afterEvent = self.afterEvent;
$list.on('click', 'a[commandValue]', function (e) {
// 正式命令执行之前
if (beforeEvent && typeof beforeEvent === 'function') {
beforeEvent.call(e);
}
// 执行命令
var commandValue = $(e.currentTarget).attr('commandValue');
if (menu.selected && editor.isRangeEmpty() && selectorForELemCommand) {
// 当前处于选中状态,并且选中内容为空
editor.commandForElem(selectorForELemCommand, e, commandName, commandValue);
} else {
// 当前未处于选中状态,或者有选中内容。则执行默认命令
editor.command(e, commandName, commandValue);
}
// 正式命令之后的钩子
if (afterEvent && typeof afterEvent === 'function') {
afterEvent.call(e);
}
});
};
// 点击其他地方,立即隐藏 droplist
DropList.fn.initHideEvent = function () {
var self = this;
// 获取 list elem
var thisList = self.$list.get(0);
E.$body.on('click', function (e) {
if (!self.isShowing) {
return;
}
var trigger = e.target;
// 获取菜单elem
var menu = self.menu;
var menuDom;
if (menu.selected) {
menuDom = menu.$domSelected.get(0);
} else {
menuDom = menu.$domNormal.get(0);
}
if (menuDom === trigger || $.contains(menuDom, trigger)) {
// 说明由本菜单点击触发的
return;
}
if (thisList === trigger || $.contains(thisList, trigger)) {
// 说明由本list点击触发的
return;
}
// 其他情况,隐藏 list
self.hide();
});
E.$window.scroll(function () {
self.hide();
});
E.$window.on('resize', function () {
self.hide();
});
};
});
// dropListfn api
_e(function (E, $) {
var DropList = E.DropList;
// 渲染
DropList.fn._render = function () {
var self = this;
var editor = self.editor;
var $list = self.$list;
// 渲染到页面
editor.$editorContainer.append($list);
// 记录状态
self.rendered = true;
};
// 定位
DropList.fn._position = function () {
var self = this;
var $list = self.$list;
var editor = self.editor;
var menu = self.menu;
var $menuContainer = editor.menuContainer.$menuContainer;
var $menuDom = menu.selected ? menu.$domSelected : menu.$domNormal;
// 注意这里的 offsetParent() 要返回 .menu-item 的 position
// 因为 .menu-item 是 position:relative
var menuPosition = $menuDom.offsetParent().position();
// 取得 menu 的位置、尺寸属性
var menuTop = menuPosition.top;
var menuLeft = menuPosition.left;
var menuHeight = $menuDom.offsetParent().height();
var menuWidth = $menuDom.offsetParent().width();
// 取得 list 的尺寸属性
var listWidth = $list.outerWidth();
// var listHeight = $list.outerHeight();
// 取得 $txt 的尺寸
var txtWidth = editor.txt.$txt.outerWidth();
// ------------开始计算-------------
// 初步计算 list 位置属性
var top = menuTop + menuHeight;
var left = menuLeft + menuWidth/2;
var marginLeft = 0 - menuWidth/2;
// 如果超出了有边界,则要左移(且和右侧有间隙)
var valWithTxt = (left + listWidth) - txtWidth;
if (valWithTxt > -10) {
marginLeft = marginLeft - valWithTxt - 10;
}
// 设置样式
$list.css({
top: top,
left: left,
'margin-left': marginLeft
});
// 如果因为向下滚动而导致菜单fixed则再加一步处理
if (editor._isMenufixed) {
top = top + (($menuContainer.offset().top + $menuContainer.outerHeight()) - $list.offset().top);
// 重新设置top
$list.css({
top: top
});
}
};
// 显示
DropList.fn.show = function () {
var self = this;
var menu = self.menu;
if (!self.rendered) {
// 第一次show之前先渲染
self._render();
}
if (self.isShowing) {
return;
}
var $list = self.$list;
$list.show();
// 定位
self._position();
// 记录状态
self.isShowing = true;
// 菜单状态
menu.activeStyle(true);
};
// 隐藏
DropList.fn.hide = function () {
var self = this;
var menu = self.menu;
if (!self.isShowing) {
return;
}
var $list = self.$list;
$list.hide();
// 记录状态
self.isShowing = false;
// 菜单状态
menu.activeStyle(false);
};
});
// dropPanel 构造函数
_e(function (E, $) {
// 定义构造函数
var DropPanel = function (editor, menu, opt) {
this.editor = editor;
this.menu = menu;
this.$content = opt.$content;
this.width = opt.width || 200;
this.height = opt.height;
this.onRender = opt.onRender;
// init
this.init();
};
DropPanel.fn = DropPanel.prototype;
// 暴露给 E 即 window.wangEditor
E.DropPanel = DropPanel;
});
// dropPanel fn bind
_e(function (E, $) {
var DropPanel = E.DropPanel;
// init
DropPanel.fn.init = function () {
var self = this;
// 生成dom对象
self.initDOM();
// 声明隐藏的事件
self.initHideEvent();
};
// init DOM
DropPanel.fn.initDOM = function () {
var self = this;
var $content = self.$content;
var width = self.width;
var height = self.height;
var $panel = $('<div class="wangEditor-drop-panel clearfix"></div>');
var $triangle = $('<div class="tip-triangle"></div>');
$panel.css({
width: width,
height: height ? height : 'auto'
});
$panel.append($triangle);
$panel.append($content);
// 添加对象数据
self.$panel = $panel;
self.$triangle = $triangle;
};
// 点击其他地方,立即隐藏 dropPanel
DropPanel.fn.initHideEvent = function () {
var self = this;
// 获取 panel elem
var thisPanle = self.$panel.get(0);
E.$body.on('click', function (e) {
if (!self.isShowing) {
return;
}
var trigger = e.target;
// 获取菜单elem
var menu = self.menu;
var menuDom;
if (menu.selected) {
menuDom = menu.$domSelected.get(0);
} else {
menuDom = menu.$domNormal.get(0);
}
if (menuDom === trigger || $.contains(menuDom, trigger)) {
// 说明由本菜单点击触发的
return;
}
if (thisPanle === trigger || $.contains(thisPanle, trigger)) {
// 说明由本panel点击触发的
return;
}
// 其他情况,隐藏 panel
self.hide();
});
E.$window.scroll(function (e) {
self.hide();
});
E.$window.on('resize', function () {
self.hide();
});
};
});
// dropPanel fn api
_e(function (E, $) {
var DropPanel = E.DropPanel;
// 渲染
DropPanel.fn._render = function () {
var self = this;
var onRender = self.onRender;
var editor = self.editor;
var $panel = self.$panel;
// 渲染到页面
editor.$editorContainer.append($panel);
// 渲染后的回调事件
onRender && onRender.call(self);
// 记录状态
self.rendered = true;
};
// 定位
DropPanel.fn._position = function () {
var self = this;
var $panel = self.$panel;
var $triangle = self.$triangle;
var editor = self.editor;
var $menuContainer = editor.menuContainer.$menuContainer;
var menu = self.menu;
var $menuDom = menu.selected ? menu.$domSelected : menu.$domNormal;
// 注意这里的 offsetParent() 要返回 .menu-item 的 position
// 因为 .menu-item 是 position:relative
var menuPosition = $menuDom.offsetParent().position();
// 取得 menu 的位置、尺寸属性
var menuTop = menuPosition.top;
var menuLeft = menuPosition.left;
var menuHeight = $menuDom.offsetParent().height();
var menuWidth = $menuDom.offsetParent().width();
// 取得 panel 的尺寸属性
var panelWidth = $panel.outerWidth();
// var panelHeight = $panel.outerHeight();
// 取得 $txt 的尺寸
var txtWidth = editor.txt.$txt.outerWidth();
// ------------开始计算-------------
// 初步计算 panel 位置属性
var top = menuTop + menuHeight;
var left = menuLeft + menuWidth/2;
var marginLeft = 0 - panelWidth/2;
var marginLeft2 = marginLeft; // 下文用于和 marginLeft 比较来设置三角形tip的位置
// 如果超出了左边界则移动回来要和左侧有10px间隙
if ((0 - marginLeft) > (left - 10)) {
marginLeft = 0 - (left - 10);
}
// 如果超出了有边界则要左移且和右侧有10px间隙
var valWithTxt = (left + panelWidth + marginLeft) - txtWidth;
if (valWithTxt > -10) {
marginLeft = marginLeft - valWithTxt - 10;
}
// 设置样式
$panel.css({
top: top,
left: left,
'margin-left': marginLeft
});
// 如果因为向下滚动而导致菜单fixed则再加一步处理
if (editor._isMenufixed) {
top = top + (($menuContainer.offset().top + $menuContainer.outerHeight()) - $panel.offset().top);
// 重新设置top
$panel.css({
top: top
});
}
// 设置三角形 tip 的位置
$triangle.css({
'margin-left': marginLeft2 - marginLeft - 5
});
};
// focus 第一个 input
DropPanel.fn.focusFirstInput = function () {
var self = this;
var $panel = self.$panel;
$panel.find('input[type=text],textarea').each(function () {
var $input = $(this);
if ($input.attr('disabled') == null) {
$input.focus();
return false;
}
});
};
// 显示
DropPanel.fn.show = function () {
var self = this;
var menu = self.menu;
if (!self.rendered) {
// 第一次show之前先渲染
self._render();
}
if (self.isShowing) {
return;
}
var $panel = self.$panel;
$panel.show();
// 定位
self._position();
// 记录状态
self.isShowing = true;
// 菜单状态
menu.activeStyle(true);
if (E.w3cRange) {
// 高级浏览器
self.focusFirstInput();
} else {
// 兼容 IE8 input placeholder
E.placeholderForIE8($panel);
}
};
// 隐藏
DropPanel.fn.hide = function () {
var self = this;
var menu = self.menu;
if (!self.isShowing) {
return;
}
var $panel = self.$panel;
$panel.hide();
// 记录状态
self.isShowing = false;
// 菜单状态
menu.activeStyle(false);
};
});
// modal 构造函数
_e(function (E, $) {
// 定义构造函数
var Modal = function (editor, menu, opt) {
this.editor = editor;
this.menu = menu;
this.$content = opt.$content;
this.init();
};
Modal.fn = Modal.prototype;
// 暴露给 E 即 window.wangEditor
E.Modal = Modal;
});
// modal fn bind
_e(function (E, $) {
var Modal = E.Modal;
Modal.fn.init = function () {
var self = this;
// 初始化dom
self.initDom();
// 初始化隐藏事件
self.initHideEvent();
};
// 初始化dom
Modal.fn.initDom = function () {
var self = this;
var $content = self.$content;
var $modal = $('<div class="wangEditor-modal"></div>');
var $close = $('<div class="wangEditor-modal-close"><i class="wangeditor-menu-img-cancel-circle"></i></div>');
$modal.append($close);
$modal.append($content);
// 记录数据
self.$modal = $modal;
self.$close = $close;
};
// 初始化隐藏事件
Modal.fn.initHideEvent = function () {
var self = this;
var $close = self.$close;
var modal = self.$modal.get(0);
// 点击 $close 按钮,隐藏
$close.click(function () {
self.hide();
});
// 点击其他部分,隐藏
E.$body.on('click', function (e) {
if (!self.isShowing) {
return;
}
var trigger = e.target;
// 获取菜单elem
var menu = self.menu;
var menuDom;
if (menu) {
if (menu.selected) {
menuDom = menu.$domSelected.get(0);
} else {
menuDom = menu.$domNormal.get(0);
}
if (menuDom === trigger || $.contains(menuDom, trigger)) {
// 说明由本菜单点击触发的
return;
}
}
if (modal === trigger || $.contains(modal, trigger)) {
// 说明由本panel点击触发的
return;
}
// 其他情况,隐藏 panel
self.hide();
});
};
});
// modal fn api
_e(function (E, $) {
var Modal = E.Modal;
// 渲染
Modal.fn._render = function () {
var self = this;
var editor = self.editor;
var $modal = self.$modal;
// $modal的z-index在配置的z-index基础上再 +10
$modal.css('z-index', editor.config.zindex + 10 + '');
// 渲染到body最后面
E.$body.append($modal);
// 记录状态
self.rendered = true;
};
// 定位
Modal.fn._position = function () {
var self = this;
var $modal = self.$modal;
var top = $modal.offset().top;
var width = $modal.outerWidth();
var height = $modal.outerHeight();
var marginLeft = 0 - (width / 2);
var marginTop = 0 - (height / 2);
var sTop = E.$window.scrollTop();
// 保证modal最顶部不超过浏览器上边框
if ((height / 2) > top) {
marginTop = 0 - top;
}
$modal.css({
'margin-left': marginLeft + 'px',
'margin-top': (marginTop + sTop) + 'px'
});
};
// 显示
Modal.fn.show = function () {
var self = this;
var menu = self.menu;
if (!self.rendered) {
// 第一次show之前先渲染
self._render();
}
if (self.isShowing) {
return;
}
// 记录状态
self.isShowing = true;
var $modal = self.$modal;
$modal.show();
// 定位
self._position();
// 激活菜单状态
menu && menu.activeStyle(true);
};
// 隐藏
Modal.fn.hide = function () {
var self = this;
var menu = self.menu;
if (!self.isShowing) {
return;
}
// 记录状态
self.isShowing = false;
// 隐藏
var $modal = self.$modal;
$modal.hide();
// 菜单状态
menu && menu.activeStyle(false);
};
});
// txt 构造函数
_e(function (E, $) {
// 定义构造函数
var Txt = function (editor) {
this.editor = editor;
// 初始化
this.init();
};
Txt.fn = Txt.prototype;
// 暴露给 E 即 window.wangEditor
E.Txt = Txt;
});
// Txt.fn bind fn
_e(function (E, $) {
var Txt = E.Txt;
// 初始化
Txt.fn.init = function () {
var self = this;
var editor = self.editor;
var $valueContainer = editor.$valueContainer;
var currentValue = editor.getInitValue();
var $txt;
if ($valueContainer.get(0).nodeName === 'DIV') {
// 如果传入生成编辑器的元素就是div则直接使用
$txt = $valueContainer;
$txt.addClass("wangEditor-txt");
$txt.attr('contentEditable', 'true');
} else {
// 如果不是div是textarea则创建一个div
$txt = $(
'<div class="wangEditor-txt" contentEditable="true">' +
currentValue +
'</div>'
);
}
// 试图最后插入一个空行ready之后才行
editor.ready(function () {
self.insertEmptyP();
});
self.$txt = $txt;
// 删除时,如果没有内容了,就添加一个 <p><br></p>
self.contentEmptyHandle();
// enter时不能使用 div 换行
self.bindEnterForDiv();
// enter时用 p 包裹 text
self.bindEnterForText();
// tab 插入4个空格
self.bindTabEvent();
// 处理粘贴内容
self.bindPasteFilter();
// $txt.formatText() 方法
self.bindFormatText();
// 定义 $txt.html() 方法
self.bindHtml();
};
// 删除时,如果没有内容了,就添加一个 <p><br></p>
Txt.fn.contentEmptyHandle = function () {
var self = this;
var editor = self.editor;
var $txt = self.$txt;
var $p;
$txt.on('keydown', function (e) {
if (e.keyCode !== 8) {
return;
}
var txtHtml = $.trim($txt.html().toLowerCase());
if (txtHtml === '<p><br></p>') {
// 如果最后还剩余一个空行,就不再继续删除了
e.preventDefault();
return;
}
});
$txt.on('keyup', function (e) {
if (e.keyCode !== 8) {
return;
}
var txtHtml = $.trim($txt.html().toLowerCase());
// ff时用 txtHtml === '<br>' 判断,其他用 !txtHtml 判断
if (!txtHtml || txtHtml === '<br>') {
// 内容空了
$p = $('<p><br/></p>');
$txt.html(''); // 一定要先清空,否则在 ff 下有问题
$txt.append($p);
editor.restoreSelectionByElem($p.get(0));
}
});
};
// enter时不能使用 div 换行
Txt.fn.bindEnterForDiv = function () {
var tags = E.config.legalTags; // 配置中编辑器要求的合法标签,如 p head table blockquote ul ol 等
var self = this;
var editor = self.editor;
var $txt = self.$txt;
var $keydownDivElem;
function divHandler() {
if (!$keydownDivElem) {
return;
}
var $pElem = $('<p>' + $keydownDivElem.html() + '</p>');
$keydownDivElem.after($pElem);
$keydownDivElem.remove();
}
$txt.on('keydown keyup', function (e) {
if (e.keyCode !== 13) {
return;
}
// 查找合法标签
var rangeElem = editor.getRangeElem();
var targetElem = editor.getLegalTags(rangeElem);
var $targetElem;
var $pElem;
if (!targetElem) {
// 没找到合法标签,就去查找 div
targetElem = editor.getSelfOrParentByName(rangeElem, 'div');
if (!targetElem) {
return;
}
$targetElem = $(targetElem);
if (e.type === 'keydown') {
// 异步执行(同步执行会出现问题)
$keydownDivElem = $targetElem;
setTimeout(divHandler, 0);
}
if (e.type === 'keyup') {
// 将 div 的内容移动到 p 里面,并移除 div
$pElem = $('<p>' + $targetElem.html() + '</p>');
$targetElem.after($pElem);
$targetElem.remove();
// 如果是回车结束,将选区定位到行首
editor.restoreSelectionByElem($pElem.get(0), 'start');
}
}
});
};
// enter时用 p 包裹 text
Txt.fn.bindEnterForText = function () {
var self = this;
var $txt = self.$txt;
var handle;
$txt.on('keyup', function (e) {
if (e.keyCode !== 13) {
return;
}
if (!handle) {
handle = function() {
self.wrapImgAndText();
};
}
setTimeout(handle);
});
};
// tab 时插入4个空格
Txt.fn.bindTabEvent = function () {
var self = this;
var editor = self.editor;
var $txt = self.$txt;
$txt.on('keydown', function (e) {
if (e.keyCode !== 9) {
// 只监听 tab 按钮
return;
}
// 如果浏览器支持 insertHtml 则插入4个空格。如果不支持就不管了
if (editor.queryCommandSupported('insertHtml')) {
editor.command(e, 'insertHtml', '&nbsp;&nbsp;&nbsp;&nbsp;');
}
});
};
// 处理粘贴内容
Txt.fn.bindPasteFilter = function () {
var self = this;
var editor = self.editor;
var resultHtml = ''; //存储最终的结果
var $txt = self.$txt;
var legalTags = editor.config.legalTags;
var legalTagArr = legalTags.split(',');
$txt.on('paste', function (e) {
if (!editor.config.pasteFilter) {
// 配置中取消了粘贴过滤
return;
}
var currentNodeName = editor.getRangeElem().nodeName;
if (currentNodeName === 'TD' || currentNodeName === 'TH') {
// 在表格的单元格中粘贴,忽略所有内容。否则会出现异常情况
return;
}
resultHtml = ''; // 先清空 resultHtml
var pasteHtml, $paste, docSplitHtml;
var data = e.clipboardData || e.originalEvent.clipboardData;
var ieData = window.clipboardData;
if (editor.config.pasteText) {
// 只粘贴纯文本
if (data && data.getData) {
// w3c
pasteHtml = data.getData('text/plain');
} else if (ieData && ieData.getData) {
// IE
pasteHtml = ieData.getData('text');
} else {
// 其他情况
return;
}
// 拼接为 <p> 标签
if (pasteHtml) {
resultHtml = '<p>' + pasteHtml + '</p>';
}
} else {
// 粘贴过滤了样式的、只有标签的 html
if (data && data.getData) {
// w3c
// 获取粘贴过来的html
pasteHtml = data.getData('text/html');
// 过滤从 word excel 粘贴过来的乱码
docSplitHtml = pasteHtml.split('</html>');
if (docSplitHtml.length === 2) {
pasteHtml = docSplitHtml[0];
}
if (pasteHtml) {
// 创建dom
$paste = $('<div>' + pasteHtml + '</div>');
// 处理,并将结果存储到 resultHtml 『全局』变量
handle($paste.get(0));
} else {
// 得不到html试图获取text
pasteHtml = data.getData('text/plain');
if (pasteHtml) {
// 替换特殊字符
pasteHtml = pasteHtml.replace(/[ ]/g, '&nbsp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/\n/g, '</p><p>');
// 拼接
resultHtml = '<p>' + pasteHtml + '</p>';
// 查询链接
resultHtml = resultHtml.replace(/<p>(https?:\/\/.*?)<\/p>/ig, function (match, link) {
return '<p><a href="' + link + '" target="_blank">' + link + '</p>';
});
}
}
} else if (ieData && ieData.getData) {
// IE 直接从剪切板中取出纯文本格式
resultHtml = ieData.getData('text');
if (!resultHtml) {
return;
}
// 拼接为 <p> 标签
resultHtml = '<p>' + resultHtml + '</p>';
resultHtml = resultHtml.replace(new RegExp('\n', 'g'), '</p><p>');
} else {
// 其他情况
return;
}
}
// 执行命令
if (resultHtml) {
editor.command(e, 'insertHtml', resultHtml);
// 删除内容为空的 p 和嵌套的 p
self.clearEmptyOrNestP();
}
});
// 处理粘贴的内容
function handle(elem) {
if (!elem || !elem.nodeType || !elem.nodeName) {
return;
}
var $elem;
var nodeName = elem.nodeName.toLowerCase();
var nodeType = elem.nodeType;
var childNodesClone;
// 只处理文本和普通node标签
if (nodeType !== 3 && nodeType !== 1) {
return;
}
$elem = $(elem);
// 如果是容器,则继续深度遍历
if (nodeName === 'div') {
childNodesClone = [];
$.each(elem.childNodes, function (index, item) {
// elem.childNodes 可获取TEXT节点而 $elem.children() 就获取不到
// 先将 elem.childNodes 拷贝一份,一面在循环递归过程中 elem 发生变化
childNodesClone.push(item);
});
// 遍历子元素,执行操作
$.each(childNodesClone, function () {
handle(this);
});
return;
}
if (legalTagArr.indexOf(nodeName) >= 0) {
// 如果是合法标签之内的,则根据元素类型,获取值
resultHtml += getResult(elem);
} else if (nodeType === 3) {
// 如果是文本,则直接插入 p 标签
resultHtml += '<p>' + elem.textContent + '</p>';
} else if (nodeName === 'br') {
// <br>保留
resultHtml += '<br/>';
}
else {
// 忽略的标签
if (['meta', 'style', 'script', 'object', 'form', 'iframe', 'hr'].indexOf(nodeName) >= 0) {
return;
}
// 其他标签,移除属性,插入 p 标签
$elem = $(removeAttrs(elem));
// 注意,这里的 clone() 是必须的,否则会出错
resultHtml += $('<div>').append($elem.clone()).html();
}
}
// 获取元素的结果
function getResult(elem) {
var nodeName = elem.nodeName.toLowerCase();
var $elem;
var htmlForP = '';
var htmlForLi = '';
if (['blockquote'].indexOf(nodeName) >= 0) {
// 直接取出元素text即可
$elem = $(elem);
return '<' + nodeName + '>' + $elem.text() + '</' + nodeName + '>';
} else if (['p', 'h1', 'h2', 'h3', 'h4', 'h5'].indexOf(nodeName) >= 0) {
//p head 取出 text 和链接
elem = removeAttrs(elem);
$elem = $(elem);
htmlForP = $elem.html();
// 剔除 a img 之外的元素
htmlForP = htmlForP.replace(/<.*?>/ig, function (tag) {
if (tag === '</a>' || tag.indexOf('<a ') === 0 || tag.indexOf('<img ') === 0) {
return tag;
} else {
return '';
}
});
return '<' + nodeName + '>' + htmlForP + '</' + nodeName + '>';
} else if (['ul', 'ol'].indexOf(nodeName) >= 0) {
// ul ol元素获取子元素li元素的text link img再拼接
$elem = $(elem);
$elem.children().each(function () {
var $li = $(removeAttrs(this));
var html = $li.html();
html = html.replace(/<.*?>/ig, function (tag) {
if (tag === '</a>' || tag.indexOf('<a ') === 0 || tag.indexOf('<img ') === 0) {
return tag;
} else {
return '';
}
});
htmlForLi += '<li>' + html + '</li>';
});
return '<' + nodeName + '>' + htmlForLi + '</' + nodeName + '>';
} else {
// 其他元素,移除元素属性
$elem = $(removeAttrs(elem));
return $('<div>').append($elem).html();
}
}
// 移除一个元素子元素的attr
function removeAttrs(elem) {
var attrs = elem.attributes || [];
var attrNames = [];
var exception = ['href', 'target', 'src', 'alt', 'rowspan', 'colspan']; //例外情况
// 先存储下elem中所有 attr 的名称
$.each(attrs, function (key, attr) {
if (attr && attr.nodeType === 2) {
attrNames.push(attr.nodeName);
}
});
// 再根据名称删除所有attr
$.each(attrNames, function (key, attr) {
if (exception.indexOf(attr) < 0) {
// 除了 exception 规定的例外情况,删除其他属性
elem.removeAttribute(attr);
}
});
// 递归子节点
var children = elem.childNodes;
if (children.length) {
$.each(children, function (key, value) {
removeAttrs(value);
});
}
return elem;
}
};
// 绑定 $txt.formatText() 方法
Txt.fn.bindFormatText = function () {
var self = this;
var editor = self.editor;
var $txt = self.$txt;
var legalTags = E.config.legalTags;
var legalTagArr = legalTags.split(',');
var length = legalTagArr.length;
var regArr = [];
// 将 E.config.legalTags 配置的有效字符,生成正则表达式
$.each(legalTagArr, function (k, tag) {
var reg = '\>\\s*\<(' + tag + ')\>';
regArr.push(new RegExp(reg, 'ig'));
});
// 增加 li
regArr.push(new RegExp('\>\\s*\<(li)\>', 'ig'));
// 增加 tr
regArr.push(new RegExp('\>\\s*\<(tr)\>', 'ig'));
// 增加 code
regArr.push(new RegExp('\>\\s*\<(code)\>', 'ig'));
// 生成 formatText 方法
$txt.formatText = function () {
var $temp = $('<div>');
var html = $txt.html();
// 去除空格
html = html.replace(/\s*</ig, '<');
// 段落、表格之间换行
$.each(regArr, function (k, reg) {
if (!reg.test(html)) {
return;
}
html = html.replace(reg, function (matchStr, tag) {
return '>\n<' + tag + '>';
});
});
$temp.html(html);
return $temp.text();
};
};
// 定制 $txt.html 方法
Txt.fn.bindHtml = function () {
var self = this;
var editor = self.editor;
var $txt = self.$txt;
var $valueContainer = editor.$valueContainer;
var valueNodeName = editor.valueNodeName;
$txt.html = function (html) {
var result;
if (valueNodeName === 'div') {
// div 生成的编辑器取值、赋值都直接触发jquery的html方法
result = $.fn.html.call($txt, html);
}
// textarea 生成的编辑器则需要考虑赋值时也给textarea赋值
if (html === undefined) {
// 取值直接触发jquery原生html方法
result = $.fn.html.call($txt);
// 替换 html 中src和href属性中的 & 字符。
// 因为 .html() 或者 .innerHTML 会把所有的 & 字符都改成 &amp; 但是 src 和 href 中的要保持 &
result = result.replace(/(href|src)\=\"(.*)\"/igm, function (a, b, c) {
return b + '="' + c.replace('&amp;', '&') + '"';
});
} else {
// 赋值,需要同时给 textarea 赋值
result = $.fn.html.call($txt, html);
$valueContainer.val(html);
}
if (html === undefined) {
return result;
} else {
// 手动触发 change 事件,因为 $txt 监控了 change 事件来判断是否需要执行 editor.onchange
$txt.change();
}
};
};
});
// Txt.fn api
_e(function (E, $) {
var Txt = E.Txt;
var txtChangeEventNames = 'propertychange change click keyup input paste';
// 渲染
Txt.fn.render = function () {
var $txt = this.$txt;
var $editorContainer = this.editor.$editorContainer;
$editorContainer.append($txt);
};
// 计算高度
Txt.fn.initHeight = function () {
var editor = this.editor;
var $txt = this.$txt;
var valueContainerHeight = editor.$valueContainer.height();
var menuHeight = editor.menuContainer.height();
var txtHeight = valueContainerHeight - menuHeight;
// 限制最小为 50px
txtHeight = txtHeight < 50 ? 50 : txtHeight;
$txt.height(txtHeight);
// 记录原始高度
editor.valueContainerHeight = valueContainerHeight;
// 设置 max-height
this.initMaxHeight(txtHeight, menuHeight);
};
// 计算最大高度
Txt.fn.initMaxHeight = function (txtHeight, menuHeight) {
var editor = this.editor;
var $menuContainer = editor.menuContainer.$menuContainer;
var $txt = this.$txt;
var $wrap = $('<div>');
// 需要浏览器支持 max-height否则不管
if (window.getComputedStyle && 'max-height'in window.getComputedStyle($txt.get(0))) {
// 获取 max-height 并判断是否有值
var maxHeight = parseInt(editor.$valueContainer.css('max-height'));
if (isNaN(maxHeight)) {
return;
}
// max-height 和『全屏』暂时有冲突
if (editor.menus.fullscreen) {
E.warn('max-height使使');
return;
}
// 标记
editor.useMaxHeight = true;
// 设置maxheight
$wrap.css({
'max-height': (maxHeight - menuHeight) + 'px',
'overflow-y': 'auto'
});
$txt.css({
'height': 'auto',
'overflow-y': 'visible',
'min-height': txtHeight + 'px'
});
// 滚动式,菜单阴影
$wrap.on('scroll', function () {
if ($txt.parent().scrollTop() > 10) {
$menuContainer.addClass('wangEditor-menu-shadow');
} else {
$menuContainer.removeClass('wangEditor-menu-shadow');
}
});
// 需在编辑器区域外面再包裹一层
$txt.wrap($wrap);
}
};
// 保存选区
Txt.fn.saveSelectionEvent = function () {
var $txt = this.$txt;
var editor = this.editor;
var timeoutId;
var dt = Date.now();
function save() {
editor.saveSelection();
}
// 同步保存选区
function saveSync() {
// 100ms之内不重复保存
if (Date.now() - dt < 100) {
return;
}
dt = Date.now();
save();
}
// 异步保存选区
function saveAync() {
// 节流,防止高频率重复操作
if (timeoutId) {
clearTimeout(timeoutId);
}
timeoutId = setTimeout(save, 300);
}
// txt change 、focus、blur 时随时保存选区
$txt.on(txtChangeEventNames + ' focus blur', function (e) {
// 先同步保存选区,为了让接下来就马上要执行 editor.getRangeElem() 的程序
// 能够获取到正确的 rangeElem
saveSync();
// 再异步保存选区,为了确定更加准确的选区,为后续的操作做准备
saveAync();
});
// 鼠标拖拽选择时,可能会拖拽到编辑器区域外面再松手,此时 $txt 就监听不到 click事件了
$txt.on('mousedown', function () {
$txt.on('mouseleave.saveSelection', function (e) {
// 先同步后异步,如上述注释
saveSync();
saveAync();
// 顺道吧菜单状态也更新了
editor.updateMenuStyle();
});
}).on('mouseup', function () {
$txt.off('mouseleave.saveSelection');
});
};
// 随时更新 value
Txt.fn.updateValueEvent = function () {
var $txt = this.$txt;
var editor = this.editor;
var timeoutId, oldValue;
// 触发 onchange 事件
function doOnchange() {
var val = $txt.html();
if (oldValue === val) {
// 无变化
return;
}
// 触发 onchange 事件
if (editor.onchange && typeof editor.onchange === 'function') {
editor.onchange.call(editor);
}
// 更新内容
editor.updateValue();
// 记录最新内容
oldValue = val;
}
// txt change 时随时更新内容
$txt.on(txtChangeEventNames, function (e) {
// 初始化
if (oldValue == null) {
oldValue = $txt.html();
}
// 监控内容变化(停止操作 100ms 之后立即执行)
if (timeoutId) {
clearTimeout(timeoutId);
}
timeoutId = setTimeout(doOnchange, 100);
});
};
// 随时更新 menustyle
Txt.fn.updateMenuStyleEvent = function () {
var $txt = this.$txt;
var editor = this.editor;
// txt change 时随时更新内容
$txt.on(txtChangeEventNames, function (e) {
editor.updateMenuStyle();
});
};
// 最后插入试图插入 <p><br><p>
Txt.fn.insertEmptyP = function () {
var $txt = this.$txt;
var $children = $txt.children();
if ($children.length === 0) {
$txt.append($('<p><br></p>'));
return;
}
if ($.trim($children.last().html()).toLowerCase() !== '<br>') {
$txt.append($('<p><br></p>'));
}
};
// 将编辑器暴露出来的文字和图片,都用 p 来包裹
Txt.fn.wrapImgAndText = function () {
var $txt = this.$txt;
var $imgs = $txt.children('img');
var txt = $txt[0];
var childNodes = txt.childNodes;
var childrenLength = childNodes.length;
var i, childNode, p;
// 处理图片
$imgs.length && $imgs.each(function () {
$(this).wrap('<p>');
});
// 处理文字
for (i = 0; i < childrenLength; i++) {
childNode = childNodes[i];
if (childNode.nodeType === 3 && childNode.textContent && $.trim(childNode.textContent)) {
$(childNode).wrap('<p>');
}
}
};
// 清空内容为空的<p>,以及重复包裹的<p>在windows下的chrome粘贴文字之后会出现上述情况
Txt.fn.clearEmptyOrNestP = function () {
var $txt = this.$txt;
var $pList = $txt.find('p');
$pList.each(function () {
var $p = $(this);
var $children = $p.children();
var childrenLength = $children.length;
var $firstChild;
var content = $.trim($p.html());
// 内容为空的p
if (!content) {
$p.remove();
return;
}
// 嵌套的p
if (childrenLength === 1) {
$firstChild = $children.first();
if ($firstChild.get(0) && $firstChild.get(0).nodeName === 'P') {
$p.html( $firstChild.html() );
}
}
});
};
// 获取 scrollTop
Txt.fn.scrollTop = function (val) {
var self = this;
var editor = self.editor;
var $txt = self.$txt;
if (editor.useMaxHeight) {
return $txt.parent().scrollTop(val);
} else {
return $txt.scrollTop(val);
}
};
// 鼠标hover时候显示p、head的高度
Txt.fn.showHeightOnHover = function () {
var editor = this.editor;
var $editorContainer = editor.$editorContainer;
var menuContainer = editor.menuContainer;
var $txt = this.$txt;
var $tip = $('<i class="height-tip"><i>');
var isTipInTxt = false;
function addAndShowTip($target) {
if (!isTipInTxt) {
$editorContainer.append($tip);
isTipInTxt = true;
}
var txtTop = $txt.position().top;
var txtHeight = $txt.outerHeight();
var height = $target.height();
var top = $target.position().top;
var marginTop = parseInt($target.css('margin-top'), 10);
var paddingTop = parseInt($target.css('padding-top'), 10);
var marginBottom = parseInt($target.css('margin-bottom'), 10);
var paddingBottom = parseInt($target.css('padding-bottom'), 10);
// 计算初步的结果
var resultHeight = height + paddingTop + marginTop + paddingBottom + marginBottom;
var resultTop = top + menuContainer.height();
// var spaceValue;
// // 判断是否超出下边界
// spaceValue = (resultTop + resultHeight) - (txtTop + txtHeight);
// if (spaceValue > 0) {
// resultHeight = resultHeight - spaceValue;
// }
// // 判断是否超出了下边界
// spaceValue = txtTop > resultTop;
// if (spaceValue) {
// resultHeight = resultHeight - spaceValue;
// top = top + spaceValue;
// }
// 按照最终结果渲染
$tip.css({
height: height + paddingTop + marginTop + paddingBottom + marginBottom,
top: top + menuContainer.height()
});
}
function removeTip() {
if (!isTipInTxt) {
return;
}
$tip.remove();
isTipInTxt = false;
}
$txt.on('mouseenter', 'ul,ol,blockquote,p,h1,h2,h3,h4,h5,table,pre', function (e) {
addAndShowTip($(e.currentTarget));
}).on('mouseleave', function () {
removeTip();
});
};
});
// 工具函数
_e(function (E, $) {
// IE8 [].indexOf()
if(!Array.prototype.indexOf){
//IE低版本不支持 arr.indexOf
Array.prototype.indexOf = function(elem){
var i = 0,
length = this.length;
for(; i<length; i++){
if(this[i] === elem){
return i;
}
}
return -1;
};
//IE低版本不支持 arr.lastIndexOf
Array.prototype.lastIndexOf = function(elem){
var length = this.length;
for(length = length - 1; length >= 0; length--){
if(this[length] === elem){
return length;
}
}
return -1;
};
}
// IE8 Date.now()
if (!Date.now) {
Date.now = function () {
return new Date().valueOf();
};
}
// console.log && console.warn && console.error
var console = window.console;
var emptyFn = function () {};
$.each(['info', 'log', 'warn', 'error'], function (key, value) {
if (console == null) {
E[value] = emptyFn;
} else {
E[value] = function (info) {
// 通过配置来控制打印输出
if (E.config && E.config.printLog) {
console[value]('wangEditor: ' + info);
}
};
}
});
// 获取随机数
E.random = function () {
return Math.random().toString().slice(2);
};
// 浏览器是否支持 placeholder
E.placeholder = 'placeholder' in document.createElement('input');
// 兼容IE8的 input placeholder
E.placeholderForIE8 = function ($container) {
if (E.placeholder) {
return;
}
$container.find('input[placeholder]').each(function () {
var $input = $(this);
var placeholder = $input.attr('placeholder');
if ($input.val() === '') {
$input.css('color', '#666');
$input.val(placeholder);
$input.on('focus.placeholder click.placeholder', function () {
$input.val('');
$input.css('color', '#333');
$input.off('focus.placeholder click.placeholder');
});
}
});
};
});
// 语言包
_e(function (E, $) {
E.langs = {};
// 中文
E.langs['zh-cn'] = {
bold: '',
underline: '线',
italic: '',
forecolor: '',
bgcolor: '',
strikethrough: '线',
eraser: '',
source: '',
quote: '',
fontfamily: '',
fontsize: '',
head: '',
orderlist: '',
unorderlist: '',
alignleft: '',
aligncenter: '',
alignright: '',
link: '',
text: '',
submit: '',
cancel: '',
unlink: '',
table: '',
emotion: '',
img: '',
uploadImg: '',
linkImg: '',
video: '',
'width': '宽',
'height': '高',
location: '',
loading: '',
searchlocation: '',
dynamicMap: '',
clearLocation: '',
langDynamicOneLocation: '',
insertcode: '',
undo: '',
redo: '',
fullscreen: '',
openLink: ''
};
// 英文
E.langs.en = {
bold: 'Bold',
underline: 'Underline',
italic: 'Italic',
forecolor: 'Color',
bgcolor: 'Backcolor',
strikethrough: 'Strikethrough',
eraser: 'Eraser',
source: 'Codeview',
quote: 'Quote',
fontfamily: 'Font family',
fontsize: 'Font size',
head: 'Head',
orderlist: 'Ordered list',
unorderlist: 'Unordered list',
alignleft: 'Align left',
aligncenter: 'Align center',
alignright: 'Align right',
link: 'Insert link',
text: 'Text',
submit: 'Submit',
cancel: 'Cancel',
unlink: 'Unlink',
table: 'Table',
emotion: 'Emotions',
img: 'Image',
uploadImg: 'Upload',
linkImg: 'Link',
video: 'Video',
'width': 'width',
'height': 'height',
location: 'Location',
loading: 'Loading',
searchlocation: 'search',
dynamicMap: 'Dynamic',
clearLocation: 'Clear',
langDynamicOneLocation: 'Only one location in dynamic map',
insertcode: 'Insert Code',
undo: 'Undo',
redo: 'Redo',
fullscreen: 'Full screnn',
openLink: 'open link'
};
});
// 全局配置
_e(function (E, $) {
E.config = {};
// 全屏时的 z-index
E.config.zindex = 10000;
// 是否打印log
E.config.printLog = true;
// 菜单吸顶false - 不吸顶number - 吸顶值为top值
E.config.menuFixed = 0;
// 编辑源码时,过滤 javascript
E.config.jsFilter = true;
// 编辑器允许的标签
E.config.legalTags = 'p,h1,h2,h3,h4,h5,h6,blockquote,table,ul,ol,pre';
// 语言包
E.config.lang = E.langs['zh-cn'];
// 菜单配置
E.config.menus = [
'source',
'|',
'bold',
'underline',
'italic',
'strikethrough',
'eraser',
'forecolor',
'bgcolor',
'|',
'quote',
'fontfamily',
'fontsize',
'head',
'unorderlist',
'orderlist',
'alignleft',
'aligncenter',
'alignright',
'|',
'link',
'unlink',
'table',
'emotion',
'|',
'img',
'video',
'location',
'insertcode',
'|',
'undo',
'redo',
'fullscreen'
];
// 颜色配置
E.config.colors = {
// 'value': 'title'
'#880000': '',
'#800080': '',
'#ff0000': '',
'#ff00ff': '',
'#000080': '',
'#0000ff': '',
'#00ffff': '',
'#008080': '绿',
'#008000': '绿',
'#808000': '',
'#00ff00': '绿',
'#ffcc00': '',
'#808080': '',
'#c0c0c0': '',
'#000000': '',
'#ffffff': ''
};
// 字体
E.config.familys = [
'', '', '', '',
'Arial', 'Verdana', 'Georgia',
'Times New Roman', 'Microsoft JhengHei',
'Trebuchet MS', 'Courier New', 'Impact', 'Comic Sans MS', 'Consolas'
];
// 字号
E.config.fontsizes = {
// 格式:'value': 'title'
1: '12px',
2: '13px',
3: '16px',
4: '18px',
5: '24px',
6: '32px',
7: '48px'
};
// 表情包
E.config.emotionsShow = 'icon'; // 显示项,默认为'icon',也可以配置成'value'
E.config.emotions = {
// 'default': {
// title: '默认',
// data: './emotions.data'
// },
'weibo': {
title: '',
data: [
{
icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/7a/shenshou_thumb.gif',
value: '[]'
},
{
icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/60/horse2_thumb.gif',
value: '[]'
},
{
icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/bc/fuyun_thumb.gif',
value: '[]'
},
{
icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/c9/geili_thumb.gif',
value: '[]'
},
{
icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/f2/wg_thumb.gif',
value: '[]'
},
{
icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/70/vw_thumb.gif',
value: '[]'
},
{
icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/6e/panda_thumb.gif',
value: '[]'
},
{
icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/81/rabbit_thumb.gif',
value: '[]'
},
{
icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/bc/otm_thumb.gif',
value: '[]'
},
{
icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/15/j_thumb.gif',
value: '[]'
},
{
icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/89/hufen_thumb.gif',
value: '[]'
},
{
icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/c4/liwu_thumb.gif',
value: '[]'
},
{
icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/ac/smilea_thumb.gif',
value: '[]'
},
{
icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/0b/tootha_thumb.gif',
value: '[]'
}
]
}
};
// 百度地图的key
E.config.mapAk = 'TVhjYjq1ICT2qqL5LdS8mwas';
// 上传图片的配置
// server地址
E.config.uploadImgUrl = '';
// 超时时间
E.config.uploadTimeout = 20 * 1000;
// 用于存储上传回调事件
E.config.uploadImgFns = {};
// 自定义上传图片的filename
// E.config.uploadImgFileName = 'customFileName';
// 自定义上传,设置为 true 之后,显示上传图标
E.config.customUpload = false;
// 自定义上传的init事件
// E.config.customUploadInit = function () {....};
// 自定义上传时传递的参数(如 token
E.config.uploadParams = {
/* token: 'abcdef12345' */
};
// 自定义上传是的header参数
E.config.uploadHeaders = {
/* 'Accept' : 'text/x-json' */
};
// 跨域上传时传递 cookie默认为 true
E.config.withCredentials = true;
// 隐藏网络图片,默认为 false
E.config.hideLinkImg = false;
// 是否过滤粘贴内容
E.config.pasteFilter = true;
// 是否粘贴纯文本,当 editor.config.pasteFilter === false 时候,此配置将失效
E.config.pasteText = false;
// 插入代码时,默认的语言
E.config.codeDefaultLang = 'javascript';
});
// 全局UI
_e(function (E, $) {
E.UI = {};
// 为菜单自定义配置的UI
E.UI.menus = {
// 这个 default 不加引号,在 IE8 会报错
'default': {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-command"></i></a>',
selected: '.selected'
},
bold: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-bold"></i></a>',
selected: '.selected'
},
underline: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-underline"></i></a>',
selected: '.selected'
},
italic: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-italic"></i></a>',
selected: '.selected'
},
forecolor: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-pencil"></i></a>',
selected: '.selected'
},
bgcolor: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-brush"></i></a>',
selected: '.selected'
},
strikethrough: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-strikethrough"></i></a>',
selected: '.selected'
},
eraser: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-eraser"></i></a>',
selected: '.selected'
},
quote: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-quotes-left"></i></a>',
selected: '.selected'
},
source: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-code"></i></a>',
selected: '.selected'
},
fontfamily: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-font2"></i></a>',
selected: '.selected'
},
fontsize: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-text-height"></i></a>',
selected: '.selected'
},
head: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-header"></i></a>',
selected: '.selected'
},
orderlist: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-list-numbered"></i></a>',
selected: '.selected'
},
unorderlist: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-list-bullet"></i></a>',
selected: '.selected'
},
alignleft: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-align-left"></i></a>',
selected: '.selected'
},
aligncenter: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-align-center"></i></a>',
selected: '.selected'
},
alignright: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-align-right"></i></a>',
selected: '.selected'
},
link: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-link"></i></a>',
selected: '.selected'
},
unlink: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-unlink"></i></a>',
selected: '.selected'
},
table: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-table"></i></a>',
selected: '.selected'
},
emotion: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-happy"></i></a>',
selected: '.selected'
},
img: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-picture"></i></a>',
selected: '.selected'
},
video: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-play"></i></a>',
selected: '.selected'
},
location: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-location"></i></a>',
selected: '.selected'
},
insertcode: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-terminal"></i></a>',
selected: '.selected'
},
undo: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-ccw"></i></a>',
selected: '.selected'
},
redo: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-cw"></i></a>',
selected: '.selected'
},
fullscreen: {
normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-enlarge2"></i></a>',
selected: '<a href="#" tabindex="-1" class="selected"><i class="wangeditor-menu-img-shrink2"></i></a>'
}
};
});
// 对象配置
_e(function (E, $) {
E.fn.initDefaultConfig = function () {
var editor = this;
editor.config = $.extend({}, E.config);
editor.UI = $.extend({}, E.UI);
};
});
// 增加 container
_e(function (E, $) {
E.fn.addEditorContainer = function () {
this.$editorContainer = $('<div class="wangEditor-container"></div>');
};
});
// 增加编辑区域对象
_e(function (E, $) {
E.fn.addTxt = function () {
var editor = this;
var txt = new E.Txt(editor);
editor.txt = txt;
};
});
// 增加menuContainer对象
_e(function (E, $) {
E.fn.addMenuContainer = function () {
var editor = this;
editor.menuContainer = new E.MenuContainer(editor);
};
});
// 增加menus
_e(function (E, $) {
// 存储创建菜单的函数
E.createMenuFns = [];
E.createMenu = function (fn) {
E.createMenuFns.push(fn);
};
// 创建所有菜单
E.fn.addMenus = function () {
var editor = this;
var menuIds = editor.config.menus;
// 检验 menuId 是否在配置中存在
function check(menuId) {
if (menuIds.indexOf(menuId) >= 0) {
return true;
}
return false;
}
// 遍历所有的菜单创建函数,并执行
$.each(E.createMenuFns, function (k, createMenuFn) {
createMenuFn.call(editor, check);
});
};
});
// bold菜单
_e(function (E, $) {
E.createMenu(function (check) {
var menuId = 'bold';
if (!check(menuId)) {
return;
}
var editor = this;
var lang = editor.config.lang;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.bold,
commandName: 'Bold'
});
// 定义选中状态下的click事件
menu.clickEventSelected = function (e) {
var isRangeEmpty = editor.isRangeEmpty();
if (!isRangeEmpty) {
// 如果选区有内容,则执行基础命令
editor.command(e, 'Bold');
} else {
// 如果选区没有内容
editor.commandForElem('b,strong,h1,h2,h3,h4,h5', e, 'Bold');
}
};
// 增加到editor对象中
editor.menus[menuId] = menu;
});
});
// underline菜单
_e(function (E, $) {
E.createMenu(function (check) {
var menuId = 'underline';
if (!check(menuId)) {
return;
}
var editor = this;
var lang = editor.config.lang;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.underline,
commandName: 'Underline'
});
// 定义选中状态下的click事件
menu.clickEventSelected = function (e) {
var isRangeEmpty = editor.isRangeEmpty();
if (!isRangeEmpty) {
// 如果选区有内容,则执行基础命令
editor.command(e, 'Underline');
} else {
// 如果选区没有内容
editor.commandForElem('u,a', e, 'Underline');
}
};
// 增加到editor对象中
editor.menus[menuId] = menu;
});
});
// italic 菜单
_e(function (E, $) {
E.createMenu(function (check) {
var menuId = 'italic';
if (!check(menuId)) {
return;
}
var editor = this;
var lang = editor.config.lang;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.italic,
commandName: 'Italic'
});
// 定义选中状态下的click事件
menu.clickEventSelected = function (e) {
var isRangeEmpty = editor.isRangeEmpty();
if (!isRangeEmpty) {
// 如果选区有内容,则执行基础命令
editor.command(e, 'Italic');
} else {
// 如果选区没有内容
editor.commandForElem('i', e, 'Italic');
}
};
// 增加到editor对象中
editor.menus[menuId] = menu;
});
});
// forecolor 菜单
_e(function (E, $) {
E.createMenu(function (check) {
var menuId = 'forecolor';
if (!check(menuId)) {
return;
}
var editor = this;
var lang = editor.config.lang;
var configColors = editor.config.colors;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.forecolor
});
// 创建 dropPanel
var $content = $('<div></div>');
$.each(configColors, function (k, v) {
$content.append(
[
'<a href="#" class="color-item"',
' title="' + v + '" commandValue="' + k + '" ',
' style="color: ' + k + '" ',
'><i class="wangeditor-menu-img-pencil"></i></a>'
].join('')
);
});
$content.on('click', 'a[commandValue]', function (e) {
// 执行命令
var $elem = $(this);
var commandValue = $elem.attr('commandValue');
if (menu.selected && editor.isRangeEmpty()) {
// 当前处于选中状态,并且选中内容为空
editor.commandForElem('font[color]', e, 'forecolor', commandValue);
} else {
// 当前未处于选中状态,或者有选中内容。则执行默认命令
editor.command(e, 'forecolor', commandValue);
}
});
menu.dropPanel = new E.DropPanel(editor, menu, {
$content: $content,
width: 125
});
// 定义 update selected 事件
menu.updateSelectedEvent = function () {
var rangeElem = editor.getRangeElem();
rangeElem = editor.getSelfOrParentByName(rangeElem, 'font[color]');
if (rangeElem) {
return true;
}
return false;
};
// 增加到editor对象中
editor.menus[menuId] = menu;
});
});
// bgcolor 菜单
_e(function (E, $) {
E.createMenu(function (check) {
var menuId = 'bgcolor';
if (!check(menuId)) {
return;
}
var editor = this;
var lang = editor.config.lang;
var configColors = editor.config.colors;
// 检查元素是否有 background-color: 内联样式
function checkElemFn(elem) {
var cssText;
if (elem && elem.style && elem.style.cssText != null) {
cssText = elem.style.cssText;
if (cssText && cssText.indexOf('background-color:') >= 0) {
return true;
}
}
return false;
}
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.bgcolor
});
// 创建 dropPanel
var $content = $('<div></div>');
$.each(configColors, function (k, v) {
$content.append(
[
'<a href="#" class="color-item"',
' title="' + v + '" commandValue="' + k + '" ',
' style="color: ' + k + '" ',
'><i class="wangeditor-menu-img-brush"></i></a>'
].join('')
);
});
$content.on('click', 'a[commandValue]', function (e) {
// 执行命令
var $elem = $(this);
var commandValue = $elem.attr('commandValue');
if (menu.selected && editor.isRangeEmpty()) {
// 当前处于选中状态,并且选中内容为空。使用 commandForElem 执行命令
editor.commandForElem({
selector: 'span,font',
check: checkElemFn
}, e, 'BackColor', commandValue);
} else {
// 当前未处于选中状态,或者有选中内容。则执行默认命令
editor.command(e, 'BackColor', commandValue);
}
});
menu.dropPanel = new E.DropPanel(editor, menu, {
$content: $content,
width: 125
});
// 定义 update selected 事件
menu.updateSelectedEvent = function () {
var rangeElem = editor.getRangeElem();
rangeElem = editor.getSelfOrParentByName(rangeElem, 'span,font', checkElemFn);
if (rangeElem) {
return true;
}
return false;
};
// 增加到editor对象中
editor.menus[menuId] = menu;
});
});
// strikethrough 菜单
_e(function (E, $) {
E.createMenu(function (check) {
var menuId = 'strikethrough';
if (!check(menuId)) {
return;
}
var editor = this;
var lang = editor.config.lang;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.strikethrough,
commandName: 'StrikeThrough'
});
// 定义选中状态下的click事件
menu.clickEventSelected = function (e) {
var isRangeEmpty = editor.isRangeEmpty();
if (!isRangeEmpty) {
// 如果选区有内容,则执行基础命令
editor.command(e, 'StrikeThrough');
} else {
// 如果选区没有内容
editor.commandForElem('strike', e, 'StrikeThrough');
}
};
// 增加到editor对象中
editor.menus[menuId] = menu;
});
});
// eraser 菜单
_e(function (E, $) {
E.createMenu(function (check) {
var menuId = 'eraser';
if (!check(menuId)) {
return;
}
var editor = this;
var lang = editor.config.lang;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.eraser,
commandName: 'RemoveFormat'
});
// 定义点击事件
menu.clickEvent = function (e) {
var isRangeEmpty = editor.isRangeEmpty();
if (!isRangeEmpty) {
// 选区不是空的,则执行默认命令
editor.command(e, 'RemoveFormat');
return;
}
var $clearElem;
// 自定义的命令函数
function commandFn() {
var editor = this;
var rangeElem;
var pElem, $pElem;
var quoteElem, $quoteElem;
var listElem, $listElem;
// 获取选区 elem
rangeElem = editor.getRangeElem();
// 第一步,获取 quote 父元素
quoteElem = editor.getSelfOrParentByName(rangeElem, 'blockquote');
if (quoteElem) {
$quoteElem = $(quoteElem);
$clearElem = $('<p>' + $quoteElem.text() + '</p>');
$quoteElem.after($clearElem).remove();
}
// 第二步,获取 p h 父元素
pElem = editor.getSelfOrParentByName(rangeElem, 'p,h1,h2,h3,h4,h5');
if (pElem) {
$pElem = $(pElem);
$clearElem = $('<p>' + $pElem.text() + '</p>');
$pElem.after($clearElem).remove();
}
// 第三步获取list
listElem = editor.getSelfOrParentByName(rangeElem, 'ul,ol');
if (listElem) {
$listElem = $(listElem);
$clearElem = $('<p>' + $listElem.text() + '</p>');
$listElem.after($clearElem).remove();
}
}
// 自定义 callback 事件
function callback() {
// callback中设置range为clearElem
var editor = this;
if ($clearElem) {
editor.restoreSelectionByElem($clearElem.get(0));
}
}
// 执行自定义命令
editor.customCommand(e, commandFn, callback);
};
// 增加到editor对象中
editor.menus[menuId] = menu;
});
});
// source 菜单
_e(function (E, $) {
E.createMenu(function (check) {
var menuId = 'source';
if (!check(menuId)) {
return;
}
var editor = this;
var lang = editor.config.lang;
var txtHtml;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.source
});
menu.isShowCode = false;
// 更新内容
function updateValue() {
var $code = menu.$codeTextarea;
var $txt = editor.txt.$txt;
var value = $.trim($code.val()); // 取值
if (!value) {
value = '<p><br></p>';
}
// 过滤js代码
if (editor.config.jsFilter) {
value = value.replace(/<script[\s\S]*?<\/script>/ig, '');
}
// 赋值
try {
$txt.html(value);
} catch (ex) {
// 更新 html 源码出错,一般都是取消了 js 过滤之后js报错导致的
}
}
// 定义click事件
menu.clickEvent = function (e) {
var self = this;
var editor = self.editor;
var $txt = editor.txt.$txt;
var txtOuterHeight = $txt.outerHeight();
var txtHeight = $txt.height();
if (!self.$codeTextarea) {
self.$codeTextarea = $('<textarea class="code-textarea"></textarea>');
}
var $code = self.$codeTextarea;
$code.css({
height: txtHeight,
'margin-top': txtOuterHeight - txtHeight
});
// 赋值
$code.val($txt.html());
// 监控变化
$code.on('change', function (e) {
updateValue();
});
// 渲染
$txt.after($code).hide();
$code.show();
// 更新状态
menu.isShowCode = true;
// 执行 updateSelected 事件
this.updateSelected();
// 禁用其他菜单
editor.disableMenusExcept('source');
// 记录当前html值
txtHtml = $txt.html();
};
// 定义选中状态下的click事件
menu.clickEventSelected = function (e) {
var self = this;
var editor = self.editor;
var $txt = editor.txt.$txt;
var $code = self.$codeTextarea;
var value;
if (!$code) {
return;
}
// 更新内容
updateValue();
// 渲染
$code.after($txt).hide();
$txt.show();
// 更新状态
menu.isShowCode = false;
// 执行 updateSelected 事件
this.updateSelected();
// 启用其他菜单
editor.enableMenusExcept('source');
// 判断是否执行 onchange 事件
if ($txt.html() !== txtHtml) {
if (editor.onchange && typeof editor.onchange === 'function') {
editor.onchange.call(editor);
}
}
};
// 定义切换选中状态事件
menu.updateSelectedEvent = function () {
return this.isShowCode;
};
// 增加到editor对象中
editor.menus[menuId] = menu;
});
});
// quote 菜单
_e(function (E, $) {
E.createMenu(function (check) {
var menuId = 'quote';
if (!check(menuId)) {
return;
}
var editor = this;
var lang = editor.config.lang;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.quote,
commandName: 'formatBlock',
commandValue: 'blockquote'
});
// 定义click事件
menu.clickEvent = function (e) {
var rangeElem = editor.getRangeElem();
var $rangeElem;
if (!rangeElem) {
e.preventDefault();
return;
}
var currentQuote = editor.getSelfOrParentByName(rangeElem, 'blockquote');
var $quote;
if (currentQuote) {
// 说明当前在quote之内不做任何处理
e.preventDefault();
return;
}
rangeElem = editor.getLegalTags(rangeElem);
$rangeElem = $(rangeElem);
// 无文字,则不允许执行引用
if (!$rangeElem.text()) {
return;
}
if (!rangeElem) {
// 执行默认命令
// IE8 下执行此处(不过,经测试代码无效,也不报错)
editor.command(e, 'formatBlock', 'blockquote');
return;
}
// 自定义command事件
function commandFn() {
$quote = $('<p>' + $rangeElem.text() + '</p>');
$rangeElem.after($quote).remove();
$quote.wrap('<blockquote>');
}
// 自定义 callback 事件
function callback() {
// callback中设置range为quote
var editor = this;
if ($quote) {
editor.restoreSelectionByElem($quote.get(0));
}
}
// 执行自定义命令
editor.customCommand(e, commandFn, callback);
};
// 定义选中状态下的click事件
menu.clickEventSelected = function (e) {
var rangeElem;
var quoteElem;
var $lastChild;
// 获取当前选区的elem并试图往上找 quote 元素
rangeElem = editor.getRangeElem();
quoteElem = editor.getSelfOrParentByName(rangeElem, 'blockquote');
if (!quoteElem) {
// 没找到,则返回
e.preventDefault();
return;
}
// 自定义的command事件
function commandFn() {
var $quoteElem;
var $children;
$quoteElem = $(quoteElem);
$children = $quoteElem.children();
if ($children.length) {
$children.each(function (k) {
var $item = $(this);
if ($item.get(0).nodeName === 'P') {
$quoteElem.after($item);
} else {
$quoteElem.after('<p>' + $item.text() + '</p>');
}
$lastChild = $item; // 记录最后一个子元素用于callback中的range定位
});
$quoteElem.remove();
return;
}
}
// 自定义的callback函数
function callback() {
// callback中设置range为lastChild
var editor = this;
if ($lastChild) {
editor.restoreSelectionByElem($lastChild.get(0));
}
}
// 执行自定义命令
editor.customCommand(e, commandFn, callback);
};
// 定义更新选中状态的事件
menu.updateSelectedEvent = function () {
var self = this; //菜单对象
var editor = self.editor;
var rangeElem;
rangeElem = editor.getRangeElem();
rangeElem = editor.getSelfOrParentByName(rangeElem, 'blockquote');
if (rangeElem) {
return true;
}
return false;
};
// 增加到editor对象中
editor.menus[menuId] = menu;
// --------------- 两次点击 enter 跳出引用 ---------------
editor.ready(function () {
var editor = this;
var $txt = editor.txt.$txt;
var isPrevEnter = false; // 是不是刚刚在quote中按了 enter 键
$txt.on('keydown', function (e) {
if (e.keyCode !== 13) {
// 不是 enter 键
isPrevEnter = false;
return;
}
var rangeElem = editor.getRangeElem();
rangeElem = editor.getSelfOrParentByName(rangeElem, 'blockquote');
if (!rangeElem) {
// 选区不是 quote
isPrevEnter = false;
return;
}
if (!isPrevEnter) {
// 最近没有在qote中按enter键
isPrevEnter = true;
return;
}
var currentRangeElem = editor.getRangeElem();
var $currentRangeElem = $(currentRangeElem);
if ($currentRangeElem.length) {
$currentRangeElem.parent().after($currentRangeElem);
}
// 设置选区
editor.restoreSelectionByElem(currentRangeElem, 'start');
isPrevEnter = false;
// 阻止默认行文
e.preventDefault();
});
}); // editor.ready(
// --------------- 处理quote中无内容时不能删除的问题 ---------------
editor.ready(function () {
var editor = this;
var $txt = editor.txt.$txt;
var $rangeElem;
function commandFn() {
$rangeElem && $rangeElem.remove();
}
function callback() {
if (!$rangeElem) {
return;
}
var $prev = $rangeElem.prev();
if ($prev.length) {
// 有 prev 则定位到 prev 最后
editor.restoreSelectionByElem($prev.get(0));
} else {
// 无 prev 则初始化选区
editor.initSelection();
}
}
$txt.on('keydown', function (e) {
if (e.keyCode !== 8) {
// 不是 backspace 键
return;
}
var rangeElem = editor.getRangeElem();
rangeElem = editor.getSelfOrParentByName(rangeElem, 'blockquote');
if (!rangeElem) {
// 选区不是 quote
return;
}
$rangeElem = $(rangeElem);
var text = $rangeElem.text();
if (text) {
// quote 中还有内容
return;
}
editor.customCommand(e, commandFn, callback);
}); // $txt.on
}); // editor.ready(
});
});
// 字体 菜单
_e(function (E, $) {
E.createMenu(function (check) {
var menuId = 'fontfamily';
if (!check(menuId)) {
return;
}
var editor = this;
var lang = editor.config.lang;
var configFamilys = editor.config.familys;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.fontfamily,
commandName: 'fontName'
});
// 初始化数据
var data = {};
/*
data
{
'commandValue': 'title'
...
}
*/
$.each(configFamilys, function (k, v) {
// configFamilys 是数组data 是对象
data[v] = v;
});
// 创建droplist
var tpl = '<span style="font-family:{#commandValue};">{#title}</span>';
menu.dropList = new E.DropList(editor, menu, {
data: data,
tpl: tpl,
selectorForELemCommand: 'font[face]' // 为了执行 editor.commandForElem 而传入的elem查询方式
});
// 定义 update selected 事件
menu.updateSelectedEvent = function () {
var rangeElem = editor.getRangeElem();
rangeElem = editor.getSelfOrParentByName(rangeElem, 'font[face]');
if (rangeElem) {
return true;
}
return false;
};
// 增加到editor对象中
editor.menus[menuId] = menu;
});
});
// 字号 菜单
_e(function (E, $) {
E.createMenu(function (check) {
var menuId = 'fontsize';
if (!check(menuId)) {
return;
}
var editor = this;
var lang = editor.config.lang;
var configSize = editor.config.fontsizes;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.fontsize,
commandName: 'fontSize'
});
// 初始化数据
var data = configSize;
/*
data
{
'commandValue': 'title'
...
}
*/
// 创建droplist
var tpl = '<span style="font-size:{#title};">{#title}</span>';
menu.dropList = new E.DropList(editor, menu, {
data: data,
tpl: tpl,
selectorForELemCommand: 'font[size]' // 为了执行 editor.commandForElem 而传入的elem查询方式
});
// 定义 update selected 事件
menu.updateSelectedEvent = function () {
var rangeElem = editor.getRangeElem();
rangeElem = editor.getSelfOrParentByName(rangeElem, 'font[size]');
if (rangeElem) {
return true;
}
return false;
};
// 增加到editor对象中
editor.menus[menuId] = menu;
});
});
// head 菜单
_e(function (E, $) {
E.createMenu(function (check) {
var menuId = 'head';
if (!check(menuId)) {
return;
}
var editor = this;
var lang = editor.config.lang;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.head,
commandName: 'formatBlock'
});
// 初始化数据
var data = {
'<h1>': '1',
'<h2>': '2',
'<h3>': '3',
'<h4>': '4',
'<h5>': '5'
};
/*
data
{
'commandValue': 'title'
...
}
*/
var isOrderedList;
function beforeEvent(e) {
if (editor.queryCommandState('InsertOrderedList')) {
isOrderedList = true;
// 先取消有序列表
editor.command(e, 'InsertOrderedList');
} else {
isOrderedList = false;
}
}
function afterEvent(e) {
if (isOrderedList) {
// 再设置有序列表
editor.command(e, 'InsertOrderedList');
}
}
// 创建droplist
var tpl = '{#commandValue}{#title}';
menu.dropList = new E.DropList(editor, menu, {
data: data,
tpl: tpl,
// 对 ol 直接设置 head会出现每个 li 的 index 都变成 1 的问题,因此要先取消 ol然后设置 head最后再增加上 ol
beforeEvent: beforeEvent,
afterEvent: afterEvent
});
// 定义 update selected 事件
menu.updateSelectedEvent = function () {
var rangeElem = editor.getRangeElem();
rangeElem = editor.getSelfOrParentByName(rangeElem, 'h1,h2,h3,h4,h5');
if (rangeElem) {
return true;
}
return false;
};
// 增加到editor对象中
editor.menus[menuId] = menu;
});
});
// unorderlist 菜单
_e(function (E, $) {
E.createMenu(function (check) {
var menuId = 'unorderlist';
if (!check(menuId)) {
return;
}
var editor = this;
var lang = editor.config.lang;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.unorderlist,
commandName: 'InsertUnorderedList'
});
// 增加到editor对象中
editor.menus[menuId] = menu;
});
});
// orderlist 菜单
_e(function (E, $) {
E.createMenu(function (check) {
var menuId = 'orderlist';
if (!check(menuId)) {
return;
}
var editor = this;
var lang = editor.config.lang;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.orderlist,
commandName: 'InsertOrderedList'
});
// 增加到editor对象中
editor.menus[menuId] = menu;
});
});
// alignleft 菜单
_e(function (E, $) {
E.createMenu(function (check) {
var menuId = 'alignleft';
if (!check(menuId)) {
return;
}
var editor = this;
var lang = editor.config.lang;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.alignleft,
commandName: 'JustifyLeft'
});
// 定义 update selected 事件
menu.updateSelectedEvent = function () {
var rangeElem = editor.getRangeElem();
rangeElem = editor.getSelfOrParentByName(rangeElem, 'p,h1,h2,h3,h4,h5,li', function (elem) {
var cssText;
if (elem && elem.style && elem.style.cssText != null) {
cssText = elem.style.cssText;
if (cssText && /text-align:\s*left;/.test(cssText)) {
return true;
}
}
if ($(elem).attr('align') === 'left') {
// ff 中设置align-left之后会是 <p align="left">xxx</p>
return true;
}
return false;
});
if (rangeElem) {
return true;
}
return false;
};
// 增加到editor对象中
editor.menus[menuId] = menu;
});
});
// aligncenter 菜单
_e(function (E, $) {
E.createMenu(function (check) {
var menuId = 'aligncenter';
if (!check(menuId)) {
return;
}
var editor = this;
var lang = editor.config.lang;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.aligncenter,
commandName: 'JustifyCenter'
});
// 定义 update selected 事件
menu.updateSelectedEvent = function () {
var rangeElem = editor.getRangeElem();
rangeElem = editor.getSelfOrParentByName(rangeElem, 'p,h1,h2,h3,h4,h5,li', function (elem) {
var cssText;
if (elem && elem.style && elem.style.cssText != null) {
cssText = elem.style.cssText;
if (cssText && /text-align:\s*center;/.test(cssText)) {
return true;
}
}
if ($(elem).attr('align') === 'center') {
// ff 中设置align-center之后会是 <p align="center">xxx</p>
return true;
}
return false;
});
if (rangeElem) {
return true;
}
return false;
};
// 增加到editor对象中
editor.menus[menuId] = menu;
});
});
// alignright 菜单
_e(function (E, $) {
E.createMenu(function (check) {
var menuId = 'alignright';
if (!check(menuId)) {
return;
}
var editor = this;
var lang = editor.config.lang;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.alignright,
commandName: 'JustifyRight'
});
// 定义 update selected 事件
menu.updateSelectedEvent = function () {
var rangeElem = editor.getRangeElem();
rangeElem = editor.getSelfOrParentByName(rangeElem, 'p,h1,h2,h3,h4,h5,li', function (elem) {
var cssText;
if (elem && elem.style && elem.style.cssText != null) {
cssText = elem.style.cssText;
if (cssText && /text-align:\s*right;/.test(cssText)) {
return true;
}
}
if ($(elem).attr('align') === 'right') {
// ff 中设置align-right之后会是 <p align="right">xxx</p>
return true;
}
return false;
});
if (rangeElem) {
return true;
}
return false;
};
// 增加到editor对象中
editor.menus[menuId] = menu;
});
});
// link 菜单
_e(function (E, $) {
E.createMenu(function (check) {
var menuId = 'link';
if (!check(menuId)) {
return;
}
var editor = this;
var lang = editor.config.lang;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.link
});
// 创建 dropPanel
var $content = $('<div></div>');
var $div1 = $('<div style="margin:20px 10px;" class="clearfix"></div>');
var $div2 = $div1.clone();
var $div3 = $div1.clone().css('margin', '0 10px');
var $textInput = $('<input type="text" class="block" placeholder="' + lang.text + '"/>');
var $urlInput = $('<input type="text" class="block" placeholder="' + lang.link + '"/>');
var $btnSubmit = $('<button class="right">' + lang.submit + '</button>');
var $btnCancel = $('<button class="right gray">' + lang.cancel + '</button>');
$div1.append($textInput);
$div2.append($urlInput);
$div3.append($btnSubmit).append($btnCancel);
$content.append($div1).append($div2).append($div3);
menu.dropPanel = new E.DropPanel(editor, menu, {
$content: $content,
width: 300
});
// 定义click事件
menu.clickEvent = function (e) {
var menu = this;
var dropPanel = menu.dropPanel;
// -------------隐藏----------------
if (dropPanel.isShowing) {
dropPanel.hide();
return;
}
// -------------显示----------------
// 重置 input
$textInput.val('');
$urlInput.val('http://');
// 获取url
var url = '';
var rangeElem = editor.getRangeElem();
rangeElem = editor.getSelfOrParentByName(rangeElem, 'a');
if (rangeElem) {
url = rangeElem.href || '';
}
// 获取 text
var text = '';
var isRangeEmpty = editor.isRangeEmpty();
if (!isRangeEmpty) {
// 选区不是空
text = editor.getRangeText() || '';
} else if (rangeElem) {
// 如果选区空,并且在 a 标签之内
text = rangeElem.textContent || rangeElem.innerHTML;
}
// 设置 url 和 text
url && $urlInput.val(url);
text && $textInput.val(text);
// 如果有选区内容textinput 不能修改
if (!isRangeEmpty) {
$textInput.attr('disabled', true);
} else {
$textInput.removeAttr('disabled');
}
// 显示要设置好了所有input的值和属性之后再显示
dropPanel.show();
};
// 定义 update selected 事件
menu.updateSelectedEvent = function () {
var rangeElem = editor.getRangeElem();
rangeElem = editor.getSelfOrParentByName(rangeElem, 'a');
if (rangeElem) {
return true;
}
return false;
};
// 『取消』 按钮
$btnCancel.click(function (e) {
e.preventDefault();
menu.dropPanel.hide();
});
// 『确定』按钮
$btnSubmit.click(function (e) {
e.preventDefault();
var rangeElem = editor.getRangeElem();
var targetElem = editor.getSelfOrParentByName(rangeElem, 'a');
var isRangeEmpty = editor.isRangeEmpty();
var $linkElem, linkHtml;
var commandFn, callback;
var $txt = editor.txt.$txt;
var $oldLinks, $newLinks;
var uniqId = 'link' + E.random();
// 获取数据
var url = $.trim($urlInput.val());
var text = $.trim($textInput.val());
if (!url) {
menu.dropPanel.focusFirstInput();
return;
}
if (!text) {
text = url;
}
if (!isRangeEmpty) {
// 选中区域有内容,则执行默认命令
// 获取目前 txt 内所有链接,并为当前链接做一个标记
$oldLinks = $txt.find('a');
$oldLinks.attr(uniqId, '1');
// 执行命令
editor.command(e, 'createLink', url);
// 去的没有标记的链接,即刚刚插入的链接
$newLinks = $txt.find('a').not('[' + uniqId + ']');
$newLinks.attr('target', '_blank'); // 增加 _blank
// 去掉之前做的标记
$oldLinks.removeAttr(uniqId);
} else if (targetElem) {
// 无选中区域,在 a 标签之内,修改该 a 标签的内容和链接
$linkElem = $(targetElem);
commandFn = function () {
$linkElem.attr('href', url);
$linkElem.text(text);
};
callback = function () {
var editor = this;
editor.restoreSelectionByElem(targetElem);
};
// 执行命令
editor.customCommand(e, commandFn, callback);
} else {
// 无选中区域,不在 a 标签之内,插入新的链接
linkHtml = '<a href="' + url + '" target="_blank">' + text + '</a>';
if (E.userAgent.indexOf('Firefox') > 0) {
linkHtml += '<span>&nbsp;</span>';
}
editor.command(e, 'insertHtml', linkHtml);
}
});
// 增加到editor对象中
editor.menus[menuId] = menu;
});
});
// unlink 菜单
_e(function (E, $) {
E.createMenu(function (check) {
var menuId = 'unlink';
if (!check(menuId)) {
return;
}
var editor = this;
var lang = editor.config.lang;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.unlink,
commandName: 'unLink'
});
// click 事件
menu.clickEvent = function (e) {
var isRangeEmpty = editor.isRangeEmpty();
if (!isRangeEmpty) {
// 有选中区域或者IE8执行默认命令
editor.command(e, 'unLink');
return;
}
// 无选中区域...
var rangeElem = editor.getRangeElem();
var aElem = editor.getSelfOrParentByName(rangeElem, 'a');
if (!aElem) {
// 不在 a 之内,返回
e.preventDefault();
return;
}
// 在 a 之内
var $a = $(aElem);
var $span = $('<span>' + $a.text() + '</span>');
function commandFn() {
$a.after($span).remove();
}
function callback() {
editor.restoreSelectionByElem($span.get(0));
}
editor.customCommand(e, commandFn, callback);
};
// 增加到editor对象中
editor.menus[menuId] = menu;
});
});
// table 菜单
_e(function (E, $) {
E.createMenu(function (check) {
var menuId = 'table';
if (!check(menuId)) {
return;
}
var editor = this;
var lang = editor.config.lang;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.table
});
// dropPanel 内容
var $content = $('<div style="font-size: 14px; color: #666; text-align:right;"></div>');
var $table = $('<table class="choose-table" style="margin-bottom:10px;margin-top:5px;">');
var $row = $('<span>0</span>');
var $rowspan = $('<span> </span>');
var $col = $('<span>0</span>');
var $colspan = $('<span> </span>');
var $tr;
var i, j;
// 创建一个n行n列的表格
for (i = 0; i < 15; i++) {
$tr = $('<tr index="' + (i + 1) + '">');
for (j = 0; j < 20; j++) {
$tr.append($('<td index="' + (j + 1) + '">'));
}
$table.append($tr);
}
$content.append($table);
$content.append($row).append($rowspan).append($col).append($colspan);
// 定义table事件
$table.on('mouseenter', 'td', function (e) {
var $currentTd = $(e.currentTarget);
var currentTdIndex = $currentTd.attr('index');
var $currentTr = $currentTd.parent();
var currentTrIndex = $currentTr.attr('index');
// 显示
$row.text(currentTrIndex);
$col.text(currentTdIndex);
// 遍历设置背景颜色
$table.find('tr').each(function () {
var $tr = $(this);
var trIndex = $tr.attr('index');
if (parseInt(trIndex, 10) <= parseInt(currentTrIndex, 10)) {
// 该行需要可能需要设置背景色
$tr.find('td').each(function () {
var $td = $(this);
var tdIndex = $td.attr('index');
if (parseInt(tdIndex, 10) <= parseInt(currentTdIndex, 10)) {
// 需要设置背景色
$td.addClass('active');
} else {
// 需要移除背景色
$td.removeClass('active');
}
});
} else {
// 改行不需要设置背景色
$tr.find('td').removeClass('active');
}
});
}).on('mouseleave', function (e) {
// mouseleave 删除背景色
$table.find('td').removeClass('active');
$row.text(0);
$col.text(0);
});
// 插入表格
$table.on('click', 'td', function (e) {
var $currentTd = $(e.currentTarget);
var currentTdIndex = $currentTd.attr('index');
var $currentTr = $currentTd.parent();
var currentTrIndex = $currentTr.attr('index');
var rownum = parseInt(currentTrIndex, 10);
var colnum = parseInt(currentTdIndex, 10);
// -------- 拼接tabel html --------
var i, j;
var tableHtml = '<table>';
for (i = 0; i < rownum; i++) {
tableHtml += '<tr>';
for (j = 0; j < colnum; j++) {
tableHtml += '<td><span>&nbsp;</span></td>';
}
tableHtml += '</tr>';
}
tableHtml += '</table>';
// -------- 执行命令 --------
editor.command(e, 'insertHtml', tableHtml);
});
// 创建 panel
menu.dropPanel = new E.DropPanel(editor, menu, {
$content: $content,
width: 262
});
// 增加到editor对象中
editor.menus[menuId] = menu;
});
});
// emotion 菜单
_e(function (E, $) {
E.createMenu(function (check) {
var menuId = 'emotion';
if (!check(menuId)) {
return;
}
var editor = this;
var config = editor.config;
var lang = config.lang;
var configEmotions = config.emotions;
var emotionsShow = config.emotionsShow;
// 记录每一个表情图片的地址
editor.emotionUrls = [];
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.emotion
});
// 添加表情图片的函数
function insertEmotionImgs(data, $tabContent) {
// 添加表情图片
$.each(data, function (k, emotion) {
var src = emotion.icon || emotion.url;
var value = emotion.value || emotion.title;
// 通过配置 editor.config.emotionsShow 的值来修改插入到编辑器的内容(图片/value
var commandValue = emotionsShow === 'icon' ? src : value;
var $command = $('<a href="#" commandValue="' + commandValue + '"></a>');
var $img = $('<img>');
$img.attr('_src', src); // 先将 src 复制到 '_src' 属性,先不加载
$command.append($img);
$tabContent.append($command);
// 记录下每一个表情图片的地址
editor.emotionUrls.push(src);
});
}
// 拼接 dropPanel 内容
var $panelContent = $('<div class="panel-tab"></div>');
var $tabContainer = $('<div class="tab-container"></div>');
var $contentContainer = $('<div class="content-container emotion-content-container"></div>');
$.each(configEmotions, function (k, emotion) {
var title = emotion.title;
var data = emotion.data;
E.log(' ' + title + ' ...');
// 增加该组表情的tab和content
var $tab = $('<a href="#">' + title +' </a>');
$tabContainer.append($tab);
var $tabContent = $('<div class="content"></div>');
$contentContainer.append($tabContent);
// tab 切换事件
$tab.click(function (e) {
$tabContainer.children().removeClass('selected');
$contentContainer.children().removeClass('selected');
$tabContent.addClass('selected');
$tab.addClass('selected');
e.preventDefault();
});
// 处理data
if (typeof data === 'string') {
// url 形式需要通过ajax从该url获取数据
E.log(' ' + data + ' ajax');
$.get(data, function (result) {
result = $.parseJSON(result);
E.log(' ' + result.length + ' ');
insertEmotionImgs(result, $tabContent);
});
} else if ( Object.prototype.toString.call(data).toLowerCase().indexOf('array') > 0 ) {
// 数组,即 data 直接就是表情包数据
insertEmotionImgs(data, $tabContent);
} else {
// 其他情况data格式不对
E.error('data ' + E.docsite);
return;
}
});
$panelContent.append($tabContainer).append($contentContainer);
// 默认显示第一个tab
$tabContainer.children().first().addClass('selected');
$contentContainer.children().first().addClass('selected');
// 插入表情command事件
$contentContainer.on('click', 'a[commandValue]', function (e) {
var $a = $(e.currentTarget);
var commandValue = $a.attr('commandValue');
var img;
// commandValue 有可能是图片url也有可能是表情的 value需要区别对待
if (emotionsShow === 'icon') {
// 插入图片
editor.command(e, 'InsertImage', commandValue);
} else {
// 插入value
editor.command(e, 'insertHtml', '<span>' + commandValue + '</span>');
}
e.preventDefault();
});
// 添加panel
menu.dropPanel = new E.DropPanel(editor, menu, {
$content: $panelContent,
width: 350
});
// 定义click事件异步加载表情图片
menu.clickEvent = function (e) {
var menu = this;
var dropPanel = menu.dropPanel;
// -------------隐藏-------------
if (dropPanel.isShowing) {
dropPanel.hide();
return;
}
// -------------显示-------------
dropPanel.show();
// 异步加载图片
if (menu.imgLoaded) {
return;
}
$contentContainer.find('img').each(function () {
var $img = $(this);
var _src = $img.attr('_src');
$img.on('error', function () {
E.error(' ' + _src);
});
$img.attr('src', _src);
$img.removeAttr('_src');
});
menu.imgLoaded = true;
};
// 增加到editor对象中
editor.menus[menuId] = menu;
});
});
// img 菜单
_e(function (E, $) {
E.createMenu(function (check) {
var menuId = 'img';
if (!check(menuId)) {
return;
}
var editor = this;
var lang = editor.config.lang;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.img
});
// 创建 panel content
var $panelContent = $('<div class="panel-tab"></div>');
var $tabContainer = $('<div class="tab-container"></div>');
var $contentContainer = $('<div class="content-container"></div>');
$panelContent.append($tabContainer).append($contentContainer);
// tab
var $uploadTab = $('<a href="#">' + lang.uploadImg + '</a>');
var $linkTab = $('<a href="#">' + lang.linkImg + '</a>');
$tabContainer.append($uploadTab).append($linkTab);
// 上传图片 content
var $uploadContent = $('<div class="content"></div>');
$contentContainer.append($uploadContent);
// 网络图片 content
var $linkContent = $('<div class="content"></div>');
$contentContainer.append($linkContent);
linkContentHandler(editor, menu, $linkContent);
// 添加panel
menu.dropPanel = new E.DropPanel(editor, menu, {
$content: $panelContent,
width: 400,
onRender: function () {
// 渲染后的回调事件用于执行自定义上传的init
// 因为渲染之后上传面板的dom才会被渲染到页面才能让第三方空间获取到
var init = editor.config.customUploadInit;
init && init.call(editor);
}
});
// 增加到editor对象中
editor.menus[menuId] = menu;
// tab 切换事件
function tabToggle() {
$uploadTab.click(function (e) {
$tabContainer.children().removeClass('selected');
$contentContainer.children().removeClass('selected');
$uploadContent.addClass('selected');
$uploadTab.addClass('selected');
e.preventDefault();
});
$linkTab.click(function (e) {
$tabContainer.children().removeClass('selected');
$contentContainer.children().removeClass('selected');
$linkContent.addClass('selected');
$linkTab.addClass('selected');
e.preventDefault();
// focus input
if (E.placeholder) {
$linkContent.find('input[type=text]').focus();
}
});
// 默认情况
// $uploadTab.addClass('selected');
// $uploadContent.addClass('selected');
$uploadTab.click();
}
// 隐藏上传图片
function hideUploadImg() {
$tabContainer.remove();
$uploadContent.remove();
$linkContent.addClass('selected');
}
// 隐藏网络图片
function hideLinkImg() {
$tabContainer.remove();
$linkContent.remove();
$uploadContent.addClass('selected');
}
// 判断用户是否配置了上传图片
editor.ready(function () {
var editor = this;
var config = editor.config;
var uploadImgUrl = config.uploadImgUrl;
var customUpload = config.customUpload;
var linkImg = config.hideLinkImg;
var $uploadImgPanel;
if (uploadImgUrl || customUpload) {
// 第一,暴露出 $uploadContent 以便用户自定义 !!!重要
editor.$uploadContent = $uploadContent;
// 第二绑定tab切换事件
tabToggle();
if (linkImg) {
// 隐藏网络图片
hideLinkImg();
}
} else {
// 未配置上传图片功能
hideUploadImg();
}
// 点击 $uploadContent 立即隐藏 dropPanel
// 为了兼容IE8、9的上传因为IE8、9使用 modal 上传
// 这里使用异步,为了不妨碍高级浏览器通过点击 $uploadContent 选择文件
function hidePanel() {
menu.dropPanel.hide();
}
$uploadContent.click(function () {
setTimeout(hidePanel);
});
});
});
// --------------- 处理网络图片content ---------------
function linkContentHandler (editor, menu, $linkContent) {
var lang = editor.config.lang;
var $urlContainer = $('<div style="margin:20px 10px 10px 10px;"></div>');
var $urlInput = $('<input type="text" class="block" placeholder="http://"/>');
$urlContainer.append($urlInput);
var $btnSubmit = $('<button class="right">' + lang.submit + '</button>');
var $btnCancel = $('<button class="right gray">' + lang.cancel + '</button>');
$linkContent.append($urlContainer).append($btnSubmit).append($btnCancel);
// 取消
$btnCancel.click(function (e) {
e.preventDefault();
menu.dropPanel.hide();
});
// callback
function callback() {
$urlInput.val('');
}
// 确定
$btnSubmit.click(function (e) {
e.preventDefault();
var url = $.trim($urlInput.val());
if (!url) {
// 无内容
$urlInput.focus();
return;
}
var imgHtml = '<img style="max-width:100%;" src="' + url + '"/>';
editor.command(e, 'insertHtml', imgHtml, callback);
});
}
});
// video 菜单
_e(function (E, $) {
E.createMenu(function (check) {
var menuId = 'video';
if (!check(menuId)) {
return;
}
var editor = this;
var lang = editor.config.lang;
var reg = /^<(iframe)|(embed)/i; // <iframe... 或者 <embed... 格式
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.video
});
// 创建 panel 内容
var $content = $('<div></div>');
var $linkInputContainer = $('<div style="margin:20px 10px;"></div>');
var $linkInput = $('<input type="text" class="block" placeholder=\'<iframe src="..." frameborder=0 allowfullscreen></iframe>\'/>');
$linkInputContainer.append($linkInput);
var $sizeContainer = $('<div style="margin:20px 10px;"></div>');
var $widthInput = $('<input type="text" value="640" style="width:50px;text-align:center;"/>');
var $heightInput = $('<input type="text" value="498" style="width:50px;text-align:center;"/>');
$sizeContainer.append('<span> ' + lang.width + ' </span>')
.append($widthInput)
.append('<span> px &nbsp;&nbsp;&nbsp;</span>')
.append('<span> ' + lang.height + ' </span>')
.append($heightInput)
.append('<span> px </span>');
var $btnContainer = $('<div></div>');
var $howToCopy = $('<a href="http://www.kancloud.cn/wangfupeng/wangeditor2/134973" target="_blank" style="display:inline-block;margin-top:10px;margin-left:10px;color:#999;"></a>');
var $btnSubmit = $('<button class="right">' + lang.submit + '</button>');
var $btnCancel = $('<button class="right gray">' + lang.cancel + '</button>');
$btnContainer.append($howToCopy).append($btnSubmit).append($btnCancel);
$content.append($linkInputContainer).append($sizeContainer).append($btnContainer);
// 取消按钮
$btnCancel.click(function (e) {
e.preventDefault();
$linkInput.val('');
menu.dropPanel.hide();
});
// 确定按钮
$btnSubmit.click(function (e) {
e.preventDefault();
var link = $.trim($linkInput.val());
var $link;
var width = parseInt($widthInput.val());
var height = parseInt($heightInput.val());
var $div = $('<div>');
var html = '<p>{content}</p>';
// 验证数据
if (!link) {
menu.dropPanel.focusFirstInput();
return;
}
if (!reg.test(link)) {
alert('');
menu.dropPanel.focusFirstInput();
return;
}
if (isNaN(width) || isNaN(height)) {
alert('');
return;
}
$link = $(link);
// 设置高度和宽度
$link.attr('width', width)
.attr('height', height);
// 拼接字符串
html = html.replace('{content}', $div.append($link).html());
// 执行命令
editor.command(e, 'insertHtml', html);
$linkInput.val('');
});
// 创建panel
menu.dropPanel = new E.DropPanel(editor, menu, {
$content: $content,
width: 400
});
// 增加到editor对象中
editor.menus[menuId] = menu;
});
});
// location 菜单
_e(function (E, $) {
// 判断浏览器的 input 是否支持 keyup
var inputKeyup = (function (input) {
return 'onkeyup' in input;
})(document.createElement('input'));
// 百度地图的key
E.baiduMapAk = 'TVhjYjq1ICT2qqL5LdS8mwas';
// 一个页面中,如果有多个编辑器,地图会出现问题。这个参数记录一下,如果超过 1 就提示
E.numberOfLocation = 0;
E.createMenu(function (check) {
var menuId = 'location';
if (!check(menuId)) {
return;
}
if (++E.numberOfLocation > 1) {
E.error('使');
return;
}
var editor = this;
var config = editor.config;
var lang = config.lang;
var ak = config.mapAk;
// 地图的变量存储到这个地方
editor.mapData = {};
var mapData = editor.mapData;
// ---------- 地图事件 ----------
mapData.markers = [];
mapData.mapContainerId = 'map' + E.random();
mapData.clearLocations = function () {
var map = mapData.map;
if (!map) {
return;
}
map.clearOverlays();
//同时清空marker数组
mapData.markers = [];
};
mapData.searchMap = function () {
var map = mapData.map;
if (!map) {
return;
}
var BMap = window.BMap;
var cityName = $cityInput.val();
var locationName = $searchInput.val();
var myGeo, marker;
if(cityName !== ''){
if(!locationName || locationName === ''){
map.centerAndZoom(cityName, 11);
}
//地址解析
if(locationName && locationName !== ''){
myGeo = new BMap.Geocoder();
// 将地址解析结果显示在地图上,并调整地图视野
myGeo.getPoint(locationName, function(point){
if (point) {
map.centerAndZoom(point, 13);
marker = new BMap.Marker(point);
map.addOverlay(marker);
marker.enableDragging(); //允许拖拽
mapData.markers.push(marker); //将marker加入到数组中
}else{
// alert('未找到');
map.centerAndZoom(cityName, 11); //找不到则重新定位到城市
}
}, cityName);
}
} // if(cityName !== '')
};
// load script 之后的 callback
var hasCallback = false;
window.baiduMapCallBack = function(){
// 避免重复加载
if (hasCallback) {
return;
} else {
hasCallback = true;
}
var BMap = window.BMap;
if (!mapData.map) {
// 创建Map实例
mapData.map = new BMap.Map(mapData.mapContainerId);
}
var map = mapData.map;
map.centerAndZoom(new BMap.Point(116.404, 39.915), 11); // 初始化地图,设置中心点坐标和地图级别
map.addControl(new BMap.MapTypeControl()); //添加地图类型控件
map.setCurrentCity("北京"); // 设置地图显示的城市 此项是必须设置的
map.enableScrollWheelZoom(true); //开启鼠标滚轮缩放
//根据IP定位
function locationFun(result){
var cityName = result.name;
map.setCenter(cityName);
// 设置城市名称
$cityInput.val(cityName);
if (E.placeholder) {
$searchInput.focus();
}
var timeoutId, searchFn;
if (inputKeyup) {
// 并绑定搜索事件 - input 支持 keyup
searchFn = function (e) {
if (e.type === 'keyup' && e.keyCode === 13) {
e.preventDefault();
}
if (timeoutId) {
clearTimeout(timeoutId);
}
timeoutId = setTimeout(mapData.searchMap, 500);
};
$cityInput.on('keyup change paste', searchFn);
$searchInput.on('keyup change paste', searchFn);
} else {
// 并绑定搜索事件 - input 不支持 keyup
searchFn = function () {
if (!$content.is(':visible')) {
// panel 不显示了,就不用再监控了
clearTimeout(timeoutId);
return;
}
var currentCity = '';
var currentSearch = '';
var city = $cityInput.val();
var search = $searchInput.val();
if (city !== currentCity || search !== currentSearch) {
// 刚获取的数据和之前的数据不一致,执行查询
mapData.searchMap();
// 更新数据
currentCity = city;
currentSearch = search;
}
// 继续监控
if (timeoutId) {
clearTimeout(timeoutId);
}
timeoutId = setTimeout(searchFn, 1000);
};
// 开始监控
timeoutId = setTimeout(searchFn, 1000);
}
}
var myCity = new BMap.LocalCity();
myCity.get(locationFun);
//鼠标点击,创建位置
map.addEventListener("click", function(e){
var marker = new BMap.Marker(new BMap.Point(e.point.lng, e.point.lat));
map.addOverlay(marker);
marker.enableDragging();
mapData.markers.push(marker); //加入到数组中
}, false);
};
mapData.loadMapScript = function () {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "https://api.map.baidu.com/api?v=2.0&ak=" + ak + "&s=1&callback=baiduMapCallBack"; // baiduMapCallBack是一个本地函数
try {
// IE10- 报错
document.body.appendChild(script);
} catch (ex) {
E.error('');
}
};
// 初始化地图
mapData.initMap = function () {
if (window.BMap) {
// 不是第一次,直接处理地图即可
window.baiduMapCallBack();
} else {
// 第一次,先加载地图 script再处理地图script加载完自动执行处理
mapData.loadMapScript();
}
};
// ---------- 创建 menu 对象 ----------
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.location
});
editor.menus[menuId] = menu;
// ---------- 构建UI ----------
// panel content
var $content = $('<div></div>');
// 搜索框
var $inputContainer = $('<div style="margin:10px 0;"></div>');
var $cityInput = $('<input type="text"/>');
$cityInput.css({
width: '80px',
'text-align': 'center'
});
var $searchInput = $('<input type="text"/>');
$searchInput.css({
width: '300px',
'margin-left': '10px'
}).attr('placeholder', lang.searchlocation);
var $clearBtn = $('<button class="right link">' + lang.clearLocation + '</button>');
$inputContainer.append($clearBtn)
.append($cityInput)
.append($searchInput);
$content.append($inputContainer);
// 清除位置按钮
$clearBtn.click(function (e) {
$searchInput.val('');
$searchInput.focus();
mapData.clearLocations();
e.preventDefault();
});
// 地图
var $map = $('<div id="' + mapData.mapContainerId + '"></div>');
$map.css({
height: '260px',
width: '100%',
position: 'relative',
'margin-top': '10px',
border: '1px solid #f1f1f1'
});
var $mapLoading = $('<span>' + lang.loading + '</span>');
$mapLoading.css({
position: 'absolute',
width: '100px',
'text-align': 'center',
top: '45%',
left: '50%',
'margin-left': '-50px'
});
$map.append($mapLoading);
$content.append($map);
// 按钮
var $btnContainer = $('<div style="margin:10px 0;"></div>');
var $btnSubmit = $('<button class="right">' + lang.submit + '</button>');
var $btnCancel = $('<button class="right gray">' + lang.cancel + '</button>');
var $checkLabel = $('<label style="display:inline-block;margin-top:10px;color:#666;"></label>');
var $check = $('<input type="checkbox">');
$checkLabel.append($check).append('<span style="display:inline-block;margin-left:5px;"> ' + lang.dynamicMap + '</span>');
$btnContainer.append($checkLabel)
.append($btnSubmit)
.append($btnCancel);
$content.append($btnContainer);
function callback() {
$searchInput.val('');
}
// 『取消』按钮事件
$btnCancel.click(function (e) {
e.preventDefault();
callback();
menu.dropPanel.hide();
});
// 『确定』按钮事件
$btnSubmit.click(function (e) {
e.preventDefault();
var map = mapData.map,
isDynamic = $check.is(':checked'),
markers = mapData.markers,
center = map.getCenter(),
centerLng = center.lng,
centerLat = center.lat,
zoom = map.getZoom(),
size = map.getSize(),
sizeWidth = size.width,
sizeHeight = size.height,
position,
src,
iframe;
if(isDynamic){
//动态地址
src = 'http://ueditor.baidu.com/ueditor/dialogs/map/show.html#';
}else{
//静态地址
src = 'http://api.map.baidu.com/staticimage?';
}
//src参数
src = src +'center=' + centerLng + ',' + centerLat +
'&zoom=' + zoom +
'&width=' + sizeWidth +
'&height=' + sizeHeight;
if(markers.length > 0){
src = src + '&markers=';
//添加所有的marker
$.each(markers, function(key, value){
position = value.getPosition();
if(key > 0){
src = src + '|';
}
src = src + position.lng + ',' + position.lat;
});
}
if(isDynamic){
if(markers.length > 1){
alert( lang.langDynamicOneLocation );
return;
}
src += '&markerStyles=l,A';
//插入iframe
iframe = '<iframe class="ueditor_baidumap" src="{src}" frameborder="0" width="' + sizeWidth + '" height="' + sizeHeight + '"></iframe>';
iframe = iframe.replace('{src}', src);
editor.command(e, 'insertHtml', iframe, callback);
}else{
//插入图片
editor.command(e, 'insertHtml', '<img style="max-width:100%;" src="' + src + '"/>', callback);
}
});
// 根据 UI 创建菜单 panel
menu.dropPanel = new E.DropPanel(editor, menu, {
$content: $content,
width: 500
});
// ---------- 事件 ----------
// render 时执行事件
menu.onRender = function () {
if (ak === E.baiduMapAk) {
E.warn('mapAk' + E.docsite);
}
};
// click 事件
menu.clickEvent = function (e) {
var menu = this;
var dropPanel = menu.dropPanel;
var firstTime = false;
// -------------隐藏-------------
if (dropPanel.isShowing) {
dropPanel.hide();
return;
}
// -------------显示-------------
if (!mapData.map) {
// 第一次,先加载地图
firstTime = true;
}
dropPanel.show();
mapData.initMap();
if (!firstTime) {
$searchInput.focus();
}
};
});
});
// insertcode 菜单
_e(function (E, $) {
// 加载 highlightjs 代码
function loadHljs() {
if (E.userAgent.indexOf('MSIE 8') > 0) {
// 不支持 IE8
return;
}
if (window.hljs) {
// 不要重复加载
return;
}
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "//cdn.bootcss.com/highlight.js/9.2.0/highlight.min.js";
document.body.appendChild(script);
}
E.createMenu(function (check) {
var menuId = 'insertcode';
if (!check(menuId)) {
return;
}
// 加载 highlightjs 代码
setTimeout(loadHljs, 0);
var editor = this;
var config = editor.config;
var lang = config.lang;
var $txt = editor.txt.$txt;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.insertcode
});
// click 事件
menu.clickEvent = function (e) {
var menu = this;
var dropPanel = menu.dropPanel;
// 隐藏
if (dropPanel.isShowing) {
dropPanel.hide();
return;
}
// 显示
$textarea.val('');
dropPanel.show();
// highlightjs 语言列表
var hljs = window.hljs;
if (hljs && hljs.listLanguages) {
if ($langSelect.children().length !== 0) {
return;
}
$langSelect.css({
'margin-top': '9px',
'margin-left': '5px'
});
$.each(hljs.listLanguages(), function (key, lang) {
if (lang === 'xml') {
lang = 'html';
}
if (lang === config.codeDefaultLang) {
$langSelect.append('<option value="' + lang + '" selected="selected">' + lang + '</option>');
} else {
$langSelect.append('<option value="' + lang + '">' + lang + '</option>');
}
});
} else {
$langSelect.hide();
}
};
// 选中状态下的 click 事件
menu.clickEventSelected = function (e) {
var menu = this;
var dropPanel = menu.dropPanel;
// 隐藏
if (dropPanel.isShowing) {
dropPanel.hide();
return;
}
// 显示
dropPanel.show();
var rangeElem = editor.getRangeElem();
var targetElem = editor.getSelfOrParentByName(rangeElem, 'pre');
var $targetElem;
var className;
if (targetElem) {
// 确定找到 pre 之后,再找 code
targetElem = editor.getSelfOrParentByName(rangeElem, 'code');
}
if (!targetElem) {
return;
}
$targetElem = $(targetElem);
// 赋值内容
$textarea.val($targetElem.text());
if ($langSelect) {
// 赋值语言
className = $targetElem.attr('class');
if (className) {
$langSelect.val(className.split(' ')[0]);
}
}
};
// 定义更新选中状态的事件
menu.updateSelectedEvent = function () {
var self = this; //菜单对象
var editor = self.editor;
var rangeElem;
rangeElem = editor.getRangeElem();
rangeElem = editor.getSelfOrParentByName(rangeElem, 'pre');
if (rangeElem) {
return true;
}
return false;
};
// 创建 panel
var $content = $('<div></div>');
var $textarea = $('<textarea></textarea>');
var $langSelect = $('<select></select>');
contentHandle($content);
menu.dropPanel = new E.DropPanel(editor, menu, {
$content: $content,
width: 500
});
// 增加到editor对象中
editor.menus[menuId] = menu;
// ------ 增加 content 内容 ------
function contentHandle($content) {
// textarea 区域
var $textareaContainer = $('<div></div>');
$textareaContainer.css({
margin: '15px 5px 5px 5px',
height: '160px',
'text-align': 'center'
});
$textarea.css({
width: '100%',
height: '100%',
padding: '10px'
});
$textarea.on('keydown', function (e) {
// 取消 tab 键默认行为
if (e.keyCode === 9) {
e.preventDefault();
}
});
$textareaContainer.append($textarea);
$content.append($textareaContainer);
// 按钮区域
var $btnContainer = $('<div></div>');
var $btnSubmit = $('<button class="right">' + lang.submit + '</button>');
var $btnCancel = $('<button class="right gray">' + lang.cancel + '</button>');
$btnContainer.append($btnSubmit).append($btnCancel).append($langSelect);
$content.append($btnContainer);
// 取消按钮
$btnCancel.click(function (e) {
e.preventDefault();
menu.dropPanel.hide();
});
// 确定按钮
var codeTpl = '<pre style="max-width:100%;overflow-x:auto;"><code{#langClass}>{#content}</code></pre>';
$btnSubmit.click(function (e) {
e.preventDefault();
var val = $textarea.val();
if (!val) {
// 无内容
$textarea.focus();
return;
}
var rangeElem = editor.getRangeElem();
if ($.trim($(rangeElem).text()) && codeTpl.indexOf('<p><br></p>') !== 0) {
codeTpl = '<p><br></p>' + codeTpl;
}
var lang = $langSelect ? $langSelect.val() : ''; // 获取高亮语言
var langClass = '';
var doHightlight = function () {
$txt.find('pre code').each(function (i, block) {
var $block = $(block);
if ($block.attr('codemark')) {
// 有 codemark 标记的代码块,就不再重新格式化了
return;
} else if (window.hljs) {
// 新代码块,格式化之后,立即标记 codemark
window.hljs.highlightBlock(block);
$block.attr('codemark', '1');
}
});
};
// 语言高亮样式
if (lang) {
langClass = ' class="' + lang + ' hljs"';
}
// 替换标签
val = val.replace(/&/gm, '&amp;')
.replace(/</gm, '&lt;')
.replace(/>/gm, '&gt;');
// ---- menu 未选中状态 ----
if (!menu.selected) {
// 拼接html
var html = codeTpl.replace('{#langClass}', langClass).replace('{#content}', val);
editor.command(e, 'insertHtml', html, doHightlight);
return;
}
// ---- menu 选中状态 ----
var targetElem = editor.getSelfOrParentByName(rangeElem, 'pre');
var $targetElem;
if (targetElem) {
// 确定找到 pre 之后,再找 code
targetElem = editor.getSelfOrParentByName(rangeElem, 'code');
}
if (!targetElem) {
return;
}
$targetElem = $(targetElem);
function commandFn() {
var className;
if (lang) {
className = $targetElem.attr('class');
if (className !== lang + ' hljs') {
$targetElem.attr('class', lang + ' hljs');
}
}
$targetElem.html(val);
}
function callback() {
editor.restoreSelectionByElem(targetElem);
doHightlight();
}
editor.customCommand(e, commandFn, callback);
});
}
// ------ enter 时,不另起标签,只换行 ------
$txt.on('keydown', function (e) {
if (e.keyCode !== 13) {
return;
}
var rangeElem = editor.getRangeElem();
var targetElem = editor.getSelfOrParentByName(rangeElem, 'code');
if (!targetElem) {
return;
}
editor.command(e, 'insertHtml', '\n');
});
// ------ 点击时,禁用其他标签 ------
function updateMenu() {
var rangeElem = editor.getRangeElem();
var targetElem = editor.getSelfOrParentByName(rangeElem, 'code');
if (targetElem) {
// 在 code 之内,禁用其他菜单
editor.disableMenusExcept('insertcode');
} else {
// 不是在 code 之内,启用其他菜单
editor.enableMenusExcept('insertcode');
}
}
$txt.on('keydown click', function (e) {
// 此处必须使用 setTimeout 异步处理,否则不对
setTimeout(updateMenu);
});
});
});
// undo 菜单
_e(function (E, $) {
E.createMenu(function (check) {
var menuId = 'undo';
if (!check(menuId)) {
return;
}
var editor = this;
var lang = editor.config.lang;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.undo
});
// click 事件
menu.clickEvent = function (e) {
editor.undo();
};
// 增加到editor对象中
editor.menus[menuId] = menu;
// ------------ 初始化时、enter 时、打字中断时,做记录 ------------
// ------------ ctrl + z 是调用记录撤销,而不是使用浏览器默认的撤销 ------------
editor.ready(function () {
var editor = this;
var $txt = editor.txt.$txt;
var timeoutId;
// 执行undo记录
function undo() {
editor.undoRecord();
}
$txt.on('keydown', function (e) {
var keyCode = e.keyCode;
// 撤销 ctrl + z
if (e.ctrlKey && keyCode === 90) {
editor.undo();
return;
}
if (keyCode === 13) {
// enter 做记录
undo();
} else {
// keyup 之后 1s 之内不操作,则做一次记录
if (timeoutId) {
clearTimeout(timeoutId);
}
timeoutId = setTimeout(undo, 1000);
}
});
// 初始化做记录
editor.undoRecord();
});
});
});
// redo 菜单
_e(function (E, $) {
E.createMenu(function (check) {
var menuId = 'redo';
if (!check(menuId)) {
return;
}
var editor = this;
var lang = editor.config.lang;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.redo
});
// click 事件
menu.clickEvent = function (e) {
editor.redo();
};
// 增加到editor对象中
editor.menus[menuId] = menu;
});
});
// 全屏 菜单
_e(function (E, $) {
// 记录全屏时的scrollTop
var scrollTopWhenFullScreen;
E.createMenu(function (check) {
var menuId = 'fullscreen';
if (!check(menuId)) {
return;
}
var editor = this;
var $txt = editor.txt.$txt;
var config = editor.config;
var zIndexConfig = config.zindex || 10000;
var lang = config.lang;
var isSelected = false;
var zIndex;
var maxHeight;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor,
id: menuId,
title: lang.fullscreen
});
// 定义click事件
menu.clickEvent = function (e) {
// 增加样式
var $editorContainer = editor.$editorContainer;
$editorContainer.addClass('wangEditor-fullscreen');
// 先保存当前的再设置z-index
zIndex = $editorContainer.css('z-index');
$editorContainer.css('z-index', zIndexConfig);
var $wrapper;
var txtHeight = $txt.height();
var txtOuterHeight = $txt.outerHeight();
if (editor.useMaxHeight) {
// 记录 max-height并暂时去掉maxheight
maxHeight = $txt.css('max-height');
$txt.css('max-height', 'none');
// 如果使用了maxHeight 将$txt从它的父元素中移出来
$wrapper = $txt.parent();
$wrapper.after($txt);
$wrapper.remove();
$txt.css('overflow-y', 'auto');
}
// 设置高度到全屏
var menuContainer = editor.menuContainer;
$txt.height(
E.$window.height() -
menuContainer.height() -
(txtOuterHeight - txtHeight) // 去掉内边距和外边距
);
// 取消menuContainer的内联样式menu吸顶时会为 menuContainer 设置一些内联样式)
editor.menuContainer.$menuContainer.attr('style', '');
// 保存状态
isSelected = true;
// 记录编辑器是否全屏
editor.isFullScreen = true;
// 记录设置全屏时的高度
scrollTopWhenFullScreen = E.$window.scrollTop();
};
// 定义选中状态的 click 事件
menu.clickEventSelected = function (e) {
// 取消样式
var $editorContainer = editor.$editorContainer;
$editorContainer.removeClass('wangEditor-fullscreen');
$editorContainer.css('z-index', zIndex);
// 还原height
if (editor.useMaxHeight) {
$txt.css('max-height', maxHeight);
} else {
// editor.valueContainerHeight 在 editor.txt.initHeight() 中事先保存了
editor.$valueContainer.css('height', editor.valueContainerHeight);
}
// 重新计算高度
editor.txt.initHeight();
// 保存状态
isSelected = false;
// 记录编辑器是否全屏
editor.isFullScreen = false;
// 还原scrollTop
if (scrollTopWhenFullScreen != null) {
E.$window.scrollTop(scrollTopWhenFullScreen);
}
};
// 定义选中事件
menu.updateSelectedEvent = function (e) {
return isSelected;
};
// 增加到editor对象中
editor.menus[menuId] = menu;
});
});
// 渲染menus
_e(function (E, $) {
E.fn.renderMenus = function () {
var editor = this;
var menus = editor.menus;
var menuIds = editor.config.menus;
var menuContainer = editor.menuContainer;
var menu;
var groupIdx = 0;
$.each(menuIds, function (k, v) {
if (v === '|') {
groupIdx++;
return;
}
menu = menus[v];
if (menu) {
menu.render(groupIdx);
}
});
};
});
// 渲染menus
_e(function (E, $) {
E.fn.renderMenuContainer = function () {
var editor = this;
var menuContainer = editor.menuContainer;
var $editorContainer = editor.$editorContainer;
menuContainer.render();
};
});
// 渲染 txt
_e(function (E, $) {
E.fn.renderTxt = function () {
var editor = this;
var txt = editor.txt;
txt.render();
// ready 时候计算txt的高度
editor.ready(function () {
txt.initHeight();
});
};
});
// 渲染 container
_e(function (E, $) {
E.fn.renderEditorContainer = function () {
var editor = this;
var $valueContainer = editor.$valueContainer;
var $editorContainer = editor.$editorContainer;
var $txt = editor.txt.$txt;
var $prev, $parent;
// 将编辑器渲染到页面中
if ($valueContainer === $txt) {
$prev = editor.$prev;
$parent = editor.$parent;
if ($prev && $prev.length) {
// 有前置节点,就插入到前置节点的后面
$prev.after($editorContainer);
} else {
// 没有前置节点,就直接插入到父元素
$parent.prepend($editorContainer);
}
} else {
$valueContainer.after($editorContainer);
$valueContainer.hide();
}
// 设置宽度(这样设置宽度有问题)
// $editorContainer.css('width', $valueContainer.css('width'));
};
});
// 菜单事件
_e(function (E, $) {
// 绑定每个菜单的click事件
E.fn.eventMenus = function () {
var menus = this.menus;
// 绑定菜单的点击事件
$.each(menus, function (k, v) {
v.bindEvent();
});
};
});
// 菜单container事件
_e(function (E, $) {
E.fn.eventMenuContainer = function () {
};
});
// 编辑区域事件
_e(function (E, $) {
E.fn.eventTxt = function () {
var txt = this.txt;
// txt内容变化时保存选区
txt.saveSelectionEvent();
// txt内容变化时随时更新 value
txt.updateValueEvent();
// txt内容变化时随时更新 menu style
txt.updateMenuStyleEvent();
// // 鼠标hover时显示 p head 高度(暂时关闭这个功能)
// if (!/ie/i.test(E.userAgent)) {
// // 暂时不支持IE
// txt.showHeightOnHover();
// }
};
});
// 上传图片事件
_e(function (E, $) {
E.plugin(function () {
var editor = this;
var fns = editor.config.uploadImgFns; // editor.config.uploadImgFns = {} 在config文件中定义了
// -------- 定义load函数 --------
fns.onload || (fns.onload = function (resultText, xhr) {
E.log(' ' + resultText);
var editor = this;
var originalName = editor.uploadImgOriginalName || ''; // 上传图片时,已经将图片的名字存在 editor.uploadImgOriginalName
var img;
if (resultText.indexOf('error|') === 0) {
// 提示错误
E.warn('' + resultText.split('|')[1]);
alert(resultText.split('|')[1]);
} else {
E.log('' + resultText);
// 将结果插入编辑器
img = document.createElement('img');
img.onload = function () {
var html = '<img src="' + resultText + '" alt="' + originalName + '" style="max-width:100%;"/>';
editor.command(null, 'insertHtml', html);
E.log(' ' + resultText);
img = null;
};
img.onerror = function () {
E.error('使' + resultText);
img = null;
};
img.src = resultText;
}
});
// -------- 定义tiemout函数 --------
fns.ontimeout || (fns.ontimeout = function (xhr) {
E.error('');
alert('');
});
// -------- 定义error函数 --------
fns.onerror || (fns.onerror = function (xhr) {
E.error('');
alert('');
});
});
});
// xhr 上传图片
_e(function (E, $) {
if (!window.FileReader || !window.FormData) {
// 如果不支持html5的文档操作直接返回
return;
}
E.plugin(function () {
var editor = this;
var config = editor.config;
var uploadImgUrl = config.uploadImgUrl;
var uploadTimeout = config.uploadTimeout;
// 获取配置中的上传事件
var uploadImgFns = config.uploadImgFns;
var onload = uploadImgFns.onload;
var ontimeout = uploadImgFns.ontimeout;
var onerror = uploadImgFns.onerror;
if (!uploadImgUrl) {
return;
}
// -------- 将以base64的图片url数据转换为Blob --------
function convertBase64UrlToBlob(urlData, filetype){
//去掉url的头并转换为byte
var bytes = window.atob(urlData.split(',')[1]);
//处理异常,将ascii码小于0的转换为大于0
var ab = new ArrayBuffer(bytes.length);
var ia = new Uint8Array(ab);
var i;
for (i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
return new Blob([ab], {type : filetype});
}
// -------- 插入图片的方法 --------
function insertImg(src, event) {
var img = document.createElement('img');
img.onload = function () {
var html = '<img src="' + src + '" style="max-width:100%;"/>';
editor.command(event, 'insertHtml', html);
E.log(' ' + src);
img = null;
};
img.onerror = function () {
E.error('使' + src);
img = null;
};
img.src = src;
}
// -------- onprogress 事件 --------
function updateProgress(e) {
if (e.lengthComputable) {
var percentComplete = e.loaded / e.total;
editor.showUploadProgress(percentComplete * 100);
}
}
// -------- xhr 上传图片 --------
editor.xhrUploadImg = function (opt) {
// opt 数据
var event = opt.event;
var fileName = opt.filename || '';
var base64 = opt.base64;
var fileType = opt.fileType || 'image/png'; // 无扩展名则默认使用 png
var name = opt.name || 'wangEditor_upload_file';
var loadfn = opt.loadfn || onload;
var errorfn = opt.errorfn || onerror;
var timeoutfn = opt.timeoutfn || ontimeout;
// 上传参数(如 token
var params = editor.config.uploadParams || {};
// headers
var headers = editor.config.uploadHeaders || {};
// 获取文件扩展名
var fileExt = 'png'; // 默认为 png
if (fileName.indexOf('.') > 0) {
// 原来的文件名有扩展名
fileExt = fileName.slice(fileName.lastIndexOf('.') - fileName.length + 1);
} else if (fileType.indexOf('/') > 0 && fileType.split('/')[1]) {
// 文件名没有扩展名,通过类型获取,如从 'image/png' 取 'png'
fileExt = fileType.split('/')[1];
}
// ------------ begin 预览模拟上传 ------------
if (E.isOnWebsite) {
E.log('');
insertImg(base64, event);
return;
}
// ------------ end 预览模拟上传 ------------
// 变量声明
var xhr = new XMLHttpRequest();
var timeoutId;
var src;
var formData = new FormData();
// 超时处理
function timeoutCallback() {
if (timeoutId) {
clearTimeout(timeoutId);
}
if (xhr && xhr.abort) {
xhr.abort();
}
// 超时了就阻止默认行为
event.preventDefault();
// 执行回调函数,提示什么内容,都应该在回调函数中定义
timeoutfn && timeoutfn.call(editor, xhr);
// 隐藏进度条
editor.hideUploadProgress();
}
xhr.onload = function () {
if (timeoutId) {
clearTimeout(timeoutId);
}
// 记录文件名到 editor.uploadImgOriginalName ,插入图片时,可做 alt 属性用
editor.uploadImgOriginalName = fileName;
if (fileName.indexOf('.') > 0) {
editor.uploadImgOriginalName = fileName.split('.')[0];
}
// 执行load函数任何操作都应该在load函数中定义
loadfn && loadfn.call(editor, xhr.responseText, xhr);
// 隐藏进度条
editor.hideUploadProgress();
};
xhr.onerror = function () {
if (timeoutId) {
clearTimeout(timeoutId);
}
// 超时了就阻止默认行为
event.preventDefault();
// 执行error函数错误提示应该在error函数中定义
errorfn && errorfn.call(editor, xhr);
// 隐藏进度条
editor.hideUploadProgress();
};
// xhr.onprogress = updateProgress;
xhr.upload.onprogress = updateProgress;
// 填充数据
formData.append(name, convertBase64UrlToBlob(base64, fileType), E.random() + '.' + fileExt);
// 添加参数
$.each(params, function (key, value) {
formData.append(key, value);
});
// 开始上传
xhr.open('POST', uploadImgUrl, true);
// xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded"); // 将参数解析成传统form的方式上传
// 修改自定义配置的headers
$.each(headers, function (key, value) {
xhr.setRequestHeader(key, value);
});
// 跨域上传时传cookie
xhr.withCredentials = editor.config.withCredentials || true;
// 发送数据
xhr.send(formData);
timeoutId = setTimeout(timeoutCallback, uploadTimeout);
E.log('...');
};
});
});
// 进度条
_e(function (E, $) {
E.plugin(function () {
var editor = this;
var menuContainer = editor.menuContainer;
var menuHeight = menuContainer.height();
var $editorContainer = editor.$editorContainer;
var width = $editorContainer.width();
var $progress = $('<div class="wangEditor-upload-progress"></div>');
// 渲染事件
var isRender = false;
function render() {
if (isRender) {
return;
}
isRender = true;
$progress.css({
top: menuHeight + 'px'
});
$editorContainer.append($progress);
}
// ------ 显示进度 ------
editor.showUploadProgress = function (progress) {
if (timeoutId) {
clearTimeout(timeoutId);
}
// 显示之前,先判断是否渲染
render();
$progress.show();
$progress.width(progress * width / 100);
};
// ------ 隐藏进度条 ------
var timeoutId;
function hideProgress() {
$progress.hide();
timeoutId = null;
}
editor.hideUploadProgress = function (time) {
if (timeoutId) {
clearTimeout(timeoutId);
}
time = time || 750;
timeoutId = setTimeout(hideProgress, time);
};
});
});
// upload img 插件
_e(function (E, $) {
E.plugin(function () {
var editor = this;
var config = editor.config;
var uploadImgUrl = config.uploadImgUrl;
var uploadTimeout = config.uploadTimeout;
var event;
if (!uploadImgUrl) {
return;
}
// 获取editor的上传dom
var $uploadContent = editor.$uploadContent;
if (!$uploadContent) {
return;
}
// 自定义UI并添加到上传dom节点上
var $uploadIcon = $('<div class="upload-icon-container"><i class="wangeditor-menu-img-upload"></i></div>');
$uploadContent.append($uploadIcon);
// ---------- 构建上传对象 ----------
var upfile = new E.UploadFile({
editor: editor,
uploadUrl: uploadImgUrl,
timeout: uploadTimeout,
fileAccept: 'image/jpg,image/jpeg,image/png,image/gif,image/bmp' // 只允许选择图片
});
// 选择本地文件,上传
$uploadIcon.click(function (e) {
event = e;
upfile.selectFiles();
});
});
});
// h5 方式上传图片
_e(function (E, $) {
if (!window.FileReader || !window.FormData) {
// 如果不支持html5的文档操作直接返回
return;
}
// 构造函数
var UploadFile = function (opt) {
this.editor = opt.editor;
this.uploadUrl = opt.uploadUrl;
this.timeout = opt.timeout;
this.fileAccept = opt.fileAccept;
this.multiple = true;
};
UploadFile.fn = UploadFile.prototype;
// clear
UploadFile.fn.clear = function () {
this.$input.val('');
E.log('input value ');
};
// 渲染
UploadFile.fn.render = function () {
var self = this;
if (self._hasRender) {
// 不要重复渲染
return;
}
E.log('dom');
var fileAccept = self.fileAccept;
var acceptTpl = fileAccept ? 'accept="' + fileAccept + '"' : '';
var multiple = self.multiple;
var multipleTpl = multiple ? 'multiple="multiple"' : '';
var $input = $('<input type="file" ' + acceptTpl + ' ' + multipleTpl + '/>');
var $container = $('<div style="display:none;"></div>');
$container.append($input);
E.$body.append($container);
// onchange 事件
$input.on('change', function (e) {
self.selected(e, $input.get(0));
});
// 记录对象数据
self.$input = $input;
// 记录
self._hasRender = true;
};
// 选择
UploadFile.fn.selectFiles = function () {
var self = this;
E.log('使 html5 ');
// 先渲染
self.render();
// 选择
E.log('');
self.$input.click();
};
// 选中文件之后
UploadFile.fn.selected = function (e, input) {
var self = this;
var files = input.files || [];
if (files.length === 0) {
return;
}
E.log(' ' + files.length + ' ');
// 遍历选中的文件,预览、上传
$.each(files, function (key, value) {
self.upload(value);
});
};
// 上传单个文件
UploadFile.fn.upload = function (file) {
var self = this;
var editor = self.editor;
var filename = file.name || '';
var fileType = file.type || '';
var uploadImgFns = editor.config.uploadImgFns;
var uploadFileName = editor.config.uploadImgFileName || 'wangEditorH5File';
var onload = uploadImgFns.onload;
var ontimeout = uploadImgFns.ontimeout;
var onerror = uploadImgFns.onerror;
var reader = new FileReader();
if (!onload || !ontimeout || !onerror) {
E.error(' onload ontimeout onerror ');
return;
}
E.log(' ' + filename + ' ');
// 清空 input 数据
function clearInput() {
self.clear();
}
// onload事件
reader.onload = function (e) {
E.log('' + filename + '');
var base64 = e.target.result || this.result;
editor.xhrUploadImg({
event: e,
filename: filename,
base64: base64,
fileType: fileType,
name: uploadFileName,
loadfn: function (resultText, xhr) {
clearInput();
// 执行配置中的方法
var editor = this;
onload.call(editor, resultText, xhr);
},
errorfn: function (xhr) {
clearInput();
if (E.isOnWebsite) {
alert('wangEditor');
}
// 执行配置中的方法
var editor = this;
onerror.call(editor, xhr);
},
timeoutfn: function (xhr) {
clearInput();
if (E.isOnWebsite) {
alert('wangEditor');
}
// 执行配置中的方法
var editor = this;
ontimeout(editor, xhr);
}
});
};
// 开始取文件
reader.readAsDataURL(file);
};
// 暴露给 E
E.UploadFile = UploadFile;
});
// form方式上传图片
_e(function (E, $) {
if (window.FileReader && window.FormData) {
// 如果支持 html5 上传,则返回
return;
}
// 构造函数
var UploadFile = function (opt) {
this.editor = opt.editor;
this.uploadUrl = opt.uploadUrl;
this.timeout = opt.timeout;
this.fileAccept = opt.fileAccept;
this.multiple = false;
};
UploadFile.fn = UploadFile.prototype;
// clear
UploadFile.fn.clear = function () {
this.$input.val('');
E.log('input value ');
};
// 隐藏modal
UploadFile.fn.hideModal = function () {
this.modal.hide();
};
// 渲染
UploadFile.fn.render = function () {
var self = this;
var editor = self.editor;
var uploadFileName = editor.config.uploadImgFileName || 'wangEditorFormFile';
if (self._hasRender) {
// 不要重复渲染
return;
}
// 服务器端路径
var uploadUrl = self.uploadUrl;
E.log('dom');
// 创建 form 和 iframe
var iframeId = 'iframe' + E.random();
var $iframe = $('<iframe name="' + iframeId + '" id="' + iframeId + '" frameborder="0" width="0" height="0"></iframe>');
var multiple = self.multiple;
var multipleTpl = multiple ? 'multiple="multiple"' : '';
var $p = $('<p></p>');
var $input = $('<input type="file" ' + multipleTpl + ' name="' + uploadFileName + '"/>');
var $btn = $('<input type="submit" value="上传"/>');
var $form = $('<form enctype="multipart/form-data" method="post" action="' + uploadUrl + '" target="' + iframeId + '"></form>');
var $container = $('<div style="margin:10px 20px;"></div>');
$form.append($p).append($input).append($btn);
// 增加用户配置的参数,如 token
$.each(editor.config.uploadParams, function (key, value) {
$form.append( $('<input type="hidden" name="' + key + '" value="' + value + '"/>') );
});
$container.append($form);
$container.append($iframe);
self.$input = $input;
self.$iframe = $iframe;
// 生成 modal
var modal = new E.Modal(editor, undefined, {
$content: $container
});
self.modal = modal;
// 记录
self._hasRender = true;
};
// 绑定 iframe load 事件
UploadFile.fn.bindLoadEvent = function () {
var self = this;
if (self._hasBindLoad) {
// 不要重复绑定
return;
}
var editor = self.editor;
var $iframe = self.$iframe;
var iframe = $iframe.get(0);
var iframeWindow = iframe.contentWindow;
var onload = editor.config.uploadImgFns.onload;
// 定义load事件
function onloadFn() {
var resultText = $.trim(iframeWindow.document.body.innerHTML);
if (!resultText) {
return;
}
// 获取文件名
var fileFullName = self.$input.val(); // 结果如 C:\folder\abc.png 格式
var fileOriginalName = fileFullName;
if (fileFullName.lastIndexOf('\\') >= 0) {
// 获取 abc.png 格式
fileOriginalName = fileFullName.slice(fileFullName.lastIndexOf('\\') + 1);
if (fileOriginalName.indexOf('.') > 0) {
// 获取 abc (即不带扩展名的文件名)
fileOriginalName = fileOriginalName.split('.')[0];
}
}
// 将文件名暂存到 editor.uploadImgOriginalName ,插入图片时,可作为 alt 属性来用
editor.uploadImgOriginalName = fileOriginalName;
// 执行load函数插入图片的操作应该在load函数中执行
onload.call(editor, resultText);
// 清空 input 数据
self.clear();
// 隐藏modal
self.hideModal();
}
// 绑定 load 事件
if (iframe.attachEvent) {
iframe.attachEvent('onload', onloadFn);
} else {
iframe.onload = onloadFn;
}
// 记录
self._hasBindLoad = true;
};
UploadFile.fn.show = function () {
var self = this;
var modal = self.modal;
function show() {
modal.show();
self.bindLoadEvent();
}
setTimeout(show);
};
// 选择
UploadFile.fn.selectFiles = function () {
var self = this;
E.log('使 form ');
// 先渲染
self.render();
// 先清空
self.clear();
// 显示
self.show();
};
// 暴露给 E
E.UploadFile = UploadFile;
});
// upload img 插件 粘贴图片
_e(function (E, $) {
E.plugin(function () {
var editor = this;
var txt = editor.txt;
var $txt = txt.$txt;
var config = editor.config;
var uploadImgUrl = config.uploadImgUrl;
var uploadFileName = config.uploadImgFileName || 'wangEditorPasteFile';
var pasteEvent;
var $imgsBeforePaste;
// 未配置上传图片url则忽略
if (!uploadImgUrl) {
return;
}
// -------- 非 chrome 下,通过查找粘贴的图片的方式上传 --------
function findPasteImgAndUpload() {
var reg = /^data:(image\/\w+);base64/;
var $imgs = $txt.find('img');
E.log('' + $imgs.length + '');
$.each($imgs, function () {
var img = this;
var $img = $(img);
var flag;
var base64 = $img.attr('src');
var type;
// 判断当前图片是否是粘贴之前的
$imgsBeforePaste.each(function () {
if (img === this) {
// 当前图片是粘贴之前的
flag = true;
return false;
}
});
// 当前图片是粘贴之前的,则忽略
if (flag) {
return;
}
E.log('');
if (reg.test(base64)) {
// 得到的粘贴的图片是 base64 格式,符合要求
E.log('src base64 ');
type = base64.match(reg)[1];
editor.xhrUploadImg({
event: pasteEvent,
base64: base64,
fileType: type,
name: uploadFileName
});
} else {
E.log('src ' + base64 + ' base64 ');
}
// 最终移除原图片
$img.remove();
});
E.log('');
}
// 开始监控粘贴事件
$txt.on('paste', function (e) {
pasteEvent = e;
var data = pasteEvent.clipboardData || pasteEvent.originalEvent.clipboardData;
var text;
var items;
// -------- 试图获取剪切板中的文字,有文字的情况下,就不处理图片粘贴 --------
if (data == null) {
text = window.clipboardData && window.clipboardData.getData('text');
} else {
text = data.getData('text/plain') || data.getData('text/html');
}
if (text) {
return;
}
items = data && data.items;
if (items) {
// -------- chrome 可以用 data.items 取出图片 -----
E.log(' data.items ');
$.each(items, function (key, value) {
var fileType = value.type || '';
if(fileType.indexOf('image') < 0) {
// 不是图片
return;
}
var file = value.getAsFile();
var reader = new FileReader();
E.log('');
reader.onload = function (e) {
E.log('');
// 执行上传
var base64 = e.target.result || this.result;
editor.xhrUploadImg({
event: pasteEvent,
base64: base64,
fileType: fileType,
name: uploadFileName
});
};
//读取粘贴的文件
reader.readAsDataURL(file);
});
} else {
// -------- 非 chrome 不能用 data.items 取图片 -----
E.log(' data.items 使');
// 获取
$imgsBeforePaste = $txt.find('img');
E.log('' + $imgsBeforePaste.length + '');
// 异步上传找到的图片
setTimeout(findPasteImgAndUpload, 0);
}
});
});
});
// 拖拽上传图片 插件
_e(function (E, $) {
E.plugin(function () {
var editor = this;
var txt = editor.txt;
var $txt = txt.$txt;
var config = editor.config;
var uploadImgUrl = config.uploadImgUrl;
var uploadFileName = config.uploadImgFileName || 'wangEditorDragFile';
// 未配置上传图片url则忽略
if (!uploadImgUrl) {
return;
}
// 阻止浏览器默认行为
E.$document.on('dragleave drop dragenter dragover', function (e) {
e.preventDefault();
});
// 监控 $txt drop 事件
$txt.on('drop', function (dragEvent) {
dragEvent.preventDefault();
var originalEvent = dragEvent.originalEvent;
var files = originalEvent.dataTransfer && originalEvent.dataTransfer.files;
if (!files || !files.length) {
return;
}
$.each(files, function (k, file) {
var type = file.type;
var name = file.name;
if (type.indexOf('image/') < 0) {
// 只接收图片
return;
}
E.log(' ' + name);
var reader = new FileReader();
reader.onload = function (e) {
E.log(' ' + name);
// 执行上传
var base64 = e.target.result || this.result;
editor.xhrUploadImg({
event: dragEvent,
base64: base64,
fileType: type,
name: uploadFileName
});
};
//读取粘贴的文件
reader.readAsDataURL(file);
});
});
});
});
// 编辑器区域 table toolbar
_e(function (E, $) {
E.plugin(function () {
var editor = this;
var txt = editor.txt;
var $txt = txt.$txt;
var html = '';
// 说明:设置了 max-height 之后,$txt.parent() 负责滚动处理
var $currentTxt = editor.useMaxHeight ? $txt.parent() : $txt;
var $currentTable;
// 用到的dom节点
var isRendered = false;
var $toolbar = $('<div class="txt-toolbar"></div>');
var $triangle = $('<div class="tip-triangle"></div>');
var $delete = $('<a href="#"><i class="wangeditor-menu-img-trash-o"></i></a>');
var $zoomSmall = $('<a href="#"><i class="wangeditor-menu-img-search-minus"></i></a>');
var $zoomBig = $('<a href="#"><i class="wangeditor-menu-img-search-plus"></i></a>');
// 渲染到页面
function render() {
if (isRendered) {
return;
}
// 绑定事件
bindEvent();
// 拼接 渲染到页面上
$toolbar.append($triangle)
.append($delete)
.append($zoomSmall)
.append($zoomBig);
editor.$editorContainer.append($toolbar);
isRendered = true;
}
// 绑定事件
function bindEvent() {
// 统一执行命令的方法
var commandFn;
function command(e, callback) {
// 执行命令之前先存储html内容
html = $txt.html();
// 监控内容变化
var cb = function () {
if (callback) {
callback();
}
if (html !== $txt.html()) {
$txt.change();
}
};
// 执行命令
if (commandFn) {
editor.customCommand(e, commandFn, cb);
}
}
// 删除
$delete.click(function (e) {
commandFn = function () {
$currentTable.remove();
};
command(e, function () {
setTimeout(hide, 100);
});
});
// 放大
$zoomBig.click(function (e) {
commandFn = function () {
$currentTable.css({
width: '100%'
});
};
command(e, function () {
setTimeout(show);
});
});
// 缩小
$zoomSmall.click(function (e) {
commandFn = function () {
$currentTable.css({
width: 'auto'
});
};
command(e, function () {
setTimeout(show);
});
});
}
// 显示 toolbar
function show() {
if (editor._disabled) {
// 编辑器已经被禁用,则不让显示
return;
}
if ($currentTable == null) {
return;
}
$currentTable.addClass('clicked');
var tablePosition = $currentTable.position();
var tableTop = tablePosition.top;
var tableLeft = tablePosition.left;
var tableHeight = $currentTable.outerHeight();
var tableWidth = $currentTable.outerWidth();
// --- 定位 toolbar ---
// 计算初步结果
var top = tableTop + tableHeight;
var left = tableLeft;
var marginLeft = 0;
var txtTop = $currentTxt.position().top;
var txtHeight = $currentTxt.outerHeight();
if (top > (txtTop + txtHeight)) {
// top 不得超出编辑范围
top = txtTop + txtHeight;
}
// 显示(方便计算 margin
$toolbar.show();
// 计算 margin
var width = $toolbar.outerWidth();
marginLeft = tableWidth / 2 - width / 2;
// 定位
$toolbar.css({
top: top + 5,
left: left,
'margin-left': marginLeft
});
// 如果定位太靠左了
if (marginLeft < 0) {
// 得到三角形的margin-left
$toolbar.css('margin-left', '0');
$triangle.hide();
} else {
$triangle.show();
}
}
// 隐藏 toolbar
function hide() {
if ($currentTable == null) {
return;
}
$currentTable.removeClass('clicked');
$currentTable = null;
$toolbar.hide();
}
// click table 事件
$currentTxt.on('click', 'table', function (e) {
var $table = $(e.currentTarget);
// 渲染
render();
if ($currentTable && ($currentTable.get(0) === $table.get(0))) {
setTimeout(hide, 100);
return;
}
// 显示 toolbar
$currentTable = $table;
show();
// 阻止冒泡
e.preventDefault();
e.stopPropagation();
}).on('click keydown scroll', function (e) {
setTimeout(hide, 100);
});
E.$body.on('click keydown scroll', function (e) {
setTimeout(hide, 100);
});
});
});
// 编辑器区域 img toolbar
_e(function (E, $) {
if (E.userAgent.indexOf('MSIE 8') > 0) {
return;
}
E.plugin(function () {
var editor = this;
var lang = editor.config.lang;
var txt = editor.txt;
var $txt = txt.$txt;
var html = '';
// 说明:设置了 max-height 之后,$txt.parent() 负责滚动处理
var $currentTxt = editor.useMaxHeight ? $txt.parent() : $txt;
var $editorContainer = editor.$editorContainer;
var $currentImg;
var currentLink = '';
// 用到的dom节点
var isRendered = false;
var $dragPoint = $('<div class="img-drag-point"></div>');
var $toolbar = $('<div class="txt-toolbar"></div>');
var $triangle = $('<div class="tip-triangle"></div>');
var $menuContainer = $('<div></div>');
var $delete = $('<a href="#"><i class="wangeditor-menu-img-trash-o"></i></a>');
var $zoomSmall = $('<a href="#"><i class="wangeditor-menu-img-search-minus"></i></a>');
var $zoomBig = $('<a href="#"><i class="wangeditor-menu-img-search-plus"></i></a>');
// var $floatLeft = $('<a href="#"><i class="wangeditor-menu-img-align-left"></i></a>');
// var $noFloat = $('<a href="#"><i class="wangeditor-menu-img-align-justify"></i></a>');
// var $floatRight = $('<a href="#"><i class="wangeditor-menu-img-align-right"></i></a>');
var $alignLeft = $('<a href="#"><i class="wangeditor-menu-img-align-left"></i></a>');
var $alignCenter = $('<a href="#"><i class="wangeditor-menu-img-align-center"></i></a>');
var $alignRight = $('<a href="#"><i class="wangeditor-menu-img-align-right"></i></a>');
var $link = $('<a href="#"><i class="wangeditor-menu-img-link"></i></a>');
var $unLink = $('<a href="#"><i class="wangeditor-menu-img-unlink"></i></a>');
var $linkInputContainer = $('<div style="display:none;"></div>');
var $linkInput = $('<input type="text" style="height:26px; margin-left:10px; width:200px;"/>');
var $linkBtnSubmit = $('<button class="right">' + lang.submit + '</button>');
var $linkBtnCancel = $('<button class="right gray">' + lang.cancel + '</button>');
// 记录是否正在拖拽
var isOnDrag = false;
// 获取 / 设置 链接
function imgLink(e, url) {
if (!$currentImg) {
return;
}
var commandFn;
var callback = function () {
// 及时保存currentLink
if (url != null) {
currentLink = url;
}
if (html !== $txt.html()) {
$txt.change();
}
};
var $link;
var inLink = false;
var $parent = $currentImg.parent();
if ($parent.get(0).nodeName.toLowerCase() === 'a') {
// 父元素就是图片链接
$link = $parent;
inLink = true;
} else {
// 父元素不是图片链接,则重新创建一个链接
$link = $('<a target="_blank"></a>');
}
if (url == null) {
// url 无值,是获取链接
return $link.attr('href') || '';
} else if (url === '') {
// url 是空字符串,是取消链接
if (inLink) {
commandFn = function () {
$currentImg.unwrap();
};
}
} else {
// url 有值,是设置链接
if (url === currentLink) {
return;
}
commandFn = function () {
$link.attr('href', url);
if (!inLink) {
// 当前图片未包含在链接中,则包含进来
$currentImg.wrap($link);
}
};
}
// 执行命令
if (commandFn) {
// 记录下执行命令之前的html内容
html = $txt.html();
// 执行命令
editor.customCommand(e, commandFn, callback);
}
}
// 渲染到页面
function render() {
if (isRendered) {
return;
}
// 绑定事件
bindToolbarEvent();
bindDragEvent();
// 菜单放入 container
$menuContainer.append($delete)
.append($zoomSmall)
.append($zoomBig)
// .append($floatLeft)
// .append($noFloat)
// .append($floatRight);
.append($alignLeft)
.append($alignCenter)
.append($alignRight)
.append($link)
.append($unLink);
// 链接input放入container
$linkInputContainer.append($linkInput)
.append($linkBtnCancel)
.append($linkBtnSubmit);
// 拼接 渲染到页面上
$toolbar.append($triangle)
.append($menuContainer)
.append($linkInputContainer);
editor.$editorContainer.append($toolbar).append($dragPoint);
isRendered = true;
}
// 绑定toolbar事件
function bindToolbarEvent() {
// 统一执行命令的方法
var commandFn;
function customCommand(e, callback) {
var cb;
// 记录下执行命令之前的html内容
html = $txt.html();
cb = function () {
if (callback) {
callback();
}
if (html !== $txt.html()) {
$txt.change();
}
};
// 执行命令
if (commandFn) {
editor.customCommand(e, commandFn, cb);
}
}
// 删除
$delete.click(function (e) {
// 删除之前先unlink
imgLink(e, '');
// 删除图片
commandFn = function () {
$currentImg.remove();
};
customCommand(e, function () {
setTimeout(hide, 100);
});
});
// 放大
$zoomBig.click(function (e) {
commandFn = function () {
var img = $currentImg.get(0);
var width = img.width;
var height = img.height;
width = width * 1.1;
height = height * 1.1;
$currentImg.css({
width: width + 'px',
height: height + 'px'
});
};
customCommand(e, function () {
setTimeout(show);
});
});
// 缩小
$zoomSmall.click(function (e) {
commandFn = function () {
var img = $currentImg.get(0);
var width = img.width;
var height = img.height;
width = width * 0.9;
height = height * 0.9;
$currentImg.css({
width: width + 'px',
height: height + 'px'
});
};
customCommand(e, function () {
setTimeout(show);
});
});
// // 左浮动
// $floatLeft.click(function (e) {
// commandFn = function () {
// $currentImg.css({
// float: 'left'
// });
// };
// customCommand(e, function () {
// setTimeout(hide, 100);
// });
// });
// alignLeft
$alignLeft.click(function (e) {
commandFn = function () {
// 如果 img 增加了链接,那么 img.parent() 就是 a 标签,设置 align 没用的,因此必须找到 P 父节点来设置 align
$currentImg.parents('p').css({
'text-align': 'left'
}).attr('align', 'left');
};
customCommand(e, function () {
setTimeout(hide, 100);
});
});
// // 右浮动
// $floatRight.click(function (e) {
// commandFn = function () {
// $currentImg.css({
// float: 'right'
// });
// };
// customCommand(e, function () {
// setTimeout(hide, 100);
// });
// });
// alignRight
$alignRight.click(function (e) {
commandFn = function () {
// 如果 img 增加了链接,那么 img.parent() 就是 a 标签,设置 align 没用的,因此必须找到 P 父节点来设置 align
$currentImg.parents('p').css({
'text-align': 'right'
}).attr('align', 'right');
};
customCommand(e, function () {
setTimeout(hide, 100);
});
});
// // 无浮动
// $noFloat.click(function (e) {
// commandFn = function () {
// $currentImg.css({
// float: 'none'
// });
// };
// customCommand(e, function () {
// setTimeout(hide, 100);
// });
// });
// alignCenter
$alignCenter.click(function (e) {
commandFn = function () {
// 如果 img 增加了链接,那么 img.parent() 就是 a 标签,设置 align 没用的,因此必须找到 P 父节点来设置 align
$currentImg.parents('p').css({
'text-align': 'center'
}).attr('align', 'center');
};
customCommand(e, function () {
setTimeout(hide, 100);
});
});
// link
// 显示链接input
$link.click(function (e) {
e.preventDefault();
// 获取当前链接,并显示
currentLink = imgLink(e);
$linkInput.val(currentLink);
$menuContainer.hide();
$linkInputContainer.show();
});
// 设置链接
$linkBtnSubmit.click(function (e) {
e.preventDefault();
var url = $.trim($linkInput.val());
if (url) {
// 设置链接,同时会自动更新 currentLink 的值
imgLink(e, url);
}
// 隐藏 toolbar
setTimeout(hide);
});
// 取消设置链接
$linkBtnCancel.click(function (e) {
e.preventDefault();
// 重置链接 input
$linkInput.val(currentLink);
$menuContainer.show();
$linkInputContainer.hide();
});
// unlink
$unLink.click(function (e) {
e.preventDefault();
// 执行 unlink
imgLink(e, '');
// 隐藏 toolbar
setTimeout(hide);
});
}
// 绑定drag事件
function bindDragEvent() {
var _x, _y;
var dragMarginLeft, dragMarginTop;
var imgWidth, imgHeight;
function mousemove (e) {
var diffX, diffY;
// 计算差额
diffX = e.pageX - _x;
diffY = e.pageY - _y;
// --------- 计算拖拽点的位置 ---------
var currentDragMarginLeft = dragMarginLeft + diffX;
var currentDragMarginTop = dragMarginTop + diffY;
$dragPoint.css({
'margin-left': currentDragMarginLeft,
'margin-top': currentDragMarginTop
});
// --------- 计算图片的大小 ---------
var currentImgWidth = imgWidth + diffX;
var currentImggHeight = imgHeight + diffY;
$currentImg && $currentImg.css({
width: currentImgWidth,
height: currentImggHeight
});
}
$dragPoint.on('mousedown', function(e){
if (!$currentImg) {
return;
}
// 当前鼠标位置
_x = e.pageX;
_y = e.pageY;
// 当前拖拽点的位置
dragMarginLeft = parseFloat($dragPoint.css('margin-left'), 10);
dragMarginTop = parseFloat($dragPoint.css('margin-top'), 10);
// 当前图片的大小
imgWidth = $currentImg.width();
imgHeight = $currentImg.height();
// 隐藏 $toolbar
$toolbar.hide();
// 绑定计算事件
E.$document.on('mousemove._dragResizeImg', mousemove);
E.$document.on('mouseup._dragResizeImg', function (e) {
// 取消绑定
E.$document.off('mousemove._dragResizeImg');
E.$document.off('mouseup._dragResizeImg');
// 隐藏,并还原拖拽点的位置
hide();
$dragPoint.css({
'margin-left': dragMarginLeft,
'margin-top': dragMarginTop
});
// 记录
isOnDrag = false;
});
// 记录
isOnDrag = true;
});
}
// 显示 toolbar
function show() {
if (editor._disabled) {
// 编辑器已经被禁用,则不让显示
return;
}
if ($currentImg == null) {
return;
}
$currentImg.addClass('clicked');
var imgPosition = $currentImg.position();
var imgTop = imgPosition.top;
var imgLeft = imgPosition.left;
var imgHeight = $currentImg.outerHeight();
var imgWidth = $currentImg.outerWidth();
// --- 定位 dragpoint ---
$dragPoint.css({
top: imgTop + imgHeight,
left: imgLeft + imgWidth
});
// --- 定位 toolbar ---
// 计算初步结果
var top = imgTop + imgHeight;
var left = imgLeft;
var marginLeft = 0;
var txtTop = $currentTxt.position().top;
var txtHeight = $currentTxt.outerHeight();
if (top > (txtTop + txtHeight)) {
// top 不得超出编辑范围
top = txtTop + txtHeight;
} else {
// top 超出编辑范围dragPoint就不显示了
$dragPoint.show();
}
// 显示(方便计算 margin
$toolbar.show();
// 计算 margin
var width = $toolbar.outerWidth();
marginLeft = imgWidth / 2 - width / 2;
// 定位
$toolbar.css({
top: top + 5,
left: left,
'margin-left': marginLeft
});
// 如果定位太靠左了
if (marginLeft < 0) {
// 得到三角形的margin-left
$toolbar.css('margin-left', '0');
$triangle.hide();
} else {
$triangle.show();
}
// disable 菜单
editor.disableMenusExcept();
}
// 隐藏 toolbar
function hide() {
if ($currentImg == null) {
return;
}
$currentImg.removeClass('clicked');
$currentImg = null;
$toolbar.hide();
$dragPoint.hide();
// enable 菜单
editor.enableMenusExcept();
}
// 判断img是否是一个表情
function isEmotion(imgSrc) {
var result = false;
if (!editor.emotionUrls) {
return result;
}
$.each(editor.emotionUrls, function (index, url) {
var flag = false;
if (imgSrc === url) {
result = true;
flag = true;
}
if (flag) {
return false; // break 循环
}
});
return result;
}
// click img 事件
$currentTxt.on('mousedown', 'img', function (e) {
e.preventDefault();
}).on('click', 'img', function (e) {
var $img = $(e.currentTarget);
var src = $img.attr('src');
if (!src || isEmotion(src)) {
// 是一个表情图标
return;
}
// ---------- 不是表情图标 ----------
// 渲染
render();
if ($currentImg && ($currentImg.get(0) === $img.get(0))) {
setTimeout(hide, 100);
return;
}
// 显示 toolbar
$currentImg = $img;
show();
// 默认显示menuContainer其他默认隐藏
$menuContainer.show();
$linkInputContainer.hide();
// 阻止冒泡
e.preventDefault();
e.stopPropagation();
}).on('click keydown scroll', function (e) {
if (!isOnDrag) {
setTimeout(hide, 100);
}
});
});
});
// 编辑区域 link toolbar
_e(function (E, $) {
E.plugin(function () {
var editor = this;
var lang = editor.config.lang;
var $txt = editor.txt.$txt;
// 当前命中的链接
var $currentLink;
var $toolbar = $('<div class="txt-toolbar"></div>');
var $triangle = $('<div class="tip-triangle"></div>');
var $triggerLink = $('<a href="#" target="_blank"><i class="wangeditor-menu-img-link"></i> ' + lang.openLink + '</a>');
var isRendered;
// 记录当前的显示/隐藏状态
var isShow = false;
var showTimeoutId, hideTimeoutId;
var showTimeoutIdByToolbar, hideTimeoutIdByToolbar;
// 渲染 dom
function render() {
if (isRendered) {
return;
}
$toolbar.append($triangle)
.append($triggerLink);
editor.$editorContainer.append($toolbar);
isRendered = true;
}
// 定位
function setPosition() {
if (!$currentLink) {
return;
}
var position = $currentLink.position();
var left = position.left;
var top = position.top;
var height = $currentLink.height();
// 初步计算top值
var topResult = top + height + 5;
// 判断 toolbar 是否超过了编辑器区域的下边界
var menuHeight = editor.menuContainer.height();
var txtHeight = editor.txt.$txt.outerHeight();
if (topResult > menuHeight + txtHeight) {
topResult = menuHeight + txtHeight + 5;
}
// 最终设置
$toolbar.css({
top: topResult,
left: left
});
}
// 显示 toolbar
function show() {
if (isShow) {
return;
}
if (!$currentLink) {
return;
}
render();
$toolbar.show();
// 设置链接
var href = $currentLink.attr('href');
$triggerLink.attr('href', href);
// 定位
setPosition();
isShow = true;
}
// 隐藏 toolbar
function hide() {
if (!isShow) {
return;
}
if (!$currentLink) {
return;
}
$toolbar.hide();
isShow = false;
}
// $txt 绑定事件
$txt.on('mouseenter', 'a', function (e) {
// 延时 500ms 显示toolbar
if (showTimeoutId) {
clearTimeout(showTimeoutId);
}
showTimeoutId = setTimeout(function () {
var a = e.currentTarget;
var $a = $(a);
$currentLink = $a;
var $img = $a.children('img');
if ($img.length) {
// 该链接下包含一个图片
// 图片点击时隐藏toolbar
$img.click(function (e) {
hide();
});
if ($img.hasClass('clicked')) {
// 图片还处于clicked状态则不显示toolbar
return;
}
}
// 显示toolbar
show();
}, 500);
}).on('mouseleave', 'a', function (e) {
// 延时 500ms 隐藏toolbar
if (hideTimeoutId) {
clearTimeout(hideTimeoutId);
}
hideTimeoutId = setTimeout(hide, 500);
}).on('click keydown scroll', function (e) {
setTimeout(hide, 100);
});
// $toolbar 绑定事件
$toolbar.on('mouseenter', function (e) {
// 先中断掉 $txt.mouseleave 导致的隐藏
if (hideTimeoutId) {
clearTimeout(hideTimeoutId);
}
}).on('mouseleave', function (e) {
// 延时 500ms 显示toolbar
if (showTimeoutIdByToolbar) {
clearTimeout(showTimeoutIdByToolbar);
}
showTimeoutIdByToolbar = setTimeout(hide, 500);
});
});
});
// menu吸顶
_e(function (E, $) {
E.plugin(function () {
var editor = this;
var menuFixed = editor.config.menuFixed;
if (menuFixed === false || typeof menuFixed !== 'number') {
// 没有配置菜单吸顶
return;
}
var bodyMarginTop = parseFloat(E.$body.css('margin-top'), 10);
if (isNaN(bodyMarginTop)) {
bodyMarginTop = 0;
}
var $editorContainer = editor.$editorContainer;
var editorTop = $editorContainer.offset().top;
var editorHeight = $editorContainer.outerHeight();
var $menuContainer = editor.menuContainer.$menuContainer;
var menuCssPosition = $menuContainer.css('position');
var menuCssTop = $menuContainer.css('top');
var menuTop = $menuContainer.offset().top;
var menuHeight = $menuContainer.outerHeight();
var $txt = editor.txt.$txt;
E.$window.scroll(function () {
//全屏模式不支持
if (editor.isFullScreen) {
return;
}
var sTop = E.$window.scrollTop();
// 需要重新计算宽度,因为浏览器可能此时出现滚动条
var menuWidth = $menuContainer.width();
// 如果 menuTop === 0 说明此前编辑器一直隐藏,后来显示出来了,要重新计算相关数据
if (menuTop === 0) {
menuTop = $menuContainer.offset().top;
editorTop = $editorContainer.offset().top;
editorHeight = $editorContainer.outerHeight();
menuHeight = $menuContainer.outerHeight();
}
if (sTop >= menuTop && sTop + menuFixed + menuHeight + 30 < editorTop + editorHeight) {
// 吸顶
$menuContainer.css({
position: 'fixed',
top: menuFixed
});
// 固定宽度
$menuContainer.width(menuWidth);
// 增加body margin-top
E.$body.css({
'margin-top': bodyMarginTop + menuHeight
});
// 记录
if (!editor._isMenufixed) {
editor._isMenufixed = true;
}
} else {
// 取消吸顶
$menuContainer.css({
position: menuCssPosition,
top: menuCssTop
});
// 取消宽度固定
$menuContainer.css('width', '100%');
// 还原 body margin-top
E.$body.css({
'margin-top': bodyMarginTop
});
// 撤销记录
if (editor._isMenufixed) {
editor._isMenufixed = false;
}
}
});
});
});
// 缩进 菜单插件
_e(function (E, $) {
// 用 createMenu 方法创建菜单
E.createMenu(function (check) {
// 定义菜单id不要和其他菜单id重复。编辑器自带的所有菜单id可通过『参数配置-自定义菜单』一节查看
var menuId = 'indent';
// check将检查菜单配置『参数配置-自定义菜单』一节描述中是否该菜单id如果没有则忽略下面的代码。
if (!check(menuId)) {
return;
}
// this 指向 editor 对象自身
var editor = this;
// 创建 menu 对象
var menu = new E.Menu({
editor: editor, // 编辑器对象
id: menuId, // 菜单id
title: '', // 菜单标题
// 正常状态和选中装下的dom对象样式需要自定义
$domNormal: $('<a href="#" tabindex="-1"><i class="wangeditor-menu-img-indent-left"></i></a>'),
$domSelected: $('<a href="#" tabindex="-1" class="selected"><i class="wangeditor-menu-img-indent-left"></i></a>')
});
// 菜单正常状态下,点击将触发该事件
menu.clickEvent = function (e) {
var elem = editor.getRangeElem();
var p = editor.getSelfOrParentByName(elem, 'p');
var $p;
if (!p) {
// 未找到 p 元素,则忽略
return e.preventDefault();
}
$p = $(p);
// 使用自定义命令
function commandFn() {
$p.css('text-indent', '2em');
}
editor.customCommand(e, commandFn);
};
// 菜单选中状态下,点击将触发该事件
menu.clickEventSelected = function (e) {
var elem = editor.getRangeElem();
var p = editor.getSelfOrParentByName(elem, 'p');
var $p;
if (!p) {
// 未找到 p 元素,则忽略
return e.preventDefault();
}
$p = $(p);
// 使用自定义命令
function commandFn() {
$p.css('text-indent', '0');
}
editor.customCommand(e, commandFn);
};
// 根据当前选区,自定义更新菜单的选中状态或者正常状态
menu.updateSelectedEvent = function () {
// 获取当前选区所在的父元素
var elem = editor.getRangeElem();
var p = editor.getSelfOrParentByName(elem, 'p');
var $p;
var indent;
if (!p) {
// 未找到 p 元素,则标记为未处于选中状态
return false;
}
$p = $(p);
indent = $p.css('text-indent');
if (!indent || indent === '0px') {
// 得到的ptext-indent 属性是 0则标记为未处于选中状态
return false;
}
// 找到 p 元素,并且 text-indent 不是 0则标记为选中状态
return true;
};
// 增加到editor对象中
editor.menus[menuId] = menu;
});
});
// 行高 菜单插件
_e(function (E, $) {
// 用 createMenu 方法创建菜单
E.createMenu(function (check) {
// 定义菜单id不要和其他菜单id重复。编辑器自带的所有菜单id可通过『参数配置-自定义菜单』一节查看
var menuId = 'lineheight';
// check将检查菜单配置『参数配置-自定义菜单』一节描述中是否该菜单id如果没有则忽略下面的代码。
if (!check(menuId)) {
return;
}
// this 指向 editor 对象自身
var editor = this;
// 由于浏览器自身不支持 lineHeight 命令因此要做一个hook
editor.commandHooks.lineHeight = function (value) {
var rangeElem = editor.getRangeElem();
var targetElem = editor.getSelfOrParentByName(rangeElem, 'p,h1,h2,h3,h4,h5,pre');
if (!targetElem) {
return;
}
$(targetElem).css('line-height', value + '');
};
// 创建 menu 对象
var menu = new E.Menu({
editor: editor, // 编辑器对象
id: menuId, // 菜单id
title: '', // 菜单标题
commandName: 'lineHeight', // 命令名称
// 正常状态和选中装下的dom对象样式需要自定义
$domNormal: $('<a href="#" tabindex="-1"><i class="wangeditor-menu-img-arrows-v"></i></a>'),
$domSelected: $('<a href="#" tabindex="-1" class="selected"><i class="wangeditor-menu-img-arrows-v"></i></a>')
});
// 数据源
var data = {
// 格式: 'value' : 'title'
'1.0': '1.0',
'1.5': '1.5',
'1.8': '1.8',
'2.0': '2.0',
'2.5': '2.5',
'3.0': '3.0'
};
// 为menu创建droplist对象
var tpl = '<span style="line-height:{#commandValue}">{#title}</span>';
menu.dropList = new E.DropList(editor, menu, {
data: data, // 传入数据源
tpl: tpl // 传入模板
});
// 增加到editor对象中
editor.menus[menuId] = menu;
});
});
// 自定义上传
_e(function (E, $) {
E.plugin(function () {
var editor = this;
var customUpload = editor.config.customUpload;
if (!customUpload) {
return;
} else if (editor.config.uploadImgUrl) {
alert('console.log');
E.error(' uploadImgUrl customUpload ');
return;
}
var $uploadContent = editor.$uploadContent;
if (!$uploadContent) {
E.error(' editor.$uploadContent');
}
// UI
var $uploadIcon = $('<div class="upload-icon-container"><i class="wangeditor-menu-img-upload"></i></div>');
$uploadContent.append($uploadIcon);
// 设置id并暴露
var btnId = 'upload' + E.random();
var containerId = 'upload' + E.random();
$uploadIcon.attr('id', btnId);
$uploadContent.attr('id', containerId);
editor.customUploadBtnId = btnId;
editor.customUploadContainerId = containerId;
});
});
// 版权提示
_e(function (E, $) {
E.info(' wangEditor http://wangeditor.github.io/ ');
});
// 最终返回wangEditor构造函数
return window.wangEditor;
});