diff --git a/.eslintrc.js b/.eslintrc.js index 02708f2d5..322d2d441 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -25,6 +25,7 @@ module.exports = { 'openpgp': "readonly", 'CKEDITOR': "readonly", 'Squire': "readonly", + 'SquireUI': "readonly", // node_modules/knockout but dev/External/ko.js is used // 'ko': "readonly", // node_modules/simplestatemanager diff --git a/README.md b/README.md index 1becc7432..6bbd6656d 100644 --- a/README.md +++ b/README.md @@ -88,23 +88,23 @@ Things might work in Edge 18, Firefox 50-62 and Chrome 54-68 due to one polyfill |js/* |1.14.0 |native | |----------- |--------: |--------: | -|admin.js |2.130.942 |1.000.931 | -|app.js |4.184.455 |2.618.339 | +|admin.js |2.130.942 | 941.623 | +|app.js |4.184.455 |2.559.130 | |boot.js | 671.522 | 5.834 | -|libs.js | 647.614 | 312.343 | +|libs.js | 647.614 | 326.686 | |polyfills.js | 325.834 | 0 | -|TOTAL |7.960.367 |3.937.447 | +|TOTAL |7.960.367 |3.833.273 | |js/min/* |1.14.0 |native |gzip 1.14 |gzip |brotli | |--------------- |--------: |--------: |--------: |--------: |--------: | -|admin.min.js | 252.147 | 136.658 | 73.657 | 40.569 | 34.783 | -|app.min.js | 511.202 | 357.183 |140.462 | 94.834 | 76.231 | +|admin.min.js | 252.147 | 128.739 | 73.657 | 37.549 | 32.265 | +|app.min.js | 511.202 | 349.263 |140.462 | 91.754 | 73.588 | |boot.min.js | 66.007 | 3.166 | 22.567 | 1.571 | 1.345 | -|libs.min.js | 572.545 | 295.754 |176.720 | 91.521 | 80.871 | +|libs.min.js | 572.545 | 303.770 |176.720 | 94.774 | 83.577 | |polyfills.min.js | 32.452 | 0 | 11.312 | 0 | 0 | -|TOTAL |1.434.353 | 792.761 |424.718 |228.495 |193.230 | +|TOTAL |1.434.353 | 784.938 |424.718 |225.648 |190.775 | -641.592 bytes (196.223 gzip) is not much, but it feels faster. +649.415 bytes (199.070 gzip) is not much, but it feels faster. ### CSS changes diff --git a/dev/Common/HtmlEditor.js b/dev/Common/HtmlEditor.js index bb6c1e1d1..46d3db4bb 100644 --- a/dev/Common/HtmlEditor.js +++ b/dev/Common/HtmlEditor.js @@ -1,5 +1,4 @@ import { EventKeyCode } from 'Common/Enums'; -import { SquireUI } from 'External/SquireUI'; /** * @type {Object} diff --git a/dev/External/SquireUI.js b/dev/External/SquireUI.js index caee89e4a..42b34315d 100644 --- a/dev/External/SquireUI.js +++ b/dev/External/SquireUI.js @@ -1,309 +1,322 @@ /* eslint max-len: 0 */ + +(() => { + 'use strict'; const doc = document, -allowedElements = 'A,B,BLOCKQUOTE,BR,DIV,FONT,H1,H2,H3,H4,H5,H6,HR,IMG,LABEL,LI,OL,P,SPAN,STRONG,TABLE,TD,TH,TR,U,UL', -allowedAttributes = 'abbr,align,background,bgcolor,border,cellpadding,cellspacing,class,color,colspan,dir,face,frame,height,href,hspace,id,lang,rowspan,rules,scope,size,src,style,target,type,usemap,valign,vspace,width'.split(','), + removeElements = 'HEAD,LINK,META,NOSCRIPT,SCRIPT,TEMPLATE,TITLE', + allowedElements = 'A,B,BLOCKQUOTE,BR,DIV,FONT,H1,H2,H3,H4,H5,H6,HR,IMG,LI,OL,P,SPAN,STRONG,TABLE,TD,TH,TR,U,UL', + allowedAttributes = 'abbr,align,background,bgcolor,border,cellpadding,cellspacing,class,color,colspan,dir,face,frame,height,href,hspace,id,lang,rowspan,rules,scope,size,src,style,target,type,usemap,valign,vspace,width'.split(','), -i18n = (str, def) => rl.i18n(str) || def, + i18n = (str, def) => rl.i18n(str) || def, -SquireDefaultConfig = { -/* - blockTag: 'P', - blockAttributes: null, - tagAttributes: { - blockquote: null, - ul: null, - ol: null, - li: null, - a: null - }, - classNames: { - colour: 'colour', - fontFamily: 'font', - fontSize: 'size', - highlight: 'highlight' - }, - leafNodeNames: leafNodeNames, - undo: { - documentSizeThreshold: -1, // -1 means no threshold - undoLimit: -1 // -1 means no limit - }, - isInsertedHTMLSanitized: true, - isSetHTMLSanitized: true, - willCutCopy: null, - addLinks: true // allow_smart_html_links -*/ - sanitizeToDOMFragment: (html, isPaste/*, squire*/) => { - const frag = doc.createDocumentFragment(), - tpl = doc.createElement('div'); - tpl.innerHTML = html; - if (isPaste) { - tpl.querySelectorAll(':not('+allowedElements+',signature)').forEach(el => el.remove()); - tpl.querySelectorAll(allowedElements).forEach(el => { - if (el.hasAttributes()) { - Array.from(el.attributes).forEach(attr => { - let name = attr.name.toLowerCase(); - if (!allowedAttributes.includes(name)) { - el.removeAttribute(name); - } - }); - } - }); - } - frag.append(...tpl.childNodes); + ctrlKey = /Mac OS X/.test( navigator.userAgent ) ? 'meta + ' : 'Ctrl + ', + + getFragmentOfChildren = parent => { + let frag = doc.createDocumentFragment(); + frag.append(...parent.childNodes); return frag; - } -}, + }, -rl_signature_replacer = (editor, text, signature, isHtml, insertBefore) => { - let - prevSignature = editor.__previous_signature, - skipInsert = false, - isEmptyText = false, - newLine = (isHtml ? '
' : "\n"), - clearHtmlLine = html => rl.Utils.htmlToPlain(html).trim(); + SquireDefaultConfig = { +/* + blockTag: 'P', + blockAttributes: null, + tagAttributes: { + blockquote: null, + ul: null, + ol: null, + li: null, + a: null + }, + classNames: { + colour: 'colour', + fontFamily: 'font', + fontSize: 'size', + highlight: 'highlight' + }, + leafNodeNames: leafNodeNames, + undo: { + documentSizeThreshold: -1, // -1 means no threshold + undoLimit: -1 // -1 means no limit + }, + isInsertedHTMLSanitized: true, + isSetHTMLSanitized: true, + willCutCopy: null, + addLinks: true // allow_smart_html_links +*/ + sanitizeToDOMFragment: (html, isPaste/*, squire*/) => { + let tpl = doc.createElement('div'); + tpl.innerHTML = html + .replace(/<\/?(BODY|HTML)[^>]*>/gi,'') + .replace(//g,'') + .trim(); + if (isPaste) { + tpl.querySelectorAll(removeElements).forEach(el => el.remove()); + tpl.querySelectorAll(':not('+allowedElements+',signature)').forEach(el => el.replaceWith(getFragmentOfChildren(el))); + tpl.querySelectorAll('*').forEach(el => { + if (el.hasAttributes()) { + Array.from(el.attributes).forEach(attr => { + let name = attr.name.toLowerCase(); + if (!allowedAttributes.includes(name)) { + el.removeAttribute(name); + } + }); + } + }); + } + return getFragmentOfChildren(tpl); + } + }, - isEmptyText = !text.trim(); - if (!isEmptyText && isHtml) { - isEmptyText = !clearHtmlLine(text); - } + rl_signature_replacer = (editor, text, signature, isHtml, insertBefore) => { + let + prevSignature = editor.__previous_signature, + skipInsert = false, + isEmptyText = false, + newLine = (isHtml ? '
' : "\n"), + clearHtmlLine = html => rl.Utils.htmlToPlain(html).trim(); - if (prevSignature && !isEmptyText) { - if (isHtml && !prevSignature.isHtml) { - prevSignature = { - body: rl.Utils.plainToHtml(prevSignature.body), - isHtml: true - }; - } else if (!isHtml && prevSignature.isHtml) { - prevSignature = { - body: rl.Utils.htmlToPlain(prevSignature.body), - isHtml: true - }; + isEmptyText = !text.trim(); + if (!isEmptyText && isHtml) { + isEmptyText = !clearHtmlLine(text); } - if (isHtml) { - var clearSig = clearHtmlLine(prevSignature.body); - text = text.replace(/([\s\S]*)<\/signature>/igm, all => { - var c = clearSig === clearHtmlLine(all); - if (!c) { - skipInsert = true; - } - return c ? '' : all; - }); - } else { - var textLen = text.length; - text = text - .replace('' + prevSignature.body, '') - .replace('' + prevSignature.body, ''); - skipInsert = textLen === text.length; + if (prevSignature && !isEmptyText) { + if (isHtml && !prevSignature.isHtml) { + prevSignature = { + body: rl.Utils.plainToHtml(prevSignature.body), + isHtml: true + }; + } else if (!isHtml && prevSignature.isHtml) { + prevSignature = { + body: rl.Utils.htmlToPlain(prevSignature.body), + isHtml: true + }; + } + + if (isHtml) { + var clearSig = clearHtmlLine(prevSignature.body); + text = text.replace(/([\s\S]*)<\/signature>/igm, all => { + var c = clearSig === clearHtmlLine(all); + if (!c) { + skipInsert = true; + } + return c ? '' : all; + }); + } else { + var textLen = text.length; + text = text + .replace('' + prevSignature.body, '') + .replace('' + prevSignature.body, ''); + skipInsert = textLen === text.length; + } } - } - if (!skipInsert) { - signature = newLine + newLine + (isHtml ? '' : '') + signature + (isHtml ? '' : ''); + if (!skipInsert) { + signature = newLine + newLine + (isHtml ? '' : '') + signature + (isHtml ? '' : ''); - text = insertBefore ? signature + text : text + signature; + text = insertBefore ? signature + text : text + signature; - if (10 < signature.length) { - prevSignature = { - body: signature, - isHtml: isHtml - }; + if (10 < signature.length) { + prevSignature = { + body: signature, + isHtml: isHtml + }; + } } - } - editor.__previous_signature = prevSignature; + editor.__previous_signature = prevSignature; - return text; -}; + return text; + }; class SquireUI { constructor(container) { const - ctrlKey = /Mac OS X/.test( navigator.userAgent ) ? 'meta + ' : 'Ctrl + ', - actions = { - mode: { - plain: { - html: '〈〉', - cmd: () => this.setMode('plain' == this.mode ? 'wysiwyg' : 'plain'), - hint: i18n('EDITOR/TEXT_SWITCHER_PLAINT_TEXT', 'Plain') - } - }, - font: { - fontFamily: { - select: { - 'sans-serif': { - Arial: "'Nimbus Sans L', 'Liberation sans', 'Arial Unicode MS', Arial, Helvetica, Garuda, Utkal, FreeSans, sans-serif", - Tahoma: "'Luxi Sans', Tahoma, Loma, Geneva, Meera, sans-serif", - Trebuchet: "'DejaVu Sans Condensed', Trebuchet, 'Trebuchet MS', sans-serif", - Lucida: "'Lucida Sans Unicode', 'Lucida Sans', 'DejaVu Sans', 'Bitstream Vera Sans', 'DejaVu LGC Sans', sans-serif", - Verdana: "'DejaVu Sans', Verdana, Geneva, 'Bitstream Vera Sans', 'DejaVu LGC Sans', sans-serif" + actions = { + mode: { + plain: { + html: '〈〉', + cmd: () => this.setMode('plain' == this.mode ? 'wysiwyg' : 'plain'), + hint: i18n('EDITOR/TEXT_SWITCHER_PLAINT_TEXT', 'Plain') + } + }, + font: { + fontFamily: { + select: { + 'sans-serif': { + Arial: "'Nimbus Sans L', 'Liberation sans', 'Arial Unicode MS', Arial, Helvetica, Garuda, Utkal, FreeSans, sans-serif", + Tahoma: "'Luxi Sans', Tahoma, Loma, Geneva, Meera, sans-serif", + Trebuchet: "'DejaVu Sans Condensed', Trebuchet, 'Trebuchet MS', sans-serif", + Lucida: "'Lucida Sans Unicode', 'Lucida Sans', 'DejaVu Sans', 'Bitstream Vera Sans', 'DejaVu LGC Sans', sans-serif", + Verdana: "'DejaVu Sans', Verdana, Geneva, 'Bitstream Vera Sans', 'DejaVu LGC Sans', sans-serif" + }, + monospace: { + Courier: "'Liberation Mono', 'Courier New', FreeMono, Courier, monospace", + Lucida: "'DejaVu Sans Mono', 'DejaVu LGC Sans Mono', 'Bitstream Vera Sans Mono', 'Lucida Console', Monaco, monospace" + }, + sans: { + Times: "'Nimbus Roman No9 L', 'Times New Roman', Times, FreeSerif, serif", + Palatino: "'Bitstream Charter', 'Palatino Linotype', Palatino, Palladio, 'URW Palladio L', 'Book Antiqua', Times, serif", + Georgia: "'URW Palladio L', Georgia, Times, serif" + } }, - monospace: { - Courier: "'Liberation Mono', 'Courier New', FreeMono, Courier, monospace", - Lucida: "'DejaVu Sans Mono', 'DejaVu LGC Sans Mono', 'Bitstream Vera Sans Mono', 'Lucida Console', Monaco, monospace" - }, - sans: { - Times: "'Nimbus Roman No9 L', 'Times New Roman', Times, FreeSerif, serif", - Palatino: "'Bitstream Charter', 'Palatino Linotype', Palatino, Palladio, 'URW Palladio L', 'Book Antiqua', Times, serif", - Georgia: "'URW Palladio L', Georgia, Times, serif" - } + cmd: s => squire.setFontFace(s.value) }, - cmd: s => squire.setFontFace(s.value) + fontSize: { + select: ['11px','13px','16px','20px','24px','30px'], + cmd: s => squire.setFontSize(s.value) + } }, - fontSize: { - select: ['11px','13px','16px','20px','24px','30px'], - cmd: s => squire.setFontSize(s.value) - } - }, - colors: { - textColor: { - input: 'color', - cmd: s => squire.setTextColour(s.value), - hint: 'Text color' + colors: { + textColor: { + input: 'color', + cmd: s => squire.setTextColour(s.value), + hint: 'Text color' + }, + backgroundColor: { + input: 'color', + cmd: s => squire.setHighlightColour(s.value), + hint: 'Background color' + }, }, - backgroundColor: { - input: 'color', - cmd: s => squire.setHighlightColour(s.value), - hint: 'Background color' - }, - }, /* - bidi: { - allowHtmlEditorBitiButtons - }, + bidi: { + allowHtmlEditorBitiButtons + }, */ - inline: { - bold: { - html: '𝐁', - cmd: () => this.doAction('bold','B'), - key: 'B', - hint: 'Bold' - }, - italic: { - html: '𝐼', - cmd: () => this.doAction('italic','I'), - key: 'I', - hint: 'Italic' - }, - underline: { - html: 'U', - cmd: () => this.doAction('underline','U'), - key: 'U', - hint: 'Underline' - }, - strike: { - html: 'S', - cmd: () => this.doAction('strikethrough','S'), - key: 'Shift + 7', - hint: 'Strikethrough' - }, - sub: { - html: 'Sx', - cmd: () => this.doAction('subscript','SUB'), - key: 'Shift + 5', - hint: 'Subscript' - }, - sup: { - html: 'Sx', - cmd: () => this.doAction('superscript','SUP'), - key: 'Shift + 6', - hint: 'Superscript' - } - }, - block: { - ol: { - html: '#', - cmd: () => this.doList('OL'), - key: 'Shift + 8', - hint: 'Ordered list' - }, - ul: { - html: '⋮', - cmd: () => this.doList('UL'), - key: 'Shift + 9', - hint: 'Unordered list' - }, - quote: { - html: '"', - cmd: () => { - let parent = this.getParentNodeName('UL,OL'); - (parent && 'BLOCKQUOTE' == parent) ? squire.decreaseQuoteLevel() : squire.increaseQuoteLevel(); + inline: { + bold: { + html: '𝐁', + cmd: () => this.doAction('bold','B'), + key: 'B', + hint: 'Bold' }, - hint: 'Blockquote' - }, - indentDecrease: { - html: '⇤', - cmd: () => squire.changeIndentationLevel('decrease'), - key: ']', - hint: 'Decrease indent' - }, - indentIncrease: { - html: '⇥', - cmd: () => squire.changeIndentationLevel('increase'), - key: '[', - hint: 'Increase indent' - } - }, - targets: { - link: { - html: '🔗', - cmd: () => { - if ('A' === this.getParentNodeName()) { - squire.removeLink(); - } else { - let url = prompt("Link","https://"); - url != null && url.length && squire.makeLink(url); - } + italic: { + html: '𝐼', + cmd: () => this.doAction('italic','I'), + key: 'I', + hint: 'Italic' }, - hint: 'Link' - }, - image: { - html: '🖼️', - cmd: () => { - if ('IMG' === this.getParentNodeName()) { -// squire.removeLink(); - } else { - let src = prompt("Image","https://"); - src != null && src.length && squire.insertImage(src); - } + underline: { + html: 'U', + cmd: () => this.doAction('underline','U'), + key: 'U', + hint: 'Underline' }, - hint: 'Image' + strike: { + html: 'S', + cmd: () => this.doAction('strikethrough','S'), + key: 'Shift + 7', + hint: 'Strikethrough' + }, + sub: { + html: 'Sx', + cmd: () => this.doAction('subscript','SUB'), + key: 'Shift + 5', + hint: 'Subscript' + }, + sup: { + html: 'Sx', + cmd: () => this.doAction('superscript','SUP'), + key: 'Shift + 6', + hint: 'Superscript' + } + }, + block: { + ol: { + html: '#', + cmd: () => this.doList('OL'), + key: 'Shift + 8', + hint: 'Ordered list' + }, + ul: { + html: '⋮', + cmd: () => this.doList('UL'), + key: 'Shift + 9', + hint: 'Unordered list' + }, + quote: { + html: '"', + cmd: () => { + let parent = this.getParentNodeName('UL,OL'); + (parent && 'BLOCKQUOTE' == parent) ? squire.decreaseQuoteLevel() : squire.increaseQuoteLevel(); + }, + hint: 'Blockquote' + }, + indentDecrease: { + html: '⇤', + cmd: () => squire.changeIndentationLevel('decrease'), + key: ']', + hint: 'Decrease indent' + }, + indentIncrease: { + html: '⇥', + cmd: () => squire.changeIndentationLevel('increase'), + key: '[', + hint: 'Increase indent' + } + }, + targets: { + link: { + html: '🔗', + cmd: () => { + if ('A' === this.getParentNodeName()) { + squire.removeLink(); + } else { + let url = prompt("Link","https://"); + url != null && url.length && squire.makeLink(url); + } + }, + hint: 'Link' + }, + image: { + html: '🖼️', + cmd: () => { + if ('IMG' === this.getParentNodeName()) { +// squire.removeLink(); + } else { + let src = prompt("Image","https://"); + src != null && src.length && squire.insertImage(src); + } + }, + hint: 'Image' + }, +/* + imageUpload: { + // TODO + } +*/ }, /* - imageUpload: { + table: { // TODO - } -*/ - }, -/* - table: { - // TODO - }, -*/ - changes: { - undo: { - html: '↶', - cmd: () => squire.undo(), - key: 'Z', - hint: 'Undo' }, - redo: { - html: '↷', - cmd: () => squire.redo(), - key: 'Y', - hint: 'Redo' +*/ + changes: { + undo: { + html: '↶', + cmd: () => squire.undo(), + key: 'Z', + hint: 'Undo' + }, + redo: { + html: '↷', + cmd: () => squire.redo(), + key: 'Y', + hint: 'Redo' + } } - } - }, + }, - plain = doc.createElement('textarea'), - wysiwyg = doc.createElement('div'), - toolbar = doc.createElement('div'), - squire = new Squire(wysiwyg, SquireDefaultConfig); + plain = doc.createElement('textarea'), + wysiwyg = doc.createElement('div'), + toolbar = doc.createElement('div'), + squire = new Squire(wysiwyg, SquireDefaultConfig); plain.className = 'squire-plain cke_plain cke_editable'; wysiwyg.className = 'squire-wysiwyg cke_wysiwyg_div cke_editable'; @@ -331,7 +344,7 @@ class SquireUI if (cfg.input) { input = doc.createElement('input'); input.type = cfg.input; - input.addEventListener('input', () => cfg.cmd(input)); + input.addEventListener('change', () => cfg.cmd(input)); } else if (cfg.select) { input = doc.createElement('select'); if (Array.isArray(cfg.select)) { @@ -372,12 +385,10 @@ class SquireUI } toolbar.addEventListener('click', e => { let t = e.target; - if ('plain' != this.mode || 'plain' == t.dataset.action) { - t.action_cmd && t.action_cmd(t); - } + t.action_cmd && t.action_cmd(t); }); - let changes = actions.changes + let changes = actions.changes; changes.undo.input.disabled = changes.redo.input.disabled = true; squire.addEventListener('undoStateChange', state => { changes.undo.input.disabled = !state.canUndo; @@ -502,7 +513,7 @@ squire-raw.js:4089: this.fireEvent( 'willPaste', event ); } focus() { - ('plain' == this.editor.mode ? this.plain : this.squire).focus(); + ('plain' == this.mode ? this.plain : this.squire).focus(); } resize(width, height) { @@ -517,4 +528,6 @@ squire-raw.js:4089: this.fireEvent( 'willPaste', event ); } } -export { SquireUI, SquireUI as default }; +window.SquireUI = SquireUI; + +})(); diff --git a/tasks/config.js b/tasks/config.js index ff0cdc541..c15c30181 100644 --- a/tasks/config.js +++ b/tasks/config.js @@ -95,7 +95,8 @@ config.paths.js = { 'vendors/lightgallery/dist/js/lg-thumbnail.min.js', 'vendors/lightgallery/dist/js/lg-zoom.min.js', 'vendors/lightgallery/dist/js/lg-autoplay.min.js', - 'dev/External/ifvisible.js' + 'dev/External/ifvisible.js', + 'dev/External/SquireUI.js' ] }, app: { diff --git a/vendors/squire/build/squire-raw.js b/vendors/squire/build/squire-raw.js index 3d5997b2d..e755924af 100644 --- a/vendors/squire/build/squire-raw.js +++ b/vendors/squire/build/squire-raw.js @@ -1,6 +1,13 @@ /* Copyright © 2011-2015 by Neil Jenkins. MIT Licensed. */ /* eslint max-len: 0 */ +/** + TODO: modifyBlocks function doesn't work very good. + For example you have: UL > LI > [cursor here in text] + Then create blockquote at cursor, the result is: BLOCKQUOTE > UL > LI + not UL > LI > BLOCKQUOTE +*/ + ( doc => { "use strict"; @@ -58,8 +65,7 @@ const 11: 1024 }, -// blockElementNames = /^(?:ADDRESS|ARTICLE|ASIDE|BLOCKQUOTE|CANVAS|DETAILS|DIALOG|DIV|D[DLT]|FIELDSET|FIG(CAPTION|URE)|FOOTER|FORM|H[1-6]|HEADER|HGROUP|HR|LI|MAIN|NAV|OL|P|PRE|SECTION|TABLE|UL|VIDEO)$/, - inlineNodeNames = /^(?:#text|A|ABBR|ACRONYM|B|BR|BDI|BDO|CITE|CODE|DATA|DEL|DFN|EM|FONT|HR|IMG|INPUT|INS|KBD|Q|RP|RT|RUBY|SAMP|SMALL|SPAN|STRIKE|STRONG|SUB|SUP|TIME|U|VAR|WBR)$/, + inlineNodeNames = /^(?:#text|A|ABBR|ACRONYM|B|BR|BD[IO]|CITE|CODE|DATA|DEL|DFN|EM|FONT|HR|IMG|INPUT|INS|KBD|Q|RP|RT|RUBY|SAMP|SMALL|SPAN|STR(IKE|ONG)|SU[BP]|TIME|U|VAR|WBR)$/, leafNodeNames = { BR: 1, @@ -110,10 +116,12 @@ const return walker; }, getPreviousBlock = ( node, root ) => { +// node = getClosest( node, root, blockElementNames ); node = getBlockWalker( node, root ).previousNode(); return node !== root ? node : null; }, getNextBlock = ( node, root ) => { +// node = getClosest( node, root, blockElementNames ); node = getBlockWalker( node, root ).nextNode(); return node !== root ? node : null; }, @@ -261,7 +269,7 @@ const child = node.firstChild; while ( isWebKit && child && child.nodeType === TEXT_NODE && !child.data ) { - node.removeChild( child ); + child.remove( ); child = node.firstChild; } if ( !child ) { @@ -467,7 +475,7 @@ const // Remove extra
fixer if present. last = block.lastChild; if ( last && last.nodeName === 'BR' ) { - block.removeChild( last ); + last.remove( ); --offset; } @@ -817,7 +825,7 @@ const moveRangeBoundariesDownTree( range ); }, - isNodeContainedInRange = ( range, node, partial ) => { + isNodeContainedInRange = ( range, node, partial = true ) => { let nodeRange = doc.createRange(); nodeRange.selectNode( node ); @@ -970,7 +978,7 @@ const block = getNextBlock( block, root ); } // Check the block actually intersects the range - return block && isNodeContainedInRange( range, block, true ) ? block : null; + return block && isNodeContainedInRange( range, block ) ? block : null; }, // Returns the last block at least partially contained by the range, @@ -995,7 +1003,7 @@ const block = getPreviousBlock( block, root ); } // Check the block actually intersects the range - return block && isNodeContainedInRange( range, block, true ) ? block : null; + return block && isNodeContainedInRange( range, block ) ? block : null; }, newContentWalker = root => doc.createTreeWalker( root, @@ -1065,10 +1073,12 @@ const parent; if ( start && end ) { - parent = start.parentNode; - range.setStart( parent, indexOf( parent.childNodes, start ) ); - parent = end.parentNode; - range.setEnd( parent, indexOf( parent.childNodes, end ) + 1 ); + range.setStart( start, 0 ); + range.setEnd( end, end.childNodes.length ); +// parent = start.parentNode; +// range.setStart( parent, indexOf( parent.childNodes, start ) ); +// parent = end.parentNode; +// range.setEnd( parent, indexOf( parent.childNodes, end ) + 1 ); } }; @@ -1185,7 +1195,7 @@ let afterDelete = ( self, range ) => { indexOf( parent.childNodes, node ) ); range.collapse( true ); // Remove empty inline(s) - parent.removeChild( node ); + node.remove( ); // Fix cursor in block if ( !isBlock( parent ) ) { parent = getPreviousBlock( parent, self._root ); @@ -1785,6 +1795,10 @@ let stylesRewriters = { newTreeBottom.append( empty( node ) ); return newTreeBottom; }, +// KBD: +// VAR: +// CODE: +// SAMP: TT: ( node, parent, config ) => { let el = createElement( doc, 'SPAN', { 'class': config.classNames.fontFamily, @@ -1822,13 +1836,13 @@ let cleanTree = ( node, config, preserveWS ) => { child = children[i]; nodeName = child.nodeName; nodeType = child.nodeType; - rewriter = stylesRewriters[ nodeName ]; if ( nodeType === ELEMENT_NODE ) { + rewriter = stylesRewriters[ nodeName ]; childLength = child.childNodes.length; if ( rewriter ) { child = rewriter( child, node, config ); } else if ( blacklist.test( nodeName ) ) { - node.removeChild( child ); + child.remove( ); --i; --l; continue; @@ -1888,7 +1902,7 @@ let cleanTree = ( node, config, preserveWS ) => { continue; } } - node.removeChild( child ); + child.remove( ); --i; --l; } @@ -1907,10 +1921,10 @@ let removeEmptyInlines = node => { if ( child.nodeType === ELEMENT_NODE && !isLeaf( child ) ) { removeEmptyInlines( child ); if ( isInline( child ) && !child.firstChild ) { - node.removeChild( child ); + child.remove( ); } } else if ( child.nodeType === TEXT_NODE && !child.data ) { - node.removeChild( child ); + child.remove( ); } } }; @@ -1996,7 +2010,7 @@ let setClipboardData = body.append( node ); text = node.innerText || node.textContent; text = text.replace( NBSP, ' ' ); // Replace nbsp with regular space - body.removeChild( node ); + node.remove( ); } // Firefox (and others?) returns unix line endings (\n) even on Windows. // If on Windows, normalise to \r\n, since Notepad and some other crappy @@ -2668,7 +2682,7 @@ proto.getCursorPosition = function ( range ) { insertNodeInRange( range, node ); rect = node.getBoundingClientRect(); parent = node.parentNode; - parent.removeChild( node ); + node.remove( ); mergeInlines( parent, range ); } return rect; @@ -2749,7 +2763,7 @@ proto.selectionContains = function (selector) { node = range && range.commonAncestorContainer; if (node && !range.collapsed) { node = node.querySelector ? node : node.parentElement; - // TODO: startContainer / endContainer for real selection match? + // TODO: isNodeContainedInRange( range, node ) for real selection match? return !!(node && node.querySelector(selector)); } return false; @@ -2763,7 +2777,7 @@ proto.getSelectedText = function () { let walker = doc.createTreeWalker( range.commonAncestorContainer, SHOW_TEXT|SHOW_ELEMENT, - node => isNodeContainedInRange( range, node, true ) + node => isNodeContainedInRange( range, node ) ); let startContainer = range.startContainer; let endContainer = range.endContainer; @@ -2821,7 +2835,7 @@ let removeZWS = ( root, keepNode ) => { if ( node.length === 1 ) { do { parent = node.parentNode; - parent.removeChild( node ); + node.remove( ); node = parent; walker.currentNode = parent; } while ( isInline( node ) && !getLength( node ) ); @@ -3146,7 +3160,7 @@ proto.hasFormat = function ( tag, attributes, range ) { // Otherwise, check each text node at least partially contained within // the selection and make sure all of them have the format we want. - walker = doc.createTreeWalker( common, SHOW_TEXT, node => isNodeContainedInRange( range, node, true ) ); + walker = doc.createTreeWalker( common, SHOW_TEXT, node => isNodeContainedInRange( range, node ) ); let seenNode = false; while ( node = walker.nextNode() ) { @@ -3248,7 +3262,7 @@ proto._addFormat = function ( tag, attributes, range ) { node => ( node.nodeType === TEXT_NODE || node.nodeName === 'BR' || node.nodeName === 'IMG' - ) && isNodeContainedInRange( range, node, true ) + ) && isNodeContainedInRange( range, node ) ); // Start at the beginning node of the range and iterate through @@ -3356,7 +3370,7 @@ proto._removeFormat = function ( tag, attributes, range, partial ) { // If not at least partially contained, wrap entire contents // in a clone of the tag we're removing and we're done. - if ( !isNodeContainedInRange( range, node, true ) ) { + if ( !isNodeContainedInRange( range, node ) ) { // Ignore bookmarks and empty text nodes if ( node.nodeName !== 'INPUT' && ( !isText || node.data ) ) { @@ -3387,7 +3401,7 @@ proto._removeFormat = function ( tag, attributes, range, partial ) { }, formatTags = Array.prototype.filter.call( root.getElementsByTagName( tag ), function ( el ) { - return isNodeContainedInRange( range, el, true ) && + return isNodeContainedInRange( range, el ) && hasTagAttributes( el, tag, attributes ); } ); @@ -3758,7 +3772,7 @@ proto.decreaseListLevel = function ( range ) { makeNotList = !/^[OU]L$/.test( newParent.nodeName ); do { next = startLi === endLi ? null : startLi.nextSibling; - list.removeChild( startLi ); + startLi.remove( ); if ( makeNotList && startLi.nodeName === 'LI' ) { startLi = this.createDefaultBlock([ empty( startLi ) ]); } @@ -3859,7 +3873,7 @@ proto.setHTML = function ( html ) { // Remove existing root children while ( child = root.lastChild ) { - root.removeChild( child ); + child.remove( ); } // And insert new content