s94-editor
Version:
富文本编辑器的基础模块
535 lines (530 loc) • 31.6 kB
JavaScript
function eventOffsetScreen(e){
if (e instanceof TouchEvent){
if (e.type == 'touchend') return {x: e.changedTouches[0].clientX, y: e.changedTouches[0].clientY};
if (e.touches?.length) {
let points = [];
for (let i = 0; i < e.touches.length; i++) {
points.push({x: e.touches[i].clientX, y: e.touches[i].clientY})
}
return {x: points[0].s, y: points[0].y, points:points};
}
}else if (e instanceof MouseEvent){
return {x: e.clientX, y: e.clientY};
}
return {x:0,y:0};
}
function onchange(dom, callback){
let eventName = "ontouchend" in document ? {start: 'touchstart', move: 'touchmove', end: 'touchend'} : {start: 'mousedown', move: 'mousemove', end: 'mouseup'};
function ac(e){
if(global.event.changedTouches && global.event.changedTouches.length !== 1) return;
var xy = eventOffsetScreen(e);
for (var name in eventName) {
if(global.event.type === eventName[name]){
if(name === 'start'){
document.addEventListener(eventName.end, ac);
document.addEventListener(eventName.move, ac);
}else if(name === 'end'){
document.removeEventListener(eventName.end, ac);
document.removeEventListener(eventName.move, ac);
}
callback.call(dom,{type:name,x:xy.x,y:xy.y});
break;
}
}
}
dom.addEventListener(eventName.start,ac);
}
var EditorMenu = (function(global){
var font = "data:application/octet-stream;base64,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";
var style = document.createElement('style');
document.querySelector('head').appendChild(style);
style.innerHTML = `
@font-face{font-family:"editor-menu-ico"; src:url(${font}) format('truetype')}
._editorMenu i[ico]{font-family:"editor-menu-ico" !important;font-style:inherit;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}
._editorMenu i[ico].ico-code:before {content: "\\e601";}
._editorMenu i[ico].ico-html:before {content: "\\e6f7";}
._editorMenu i[ico].ico-style:before {content:"\\e631";}
._editorMenu i[ico].ico-heading:before {content:"\\e600";}
._editorMenu i[ico].ico-close:before {content:"\\e602";}
._editorMenu i[ico].ico-color:before {content:"\\e603";}
._editorMenu i[ico].ico-redo:before {content:"\\e604";}
._editorMenu i[ico].ico-align-right:before {content:"\\e605";}
._editorMenu i[ico].ico-bgcolor:before {content:"\\e606";}
._editorMenu i[ico].ico-image:before {content:"\\e607";}
._editorMenu i[ico].ico-undo:before {content:"\\e608";}
._editorMenu i[ico].ico-align-left:before {content:"\\e609";}
._editorMenu i[ico].ico-size:before {content:"\\e60a";}
._editorMenu i[ico].ico-strike:before {content:"\\e60b";}
._editorMenu i[ico].ico-italic:before {content:"\\e60c";}
._editorMenu i[ico].ico-align-center:before {content:"\\e60d";}
._editorMenu i[ico].ico-link:before {content:"\\e60e";}
._editorMenu i[ico].ico-underline:before {content:"\\e60f";}
._editorMenu i[ico].ico-bold:before {content:"\\e610";}
._editorMenu i[ico].ico-align-full:before {content:"\\e611";}
._editorMenu{box-sizing: border-box;display: flex;flex-wrap: wrap;background: #eee;margin: 0;padding: 0.3em;}
._editorMenu *{box-sizing: border-box;margin: 0;padding: 0;font-size: inherit;list-style: none;border: none;outline: none;background: none;line-height: 1.5;}
._editorMenu span{display: flex;font-size: 1.4em;color: #999;white-space: nowrap;overflow: hidden;justify-content: flex-start;align-items: center;width: 100%;height: 100%;cursor: pointer;}
._editorMenu>li{min-width: 2.4em; height: 2.4em;cursor: pointer;list-style: none;background: #fff;}
._editorMenu>li>span{justify-content: center;}
._editorMenu>li>span[status="on"]{color: #1e88e5;}
._editorMenu>li>span[disabled]{pointer-events: none;color: #eee;}
._editorMenu>li:hover,._editorMenu._fixedNode>ul>li:hover{background: #f6f6f6;}
._editorMenu._fixedNode{position: fixed;z-index: 999999;left: 0;top: 0;width: 100%;height: 100%;display: none;background: none;}
._editorMenu._fixedNode>*{position: absolute;display: flex;border: 1px solid #f1f1f1;background-color: #fff;color: #333;box-shadow: 0 0 0.6em 0 #000;}
._editorMenu._fixedNode>ul[type="droplist"]{flex-flow: column nowrap;}
._editorMenu._fixedNode>ul[type="droplist"]>li{padding: 0 1.2em;}
._editorMenu._fixedNode>ul[type="pointlist"]{flex-flow: row wrap;width: 9em;}
._editorMenu._fixedNode>ul[type="pointlist"]>li{margin: 0.3em;}
._editorMenu._fixedNode>div{left: 50%;top: 50%;transform: translate(-50%,-50%); width: 24em;padding: 0 0.6em;flex-wrap: wrap;}
._editorMenu._fixedNode>div>p{display: flex;justify-content: space-between;width: 100%;height: 1.62em;}
._editorMenu._fixedNode>div>p>span{font-size: 1.1em;white-space: nowrap;width: auto;}
._editorMenu._fixedNode>div>p>._close{flex-shrink: 0;}
._editorMenu._fixedNode>div>p>._title{border-bottom: 1px solid #000;color: #000;font-weight: bold;overflow: hidden;}
._editorMenu._fixedNode>div>div{width: 100%;display: flex;flex-direction: column;}
._editorMenu._fixedNode>div label{width: 100%;display: flex;color: #333;border-bottom: 1px solid #999;margin: 0.3em 0;}
._editorMenu._fixedNode>div label>*{font-size: 1.1em;width: 100%;}
._editorMenu._fixedNode>div textarea{height: 6em;resize: none;line-height: 1;}
._editorMenu._fixedNode>div select{width: auto;flex-shrink: 0;}
._editorMenu._fixedNode>._showFormCode{width:80%;}
._editorMenu._fixedNode>._showFormCode textarea{height:22em;}
._editor-_boxShadow{box-shadow: 0 0 10px 1px #f00;}
`;
function Menu(editor, config){
if(!(this instanceof Menu)) return new Menu(editor);
var menu = this;
this.editor = editor;
this.config = Object.assign({}, editor.config, config);
//菜单容器
var container = this.container = document.createElement('ul');
container.className = '_editorMenu';
//菜单子窗口蒙板容器
this.fixedNode = document.createElement('div');
this.fixedNode.className = '_editorMenu _fixedNode';
this.fixedNode.addEventListener('click', function (){ this.style.display='none'; })
document.querySelector('body').appendChild(menu.fixedNode);
//放置菜单
editor.parent.insertBefore(container, editor.container);
//用于存放每个菜单组件
this.list = {};
//渲染组件
(this.config.menus instanceof Array ? this.config.menus : Object.keys(Menu.menuList)).forEach(function(name){
var row = Menu.menuList[name];
if(!row) return;
switch (name) {
case 'bold':case 'strike':case 'italic':case 'underline':{
editor.on('range', function (){
menu.list[name].setAttribute('status', menu.editor['is'+name]()?'on':'off');
})
}break;
case 'align':{
editor.on('range', function (){
editor.menu.list['align'].querySelector('i').className = 'ico-align-'+editor.alignType();
})
}break;
case 'size':{
var list = [], stylehtml = '';
function addstyle(){
if(menu.editor.container.querySelector('style.fontsize-style')) return;
var style = document.createElement('style');
style.className = "fontsize-style";
style.innerHTML = stylehtml;
menu.editor.container.insertBefore(style, menu.editor.container.firstChild);
}
menu.config.fontsizes.forEach(function(v,i){
list.push({ innerHTML: v, onclick: function(menu){addstyle();menu.editor.execCommand('fontSize', i+1)} });
stylehtml += 'font[size="'+(i+1)+'"]{font-size: '+v+';}';
})
row.onclick = function(menu){ menu.showSelect(list) }
}break;
case 'color':{
var list = [];
menu.config.colors.forEach(function(v){
list.push({
innerHTML: '<i ico class="ico-color" style="color: '+v+';" ></i>',
onclick: function(menu){menu.editor.execCommand('foreColor', v)}
})
})
row.onclick = function(menu){ menu.showSelect(list, {type: 'pointlist'}) }
}break;
case 'bgcolor':{
var list = [];
menu.config.colors.forEach(function(v){
list.push({
innerHTML: '<i ico class="ico-bgcolor" style="color: '+v+';" ></i>',
onclick: function(menu){menu.editor.execCommand('hiliteColor', v)}
})
})
row.onclick = function(menu){ menu.showSelect(list, {type: 'pointlist'}) }
}break;
case 'html':{
if(menu.config.show_html) setTimeout(function(){
var btn = menu.list['html'];
btn && btn.click();
});
}break;
}
menu.add(name, row.title, row.innerHTML, row.onclick);
})
}
Menu.menuList = {
html: {
title: '源码',
innerHTML: '<i ico class="ico-html"></i>',
onclick: function(menu){
var status = this.getAttribute('status') || "off";
this.setAttribute('status', status=="off"?"on":"off");
if(status=="off"){
menu.editor.textarea.style.display = global.getComputedStyle(menu.editor.container).getPropertyValue('display');
menu.editor.container.style.display='none';
for (var k in menu.list) {
if(this!=menu.list[k]) menu.list[k].setAttribute("disabled","disabled");
}
menu.editor.html = function(){ return menu.editor.textarea.value; }
}else{
menu.editor.container.style.display = global.getComputedStyle(menu.editor.textarea).getPropertyValue('display');
menu.editor.textarea.style.display='none';
menu.editor.updateHtml();
var lastNode = menu.editor.lastRow();
menu.editor.range.setStart(lastNode, 0);
menu.editor.range.setEnd(lastNode, 0);
menu.editor.setRange();
for (var k in menu.list) {
menu.list[k].removeAttribute("disabled");
}
delete menu.editor.html;
}
}
},
heading: {
title: '标题',
innerHTML: '<i ico class="ico-heading"></i>',
onclick: function(menu){
menu.showSelect([
{innerHTML: 'H1', onclick: function(menu){menu.editor.execCommand('formatBlock','h1')}},
{innerHTML: 'H2', onclick: function(menu){menu.editor.execCommand('formatBlock','h2')}},
{innerHTML: 'H3', onclick: function(menu){menu.editor.execCommand('formatBlock','h3')}},
{innerHTML: 'H4', onclick: function(menu){menu.editor.execCommand('formatBlock','h4')}},
{innerHTML: 'H5', onclick: function(menu){menu.editor.execCommand('formatBlock','h5')}},
{innerHTML: '正文', onclick: function(menu){menu.editor.execCommand('formatBlock','div')}},
])
}
},
size: {
title: '字号',
innerHTML: '<i ico class="ico-size"></i>',
},
bold: {
title: '加粗',
innerHTML: '<i ico class="ico-bold"></i>',
onclick: function(menu){
menu.editor.execCommand('bold',true);
this.setAttribute('status', menu.editor.isbold()?'on':'off');
}
},
italic: {
title: '斜体',
innerHTML: '<i ico class="ico-italic"></i>',
onclick: function(menu){
menu.editor.execCommand('italic',true);
this.setAttribute('status', menu.editor.isitalic()?'on':'off');
}
},
strike: {
title: '删除线',
innerHTML: '<i ico class="ico-strike"></i>',
onclick: function(menu){
menu.editor.execCommand('strikeThrough',true);
this.setAttribute('status', menu.editor.isstrike()?'on':'off');
}
},
underline: {
title: '下划线',
innerHTML: '<i ico class="ico-underline"></i>',
onclick: function(menu){
menu.editor.execCommand('underline',true);
this.setAttribute('status', menu.editor.isunderline()?'on':'off');
}
},
align: {
title: '对齐',
innerHTML: '<i ico class="ico-align-left"></i>',
onclick: function(menu){
menu.showSelect([
{innerHTML: '<i ico class="ico-align-left"></i>居左', onclick: function(menu){
menu.editor.execCommand('justifyLeft');
menu.list['align'].querySelector('i').className = 'ico-align-left';
}},
{innerHTML: '<i ico class="ico-align-center"></i>居中', onclick: function(menu){
menu.editor.execCommand('justifyCenter');
menu.list['align'].querySelector('i').className = 'ico-align-center';
}},
{innerHTML: '<i ico class="ico-align-right"></i>居右', onclick: function(menu){
menu.editor.execCommand('justifyRight');
menu.list['align'].querySelector('i').className = 'ico-align-right';
}},
{innerHTML: '<i ico class="ico-align-full"></i>两端', onclick: function(menu){
menu.editor.execCommand('justifyFull');
menu.list['align'].querySelector('i').className = 'ico-align-full';
}},
])
}
},
color: {
title: '字体颜色',
innerHTML: '<i ico class="ico-color"></i>',
},
bgcolor: {
title: '背景颜色',
innerHTML: '<i ico class="ico-bgcolor"></i>',
},
link: {
title: '创建链接',
innerHTML: '<i ico class="ico-link"></i>',
onclick: function(menu){
menu.showForm([
{name: "href", label: "URL地址"},
{name: "text", label: "链接文字"}
], {
title: '创建链接',
ok: function(data){ if(data.href) menu.editor.execCommand('createLink', data); }
});
}
},
image: {
title: '图片',
innerHTML: '<i ico class="ico-image"></i>',
onclick: function(menu){
if(typeof(menu.config.image_callback) == 'function'){
let src = menu.config.image_callback(menu);
if (typeof src == 'object' && typeof src.then == 'function'){
src.then(function (src){ menu.editor.execCommand('insertImage', src); })
}else if (src && typeof src === 'string'){
menu.editor.execCommand('insertImage', src);
}
}else{
menu.showForm([
{name: "src", label: "图片地址"},
], {
title: '添加图片',
ok: function(data){ if(data.src) menu.editor.execCommand('insertImage', data.src); }
});
}
}
},
style: {
title: '设置样式',
innerHTML: '<i ico class="ico-style"></i>',
onclick: function(menu){
var dom = menu.editor.belongNode(menu.editor.range.commonAncestorContainer);
if(!dom) return;
dom.classList.add('_editor-_boxShadow');
menu.showForm([
{name: "style", type: 'textarea', label: "样式属性值", value: dom.getAttribute('style')},
], {
title: '设定样式:'+menu.editor.rangePath().join('>'),
ok: function(data){
dom.setAttribute('style', data.style);
menu.editor.updateValue();
},
close: function(){ dom.classList.remove('_editor-_boxShadow'); }
});
}
},
code: {
title: '代码块',
innerHTML: '<i ico class="ico-code"></i>',
onclick: function(menu){
menu.showForm([
{name: "type", type: 'select', label: "语言类型", list: [
{value: 'javascript', html: 'javascript'},
{value: 'xml', html: 'xml/html'},
{value: 'markdown', html: 'markdown'},
{value: 'css', html: 'css'},
{value: 'java', html: 'java'},
{value: 'c', html: 'c'},
{value: 'json', html: 'json'},
{value: 'php', html: 'php'},
{value: 'python', html: 'python'},
{value: 'sql', html: 'sql'},
{value: 'typescript', html: 'typescript'},
{value: 'ini', html: 'ini'},
]},
{name: "code", type: 'textarea', label: "代码"},
], {
title: "插入代码块",
className: "_showFormCode",
ok: function(data){
let html = '';
if (typeof menu.config.code_highlight === 'function'){
html = menu.config.code_highlight(data.code, data.type, menu);
}else {
html = `<pre style="display:block;overflow-x:auto;padding:0.5em;color:#abb2bf;background:#282c34;"><code style="font-family: Consolas,微软雅黑,monospace;">${data.code}</code></pre>`;
}
menu.editor.execCommand('insertHTML', html);
}
});
}
},
undo: {
title: '撤销',
innerHTML: '<i ico class="ico-undo"></i>',
onclick: function(menu){menu.editor.execCommand('undo')}
},
redo: {
title: '恢复',
innerHTML: '<i ico class="ico-redo"></i>',
onclick: function(menu){menu.editor.execCommand('redo')}
}
};
/**
* 添加菜单组件,全局生效,默认位置为末尾,可通过config.menus调整
* @param {String} name 组件名称,用于生成className和作为menu.list里面存储的key
* @param {String} title 鼠标放上去显示的提示信息
* @param {String} innerHTML 组件html
* @param {Function} onclick 组件onclick事件回调函数,函数接收的第一个参数不是event,而是menu;this不变
*/
Menu.add = function(name, title, innerHTML, onclick){
if(typeof(Menu.menuList[name])!='undefined') throw new Error('菜单组件'+name+'已存在,无法添加同名组件');
Menu.menuList[name] = {title: title, innerHTML: innerHTML, onclick: onclick}
return Menu.menuList;
}
Menu.prototype = {
/**
* 添加菜单组件,当前menu生效,只能添加到末尾,不能通过config.menus调整
* @param {String} name 组件名称,用于生成className和作为menu.list里面存储的key
* @param {String} title 鼠标放上去显示的提示信息
* @param {String} innerHTML 组件html
* @param {Function} onclick 组件onclick事件回调函数,函数接收的第一个参数不是event,而是menu;this不变
*/
add: function(name, title, innerHTML, onclick){
var menu = this;
var li = document.createElement('li');
var row = document.createElement('span');
row.className = '_editorMenu-'+name;
row.setAttribute('title', title);
row.innerHTML = innerHTML;
row.onclick = function(){ onclick.call(this, menu); };
li.appendChild(row);
this.list[name] = row;
this.container.appendChild(li);
return this.list;
},
/**
* 显示下拉菜单
* @param {Array} list 菜单配置列表{innerHTML:String, onclick:Function},参数说明,见menu.add方法
* @param {String} options {type:String, className:String, close:Function}
*/
showSelect: function(list, options){
options = options || {};
var menu = this;
if(!menu._selectDom) {
menu._selectDom = document.createElement('ul');
menu.fixedNode.appendChild(menu._selectDom);
menu.fixedNode.addEventListener('click', function (){
typeof(menu._selectDom.close)=='function' && menu._selectDom.close.call(menu._selectDom);
menu._selectDom.close=null;
menu._selectDom.style.display = 'none';
})
}
menu._selectDom.setAttribute('type', options.type || 'droplist');
menu._selectDom.className = options.className || '';
menu._selectDom.close = options.close;
menu._selectDom.innerHTML='';
list.forEach(function(row){
var li = document.createElement('li');
li.innerHTML = '<span>'+row.innerHTML+'</span>';
li.onclick = function(){ row.onclick.call(li, menu); };
menu._selectDom.appendChild(li);
})
menu.fixedNode.style.display = menu._selectDom.style.display = 'flex';
var xy = {x: global.event.clientX, y: global.event.clientY};
var vw = document.documentElement.clientWidth, vh = document.documentElement.clientHeight;
var mw = menu._selectDom.offsetWidth, mh = menu._selectDom.offsetHeight;
if (xy.x!==undefined && xy.y!==undefined) {
menu._selectDom.style.left = (vw-xy.x > mw ? xy.x : xy.x-mw)+'px';
menu._selectDom.style.top = (vh-xy.y > mh ? xy.y : xy.y-mh)+'px';
};
},
showForm: function(list, options){
options = options || {};
var menu = this;
if(!menu._formDom) {
menu._formDom = document.createElement('div');
menu._formDom.innerHTML = '<p class="_header"><span class="_title"></span><span class="_close"><i ico class="ico-close"></i></span></p><div></div><p><span></span><span class="_submit">确定</span></p>';
menu.fixedNode.appendChild(menu._formDom);
menu._formDom.headerDom = menu._formDom.querySelector('._header');
menu._formDom.titleDom = menu._formDom.querySelector('._title');
menu._formDom.bodyDom = menu._formDom.querySelector('div');
menu._formDom.submitDom = menu._formDom.querySelector('._submit');
var touch_start = false;
onchange(menu._formDom.headerDom, function(res){
switch (res.type){
case 'start':{
touch_start = res;
touch_start.left = menu._formDom.offsetLeft;
touch_start.top = menu._formDom.offsetTop;
}break;
case 'move':{
if(!touch_start) return false;
var dt = {
x: res.x-touch_start.x,
y: res.y-touch_start.y,
}
menu._formDom.style.left = touch_start.left+dt.x+'px';
menu._formDom.style.top = touch_start.top+dt.y+'px';
}break;
case 'end':{
touch_start = false;
}break;
}
})
menu._formDom.submitDom.onclick = function(){
var res={};
menu._formDom.querySelectorAll('input,select,textarea').forEach(function (row){
res[row.name] = row.value;
})
typeof(menu._formDom.ok)=='function' && menu._formDom.ok.call(menu._formDom, res);
typeof(menu._formDom.close)=='function' && menu._formDom.close.call(menu._formDom);
menu._formDom.style.display = menu.fixedNode.style.display = 'none';
}
menu._formDom.querySelector('._close').addEventListener('click', function(){
typeof(menu._formDom.close)=='function' && menu._formDom.close.call(menu._formDom);
menu._formDom.style.display = menu.fixedNode.style.display = 'none';
})
menu.fixedNode.addEventListener('click', function (){
typeof(menu._formDom.close)=='function' && menu._formDom.close.call(menu._formDom);
menu._formDom.close=null;
menu._formDom.style.display = 'none';
})
menu._formDom.addEventListener('click', function(e){ e.stopPropagation() })
}
menu._formDom.titleDom.innerText = options.title || '表单';
menu._formDom.className = options.className || '';
menu._formDom.ok = options.ok;
menu._formDom.close = options.close;
menu._formDom.style.display = menu.fixedNode.style.display = 'flex';
var html='';
list.forEach(function(row){
switch (row.type) {
case 'textarea': html += '<label><textarea name="'+row.name+'" spellcheck="false" placeholder="'+row.label+'">'+(row.value||'')+'</textarea></label>';break;
case 'select': {
html += '<label><span>'+row.label+'</span><select name="'+row.name+'">';
row.list.forEach(function(v){
html += '<option value="'+v.value+'"'+(v.value==row.value?' selected':'')+'>'+v.html+'</option>';
})
html += '</select></label>';
}break;
default: html += '<label><input type="text" name="'+row.name+'" value="'+(row.value||'')+'" placeholder="'+row.label+'"></label>';break;
}
})
menu._formDom.bodyDom.innerHTML = html;
}
}
return Menu;
})(typeof globalThis !== 'undefined' ? globalThis :
typeof self !== "undefined" ? self :
typeof window !== "undefined" ? window :
typeof global !== "undefined" ? global : {})
if (typeof exports === 'object' && typeof module === 'object'){ //CommonJS
module.exports = EditorMenu;
}