/** @Name:layui.layedit 富文本编辑器 @Author:贤心 @License:MIT @Edit by KnfieZ */ layui.define(['layer', 'form'], function (exports) { "use strict"; var $ = layui.$ , layer = layui.layer , form = layui.form , hint = layui.hint() , device = layui.device() , MOD_NAME = 'layedit', THIS = 'layui-this', SHOW = 'layui-show', ABLED = 'layui-disabled' , Edit = function () { var that = this; that.index = 0; //全局配置 that.config = { //默认工具bar tool: [ 'strong', 'italic', 'underline', 'del' , '|' , 'left', 'center', 'right' , '|' , 'link', 'unlink', 'face', 'image' ] , hideTool: [] , height: 280 //默认高 }; }; //全局设置 Edit.prototype.set = function (options) { var that = this; $.extend(true, that.config, options); return that; }; //事件监听 Edit.prototype.on = function (events, callback) { return layui.onevent(MOD_NAME, events, callback); }; //建立编辑器 Edit.prototype.build = function (id, settings) { settings = settings || {}; var that = this , config = that.config , ELEM = 'layui-layedit', textArea = $(typeof (id) == 'string' ? '#' + id : id) , name = 'LAY_layedit_' + (++that.index) , haveBuild = textArea.next('.' + ELEM) , set = $.extend({}, config, settings) , tool = function () { var node = [], hideTools = {}; layui.each(set.hideTool, function (_, item) { hideTools[item] = true; }); layui.each(set.tool, function (_, item) { if (tools[item] && !hideTools[item]) { node.push(tools[item]); } }); return node.join(''); }() , editor = $(['
' , '
' + tool + '
' , '
' , '' , '
' , '
'].join('')) //编辑器不兼容ie8以下 if (device.ie && device.ie < 8) { return textArea.removeClass('layui-hide').addClass(SHOW); } haveBuild[0] && (haveBuild.remove()); setIframe.call(that, editor, textArea[0], set) textArea.addClass('layui-hide').after(editor); return that.index; }; //获得编辑器中内容 Edit.prototype.getContent = function (index) { var iframeWin = getWin(index); if (!iframeWin[0]) return; return toLower(iframeWin[0].document.body.innerHTML); }; //获得编辑器中纯文本内容 Edit.prototype.getText = function (index) { var iframeWin = getWin(index); if (!iframeWin[0]) return; return $(iframeWin[0].document.body).text(); }; /** * 设置编辑器内容 * @param {[type]} index 编辑器索引 * @param {[type]} content 要设置的内容 * @param {[type]} flag 是否追加模式 */ Edit.prototype.setContent = function (index, content, flag) { var iframeWin = getWin(index); if (!iframeWin[0]) return; if (flag) { $(iframeWin[0].document.body).append(content) } else { $(iframeWin[0].document.body).html(content) }; layedit.sync(index) }; //将编辑器内容同步到textarea(一般用于异步提交时) Edit.prototype.sync = function (index) { var iframeWin = getWin(index); if (!iframeWin[0]) return; var textarea = $('#' + iframeWin[1].attr('textarea')); textarea.val(toLower(iframeWin[0].document.body.innerHTML)); }; //获取编辑器选中内容 Edit.prototype.getSelection = function (index) { var iframeWin = getWin(index); if (!iframeWin[0]) return; var range = Range(iframeWin[0].document); return document.selection ? range.text : range.toString(); }; //iframe初始化 var setIframe = function (editor, textArea, set) { var that = this, iframe = editor.find('iframe'); iframe.css({ height: set.height }).on('load', function () { var conts = iframe.contents() , iframeWin = iframe.prop('contentWindow') , head = conts.find('head') , style = $([''].join('')) , body = conts.find('body'); head.append(style); body.attr('contenteditable', 'true').css({ 'min-height': set.height }).html(textArea.value || ''); hotkey.apply(that, [iframeWin, iframe, textArea, set]); //快捷键处理 toolActive.call(that, iframeWin, editor, set); //触发工具 }); } //获得iframe窗口对象 , getWin = function (index) { var iframe = $('#LAY_layedit_' + index) , iframeWin = iframe.prop('contentWindow'); return [iframeWin, iframe]; } //IE8下将标签处理成小写 , toLower = function (html) { if (device.ie == 8) { html = html.replace(/<.+>/g, function (str) { return str.toLowerCase(); }); } return html; } //快捷键处理 , hotkey = function (iframeWin, iframe, textArea, set) { var iframeDOM = iframeWin.document, body = $(iframeDOM.body); body.on('keydown', function (e) { var keycode = e.keyCode; //处理回车 if (keycode === 13) { var range = Range(iframeDOM); var container = getContainer(range) , parentNode = container.parentNode; if (parentNode.tagName.toLowerCase() === 'pre') { if (e.shiftKey) return layer.msg('请暂时用shift+enter'); return false; } iframeDOM.execCommand('formatBlock', false, '

'); } }); //给textarea同步内容 $(textArea).parents('form').on('submit', function () { var html = body.html(); //IE8下将标签处理成小写 if (device.ie == 8) { html = html.replace(/<.+>/g, function (str) { return str.toLowerCase(); }); } textArea.value = html; }); //处理粘贴 body.on('paste', function (e) { iframeDOM.execCommand('formatBlock', false, '

'); setTimeout(function () { filter.call(iframeWin, body); textArea.value = body.html(); }, 100); }); } //标签过滤 , filter = function (body) { var iframeWin = this , iframeDOM = iframeWin.document; //清除影响版面的css属性 body.find('*[style]').each(function () { var textAlign = this.style.textAlign; this.removeAttribute('style'); $(this).css({ 'text-align': textAlign || '' }) }); //修饰表格 body.find('table').addClass('layui-table'); //移除不安全的标签 body.find('script,link').remove(); } //Range对象兼容性处理 , Range = function (iframeDOM) { return iframeDOM.selection ? iframeDOM.selection.createRange() : iframeDOM.getSelection().getRangeAt(0); } //当前Range对象的endContainer兼容性处理 , getContainer = function (range) { return range.endContainer || range.parentElement().childNodes[0] } //在选区插入内联元素 , insertInline = function (tagName, attr, range) { var iframeDOM = this.document , elem = document.createElement(tagName) for (var key in attr) { elem.setAttribute(key, attr[key]); } elem.removeAttribute('text'); if (iframeDOM.selection) { //IE var text = range.text || attr.text; if (tagName === 'a' && !text) return; if (text) { elem.innerHTML = text; } range.pasteHTML($(elem).prop('outerHTML')); range.select(); } else { //非IE var text = range.toString() || attr.text; if (tagName === 'a' && !text) return; if (text) { elem.innerHTML = text; } range.deleteContents(); range.insertNode(elem); } } //工具选中 , toolCheck = function (tools, othis) { var iframeDOM = this.document , CHECK = 'layedit-tool-active' , container = getContainer(Range(iframeDOM)) , item = function (type) { return tools.find('.layedit-tool-' + type) } if (othis) { othis[othis.hasClass(CHECK) ? 'removeClass' : 'addClass'](CHECK); } tools.find('>i').removeClass(CHECK); item('unlink').addClass(ABLED); $(container).parents().each(function () { var tagName = this.tagName.toLowerCase() , textAlign = this.style.textAlign; //文字 if (tagName === 'b' || tagName === 'strong') { item('b').addClass(CHECK) } if (tagName === 'i' || tagName === 'em') { item('i').addClass(CHECK) } if (tagName === 'u') { item('u').addClass(CHECK) } if (tagName === 'strike') { item('d').addClass(CHECK) } //对齐 if (tagName === 'p') { if (textAlign === 'center') { item('center').addClass(CHECK); } else if (textAlign === 'right') { item('right').addClass(CHECK); } else { item('left').addClass(CHECK); } } //超链接 if (tagName === 'a') { item('link').addClass(CHECK); item('unlink').removeClass(ABLED); } }); } //触发工具 , toolActive = function (iframeWin, editor, set) { var iframeDOM = iframeWin.document , body = $(iframeDOM.body) , toolEvent = { //超链接 link: function (range) { debugger; var container = getContainer(range) , parentNode = $(container).parent(); link.call(body, { href: parentNode.attr('href') , target: parentNode.attr('target') }, function (field) { var parent = parentNode[0]; if (parent.tagName === 'A') { parent.href = field.url; } else { insertInline.call(iframeWin, 'a', { target: field.target , href: field.url , text: field.url }, range); } }); } //清除超链接 , unlink: function (range) { iframeDOM.execCommand('unlink'); } //表情 , face: function (range) { face.call(this, function (img) { insertInline.call(iframeWin, 'img', { src: img.src , alt: img.alt }, range); }); } //图片1 , image: function (range) { var that = this; layui.use('upload', function (upload) { var uploadImage = set.uploadImage || {}; upload.render({ url: uploadImage.url , method: uploadImage.type , elem: $(that).find('input')[0] , done: function (res) { if (res.code == 0) { res.data = res.data[0] || {}; insertInline.call(iframeWin, 'img', { //src: res.data.src src:window.hywa.config.href+window.hywa.config.port['show_img']+encodeURIComponent(res.data.src) , alt: res.data.title }, range); } else { layer.msg(res.msg || '上传失败'); } } }); }); } //图片2 , image_alt: function (range) { debugger; var that = this; layer.open({ type: 1 , id: 'fly-jie-image-upload' , title: '插入图片' , shade: false , area: '465px' , skin: 'layui-layer-border' , content: ['

'].join('') , success: function (layero, index) { var upload = layui.upload; var loding, altStr = layero.find('input[name="altStr"]'), Imgsrc = layero.find('input[name="Imgsrc"]'); var uploadImage = set.uploadImage || {}; //执行实例 upload.render({ elem: '#LayEdit_InsertImage' , url: uploadImage.url , method: uploadImage.type , before: function (obj) { loding = layer.msg('文件上传中,请稍等哦', { icon: 16, shade: 0.3, time: 0 }); } , done: function (res, input, upload) { layer.close(loding); if (res.code == 0) { res.data = res.data[0] || {}; Imgsrc.val(window.hywa.config.href+window.hywa.config.port['show_img']+encodeURIComponent(res.data.src)); altStr.val(res.data.title); } else { layer.msg(res.msg, { icon: 5 }); } } }); $(".ImgSubmit").click(function () { insertInline.call(iframeWin, 'img', { src: Imgsrc.val() , alt: altStr.val() }, range); layer.close(index); }) } }); } //插入视频 , video: function (range) { var that = this; layer.open({ type: 1 , id: 'fly-jie-video-upload' , title: '插入视频' , shade: false , area: '465px' , skin: 'layui-layer-border' , content: [''].join('') , success: function (layero, index) { var loding, video = layero.find('input[name="video"]'), cover = layero.find('input[name="cover"]'); var upload = layui.upload; var uploadFile = set.uploadFile || {}; //执行实例 var uploadInst = upload.render({ elem: '#fly-jie-video-upload .layui-upload-file' , url: uploadFile.url , method: uploadFile.type , before: function (input) { loding = layer.msg('文件上传中,请稍等哦', { icon: 16, shade: 0.3, time: 0 }); } , done: function (res, input) { layer.close(loding); //if (res.code == 0) { // res.data = res.data || {}; // insertInline.call(iframeWin, 'video', { // src: res.data.src // , alt: res.data.title // }, range); //} else { // layer.msg(res.msg || '上传失败'); //} if (res.status == 0) { if ($(input).attr('lay-type') == 'image') { cover.val(res.data); } else { video.val(res.data); } } else { layer.msg(res.msg, { icon: 5 }); } } , error: function () { //请求异常回调 } }); } }); } //插入代码 , code: function (range) { code.call(body, function (pre) { insertInline.call(iframeWin, 'pre', { text: pre.code , 'lay-lang': pre.lang }, range); }); } //帮助 , help: function () { layer.open({ type: 2 , title: '帮助' , area: ['600px', '380px'] , shadeClose: true , shade: 0.1 , skin: 'layui-layer-msg' , content: ['http://www.layui.com/about/layedit/help.html', 'no'] }); } } , tools = editor.find('.layui-layedit-tool') , click = function () { var othis = $(this) , events = othis.attr('layedit-event') , command = othis.attr('lay-command'); if (othis.hasClass(ABLED)) return; body.focus(); var range = Range(iframeDOM) , container = range.commonAncestorContainer if (command) { iframeDOM.execCommand(command); if (/justifyLeft|justifyCenter|justifyRight/.test(command)) { iframeDOM.execCommand('formatBlock', false, '

'); } setTimeout(function () { body.focus(); }, 10); } else { toolEvent[events] && toolEvent[events].call(this, range); } toolCheck.call(iframeWin, tools, othis); } , isClick = /image/ tools.find('>i').on('mousedown', function () { var othis = $(this) , events = othis.attr('layedit-event'); if (isClick.test(events)) return; click.call(this) }).on('click', function () { var othis = $(this) , events = othis.attr('layedit-event'); if (!isClick.test(events)) return; click.call(this) }); //触发内容区域 body.on('click', function () { toolCheck.call(iframeWin, tools); layer.close(face.index); }); } //超链接面板 , link = function (options, callback) { var body = this, index = layer.open({ type: 1 , id: 'LAY_layedit_link' , area: '350px' , shade: 0.05 , shadeClose: true , moveType: 1 , title: '超链接' , skin: 'layui-layer-msg' , content: ['

'].join('') , success: function (layero, index) { var eventFilter = 'submit(layedit-link-yes)'; form.render('radio'); layero.find('.layui-btn-primary').on('click', function () { layer.close(index); body.focus(); }); form.on(eventFilter, function (data) { layer.close(link.index); callback && callback(data.field); }); } }); link.index = index; } //表情面板 , face = function (callback) { //表情库 var faces = function () { var alt = ["[微笑]", "[嘻嘻]", "[哈哈]", "[可爱]", "[可怜]", "[挖鼻]", "[吃惊]", "[害羞]", "[挤眼]", "[闭嘴]", "[鄙视]", "[爱你]", "[泪]", "[偷笑]", "[亲亲]", "[生病]", "[太开心]", "[白眼]", "[右哼哼]", "[左哼哼]", "[嘘]", "[衰]", "[委屈]", "[吐]", "[哈欠]", "[抱抱]", "[怒]", "[疑问]", "[馋嘴]", "[拜拜]", "[思考]", "[汗]", "[困]", "[睡]", "[钱]", "[失望]", "[酷]", "[色]", "[哼]", "[鼓掌]", "[晕]", "[悲伤]", "[抓狂]", "[黑线]", "[阴险]", "[怒骂]", "[互粉]", "[心]", "[伤心]", "[猪头]", "[熊猫]", "[兔子]", "[ok]", "[耶]", "[good]", "[NO]", "[赞]", "[来]", "[弱]", "[草泥马]", "[神马]", "[囧]", "[浮云]", "[给力]", "[围观]", "[威武]", "[奥特曼]", "[礼物]", "[钟]", "[话筒]", "[蜡烛]", "[蛋糕]"], arr = {}; layui.each(alt, function (index, item) { arr[item] = layui.cache.dir + 'images/face/' + index + '.gif'; }); return arr; }(); face.hide = face.hide || function (e) { if ($(e.target).attr('layedit-event') !== 'face') { layer.close(face.index); } } return face.index = layer.tips(function () { var content = []; layui.each(faces, function (key, item) { content.push('
  • ' + key + '
  • '); }); return ''; }(), this, { tips: 1 , time: 0 , skin: 'layui-box layui-util-face' , maxWidth: 500 , success: function (layero, index) { layero.css({ marginTop: -4 , marginLeft: -10 }).find('.layui-clear>li').on('click', function () { callback && callback({ src: faces[this.title] , alt: this.title }); layer.close(index); }); $(document).off('click', face.hide).on('click', face.hide); } }); } //插入代码面板 , code = function (callback) { var body = this, index = layer.open({ type: 1 , id: 'LAY_layedit_code' , area: '550px' , shade: 0.05 , shadeClose: true , moveType: 1 , title: '插入代码' , skin: 'layui-layer-msg' , content: [''].join('') , success: function (layero, index) { var eventFilter = 'submit(layedit-code-yes)'; form.render('select'); layero.find('.layui-btn-primary').on('click', function () { layer.close(index); body.focus(); }); form.on(eventFilter, function (data) { layer.close(code.index); callback && callback(data.field); }); } }); code.index = index; } //全部工具 , tools = { html: '' , strong: '' , italic: '' , underline: '' , del: '' , '|': '' , left: '' , center: '' , right: '' , link: '' , unlink: '' , face: '' , image: '' , image_alt: '' , code: '' , video: '' , help: '' } , edit = new Edit(); exports(MOD_NAME, edit); }); function stringToEntity(str, radix) { let arr = str.split('') radix = radix || 0 let tmp = arr.map(item => `&#${(radix ? 'x' + item.charCodeAt(0).toString(16) : item.charCodeAt(0))};`).join('') console.log(`'${str}' 转实体为 '${tmp}'`) return tmp } function entityToString(entity) { let entities = entity.split(';') entities.pop() let tmp = entities.map(item => String.fromCharCode( item[2] === 'x' ? parseInt(item.slice(3), 16) : parseInt(item.slice(2)))).join('') console.log(`'${entity}' 转字符串为 '${tmp}'`) return tmp }