UNPKG

s94-editor

Version:

富文本编辑器的基础模块

535 lines (530 loc) 31.6 kB
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,d09GMgABAAAAAAk0AAsAAAAAEnwAAAjlAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACFCgqTZI9XATYCJANUCywABCAFhTsHgWMbgA8jUo1OQvYXB5kc3bqb0EhkCjUwqUzY3Y286em1mwi9KvhHcryfxsO/Y6/3JflIxrQdABxP4Bgks/BTc3p2OiMj9tO7WQ+8FmpUNBXFanQF9piKSoAJeLggu86cGOvG14lYOPsz8d/Lj/ulL3ecHEFbNQIFDIEBcimx+ZYKFogskpussJU7chsqUdPqOnzcZwMCDK8FXfj/93N1b5qwtorMHx4aNb0v4+xOHZe/IWKJJCVa5Jz/EZVKgtAspchDnYWZkVQ0q1K1a3uKQNdau6WDZy9ToG48+f4RShS0OPA7xo03r56A+uJRUlJC297kHlkgvkKD9M41gS/238c/6yMAkkYGOs+hS2eeQ2oF6qpM/ifkn0ymuiG4dUIDGSsd88X79MhLyC6s2JZuHLc4BbTFSYkaX9mxsiqKVDgYBxVqW8J/5lGEhqaWto6unr6BoZGxiamZuSwRTaCFfeamcU4FHVOoiH57qwswDaob06R6XFovqxdc2i+rDzAdqh/TpQYwPWqQekVVDJgBlWCG1BBmRA1jxtQIZkKNYqbUGGZGjTNDsu4AlwzWVeglgYW54xI2kbugbArZQW6SNYSwpI1lSxuMVtgLkxpcxNIy6PVg0ivY6I2u8Zi2QTQW9PsVOLIn5MRv35LnSQYCIPTquakz22yVFdz4CtsdI/3Ckl+AmDiibYSUQ6KG9e6oXZG6bcr0Hasye8wpz3DhlH24a1elDpslaUbN0vQRqzp7z1Evz7LgvorOvP6RU5ZP2Q/xVzB6h9Ir9NIFforfpZRRwbjgv9x9/uglvYB1maqv7s5W13mdjI1DOmWCpxutzspKeFqCkzlI1Wc3O5ezePNUWZmdsbt0FVLlxFg0lo1gqawgwVQFeJq2SNjIOtUstcnuSkUdllIpt1aW5MsUHb6FG4WjgISFAVhJBHieiVc3n99um7t4z+HDz+9/eoWeU7dcJOURUbeUjNRyN2PxBg276ZAaxHoBuvuTMD0bhWHS4ZIPCTgP3BipoWlNHQBPB99jGCHiikD3fQ2kp7WEGPXIreowRq8iKJrvi/nzYzzURPEL+dUQlTN6Ts0bsw9i6N4dIvLr4RzmrjCfb5yQC56qoS1suDUupNpBd0BGns9BKCdeLBjmLo49BPoRjKHufEj/nlvo8MCCXjo6tISInK7WaglHUDiDZWr52HD1nCODi9B9thAuuTidcCCH5Urh8DuMapIdl52JXtZVxcziRIgqmxpnqDsLDqujdWN6ZAsdkEfOZWbFbd1amhVyjNoxp6Lqa0eN8e2Z4mxVb6QHc9YPLOQT1wBppgu5/VDr1tou1m2vb0fPD977zYNuxCFf2BgDeNnKeLmA8R3sdun7TPQcd874fCx6ZizI/n0mzsEibLw3Sn1+GrnnX69OfV5dCcRG5155DtCXl2+fuvWbigXt4ndlu/lzL59tvPXJ757+bqnz/n+/0NNhd9EtrKO+q1jOO07f9/Cq7rhR99LDzufRvwjUt2sev+OOx9vXH0t2nIiyC5jF88f/KuvqUuqyBi/dqxp8uEslgg5janAwKzDfttBGBYYGWSqyd9sNCS3G2uP7BvcOmrzrOKEF6xgVO2Xt8kOLs76nq65TnCrt/TJTbtdeX2CXZ747NXpT94NcruZDqtQja2NkRVkfUDnZyUe/vrGsT6ugavr2d0erSiYSUYlo8x7Uh7SJ/B21TD6eqdzUHKM2lZmPT1pOv/Vq5kY1QpUAxXgf2zFp1DpR0mu5KS+J6A/BvPeGAnFsTDSKd5BbNPhSQ4FGI3aB+P+4TRBEyXvFztxdYM+Oj8XjKU0iUA4J3gnQtkKfnrHN8bMbH5lQ1WDOpG7SknC7E5Yk3rHn6FFCTp+unupcqXfkm71PTyQSE08DQiC+hxCATpxyIADwTRSIpFDJXElKCyICso7Oqs5CYCP2OWhki6dQCqwZgMBW6CkLAI3YcP6Lxp2T4pG2ASBfxEUb+kIAOiOzI3+lfqqz+vRpQo4e7WnhI91uS2JSlzNZNWhCH81u8LY5PVPo2+adeDqReHqipTZ0tKMjbFWcVwXhJWCFRAE6E0pJhASJpwRAwf/LqBZVmzuYfx4M1eyTzgf/gVZm0nHtyCUQuevGAa04v+fmTTzKEqDvh60ZIWITCUpZTGvswsQphK5PyNIbYjU1S+tSu0YQqhZr7d6+PvU8C/N/6D/X/b6wcGvk1vbarbXtguCu4++6P2INL+xs5Sz6XPd/pXuAPiOStLa658ci+hslsYjHZ02SZMUdsScGSyYx5onYHRXiRvvO8NKFhypnTOaG1b6+1QYzIsR8IAcS5DP394T39uFmy7BXdm1ir8SvwlX9jicBdrRH0uvBHyGgR8l+SAOiCOQhfQ5ATOXPZEYAUcxUeruaQmQo/ZDs31wkiLWNPyxbHH6je2VDY/1vv/5+qz+PJJxoEqntU7lERgB+K1k288tatlpAPMxX0KsSQFv+isyQOsSfRJ1pIF1rH0WdFEC6qAYQgUpyfd4NR97fWkhCo7BnlZRKtUpGownJZVtVChkGVEoa06p0OszxGfIcAhlJSQOAelc1lUSFV1RSlG9AFif7yySHhNCoFArCAEqQ9ajSzQdztgz18UQPj5kIdtL23bTHwQVZSSkCngjHGwI4GDWElzeNXVE/w19GCv7WPObDHi5ItxpbYpsHcBDz5R8QjrnaIhGWZnkuQPc3cy2/n6NDPOfFjojRHYmEtptMbNy5jQ4uAD14GCMCc6LZ7Tbzezhwglg2Hjx2dw7PIAADRF2Ws9k0zCXKj6lozLxxpMD8JcQLI93k2tBaGbXwLQMqXWTEAXDDYlzaXqEI9rAbi8cJoPUPCfyigUMLxW/mhTlEGLkL1IVsZ8KZWK7F2KgtsKsssJZHpU+lDakjyYqq6YZpsdrsDqfL7fH6/GEmnsIsijXbgxzPQm4CEJWOL8T4DObe3vg0J6D+QkZy909Ggygb5pFbLtIiDnN11J9JexlgZc7yhTWXJLXlkgW95E4RJOAKpNlIp48cLkhuO5ixNzRvDKvGTAAAAA=="; 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; }