'use strict'; /** +------------------------------------------------------------------------------------+ + iceEditor(富文本编辑器) +------------------------------------------------------------------------------------+ + iceEditor v1.1.9 * MIT License By iceui.cn + 作者:ice + 官方:iceui.cn + 时间:2021-06-23 +------------------------------------------------------------------------------------+ + 版权声明:该版权完全归iceUI官方所有,可转载使用和学习,但请务必保留版权信息 +------------------------------------------------------------------------------------+ + iceEditor是一款简约风格的富文本编辑器,体型十分娇小,无任何依赖,整个编辑器只有一个 + 文件,功能却很不平凡!简约的唯美设计,简洁、极速、使用它的时候不需要引用jQuery、font + css……等文件,因为整个编辑器只是一个Js,支持上传图片、附件!支持添加音乐、视频! +------------------------------------------------------------------------------------+ */ var ice = ice || {}; ice.editor = function (id, callback) { class iceEditor { constructor(id) { //------------------------参数配置 开始------------------------ // 工具栏菜单 this.menu = [ 'backColor', 'fontSize', 'foreColor', 'bold', 'italic', 'underline', 'strikeThrough', 'line', 'justifyLeft', 'justifyCenter', 'justifyRight', 'indent', 'outdent', 'line', 'insertOrderedList', 'insertUnorderedList', 'line', 'superscript', 'subscript', 'createLink', 'unlink', 'line', 'hr', 'face', 'table', 'files', 'music', 'video', 'insertImage', 'removeFormat', 'paste', 'line', 'code' ]; // 不需要的工具栏菜单 this.notMenu = []; // 文字背景颜色 this.backColor = [ '#ffffff', '#000000', '#eeece1', '#1f497d', '#4f81bd', '#c0504d', '#9bbb59', '#8064a2', '#4bacc6', '#f79646', '#f2f2f2', '#979797', '#ddd9c3', '#c6d9f0', '#dbe5f1', '#f2dcdb', '#ebf1dd', '#e5e0ec', '#dbeef3', '#fdeada', '#d8d8d8', '#595959', '#c4bd97', '#8db3e2', '#b8cce4', '#e5b9b7', '#d7e3bc', '#ccc1d9', '#b7dde8', '#fbd5b5', '#bfbfbf', '#3f3f3f', '#938953', '#548dd4', '#95b3d7', '#d99694', '#c3d69b', '#b2a2c7', '#92cddc', '#fac08f', '#a5a5a5', '#262626', '#494429', '#17365d', '#366092', '#953734', '#76923c', '#5f497a', '#31859b', '#e36c09', '#7f7f7f', '#0c0c0c', '#1d1b10', '#0f243e', '#244061', '#632423', '#4f6128', '#3f3151', '#205867', '#974806', '#c00000', '#ff0000', '#ffc000', '#ffff00', '#92d050', '#00b050', '#00b0f0', '#0070c0', '#002060', '#7030a0' ]; //文字颜色 this.foreColor = this.backColor; //编辑器的尺寸 this.width = '100%'; this.height = '400px'; //查看源码 this.code = 0; //窗口最大化和最小化 this.maxWindow = 1; //编辑器禁用 this.disabled = 0; //编辑器局部样式 this.css = ''; //编辑器全局样式 this.globalCss = false; //编辑器全局图标 this.globalIcon = false; //图片和附件提交地址 this.uploadUrl = 0; //纯文本粘贴 this.pasteText = 1; //截图粘贴启用 this.screenshot = 1; //截图粘贴直接上传到服务器 this.screenshotUpload = 1; //网络图片上传到服务器 this.imgAutoUpload = 1; //图片下载到本地的域名,默认为本地域名(false),其它域名为数组类型 this.imgDomain = 0; //上传监听 this.ajax.uploadTimeout = 15000; //ajax超时时间 this.ajax.xhr = function () { }; //ajax的xhr设置 this.ajax.formData = function (e) { return e }; //ajax的formData设置 this.ajax.timeout = function () { }; //ajax超时回调 this.ajax.progress = function () { }; //ajax进度回调 this.ajax.success = function (e) { return e }; //ajax成功回调 this.ajax.error = function () { }; //ajax失败回调 this.ajax.complete = function () { }; //ajax成功或失败都回调 //上传附件 this.filesUpload = {}; this.filesUpload.name = 'file[]'; this.filesUpload.formData = function (e) { return e }; this.filesUpload.success = function (e) { return e }; this.filesUpload.error = function () { }; this.filesUpload.complete = function () { }; //上传图片 this.imgUpload = {}; this.imgUpload.name = 'file[]'; this.imgUpload.formData = function (e) { return e }; this.imgUpload.success = function (e) { return e }; this.imgUpload.error = function () { }; this.imgUpload.complete = function () { }; //表情 this.face = [{ title: '文字', type: 'text', list: [{ title: '开心', content: '(^_^)' }, { title: '受不了', content: '(>_<)' }, { title: '鄙视', content: '(¬、¬)' }, { title: '难过', content: '(*>﹏<*)' }, { title: '可爱', content: '(。◕‿◕。)' }, { title: '无奈', content: '╮(╯_╰)╭' }, { title: '惊喜', content: '╰(*°▽°*)╯' }, { title: '听音乐', content: '♪(^∇^*)' }, { title: '害羞', content: '(✿◡‿◡)' }, { title: '睡啦', content: '(∪。∪)..zzZ' }, { title: '臭美', content: '(o≖◡≖)' }, { title: '流汗', content: '(ーー゛)' } ] }]; //HTML标签过滤黑名单-忽略粘贴过来的HTML标签 this.filterTag = ['meta', 'script', 'object', 'form', 'iframe']; //style过滤黑名单-忽略粘贴过来的style样式 this.filterStyle = ['background-image']; //块级元素 this.blockTag = ['address', 'caption', 'dd', 'div', 'dl', 'dt', 'fieldset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'legend', 'fieldset', 'li', 'noframes', 'noscript', 'ol', 'ul', 'p', 'pre', 'table', 'tbody', 'tfoot', 'th', 'thead', 'tr', 'video']; //------------------------参数配置 结束------------------------ //构建功能模块唯一id this.getTime = '1' + String(new Date().getTime()).substr(4, 8); this.iframeId = '_iframe' + this.getTime; this.toolId = '_tool' + this.getTime; this.linkId = '_link' + this.getTime; this.linkInputId = '_LinkInput' + this.getTime; this.musicId = '_music' + this.getTime; this.musicInputId = '_musicInput' + this.getTime; this.videoId = '_video' + this.getTime; this.imageId = '_image' + this.getTime; this.imgUploadId = '_imgUpload' + this.getTime; this.filesId = '_files' + this.getTime; this.filesUploadId = '_filesUpload' + this.getTime; this.tableId = '_table' + this.getTime; this.dragId = '_drag' + this.getTime; //菜单列表对象 this.menuList = {}; //获取编辑器对象 var _z = this; this.editor = this.id(id); if (!this.editor) return alert('请提供一个有效的id'); this.textarea = 0; // 只能是 textarea 和 div ,其他类型的元素不行 if (this.editor.nodeName !== 'TEXTAREA' && this.editor.nodeName !== 'DIV') { return console.log('iceEditor:暂不支持该标签「' + this.editor.nodeName + '」,推荐使用div或textarea'); } if (this.editor.nodeName == 'TEXTAREA') { this.editor.style.display = 'none'; this.divId = '_div' + this.getTime; var div = this.c('div'); div.className = 'iceEditor'; div.id = this.divId; this.insertAfter(div, this.editor); //加载编辑器的内容 this.textarea = this.editor; this.editor = this.id(this.divId); this.value = this.textarea.value; } else { this.editor.className = 'iceEditor'; this.value = this.editor.innerHTML; this.editor.innerHTML = ''; } //创建编辑器配置样式 this.cssConfig = this.c('style'); this.cssConfig.type = 'text/css'; this.editor.appendChild(this.cssConfig); //创建编辑器菜单栏 this.tool = this.c('div'); this.tool.id = this.toolId; this.tool.className = 'iceEditor-tool iceEditor-noselect'; this.editor.appendChild(this.tool); //创建iframe this.iframe = this.c('iframe'); this.iframe.id = this.iframeId; this.iframe.className = 'iceEditor-noselect'; this.iframe.frameBorder = 0; this.editor.appendChild(this.iframe); //创建可拖拽层 this.dragBg = this.c('div'); this.dragBg.className = 'iceEditor-dragBg'; this.editor.appendChild(this.dragBg); //创建编辑器的高度可拖拽容器 this.drag = this.c('div'); this.drag.id = this.dragId; this.drag.className = 'iceEditor-drag iceEditor-noselect'; this.drag.innerHTML = ''; this.editor.appendChild(this.drag); //编辑器拖拽增高 this.drag.onmousedown = function (e) { _z.dragBg.style.display = 'block'; var y = e.clientY; var ch = _z.iframe.clientHeight; window.onmousemove = function (e) { var h = e.clientY - y; if (ch >= 100) { _z.iframe.height = ch + h + 'px'; _z.height = ch + h + 'px'; } else { _z.iframe.height = '100px'; _z.height = ch + h + 'px'; } } window.onmouseup = function () { window.onmousemove = null; window.onmouseup = null; _z.dragBg.style.display = 'none'; } } //创建禁用编辑器的遮罩 this.disableds = this.c('div'); this.disableds.className = 'iceEditor-disabled'; this.editor.appendChild(this.disableds); //获取iframe对象 this.w = this.iframe.contentWindow; //获取iframe Window 对象 this.d = this.iframe.contentDocument; //获取iframe documen 对象 //为了兼容万恶的IE 创建iframe中的body this.d.open(); var value = this.value.trim(); if (!value.length || value.substr(0, 3) != '
') value = '
' + this.value + '
'; this.d.write('' + value + ''); this.d.close(); // 设置元素为可编辑 this.d.body.designMode = 'on'; //打开设计模式 this.d.body.contentEditable = true; // 设置元素为可编辑 this.d.body.addEventListener('click', function () { _z.parentTagName = _z.range.anchorNode.parentNode.tagName; for (var i = 0; i < _z.menu.length; i++) { if (_z.menu[i] == 'line') continue; var a = _z.menuList[_z.menu[i]]; if (_z.d.queryCommandState(_z.menu[i])) { a.className = 'iceEditor-actives'; } else { if (a.className != 'iceEditor-line' && a.className != 'iceEditor-active-s') a.className = ''; } } }) //内容区 this.content = this.d.body; //改变textarea内容 if (this.textarea) { setInterval(function () { _z.setTextarea(); }, 1000); } this.init(); //初始化参数 this.create(); //创建编辑器 this.paste(); //监听粘贴 callback && callback.call(this, this); } id(a) { return document.getElementById(a) } c(a) { return document.createElement(a) } //初始化参数 init() { this.files = null; this.insertImage = null; this.element = this.d.body; //this.element.focus(); //默认获取焦点 this.range = this.d.createRange ? this.w.getSelection() : this.d.selection.createRange(); } //设置textarea setTextarea(n, obj) { if (!this.textarea) return; var html = this.code ? this.html(this.getHTML()) : this.getHTML(); html = html.replace(/(.*?)<\/pre>/gi, function (all, a = '', b = '') {
return '' + b.split('
').join("\n") + '';
});
this.textarea.value = html;
}
//dom后面插入节点
insertAfter(n, obj) {
var parent = obj.parentNode;
if (parent.lastChild == obj) {
parent.appendChild(n, obj);
} else {
parent.insertBefore(n, obj.nextSibling);
}
}
//插入HTML
setHTML(html, a) {
this.element.focus();
var range = this.range.getRangeAt(0);
//将选中的文档放在html中的DOM内
if (!a) html.appendChild(range.extractContents());
//删除选中的内容
range.deleteContents();
//创建文档碎片并放入新节点
range.insertNode(this.w.document.createDocumentFragment().appendChild(html)); //合并范围至末尾
//合并范围至末尾
range.collapse(false);
}
//插入文字内容
setText(text, a) {
this.element.focus();
var range = this.range.getRangeAt(0);
range.deleteContents();
var el = document.createElement('div');
if (a) { //是否为html
el.innerHTML = text;
} else {
el.appendChild(document.createTextNode(text));
}
var frag = document.createDocumentFragment(),
node, lastNode;
while ((node = el.firstChild)) {
lastNode = frag.appendChild(node);
}
range.insertNode(frag);
if (lastNode) {
range = range.cloneRange();
range.setStartAfter(lastNode);
range.collapse(true);
this.range.removeAllRanges();
this.range.addRange(range);
}
range.collapse(true);
}
//获取选中的HTML
getSelectHTML() {
var p = this.c('p');
p.appendChild(this.range.getRangeAt(0).cloneContents());
return p.innerHTML;
}
//获取选中的内容
getSelectText() {
if (this.range.toString() == 'false' || this.range.toString() == '') {
return '';
} else {
return this.range.toString();
}
}
unhtml(str) {
var s = '';
if (str.length == 0) return '';
s = str.replace(/&/g, "&");
s = s.replace(//g, ">");
s = s.replace(/\'/g, "'");
s = s.replace(/\"/g, '"');
return s;
}
html(str) {
var s = '';
if (str.length == 0) return '';
s = str.replace(/</g, "<");
s = s.replace(/>/g, ">");
s = s.replace(/'/g, "\'");
s = s.replace(/"/g, "\"");
s = s.replace(/&/g, "&");
return s;
}
//转义:HTML转成字符串
toText(html) {
var temp = this.c('div');
(temp.textContent != null) ? (temp.textContent = html) : (temp.innerText = html);
var output = temp.innerHTML;
temp = null;
return output;
}
//转义:字符串转成HTML
toHTML(text) {
var temp = this.c('div');
temp.innerHTML = text;
var output = temp.innerText || temp.textContent;
temp = null;
return output;
}
//判断祖先节点是否存在
inNodeParent(el, parent) {
if (!el) return false;
if (el.parentNode) {
if (typeof parent == 'string') {
parent = parent.toUpperCase();
if (el.tagName == parent) return true;
return el.parentNode.tagName == parent ? true : this.inNodeParent(el.parentNode, parent);
} else {
return el.parentNode == parent ? true : this.inNodeParent(el.parentNode, parent);
}
}
return false;
}
//数组查询
inArray(needle, array) {
if (typeof needle == 'string' || typeof needle == 'number') {
for (var i in array)
if (needle === array[i]) return true;
return false;
}
}
// 获取 range 对象
getRangegetRange() {
return this.range.getRangeAt(0);
}
//弹窗
popup(options) {
options = options || {};
var width = options.width || '400'; //默认宽度
var height = options.height || '200'; //默认高度
var title = options.title || ''; //默认不显示标题
var content = options.content || ''; //默认内容
return '' + title + '╳' + content + '';
}
//获取对象距离窗口页面的顶部和左部的距离
getCoords(el) {
var box = el.getBoundingClientRect(),
doc = el.ownerDocument,
body = doc.body,
html = doc.documentElement,
clientTop = html.clientTop || body.clientTop || 0,
clientLeft = html.clientLeft || body.clientLeft || 0,
top = box.top - clientTop,
left = box.left - clientLeft;
return {
'top': top,
'left': left
};
}
//阻止冒泡
pd(event) {
window.event ? window.event.cancelBubble = true : e.stopPropagation();
}
//是否为ie
isIE() {
return !!window.ActiveXObject || "ActiveXObject" in window
}
//异步请求
ajax(json) {
var _z = this;
json = json || {};
if (!json.url) return;
json.timeout = json.timeout || _z.ajax.uploadTimeout;
json.data = json.data || {};
var json2url = function (json) {
var arr = [];
for (var name in json) {
arr.push(name + '=' + encodeURIComponent(json[name]));
}
return arr.join('&');
}
//创建
var xhr = new XMLHttpRequest();
//xhr.withCredentials = false;
//连接 和 发送 - 第二步
//监听进度事件
xhr.addEventListener('progress', progress, false);
xhr.open('POST', json.url, true);
//设置表单提交时的内容类型
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
if (json.data instanceof FormData == false) {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
} else {
json.data = _z.ajax.formData(json.data);
}
_z.ajax.xhr(xhr);
xhr.send(json.data instanceof FormData ? json.data : json2url(json.data));
//接收 - 第三步
json.loading && json.loading();
json.timer = setTimeout(function () {
xhr.onreadystatechange = null;
_z.ajax.timeout(xhr);
json.error && json.error('网络超时。');
}, json.timeout);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
clearTimeout(json.timer);
if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
var res = '';
if (xhr.responseText.length > 0) res = JSON.parse(xhr.responseText);
let newRes = _z.ajax.success(res, xhr);
res = newRes ? newRes : res;
_z.ajax.complete(res, xhr);
json.success && json.success(res);
} else {
_z.ajax.error(xhr);
_z.ajax.complete(xhr);
json.error && json.error(xhr);
}
} else {
_z.ajax.error(xhr);
_z.ajax.complete(xhr);
}
};
//上传进度
function progress(evt) {
var percent = 0;
//百分比
percent = evt.lengthComputable ? Math.round(evt.loaded / evt.total * 100) : 0;
_z.ajax.progress(percent, evt, xhr);
}
}
//创建菜单
createMenu(json) {
var _z = this;
var li = this.c('li');
if (json.id) li.id = json.id;
if (json.css) li.className = json.css;
if (json.style) this.css += json.style;
//将菜单设置成文字或者图标
if (json.menu || json.icon) {
var div = this.c('div');
if (json.title) div.title = json.title;
div.className = 'iceEditor-exec';
if (json.menu) {
div.innerHTML = json.menu;
} else {
if (json.icon) div.innerHTML = '';
}
if (json.data) div.setAttribute('data', json.data);
li.appendChild(div);
}
//使用下拉菜单
if (json.dropdown) {
var div = this.c('div');
div.className = 'iceEditor-menuDropdown';
div.innerHTML = json.dropdown;
li.appendChild(div);
li.openMenu = 1;
li.onmouseover = function () {
if (li.openMenu) div.className = 'iceEditor-menuDropdown iceEditor-menuActive';
}
li.onmouseout = function () {
div.className = 'iceEditor-menuDropdown';
}
var exec = div.getElementsByClassName('iceEditor-exec');
for (var i = 0; i < exec.length; i++) {
exec[i].addEventListener('click', function () {
div.className = 'iceEditor-menuDropdown';
li.openMenu = 0;
setTimeout(function () {
li.openMenu = 1
}, 500);
})
}
}
//使用弹窗
if (json.popup) {
li.innerHTML += this.popup(json.popup);
li.popup = li.getElementsByClassName('iceEditor-popup')[0];
li.onclick = function () {
li.popup.style.display = 'block';
li.popup.getElementsByClassName('iceEditor-popupClose')[0].onclick = function () {
li.popup.style.display = 'none';
_z.pd();
}
}
li.close = function () {
li.popup.style.display = 'none';
_z.pd();
}
}
li.success = json.success ? json.success : false;
//菜单的点击事件
if (json.click) li.onclick = function () {
json.click(this, _z)
};
this.menuList[json.name] = li;
}
//插件开发
plugin(json) {
if (json.name == undefined) return console.log('plugin:menu参数不能为空');
if (this.inArray(json.name, this.menu)) return console.log('plugin:menu已经存在,请重新命名');
this.menu.push(json.name);
this.createMenu(json);
}
//工具栏菜单HTML
menuHTML() {
//文字大小
this.createMenu({
title: '文字大小',
name: 'fontSize',
icon: 'fontSize',
dropdown: '/gim, "<\/p>\n
").replace(/>
\n\n<");
_z.d.body.innerHTML = _z.unhtml(text);
} else {
_z.tool.className = 'iceEditor-tool iceEditor-noselect';
d.style.display = 'none';
this.className = 'iceEditor-exec';
_z.d.body.className = '';
var text = _z.getHTML();
_z.d.body.innerHTML = _z.html(text);
var pre = _z.d.body.getElementsByTagName('pre');
for (var s = 0; s < pre.length; s++) pre[s].innerHTML = pre[s].innerHTML.replace(/\n/g, "
");
}
break;
//最大化
case 'max':
var webHeight = window.innerHeight; //页面视口高度
if (typeof webHeight != 'number') {
if (document.compatMode == 'CSS1Compat') {
webHeight = document.documentElement.clientHeight;
} else {
webWidth = document.body.clientWidth;
}
}
_z.editor.style.position = 'fixed';
_z.editor.style.zIndex = _z.getTime;
_z.editor.style.width = '100%';
_z.editor.style.height = '100%';
_z.editor.style.top = 0;
_z.editor.style.left = 0;
_z.iframe.height = webHeight - 35 - 20 + 'px';
this.parentNode.style.display = 'none';
_z.tool.getElementsByClassName('iceEditor-minWindow')[0].style.display = 'block';
break;
//最小化
case 'min':
_z.editor.removeAttribute('style');
_z.iframe.height = _z.height;
this.parentNode.style.display = 'none';
_z.tool.getElementsByClassName('iceEditor-maxWindow')[0].style.display = 'block';
break;
//默认执行execCommand
default:
var b = this.attr.split('|');
if (!_z.w.document._useStyleWithCSS) {
_z.w.document.execCommand('styleWithCSS', null, true);
_z.w.document._useStyleWithCSS = true;
}
if (b.length > 1) {
_z.w.document.execCommand(b[0], false, b[1]);
} else {
_z.w.document.execCommand(b[0], false, null);
}
//_z.range.getRangeAt(0).collapse(); //取消选中状态
}
return false;
}
}
}
}
//粘贴world
pasteWord(html) {
//是否是word过来的内容
function isWordContent(str) {
return /(class="?Mso|style="[^"]*\bmso\-|w:WordDocument|<(v|o):|lang=)/gi.test(str);
}
//转换cm/pt单位到px
function unitToPx(v) {
if (!/(pt|cm)/.test(v)) return v;
var unit;
v.replace(/([\d.]+)(\w+)/, function (str, s, u) {
v = s, unit = u;
});
v = unit == 'cm' ? parseFloat(v) * 25 : Math.round(parseFloat(v) * 96 / 72);
return v + (v ? 'px' : '');
}
//去掉小数
function transUnit(v) {
return v.replace(/[\d.]+\w+/g,
function (m) {
return unitToPx(m)
});
}
//处理word格式
function filterPasteWord(str) {
return str
.replace(//gi, "")
.replace(//gi, "")
.replace(//gi, '')
.replace(/.*?<\/head>/gi, '')
.replace(/<\/body>/gi, '')
.replace(/<\/html>/gi, '')
.replace(/[\t\s]+/gi, ' ')
.replace(/.*?<\/xml>/gi, '')
.replace(/.*?<\/o:p>/gi, '')
.replace(/]*>\s*<\/span>/gi, '')
.replace(/<(span|font|p|b|i|u|s)[^>]*>\s*<\/\1>/gi, '')
.replace(/v:\w+=(["']?)[^'"]+\1/g, '')
.replace(/]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi, "
$1
")
//去掉多余的属性
.replace(/\s+(class|lang|align)\s*=\s*(['"]?)([\w-]+)\2/gi, function (str, name, marks, val) {
//保留list的标示
return name == "class" && val == "MsoListParagraph" ? str : '';
})
//清除多余的font/span不能匹配 有可能是空格
.replace(/<(font|span)[^>]*>(\s*)<\/\1>/gi, function (a, b, c) {
return c.replace(/[\t\r\n ]+/g, ' ');
})
//处理style的问题
.replace(/(<[a-z][^>]*)\sstyle=(["'])([^\2]*?)\2/gi, function (str, tag, tmp, style) {
var n = [],
s = style.replace(/^\s+|\s+$/, "").replace(/'/g, "'").replace(/"/gi, "'").replace(/[\d.]+(cm|pt)/g, function (str) {
return unitToPx(str);
}).split(/;\s*/g);
for (var i = 0, v; v = s[i]; i++) {
var name, value, parts = v.split(":");
if (parts.length == 2) {
name = parts[0].toLowerCase().trim(), value = parts[1].toLowerCase().trim();
if ((/^(background)\w*/.test(name) && value.replace(/(initial|\s)/g, "").length == 0) || (/^(margin)\w*/.test(name) && /^0\w+$/.test(value))) continue;
switch (name) {
case "mso-vertical-align-alt":
if (!//g, "")
.replace(//g, "")
.replace(//g, '')
.replace(/.*?<\/head>/g, '')
.replace(/<\/body>/g, '')
.replace(/<\/html>/g, '')
.replace(/\s+(id|class|lang|align|data|data-\w*)\s*=\s*(['"]?).*?\2/gi, '');
//过滤被禁止的html标签
for (var i = 0; i < this.filterTag.length; i++) {
html = html.replace(new RegExp("<" + this.filterTag[i] + "[^>]*>.*?<\/" + this.filterTag[i] + ">", "gim"), '');
}
//过滤style属性
var _z = this;
html = html.replace(/\s+style\s*=\s*(['"]?)(.*?)\1/g, function (a, q, b) {
if (b) {
b = b.replace(/('|")/gi, "'"); //防止属性中的单双引号被转义,注意转义以后的分号,因为需要通过分号来分割样式
var info = b.split(';');
if (info.length) {
var h = [];
for (var i = 0; i < info.length; i++) {
var styleName = info[i].trim();
if (styleName) {
var name = styleName.split(':')[0];
if (!_z.filterStyle.includes(name)) {
//用来将rgb颜色转为十六进制,rgba不变
var color = styleName.split(':');
if (color.length > 1 && /rgb\s*\(/gi.test(color[1])) {
color[1] = color[1].replace(/rgb\s*\(.*?\)/gi, function (a) {
a = a.split(/\D+/);
return "#" + ((1 << 24) + (parseInt(a[1]) << 16) + (parseInt(a[2]) << 8) + parseInt(a[3])).toString(16).slice(1);
});
h.push(name + ':' + color[1]);
} else {
h.push(styleName);
}
}
}
}
return ' style="' + h.join(';') + '"';
}
}
return '';
});
//div转p
return html.replace(/]*)>/g, "").replace(/<\/div>/g, "
");
}
//美化HTML格式
formatHTML(html) {
html = html.replace(/[\t\s]+/g, ' ')
//清除空标签
.replace(/<(span|font|p|b|i|u|s)[^>]*>(<(?!img)[^>]*>)*<\/\1>/gi, '')
//清除标签内末尾的空格,起到美化作用
.replace(/<(.+)\s+>/g, '<$1>')
//格式美化
//.replace(/>\s*\n
){1,}/gi, '
');
//格式化块级元素段落
for (var i = 0; i < this.blockTag.length; i++) {
html = html.replace(new RegExp("<\/" + this.blockTag[i] + ">", "gim"), "<\/" + this.blockTag[i] + ">\r\n");
}
return html.replace(/\r\n{1,}/gim, "\r\n").trim();
}
//纯文本粘贴
paste() {
// 干掉IE http之类地址自动加链接
try {
this.w.document.execCommand("AutoUrlDetect", false, false);
} catch (e) { }
var _z = this;
var _w = this.w;
//上传
var upload = function (imgBase64) {
//如果禁用上传到服务器,则直接以base64格式显示图像
if (!_z.screenshotUpload) {
var p = _z.c('p');
var a = _z.c('img');
a.src = imgBase64;
p.appendChild(a);
_z.setHTML(p, true);
return;
}
function dataURItoBlob(base64Data) {
var byteString;
if (base64Data.split(',')[0].indexOf('base64') >= 0) {
byteString = atob(base64Data.split(',')[1]);
} else {
byteString = unescape(base64Data.split(',')[1]);
}
var mimeString = base64Data.split(',')[0].split(':')[1].split(';')[0];
var a = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
a[i] = byteString.charCodeAt(i);
}
return new Blob([a], {
type: mimeString
});
}
var blob = dataURItoBlob(imgBase64);
var formData = new FormData();
formData.append(_z.imgUpload.name, blob);
formData = _z.imgUpload.formData(formData);
_z.ajax({
url: _z.uploadUrl,
data: formData,
success: function (res) {
if (res) {
for (var f = 0; f < res.length; f++) {
var obj = res[f];
if (obj.error) {
_z.imgUpload.error(obj, res);
_z.imgUpload.complete(obj, res);
alert(obj.error);
} else {
obj = _z.imgUpload.success(obj, res);
var p = _z.c('p');
var a = _z.c('img');
a.src = obj.url;
p.appendChild(a);
_z.setHTML(p, true);
_z.imgUpload.success(obj, res);
_z.imgUpload.complete(obj, res);
}
}
} else {
_z.imgUpload.error(res);
_z.imgUpload.complete(res);
}
},
error: function (xhr) {
_z.imgUpload.error(xhr);
_z.imgUpload.complete(xhr);
}
})
};
var getBase64 = function () {
setTimeout(function () {
//保证图片先插入到div里,然后去获取值
var imgList = _z.d.body.getElementsByTagName('img');
for (var i = 0; i < imgList.length; i++) {
if (imgList[i].src.substr(0, 5) == 'data:') {
upload(imgList[i].src);
imgList[i].parentNode.removeChild(imgList[i]);
break;
}
}
}, 10);
};
this.d.body.addEventListener('paste', function (e) {
// console.log(_z.range.getRangeAt(0).endContainer);
// console.log(_z.range.getRangeAt(0).startOffset);
if (!_z.isIE()) e.preventDefault();
var clip = (window.clipboardData || e.clipboardData || e.originalEvent.clipboardData);
//获取粘贴板数据
var text = clip.getData('Text');
var str = _z.pasteText && text.length ? text : (clip.getData('text/html').length ? clip.getData('text/html') : text);
var htmlContent = clip.getData('text/html') ? true : false;
//富文本粘贴模式开启状态下
if (htmlContent && !_z.pasteText) {
//复制过来的数据有些情况会被转义,需要再次转义回来,单双引号全部转为单引号比较可靠
str = str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/ /g, " ").replace(/'/g, "'").replace(/"/g, "'");
}
//截图粘贴功能 判断是否开启,判断是否在pre标签中
if (_z.screenshot && !_z.inNodeParent(_z.range.getRangeAt(0).endContainer, 'pre')) {
if (clip) {
//ie11没有items
var blob = clip.items ? (clip.items[0] && clip.items[0].type.indexOf("image") !== -1 ? clip.items[0].getAsFile() : 0) : 0;
if (blob) {
var reader = new FileReader();
reader.onload = function (e) {
//图片的Base64编码字符串
var base64_str = e.target.result;
upload(base64_str);
}
reader.readAsDataURL(blob);
}
}
getBase64();
}
if (!str.length) return;
//源码模式下直接纯文本粘贴
if (_z.code) {
_z.setText(text);
return;
}
var t = str.replace(/[\r|\n]+/g, "\n").split("\n");
//判断光标是否在pre标签中
if (_z.inNodeParent(_z.range.getRangeAt(0).endContainer, 'pre')) {
var t = text.replace(/[\r|\n]+/g, "\n").split("\n");
for (var i = 0; i < t.length; i++) {
t[i] = _z.toText(t[i]);
}
_z.setText(t.join('
'), true);
return;
}
if (_z.pasteText && t.length == 1) {
_z.setText(str);
return;
}
//纯文本粘贴
if (_z.pasteText || !htmlContent) {
for (var i = 0; i < t.length; i++) {
if (t[i]) { //有效去除空标签
_z.setText('
' + t[i].trim() + '
', true);
}
}
} else {
//过滤word
str = _z.pasteWord(str);
str = _z.formatHTML(str);
//过滤HTML
str = _z.pasteHTML(str);
//格式化HTML
str = _z.formatHTML(str);
_z.setText(str, true);
}
//处理冗余标签
str = _z.getHTML();
_z.d.body.innerHTML = str;
str = _z.getHTML();
str = str.replace(/
<\/p>/gi, '')
.replace(/<\/p>
/gi, '')
.replace(/<(span|font|p|b|i|u|s)[^>]*>(<(?!img)[^>]*>)*<\/\1>/gi, '');
_z.d.body.innerHTML = str;
//下载网络图片到本地
if (_z.imgAutoUpload) {
var str = _z.getHTML();
str.replace(/
/gi, function (all, b = '') {
//这里必须使用闭包,因为使用了异步
(function (a) {
//判断是否为本地图片
if (b.substr(0, 1) == '/' && b.substr(0, 2) != '//') return;
//如果为网络图片,过滤白名单域名
_z.imgDomain = _z.imgDomain && Array.isArray(_z.imgDomain) ? _z.imgDomain : [document.domain];
//将当前域名加入白名单
!_z.imgDomain.includes(document.domain) && _z.imgDomain.push(document.domain);
for (var i = 0; i < _z.imgDomain.length; i++) {
if (new RegExp("^((http|https):)*(\/)*" + _z.imgDomain[i], "i").test(a)) {
return;
}
}
_z.ajax({
url: _z.uploadUrl,
data: {
'iceEditor-img': a
},
success: function (res) {
if (res && !res.error) {
res = _z.imgUpload.success(res);
_z.imgUpload.complete(res);
str = str.replace(new RegExp(a, 'gi'), res.url);
_z.setValue(str);
} else {
_z.imgUpload.error(res, res);
_z.imgUpload.complete(res, res);
}
},
error: function (xhr) {
_z.imgUpload.error(xhr);
_z.imgUpload.complete(xhr);
}
})
})(b);
});
}
});
function nodePrev(el) {
if (!el) return false;
var node = el.nextSibling;
if (node && node.nodeType != 1) node = nodePrev(node);
return node;
}
this.d.body.addEventListener('keydown', function (e) {
var range = _z.range.getRangeAt(0);
if (e.keyCode == 13) {
//回车处理pre中的代码
if (_z.inNodeParent(range.endContainer, 'pre')) {
//这一步是真特么费劲
if (_z.range.anchorNode.parentNode.tagName == 'PRE') {
_z.element.focus();
//判断一下光标是否处于当前节点文字的末尾
var isCursorEnd = range.endContainer.length == range.startOffset;
//Chrome浏览器有个非常操蛋的毛病,就是如果当前节点是最后一个,输入文字后回车
//第一次换行不起作用,需要两次回车才能换行
//所以需要判断当前节点是否为最后一个节点,完全是给Chrome用的
var isNodeEnd = nodePrev(range.endContainer) ? false : true;
var br = isNodeEnd ? '
' : '
';
var range = _z.range.getRangeAt(0);
range.insertNode(range.createContextualFragment(br));
//接下来这一步是为了修正光标位置
var node = _z.range.anchorNode.nextSibling.nextSibling;
range.setStart(node, 0);
range.setEnd(node, 0);
range.collapse();
} else if (_z.parentTagName == 'PRE' || _z.range.anchorNode.tagName == 'PRE') {
_z.setText('
', true);
}
e.preventDefault();
return;
}
}
// 去除Crtl+b/Ctrl+i/Ctrl+u等快捷键
// e.metaKey for mac
if (e.ctrlKey || e.metaKey) {
switch (e.keyCode) {
case 13: {
e.preventDefault();
break;
}
case 66: //ctrl+B or ctrl+b
case 98:
case 73: //ctrl+I or ctrl+i
case 105:
case 85: //ctrl+U or ctrl+u
case 117: {
e.preventDefault();
break;
}
}
}
});
}
//配置格式化
create() {
//添加样式
if (this.cssConfig.styleSheet) {
this.cssConfig.styleSheet.cssText = this.css;
} else {
this.cssConfig.innerHTML = this.css;
}
this.menuFormat();
this.menuAction();
this.disableds.style.display = this.disabled ? 'block' : 'none';
this.styleInit(); //渲染样式
}
//获取编辑器的HTML内容
getHTML() {
return this.content.innerHTML;
}
//获取编辑器的Text内容
getText() {
return this.content.innerText;
}
//获取编辑器的HTML内容,等同getHTML
getValue() {
return this.content.innerHTML;
}
//设置编辑器的内容
setValue(v) {
this.content.innerHTML = v;
this.setTextarea();
}
//追加编辑器的内容
addValue(v) {
this.content.innerHTML += v;
this.setTextarea();
}
//禁止输入
inputDisabled() {
this.d.body.designMode = 'off';
this.d.body.contentEditable = false;
}
//启动输入
inputEnable() {
this.d.body.designMode = 'on';
this.d.body.contentEditable = true;
}
//监听输入
inputCallback(fn) {
var _z = this;
_z.d.body.oninput = function () {
fn && fn.call(_z, _z.getHTML(), _z.getText());
}
_z.d.body.addEventListener('paste', function (e) {
fn && fn.call(_z, _z.getHTML(), _z.getText());
})
}
//加载样式
styleInit() {
//编辑器图标
ice.editor.css = this.globalCss ? this.globalCss : '.iceEditor{color:#353535!important;font-family:"Microsoft YaHei";font-size:14px!important;background:#fff;position:relative;border:solid 1px #ccc}.iceEditor *{margin:0;padding:0;box-sizing:border-box;font-size:14px;}.iceEditor a{color:#606060;text-decoration:none;-webkit-tap-highlight-color:transparent}.iceEditor a:hover{color:#000}.iceEditor ul{margin:initial;padding:initial}.iceEditor-line{height:20px;width:1px;background:#cecece;margin:8px 8px 0 8px !important}.iceEditor-row{margin-bottom:10px;}.iceEditor-group{text-align:left;margin-bottom:10px;}.iceEditor-group label {min-width:50px!important;display:inline-block!important;text-align:right!important;font-weight:normal!important;}.iceEditor input{height:27px!important;line-height:27px!important;padding:3px!important;border:1px solid #B7B7B7!important;font-family:inherit;font-size:inherit;vertical-align:middle;outline:none;display:inline-block!important;}.iceEditor-exec{cursor:pointer}.iceEditor-icon{width:22px;height:16px;fill:currentColor;overflow:hidden;vertical-align:middle;font-size:16px}.iceEditor-noselect{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.iceEditor-menuDropdown{min-width:35px;min-height:35px;transition:all .4s ease;margin-top:60px;opacity:0;visibility:hidden;position:absolute;background:#fff;z-index:999;box-shadow:0 2px 9px 0 rgba(0,0,0,.2);border-bottom:2px solid #676767;border-top:1px solid #676767}.iceEditor-menuDropdown::before{content:"";display:block;width:0;height:0;border-left:8px solid transparent;border-right:8px solid transparent;border-bottom:8px solid #676767;position:absolute;top:-8px;left:9px}.iceEditor-menuTitle{width:100%!important;text-align:center;height:30px;line-height:30px;border-top:1px solid #efefef}.iceEditor-tool{width:100%;background:#eee;border-bottom:solid 1px #ccc;position:relative}.iceEditor-tool:after,.iceEditor-tool:before{display:table;content:" "}.iceEditor-tool:after{clear:both}.iceEditor-menu{margin:0!important;width:100%;padding:0 10px;display:inline-block;float:left}.iceEditor-menu a{list-style:none;float:left;min-width:35px;height:35px;padding:0 5px;text-align:center;line-height:35px;cursor:pointer}.iceEditor-menu a:hover{background:#cdcdcd}.iceEditor-menu>li>div.iceEditor-exec{list-style:none;float:left;min-width:35px;height:35px;padding:0 5px;text-align:center;line-height:35px;cursor:pointer}.iceEditor-menu>li>div.iceEditor-exec:hover{background:#cdcdcd}.iceEditor-menu svg{fill:currentColor;overflow:hidden;vertical-align:middle;font-size:16px}.iceEditor-menu .iceEditor-active{background:#e0e0e0;position:relative;z-index:999}.iceEditor-menu .iceEditor-actives,.iceEditor-menu .iceEditor-active-s{background:#d8d8d8;}.iceEditor-menu .iceEditor-disabledMask{background:rgba(238,238,238,0.7);width:100%;height:100%;position:absolute;left:0;top:0;display:none}.iceEditor-menu li{margin:0;display:inline-block;float:left;line-height:initial;}.iceEditor-menu li .iceEditor-menuDropdown.iceEditor-menuActive{margin-top:44px;opacity:1;visibility:visible}.iceEditor-menu li.iceEditor-minWindow{display:none}.iceEditor-menu li.iceEditor-maxWindow,.iceEditor-menu li.iceEditor-minWindow{float:right}.iceEditor-menu li.iceEditor-maxWindow>div,.iceEditor-menu li.iceEditor-minWindow>div{position:relative;z-index:9}.iceEditor-menu li.iceEditor-maxWindow .iceEditor-icon,.iceEditor-menu li.iceEditor-minWindow .iceEditor-icon{color:#606060}.iceEditor-codeLanguages select{padding:5px 5px;width:120px;outline:none;font-size:15px;margin-top:10px;}.iceEditor input.iceEditor-uploadInput{display:none!important}.iceEditor-uploadBtn{float:none;width:auto;font-size:15px;background:#00b7ee;height:40px;line-height:40px;padding:0 30px;color:#fff;display:inline-block;margin:0 auto 15px auto;cursor:pointer;box-shadow:0 1px 1px rgba(0,0,0,.1)}.iceEditor-uploadBtn:hover{background:#009ccb}.iceEditor-uploadIcon{width:45px;height:45px;color:#bababa;margin:20px 20px 10px}.iceEditor-backColor{width:230px;padding:5px!important}.iceEditor-backColor span{width:20px;height:20px;padding:0;margin:1px;display:inline-block}.iceEditor-fontSize{width:280px}.iceEditor-fontSize li{width:40px;text-align:center}.iceEditor-fontSize span{width:40px;display:inline-block;padding:10px 0}.iceEditor-fontSize span:hover{background:#eee;color:#4CAF50}.iceEditor-createLink label{display:inline-block;}.iceEditor .iceEditor-link{width:175px!important;}.iceEditor-popup .iceEditor-insertImage{text-align:center}.iceEditor-popup .iceEditor-insertImageUrl{width:220px!important;height:27px;outline:0;margin-right:15px}.iceEditor-popup .iceEditor-inputWidth{width:50px!important;height:27px;outline:0;margin-right:15px}.iceEditor-popup .iceEditor-inputHeight{width:50px!important;height:27px;outline:0}.iceEditor-popup .iceEditor-btn{width:auto;display:inline-block;float:none;color:#fff!important;height:27px;line-height:25px;padding:0 10px;background:#939393;vertical-align:middle;margin-left:5px;border:1px solid #7b7b7b}.iceEditor-popup .iceEditor-btn:hover{background:#7b7b7b!important;color:#fff}.iceEditor-tableBox{position:relative;width:190px;height:214px;padding:5px;overflow:hidden}.iceEditor-tableBgOn{position:absolute!important;top:5px;left:5px;z-index:4;width:18px;height:18px;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASAgMAAAAroGbEAAAACVBMVEUAAIjd6vvD2f9LKLW+AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfYAR0BKwNDEVT0AAAAG0lEQVQI12NgAAOtVatWMTCohoaGUY+EmIkEAEruEzK2J7tvAAAAAElFTkSuQmCC) repeat}.iceEditor-tableBgOff{width:180px;height:180px;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASAgMAAAAroGbEAAAACVBMVEUAAIj4+Pjp6ekKlAqjAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfYAR0BKhmnaJzPAAAAG0lEQVQI12NgAAOtVatWMTCohoaGUY+EmIkEAEruEzK2J7tvAAAAAElFTkSuQmCC) repeat}.iceEditor-tableNum{height:30px;line-height:30px;text-align:center;color:#757575}.iceEditor-video{text-align:left}.iceEditor-video label{margin-right:20px;display:inline-block}.iceEditor-video input{margin-right:5px}.iceEditor-video div{height:27px;margin-bottom:10px}.iceEditor-popup .iceEditor-videoUrl{width:255px!important;height:27px;outline:0;margin-right:0}.iceEditor-content{width:100%;height:100%;padding:20px;position:relative}.iceEditor-content:focus{outline:0}.iceEditor-dragBg{position:absolute;width:100%;height:100%;top:0;left:0;z-index:1;display:none;}.iceEditor-drag{color:#757575;background:#eee;text-align:center;height:12px;line-height:0;cursor:n-resize}.iceEditor-disabled{position:absolute;width:100%;height:100%;top:0;left:0;background:rgba(191,191,191,.79);z-index:99999;display:none}.iceEditor-popup{display:none}.iceEditor-popupMain{width:400px;height:200px;position:fixed;margin:auto;top:0;bottom:0;left:0;right:0;background:#fff;box-shadow:0 1px 1px rgba(0,0,0,.12);z-index:9999;animation-name:iceEditorPopup;animation-duration:.5s}.iceEditor-popupBox{width:100%;height:100%;position:fixed;top:0;left:0;background:rgba(0,0,0,.33);opacity:.5;filter:alpha(opacity=50);z-index:1}.iceEditor-popupTitle{width:100%;height:30px;line-height:30px;background:#2f2f2f;padding:0 10px;color:#fff}.iceEditor-popupTitle span{display:inline-block;vertical-align:middle}.iceEditor-popupTitle::before{content:"";display:inline-block;width:10px;height:10px;border-radius:10px;background:#c7f98c;vertical-align:middle;margin-right:8px}.iceEditor-popupClose{float:right;padding:0 10px;color:#fff;font-size:18px;cursor:pointer}.iceEditor-popupClose:hover{color:#8fe5ff}.iceEditor-popupContent{width:100%;padding:10px;color:#000;overflow:auto;float:left}.iceEditor-popupBtn{width:100%;border:0;color:#fff;background:#03A9F4;border-top:1px solid #efefef;padding:0 20px;margin:0;height:35px;text-align:center;line-height:35px;cursor:pointer;margin-top:20px;outline:0}.iceEditor-popupBtn:hover{color:#151515;background:#efefef}@keyframes iceEditorPopup{0%{top:-100px;opacity:0}to{top:0;opacity:1}}';
//编辑器图标
ice.editor.svg = this.globalIcon ? this.globalIcon : '';
(function svg() {
var a = document.getElementById('iceEditor-css');
if (a) a.parentNode.removeChild(a);
a = document.getElementById('iceEditor-icon');
if (a) a.parentNode.removeChild(a);
var c = document.createElement('style'),
d, s, b = document.body;
if (c.styleSheet) {
c.styleSheet.cssText = ice.editor.css;
} else {
c.innerHTML = ice.editor.css;
}
c.id = 'iceEditor-css';
document.getElementsByTagName('head')[0].appendChild(c);
d = document.createElement("div");
d.innerHTML = ice.editor.css + ice.editor.svg;
ice.editor.svg = null;
s = d.getElementsByTagName("svg")[0];
if (s) {
s.id = 'iceEditor-icon';
s.setAttribute("aria-hidden", "true");
s.style.position = "absolute";
s.style.width = 0;
s.style.height = 0;
s.style.overflow = "hidden";
if (b.firstChild) {
b.firstChild.parentNode.insertBefore(s, b.firstChild)
} else {
b.appendChild(s)
}
}
})();
}
}
return new iceEditor(id, callback);
}