/** console.trace()*/
var log=console.log;
var OakEditor = ((editor,path)=>{return{init(cfg){
return new editor(cfg,path);
}}})(class {
constructor(cfg,PATH){
this.tool=['heading','|','bold','italic','createlink','insertunorderedlist','insertorderedlist','createimg','blockquote','table','undo','redo','code'],
this.balloon=!1,
this.br=this.creatNode('<p></br>'),
this.savenode=null,
this.editnode=null,
this.area=document.createElement('textarea'),
this.lastRange=null,
this.sel=null,
this.domPath=null,
this._Event=new WeakMap(),
this.flag={},
this.queue=[],
this.path=PATH,
this.btnExample=new Map(),
this.MOD={},
this.CFG=cfg,
this._toRender=e=>this.MOD.res?this.renderer(e.currentTarget,e):this.render(e.currentTarget,e),
this.CSS=[],
this.hooks={
all:{},
add:(names, callback) =>{
let hooks = this.hooks.all;
Array.isArray(names)||(names=[names]);
names.filter(i=>{
hooks[i] = hooks[i]||[];
hooks[i].push(callback);
})
},
run:(name, env) =>{
let callbacks =this.hooks.all[name];
if (!callbacks || !callbacks.length)return;
callbacks.filter(f=>f(env))
}
}
this.selectorCode=this.CFG.selectorCode||'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'
}
async loadMod(mod,dir='./mod'){
if(!this.MOD[mod])await import(`./${dir}/${mod}.js`).then(res=>Object.assign(this.MOD,res)).catch(err=>console.error(err))
}
_getCss(css){
if(this.CSS.includes(css)) return;else{
this.CSS.push(css);
let cssPath=this.path+css,o = document.querySelector('head')||document.querySelector('body');
o&&o.appendChild(this.creatNode('<link charset="utf-8" rel="stylesheet" href="'+cssPath+'">'))
}
}
_setExtraCss(s){
if(this.CSS.includes(s)) return;else{
this.CSS.push(s);
let css,h=document.querySelector('head')||document.querySelector('body');
if(h){
css= document.createElement('style');
css.innerHTML=s;
h.appendChild(css);
}
}
}
creatNode(s){
let n = document.createElement("div");
n.innerHTML = s;
return n.childNodes[0];
}
start(str){
let ns=document.querySelectorAll(str);
this.editList=Array.from(ns),
this.editList.filter(i=>(i.addEventListener('click',this._toRender),this.deCode(i)))
}
clear(){
this._removeRendr(),
this.editList.filter(i=>(i.removeEventListener('click',this._toRender),this.deCode(i))),
this.savenode=null
}
_removeRendr(){
this.hooks.run('removeRendr',this),
this.removeCursor(this.editnode),
this.editnode&&(
this.savenode.innerHTML=this.editnode.innerHTML,
this.savenode.style.display='block',
this.toolPanle.remove()
)
}
/** 对代码里的HTML标签删除和转为换行 */
linefeed(s){
return s.replace(/(<[^>]+>)/g,(m)=>{
if(m.startsWith("</div")||m.startsWith("</p")||m.startsWith("<br")||m.startsWith("</li")) return'\n';else return'';
})
}
/** 对编辑区里所有的代码里的HTML标签删除和转为换行 */
deCode(n){this.rmNodeTag(Array.from(n.querySelectorAll(this.selectorCode)))}
rmNodeTag(ary){ary.filter(i=>i.innerHTML=this.linefeed(i.innerHTML)),ary==null}
/** 节点是否在编辑器内 */
inRoot(n,p=!1){return p=p||this.editnode,p.compareDocumentPosition(n)&16}
/** 保存 range 及节点路径 与 编辑内容到 area*/
saveRange(){
this.flag.codeNodeArray&&this.rmNodeTag(this.flag.codeNodeArray);
this.area.textContent =this.editnode.innerHTML;
this.sel=getSelection();
this.domPath=this.getdomPath(this._getChangeNode());
this.sel.rangeCount&&(this.lastRange=this.sel.getRangeAt(0));
}
bind(i,o){Object.defineProperty(this.flag,i,{set:(v)=>{o.setAttribute(i,v),v?o.innerHTML=this.lang[v]||v:o.innerHTML=this.lang[i]},get(){return o.getAttribute(i)},configurable: true})}
/**
* 根据模板生成节点
* @param {模板} tp
* @param {与模板合并的参数对象} o
* @param {节点生成后调用的函数} f
*/
UI(tp,o={},f=null){
let t={};
Object.assign(t,tp,o);
let n = document.createElement(t.tag),e = t.attributes||{};
n.innerHTML=t.innerHTML||'';
t.class&&n.classList.add(...t.class);
for (let [k,v] of Object.entries(e)){n.setAttribute(k,v)}
t.listen&&this.DOClisten(n,t.listen);
t.listenTo&&this.listenTo(n,t.listenTo);
(t=t.children) && (Array.isArray(t)?t.filter(i=>i&&n.appendChild(i.nodeName?i:Array.isArray(i)?this.UI(...i):this.UI(i))):n.appendChild(t.nodeName?t:this.UI(t)));
f&&f.call(this,n);
return n;
}
/**
* 设置节点属性,
* @param {css选择器} c
* @param {属性} s
* @param {值,false 会删除属性} v
* @param {.class 数组} l
*/
setAttrALL(c,s,v,l){this.editnode&&this.editnode.querySelectorAll(c).forEach(n=>{s&&v?n.setAttribute(s,v):n.removeAttribute(s),l&&n.classList.add(...l)})}
/**
* 异步加载按钮模块
* @param {*} n
* @param {*} e
*/
async render(n,e){
let promiseArr = [];
this._getCss('oakeditor.css');
this.btnExample.clear();
this.tool=this.CFG.toolBar||this.tool;
await import(`${this.path}translations/${this.CFG.lang||'zh-cn'}.js`).then(i=>{this.lang=i.lang});
await import(`${this.path}mod/resources.js`).then(res=>{
Object.assign(this.MOD,res);
this.B=res.btnCalss
}).then(()=>{this.tool.filter(n=>{
if(n!='|'&&!this.MOD[n]){
let p = new Promise((resolve, reject) => {
import(`${this.path}mod/${n}.js`).then(res=>resolve(Object.assign(this.MOD,res))).catch(err=>reject(console.error(err)))
})
promiseArr.push(p)
}})
Promise.all(promiseArr).then(() => {
this.tool.filter(n=>this.btnExample.set(n,new this.B(this.MOD[n],n,this)))
}).then(()=>this._setExtraCss(this.CFG.extracss||this.MOD.res.extracss)).then(()=>this.renderer(n,e))
})
}
renderer(n,e){
if(typeof(n) == "string") n= document.querySelector(n);
this.flag.currentCursor=this.saveCursor(n);
e&&this._isElement(e.target,'img')&&(this.flag.currentCursor=null)
if(this._isElement(n, 'textarea')){
this.area=n;
n.parentNode.insertBefore(this._rendertool(),n);
n.style.display='none';
this.editnode=this.toolPanle.appendChild(document.createElement('div'));
this.editnode.appendChild(this.br);
}else{
if(n==this.savenode)return;
else{
/** 清除上一次的编辑环境 */
this.savenode&&this._removeRendr(),
this.savenode=n,
n.style.display= 'none',
n.parentNode.insertBefore(this._rendertool(),n),
this.editnode=this.toolPanle.appendChild(n.cloneNode(true))
}
}
this.popbody=this.toolPanle.appendChild(this.creatNode('<div class="ck ck-reset_all ck-body ck-rounded-corners"></div>')),
this.editnode.classList.add('ck','oak_block','ck-editor__editable'),
this.editnode.style.cssText='height:auto!important;';
this.editnode.style.minHeight=this.CFG.height;
this.DOClisten(this.editnode,{click:this.pop_body,input:this.saveRange,mouseup:this.saveRange,keyup:null,cut:null,paste:this._paste});
this.toolbar.hasChildNodes()||this.DOClisten(document,{keydown:this._hotkey,click:()=>this.editnode.classList.remove('ck-focused')});
this.tool.filter(s=>{this.btnExample.get(s).getButton&&this.toolbar.appendChild(this.btnExample.get(s).getButton)});
this.editnode.setAttribute('contenteditable',true);
document.execCommand("defaultParagraphSeparator", false, "p");
this.flag.currentCursor&&this.inRoot(e.target,this.savenode)&&this.reCursor(this.flag.currentCursor);
this.hooks.run('render',this),
/**表格图片使用 */
this.saveRange();
}
/** 热键处理函数,及禁止在<figcaption>标签内回车 */
_hotkey(n,e){
if(e.ctrlKey){
if(e.key.toUpperCase()=='A'){
e.preventDefault(),document.execCommand('selectAll',!1,!1),this.saveRange();
}else
for (let v of this.btnExample.values())e.key.toUpperCase()==v.hotkey&&(e.preventDefault(),v.command(n,e))
}
e.key=='Enter'&&this._isElement(e.target,'figcaption')&&e.preventDefault();
e.key=='Backspace'&&this.editnode.querySelectorAll('figure').forEach(i=>{i.classList.contains('ck-widget_selected')&&(e.preventDefault(),i.remove(),this.balloon.remove())});
}
/** 粘体回调函数 */
_paste(n,e){
let d=e.clipboardData;
for (let i=0;i<d.items.length;i++){
let item=d.items[i];
if(item.kind==="string"&&item.type==="text/html"){
e.preventDefault(0);
let imgTag = ['<img','<a','</a'],
attr=['src','href'],
retain = ['<div','</div','<p','</p','<table','</table','<th','</th','<tr','</tr','<td','</td'],
f =(s,a,t=!1)=>{return a.filter(i=>t|=s.startsWith(i,0)),t};
item.getAsString((str)=>{
str = str.replace(/(<[^>]+>)/g, (match)=>{
let isImg = f(match,imgTag),
attrsRe=/\s+([a-z]*|data-.*?)\s*=\s*".*?"/ig;
if(isImg){
return match.replace(attrsRe,(match)=>{
return f(match.replace(/(^\s*)/g,''),attr)?match:'';
});
}else if(f(match,retain)){
return match.replace(attrsRe,'')
}else return '';
})
this.insertHTML(n,str)
})
}
}
setTimeout(()=>{this.insertQueue()},100)
}
/** 把未上传的远程图片加入队列 */
insertQueue(){
let nodes=this.editnode.querySelectorAll('img');
for(let i=0;i<nodes.length;i++){
let src=nodes[i].src;
src.includes(window.location.host)||this.queue.unshift(nodes[i]);
}
setTimeout(()=>{this.startUp()},100)
}
/** 开始上传 并更新图片地址,firefox 支持本地图片粘贴上传*/
startUp(){
if(this.queue.length&&this.CFG.uploadURL){
let n=this.queue.pop(),data = new FormData(),postvar=this.CFG.postVarsPerFile,
init={method:"POST",credentials:"include",cache:"no-cache"};
if(n.src.match('\^data:image'))
data.append('Filedata',this.base64toblob(n.src),Date.now()+'.jpg');
else data.append('url',n.src);
n=this.addfigure(n);
n.src=this.path+'translations/timg.gif';
for(let [k,v] of Object.entries(postvar))data.append(k,v);
init.body=data;
fetch(this.CFG.uploadURL,init).then((res)=>{return res.json()}).then((o)=>{
let s='';
for(let [k,v] of Object.entries(postvar)){s+=k+'='+v+'&';}
n.src=this.CFG.downURL+'?type=resizes&'+s+'filename='+o.filename;
this.startUp();
}).catch(e =>{n.remove(),console.error(e)});
}
}
/** base64转 blob */
base64toblob(base64String){
let bytes = window.atob(base64String.split(',')[1]),array = [];
for(let i = 0; i < bytes.length; i++){ array.push(bytes.charCodeAt(i)); }
return new Blob([new Uint8Array(array)], {type: 'image/jpeg'});
}
/**
* 为了插入图片的格式统一,与图片管理插件共用
* @param {替换的节点或新的节点} n
* @param {true 返回新节点的 outerHTML,false 替换页面的n节点} html
*/
addfigure(n,html=false){
let figure =this.UI(this.MOD.res.TP.figure,{children:this.MOD.res.TP.figcaption});
figure.classList.add('image');figure.setAttribute('contenteditable',false);
if(html){
figure.insertBefore(n,figure.firstChild);
return figure.outerHTML
}else{
let t=n.cloneNode(true);
figure.insertBefore(t,figure.firstChild);
n.parentNode.replaceChild(figure,n);
return t
}
}
_isElement(el, tag) {
if(el && el.tagName && (el.tagName.toLowerCase() == tag)){
return true;
}
if(el && el.getAttribute && (el.getAttribute('tag') == tag)){
return true;
}
return false;
}
/** 返回焦点节点*/
_getChangeNode(){
let sel = getSelection(),range,n=null;
if(!sel.rangeCount) return false;
range=sel.getRangeAt(0);
if (sel.anchorNode && (sel.anchorNode.nodeType == 3)) {
if (sel.anchorNode.parentNode) { /* next check parentNode */
n = sel.anchorNode.parentNode;
}
if (sel.anchorNode.nextSibling != sel.focusNode.nextSibling) {
n = sel.anchorNode.nextSibling;
}
}
if (this._isElement(n, 'br')) {n = null;}
if (!n) {
n = range.commonAncestorContainer; /* startContainer和endContainer共同的祖先节点在文档树中位置最深的那个 */
if (!range.collapsed) {
if (range.startContainer == range.endContainer) { /* 开始节点与结束节点是同一节点 */
if (range.startOffset - range.endOffset < 2) {
if (range.startContainer.hasChildNodes()) {
n = range.startContainer.childNodes[range.startOffset];
}
}
}
}
}
return n;
}
/** 得到n节点 至 编辑器根节点 的路径 */
getdomPath(n) {
let domPath = [];
while(n&&(this.editnode.compareDocumentPosition(n)&16)){
if (n.nodeName && n.nodeType && (n.nodeType === 1)){
domPath.push(n);
}
n=n.parentNode;
}
if(domPath.length === 0){
domPath[0] = this.editnode;
}
return domPath.reverse();
}
/** 把节点路径转换为 以小写tagName 为键的map */
domPathMap(n=!1){
let m = new Map(),p;
n?p=this.getdomPath(n):p=this.domPath;
return p.filter(n=>m.set(n.tagName.toLowerCase(),n)),m
}
_rendertool() {
this.toolbar=this.UI(this.MOD.res.TP.toolbar);
this.MOD.res.TP.toolPanle.children.children=this.toolbar;
return this.toolPanle=this.UI(this.MOD.res.TP.toolPanle)
}
on(s,f,c=null,i=!1){
let o={},E=this._Event;
E.has(Object)||E.set(Object,{});
E.get(Object)[s]||(E.get(Object)[s]=[]);
o.callback=f;
o.cfg=c;
o.one =i;
E.get(Object)[s].push(o);
}
del(s){delete this._Event.get(Object)[s]}
/**
* 在事件map里面建立以节点为健的对象,
* this._Event[node]:
* {
* _domNode:node,
* _domListeners:{
* click:{
* callback:f
* remove:i.removeListener
* }
* }
* }
* 给节点绑定事件,事件函数指向 this.fire
* @param {node 注册事件的节点} n
* @param {object {系统事件:函数,}} l
* @param { bool 可选。指定事件是否在捕获或冒泡阶段执行。} b
*/
DOClisten(n,l,b=!1){
this._Event.has(n)||this._Event.set(n,{_domNode:n})
let E=this._Event.get(n);
for(let [a,f] of Object.entries(l)){
if (E._domListeners && E._domListeners[a])return;
const i = this._createDomListener(E,a,b);
E._domNode.addEventListener(a, i, b),
E._domListeners || (E._domListeners = {}),
E._domListeners[a] = {callback:f,remove:i.removeListener}
}
}
/**
* [{from:发出事件的节点,listen:绑定的事件, to:执行的函数,cfg:参数[未使用]}]
* @param {node 接收事件的节点} n
* @param {object 或 Array 模板对象里的listenTo属性[{},{from:发出事件的节点,listen:系统事件名称,to:回调函数}]} o
*/
listenTo(n,l){
let E;
Array.isArray(l)||(l=[l])
l.filter(o=>{
E = this._Event.get(o.from);
E._events||(E._events={})
Array.isArray(o.listen)||(o.listen=[o.listen])
o.listen.filter(i=>{E._events[i]||(E._events[i]=new Map()),E._events[i].has(n)||E._events[i].set(n,[]),E._events[i].get(n).push(o.to)})
})
}
/**
* 创建监听函数
* 给 fire 增加删除事件绑定的函数,
* @param {object:{_Event:n,}} E
* @param {string 系统事件类型} a
* @param {bool 可选。指定事件是否在捕获或冒泡阶段执行。} b
*/
_createDomListener(E,a,b){
const t = e=>{this.fire(E._domNode,e)};
return t.removeListener = (()=>{
E._domNode.removeEventListener(a, t, b),
delete E._domListeners[a],
delete E._events[a]
}),t
}
/**
* 事件监听函数,事件发生后,遍历在此节点注册的事件类型下的函数并执行。单独使用的时与on及 once函数匹配。
* @param {发出事件的节点。单独使用时为自定义事件名称} n
* @param {事件对象。单独使用时为传递给on或once函数的第二个参数||也可是事件字符串} e
*/
fire(n,e){
if('string' === typeof n){
let E=this._Event.get(Object),a;E&&E.hasOwnProperty(n)&&(a=E[n]);
if(a)for(let [i, v] of a.entries()){v&&v.callback&&(v.callback(v.cfg,e),v.one&&a.splice(i,1))}
}else{
let E=this._Event.get(n),b,h,t=e;E._events&&(h=E._events),
'string' === typeof e || (t=e.type),
b=E._domListeners[t],
b&&b.callback&&b.callback.call(this,n,e);
if(h&&h[t])for(const [k,v] of h[t])v.filter(f=>f.call(this,k,e));
}
}
/**
* 用Range设置选择区
* @param {Range} r
*/
setRange(r){let s=getSelection();return s.removeAllRanges(),this.lastRange=r,s.addRange(r)}
/**
* 改变选择区为 光标所在节点
* @param {开关,光标在文本尾部时是否改变选择区,为真时改变} t
*/
SetRangeToBlock(t=1){
let s=this.sel,a=s.anchorNode;
if(s.isCollapsed &&a.nodeType===3){
if(t>1) this.reSetRange(a,0,a,a.length)
else (s.anchorOffset<a.length)&&this.reSetRange(a,0,a,a.length)
}
}
/**
* 用节点及其偏移设置选择区
* @param {node,startContainer} a
* @param {startOffset} s
* @param {node,endContainer} f
* @param {endOffset} e
*/
reSetRange(a,s,f,e){
let r= new Range();
return r.setStart(a,s),r.setEnd(f,e),this.setRange(r)
}
/**
* 默认恢复页面
* @param {保存的光标} t
* @param {false 仅恢复光标,不恢复修改前的内容} b
*/
reCursor(t,b=!0){
b&&(this.editnode.innerHTML=t.html||'<p></br></p>');
let s=this.editnode.querySelector('.cursor-start'),
e=this.editnode.querySelector('.cursor-end');
s||(s=this.editnode),
e||(e=this.editnode);
if(t.atype==3)s=s.childNodes[t.aindex];
if(t.ftype==3)e=e.childNodes[t.findex];
return this.reSetRange(s,t.start,e,t.end)
}
/** 删除光标 */
removeCursor(root){
root&&(root.querySelectorAll('.cursor-start').forEach(n =>n.classList.remove('cursor-start')),
root.querySelectorAll('.cursor-end').forEach(n =>n.classList.remove('cursor-end')))
}
/**
* 保存光标 支持 edge,chrome,firefox.
* @param {要保存光标的节点,为了在切换页面时恢复光标新加} root
*/
saveCursor(root){
this.removeCursor(root);
let s=getSelection();
if(!s.rangeCount)return !1;
let o={},r=s.getRangeAt(0),an=r.startContainer,fn=r.endContainer,
/** 得到n节点在父节点里的索引 */
index=(n)=>{
let ns=n.parentNode.childNodes;
for(let i=0;i<ns.length;i++){if(ns[i].isSameNode(n)) return i}
return -1
},
/** 计算文本节点合并后的光标所在节点与偏移位置*/
f=(node)=>{
let i=0,n=node;
while(n&&n.nodeType&&n.nodeType==3){
i+=n.length,node=n;
n=n.previousSibling;
}return{i,node}
};
o.start=r.startOffset,
o.end=r.endOffset;
if(an.nodeType===3){
let t=f(an);o.start+=t.i-an.length,an=t.node
o.aindex=index(an),
an=an.parentNode,o.atype=3
}else{
let n=an.childNodes[o.start];
if(n&&n.nodeType===3){
let t=f(n);o.start=t.i-n.length,
an=t.node,
o.aindex=index(an),
an=an.parentNode,o.atype=3
}
}
if(fn.nodeType===3){
let t=f(fn);o.end+=t.i-fn.length,fn=t.node
o.findex=index(fn),
fn=fn.parentNode,o.ftype=3
}else{
let n=fn.childNodes[o.end-1]
if(n&&n.nodeType===3){
let t=f(n);o.end=t.i,fn=t.node,
o.findex=index(fn),
fn=fn.parentNode,o.ftype=3
}
}
root.compareDocumentPosition(an)&16&&(an.classList.add('cursor-start'),fn.classList.add('cursor-end'));
return o.html=root.innerHTML,o
}
wrap(tag,n=!1){
let ary=[],b=document.createElement(tag),ns,r,f,a,s,e;
n?(r=new Range(),r.selectNode(n)):r=this.lastRange;
f=r.endContainer,a=r.startContainer;
if(a.isSameNode(f)){
r.surroundContents(b)
}else{
ns=r.commonAncestorContainer.childNodes;
for (let i=0; i<ns.length; i++){
if(ns[i].compareDocumentPosition(a)&16||ns[i].isSameNode(a))s=i;
if(ns[i].compareDocumentPosition(f)&16||ns[i].isSameNode(f))e=i;
}
for (let i=s; i<=e; i++)ary.push(ns[i]);
ary.filter(n=>b.appendChild(n)),
r.insertNode(b);
}
return b
}
/**
* 拆包
* @param {被拆掉的节点} n
*/
unwrap(n){
let l,r=new Range(),t=this.lastRange;
r.selectNodeContents(n),
l = r.extractContents(),
t.selectNode(n),
t.deleteContents(),
t.insertNode(l);
/** edge 专用 */
this.setRange(t)
}
pop_body(n,e){
this.editnode.classList.add('ck-focused');
e&&(e.stopPropagation(),this.domPath.includes(e.target)||this.domPath.push(e.target))
this.pop_remove();
this.domPath.filter(i=>{
let t='pop_'+i.tagName.toLowerCase();
this.MOD[t]&&this.MOD[t]().flip&&this.MOD[t](this).flip(i);
});
e&&this.figcaption(e)
}
/**
* 把气泡移动到 n 节点附近
* @param {气泡内节点} t
* @param {光标所在节点} n
* @param {给光标所在节点添加的css} css1
* @param {给balloon节点添加的css,用于this.execCommand,避免删除pop} css2
*/
pop_move(t,n,css=!1){
t&&(this.pop_remove(),
this.balloon=this.popbody.appendChild(this.UI(this.MOD.res.TP.balloon)),
this.balloon.appendChild(t))
let r,fcN=this._getChangeNode().firstChild;
n?r=n.getBoundingClientRect():r=this.lastRange.getBoundingClientRect();
r.top==0&&r.left==0&&(fcN&&(r=fcN.getBoundingClientRect()));
let HalfH=window.screen.height/2,pc=this.balloon.offsetWidth/2,
HalfW=window.screen.width/3,
pl=this.balloon.offsetLeft,
ph=this.balloon.offsetHeight,
c='n',s='w',x=window.scrollX,y=window.scrollY,
l=r.left+(r.right-r.left)/2+x,
b=r.bottom+10+y;
if(n){
let nc=(r.left+r.right)/2;
nc>HalfW?(s='',l=nc-pc-pl):l=nc-25,
n.offsetTop>HalfH&&(c='s',b=n.offsetTop-ph-10),
css&&n.classList.add(css)
}else{
if(r.left>HalfW){s='',l=l-pc-pl}else l=l-25;
if(r.top>HalfH){c='s',b=r.top-ph-10+y}
}
this.balloon.classList.add(`ck-balloon-panel_arrow_${c+s}`),//css2 会出现 null
this.balloon.style.left=l+'px',
this.balloon.style.top=b+'px'
}
/** 气泡删除 */
pop_remove(){//
this.balloon&&this.balloon.remove();
this.MOD.res.remove_css&&this.MOD.res.remove_css.filter(s=>{this.editnode.querySelectorAll('.'+s).forEach(n=>{n.classList.remove(s)})})
}
figcaption(e){
this.editnode.querySelectorAll('figcaption').forEach(n=>(n.parentNode.compareDocumentPosition(e.target)&16)||n.innerHTML.length&&(!new RegExp(/<br\s*\/*>/i).test(n.innerHTML))||n.classList.add('ck-hidden'))
}
renametag(n,v){
let t=document.createElement(v),ns=n.attributes,r=this.saveCursor(this.editnode);
t.innerHTML=n.innerHTML;
for(let i=0;i<ns.length;i++){
ns[i].value&&t.setAttribute(ns[i].name,ns[i].value)
}
n.parentNode.replaceChild(t,n);
return this.reCursor(r,!1),t
}
insertHTML(n,v){//document.execCommand('insertHTML', !1, v),
let r=new Range();
n= document.createElement('div'),
n.innerHTML=v,
r.selectNodeContents(n),
n = r.extractContents(),
this.lastRange.insertNode(n)
this.fire('savechange')
}
},document.scripts[document.scripts.length - 1].src.substring(0, document.scripts[document.scripts.length - 1].src.lastIndexOf("/") + 1));