From 638bd9942e9606117532b155360fb3360a923403 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 14 Jan 2021 11:24:59 +0000 Subject: [PATCH] lint: contentcollector --- src/static/js/contentcollector.js | 291 +++++++++++++++--------------- 1 file changed, 142 insertions(+), 149 deletions(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index e71144d82..28cf5ddff 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -31,45 +31,31 @@ const Changeset = require('./Changeset'); const hooks = require('./pluginfw/hooks'); const _ = require('./underscore'); -function sanitizeUnicode(s) { - return UNorm.nfc(s); -} - -function makeContentCollector(collectStyles, abrowser, apool, domInterface, className2Author) { - abrowser = abrowser || {}; - // I don't like the above. +const sanitizeUnicode = (s) => UNorm.nfc(s); +const makeContentCollector = function ( + collectStyles, abrowser, apool, domInterface, className2Author) { const dom = domInterface || { - isNodeText(n) { - return (n.nodeType == 3); - }, - nodeTagName(n) { - return n.tagName; - }, - nodeValue(n) { - return n.nodeValue; - }, - nodeNumChildren(n) { + isNodeText: (n) => (n.nodeType === 3), + nodeTagName: (n) => n.tagName, + nodeValue: (n) => n.nodeValue, + nodeNumChildren: (n) => { if (n.childNodes == null) return 0; return n.childNodes.length; }, - nodeChild(n, i) { + nodeChild: (n, i) => { if (n.childNodes.item == null) { return n.childNodes[i]; } return n.childNodes.item(i); }, - nodeProp(n, p) { - return n[p]; - }, - nodeAttr(n, a) { + nodeProp: (n, p) => n[p], + nodeAttr: (n, a) => { if (n.getAttribute != null) return n.getAttribute(a); if (n.attribs != null) return n.attribs[a]; return null; }, - optNodeInnerHTML(n) { - return n.innerHTML; - }, + optNodeInnerHTML: (n) => n.innerHTML, }; const _blockElems = { @@ -84,54 +70,41 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas _blockElems[element] = 1; }); - function isBlockElement(n) { - return !!_blockElems[(dom.nodeTagName(n) || '').toLowerCase()]; - } + const isBlockElement = (n) => !!_blockElems[(dom.nodeTagName(n) || '').toLowerCase()]; - function textify(str) { - return sanitizeUnicode( - str.replace(/(\n | \n)/g, ' ').replace(/[\n\r ]/g, ' ').replace(/\xa0/g, ' ').replace(/\t/g, ' ')); - } + const textify = (str) => sanitizeUnicode( + str.replace(/(\n | \n)/g, ' ') + .replace(/[\n\r ]/g, ' ') + .replace(/\xa0/g, ' ') + .replace(/\t/g, ' ')); - function getAssoc(node, name) { - return dom.nodeProp(node, `_magicdom_${name}`); - } + const getAssoc = (node, name) => dom.nodeProp(node, `_magicdom_${name}`); - const lines = (function () { + const lines = (() => { const textArray = []; const attribsArray = []; let attribsBuilder = null; const op = Changeset.newOp('+'); - var self = { - length() { - return textArray.length; - }, - atColumnZero() { - return textArray[textArray.length - 1] === ''; - }, - startNew() { + const self = { + length: () => textArray.length, + atColumnZero: () => textArray[textArray.length - 1] === '', + startNew: () => { textArray.push(''); self.flush(true); attribsBuilder = Changeset.smartOpAssembler(); }, - textOfLine(i) { - return textArray[i]; - }, - appendText(txt, attrString) { + textOfLine: (i) => textArray[i], + appendText: (txt, attrString) => { textArray[textArray.length - 1] += txt; // dmesg(txt+" / "+attrString); op.attribs = attrString; op.chars = txt.length; attribsBuilder.append(op); }, - textLines() { - return textArray.slice(); - }, - attribLines() { - return attribsArray; - }, + textLines: () => textArray.slice(), + attribLines: () => attribsArray, // call flush only when you're done - flush(withNewline) { + flush: (withNewline) => { if (attribsBuilder) { attribsArray.push(attribsBuilder.toString()); attribsBuilder = null; @@ -140,21 +113,24 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas }; self.startNew(); return self; - }()); + })(); const cc = {}; - function _ensureColumnZero(state) { + const _ensureColumnZero = (state) => { if (!lines.atColumnZero()) { cc.startNewLine(state); } - } + }; let selection, startPoint, endPoint; let selStart = [-1, -1]; let selEnd = [-1, -1]; - function _isEmpty(node, state) { + const _isEmpty = (node, state) => { // consider clean blank lines pasted in IE to be empty - if (dom.nodeNumChildren(node) == 0) return true; - if (dom.nodeNumChildren(node) == 1 && getAssoc(node, 'shouldBeEmpty') && dom.optNodeInnerHTML(node) == ' ' && !getAssoc(node, 'unpasted')) { + if (dom.nodeNumChildren(node) === 0) return true; + if (dom.nodeNumChildren(node) === 1 && + getAssoc(node, 'shouldBeEmpty') && + dom.optNodeInnerHTML(node) === ' ' && + !getAssoc(node, 'unpasted')) { if (state) { const child = dom.nodeChild(node, 0); _reachPoint(child, 0, state); @@ -163,37 +139,37 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas return true; } return false; - } + }; - function _pointHere(charsAfter, state) { + const _pointHere = (charsAfter, state) => { const ln = lines.length() - 1; let chr = lines.textOfLine(ln).length; - if (chr == 0 && !_.isEmpty(state.lineAttributes)) { + if (chr === 0 && !_.isEmpty(state.lineAttributes)) { chr += 1; // listMarker } chr += charsAfter; return [ln, chr]; - } + }; - function _reachBlockPoint(nd, idx, state) { + const _reachBlockPoint = (nd, idx, state) => { if (!dom.isNodeText(nd)) _reachPoint(nd, idx, state); - } + }; - function _reachPoint(nd, idx, state) { - if (startPoint && nd == startPoint.node && startPoint.index == idx) { + const _reachPoint = (nd, idx, state) => { + if (startPoint && nd === startPoint.node && startPoint.index === idx) { selStart = _pointHere(0, state); } - if (endPoint && nd == endPoint.node && endPoint.index == idx) { + if (endPoint && nd === endPoint.node && endPoint.index === idx) { selEnd = _pointHere(0, state); } - } - cc.incrementFlag = function (state, flagName) { + }; + cc.incrementFlag = (state, flagName) => { state.flags[flagName] = (state.flags[flagName] || 0) + 1; }; - cc.decrementFlag = function (state, flagName) { + cc.decrementFlag = (state, flagName) => { state.flags[flagName]--; }; - cc.incrementAttrib = function (state, attribName) { + cc.incrementAttrib = (state, attribName) => { if (!state.attribs[attribName]) { state.attribs[attribName] = 1; } else { @@ -201,15 +177,15 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas } _recalcAttribString(state); }; - cc.decrementAttrib = function (state, attribName) { + cc.decrementAttrib = (state, attribName) => { state.attribs[attribName]--; _recalcAttribString(state); }; - function _enterList(state, listType) { + const _enterList = (state, listType) => { if (!listType) return; const oldListType = state.lineAttributes.list; - if (listType != 'none') { + if (listType !== 'none') { state.listNesting = (state.listNesting || 0) + 1; // reminder that listType can be "number2", "number3" etc. if (listType.indexOf('number') !== -1) { @@ -224,36 +200,36 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas } _recalcAttribString(state); return oldListType; - } + }; - function _exitList(state, oldListType) { + const _exitList = (state, oldListType) => { if (state.lineAttributes.list) { state.listNesting--; } - if (oldListType && oldListType != 'none') { + if (oldListType && oldListType !== 'none') { state.lineAttributes.list = oldListType; } else { delete state.lineAttributes.list; delete state.lineAttributes.start; } _recalcAttribString(state); - } + }; - function _enterAuthor(state, author) { + const _enterAuthor = (state, author) => { const oldAuthor = state.author; state.authorLevel = (state.authorLevel || 0) + 1; state.author = author; _recalcAttribString(state); return oldAuthor; - } + }; - function _exitAuthor(state, oldAuthor) { + const _exitAuthor = (state, oldAuthor) => { state.authorLevel--; state.author = oldAuthor; _recalcAttribString(state); - } + }; - function _recalcAttribString(state) { + const _recalcAttribString = (state) => { const lst = []; for (const a in state.attribs) { if (state.attribs[a]) { @@ -285,9 +261,9 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas } } state.attribString = Changeset.makeAttribsString('+', lst, apool); - } + }; - function _produceLineAttributesMarker(state) { + const _produceLineAttributesMarker = (state) => { // TODO: This has to go to AttributeManager. const attributes = [ ['lmkr', '1'], @@ -296,24 +272,24 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas _.map(state.lineAttributes, (value, key) => [key, value]) ); lines.appendText('*', Changeset.makeAttribsString('+', attributes, apool)); - } - cc.startNewLine = function (state) { + }; + cc.startNewLine = (state) => { if (state) { - const atBeginningOfLine = lines.textOfLine(lines.length() - 1).length == 0; + const atBeginningOfLine = lines.textOfLine(lines.length() - 1).length === 0; if (atBeginningOfLine && !_.isEmpty(state.lineAttributes)) { _produceLineAttributesMarker(state); } } lines.startNew(); }; - cc.notifySelection = function (sel) { + cc.notifySelection = (sel) => { if (sel) { selection = sel; startPoint = selection.startPoint; endPoint = selection.endPoint; } }; - cc.doAttrib = function (state, na) { + cc.doAttrib = (state, na) => { state.localAttribs = (state.localAttribs || []); state.localAttribs.push(na); cc.incrementAttrib(state, na); @@ -343,9 +319,10 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas if (isBlock) _ensureColumnZero(state); const startLine = lines.length() - 1; _reachBlockPoint(node, 0, state); + if (dom.isNodeText(node)) { let txt = dom.nodeValue(node); - var tname = dom.nodeAttr(node.parentNode, 'name'); + const tname = dom.nodeAttr(node.parentNode, 'name'); const txtFromHook = hooks.callAll('collectContentLineText', { cc: this, @@ -365,11 +342,11 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas let rest = ''; let x = 0; // offset into original text - if (txt.length == 0) { - if (startPoint && node == startPoint.node) { + if (txt.length === 0) { + if (startPoint && node === startPoint.node) { selStart = _pointHere(0, state); } - if (endPoint && node == endPoint.node) { + if (endPoint && node === endPoint.node) { selEnd = _pointHere(0, state); } } @@ -382,10 +359,10 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas txt = firstLine; } else { /* will only run this loop body once */ } - if (startPoint && node == startPoint.node && startPoint.index - x <= txt.length) { + if (startPoint && node === startPoint.node && startPoint.index - x <= txt.length) { selStart = _pointHere(startPoint.index - x, state); } - if (endPoint && node == endPoint.node && endPoint.index - x <= txt.length) { + if (endPoint && node === endPoint.node && endPoint.index - x <= txt.length) { selEnd = _pointHere(endPoint.index - x, state); } let txt2 = txt; @@ -396,7 +373,7 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas // removing "\n" from pasted HTML will collapse words together. txt2 = ''; } - const atBeginningOfLine = lines.textOfLine(lines.length() - 1).length == 0; + const atBeginningOfLine = lines.textOfLine(lines.length() - 1).length === 0; if (atBeginningOfLine) { // newlines in the source mustn't become spaces at beginning of line box txt2 = txt2.replace(/^\n*/, ''); @@ -412,10 +389,13 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas } } } else { - var tname = (dom.nodeTagName(node) || '').toLowerCase(); + // Not a text node.. + const tname = (dom.nodeTagName(node) || '').toLowerCase(); + const styl = dom.nodeAttr(node, 'style'); + const cls = dom.nodeAttr(node, 'class'); - if (tname == 'img') { - const collectContentImage = hooks.callAll('collectContentImage', { + if (tname === 'img') { + hooks.callAll('collectContentImage', { cc, state, tname, @@ -425,8 +405,7 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas }); // cc.doAttrib(state, 'image:foo.png'); state.lineAttributes.img = 'foo.png'; - - } else if (tname == 'br') { + } else if (tname === 'br') { this.breakLine = true; const tvalue = dom.nodeAttr(node, 'value'); const induceLineBreak = hooks.callAll('collectContentLineBreak', { @@ -437,16 +416,18 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas styl: null, cls: null, }); - const startNewLine = (typeof (induceLineBreak) === 'object' && induceLineBreak.length == 0) ? true : induceLineBreak[0]; + const startNewLine = ( + typeof (induceLineBreak) === 'object' && + induceLineBreak.length === 0) ? true : induceLineBreak[0]; if (startNewLine) { cc.startNewLine(state); } - } else if (tname == 'script' || tname == 'style') { + } else if (tname === 'script' || tname === 'style') { // ignore } else if (!isEmpty) { - var styl = dom.nodeAttr(node, 'style'); - var cls = dom.nodeAttr(node, 'class'); - let isPre = (tname == 'pre'); + let styl = dom.nodeAttr(node, 'style'); + let cls = dom.nodeAttr(node, 'class'); + let isPre = (tname === 'pre'); if ((!isPre) && abrowser.safari) { isPre = (styl && /\bwhite-space:\s*pre\b/i.exec(styl)); } @@ -459,7 +440,8 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas styl = null; cls = null; - // We have to return here but this could break things in the future, for now it shows how to fix the problem + // We have to return here but this could break things in the future, + // for now it shows how to fix the problem return; } @@ -471,29 +453,39 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas styl, cls, }); - if (tname == 'b' || (styl && /\bfont-weight:\s*bold\b/i.exec(styl)) || tname == 'strong') { + if (tname === 'b' || + (styl && /\bfont-weight:\s*bold\b/i.exec(styl)) || + tname === 'strong') { cc.doAttrib(state, 'bold'); } - if (tname == 'i' || (styl && /\bfont-style:\s*italic\b/i.exec(styl)) || tname == 'em') { + if (tname === 'i' || + (styl && /\bfont-style:\s*italic\b/i.exec(styl)) || + tname === 'em') { cc.doAttrib(state, 'italic'); } - if (tname == 'u' || (styl && /\btext-decoration:\s*underline\b/i.exec(styl)) || tname == 'ins') { + if (tname === 'u' || + (styl && /\btext-decoration:\s*underline\b/i.exec(styl)) || + tname === 'ins') { cc.doAttrib(state, 'underline'); } - if (tname == 's' || (styl && /\btext-decoration:\s*line-through\b/i.exec(styl)) || tname == 'del') { + if (tname === 's' || + (styl && /\btext-decoration:\s*line-through\b/i.exec(styl)) || + tname === 'del') { cc.doAttrib(state, 'strikethrough'); } - if (tname == 'ul' || tname == 'ol') { + let type; + if (tname === 'ul' || tname === 'ol') { if (node.attribs) { - var type = node.attribs.class; + type = node.attribs.class; } else { - var type = null; + type = null; } const rr = cls && /(?:^| )list-([a-z]+[0-9]+)\b/.exec(cls); - // lists do not need to have a type, so before we make a wrong guess, check if we find a better hint within the node's children + // lists do not need to have a type, so before we make a wrong guess + // check if we find a better hint within the node's children if (!rr && !type) { - for (var i in node.children) { - if (node.children[i] && node.children[i].name == 'ul') { + for (const i in node.children) { + if (node.children[i] && node.children[i].name === 'ul') { type = node.children[i].attribs.class; if (type) { break; @@ -504,8 +496,14 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas if (rr && rr[1]) { type = rr[1]; } else { - if (tname == 'ul') { - if ((type && type.match('indent')) || (node.attribs && node.attribs.class && node.attribs.class.match('indent'))) { + if (tname === 'ul') { + if ((type && type.match('indent')) || + ( + node.attribs && + node.attribs.class && + node.attribs.class.match('indent') + ) + ) { type = 'indent'; } else { type = 'bullet'; @@ -516,7 +514,7 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas type += String(Math.min(_MAX_LIST_LEVEL, (state.listNesting || 0) + 1)); } oldListTypeOrNull = (_enterList(state, type) || 'none'); - } else if ((tname == 'div' || tname == 'p') && cls && cls.match(/(?:^| )ace-line\b/)) { + } else if ((tname === 'div' || tname === 'p') && cls && cls.match(/(?:^| )ace-line\b/)) { // This has undesirable behavior in Chrome but is right in other browsers. // See https://github.com/ether/etherpad-lite/issues/2412 for reasoning if (!abrowser.chrome) oldListTypeOrNull = (_enterList(state, type) || 'none'); @@ -564,8 +562,8 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas if (className2Author && cls) { const classes = cls.match(/\S+/g); if (classes && classes.length > 0) { - for (var i = 0; i < classes.length; i++) { - var c = classes[i]; + for (let i = 0; i < classes.length; i++) { + const c = classes[i]; const a = className2Author(c); if (a) { oldAuthorOrNull = (_enterAuthor(state, a) || 'none'); @@ -577,8 +575,8 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas } const nc = dom.nodeNumChildren(node); - for (var i = 0; i < nc; i++) { - var c = dom.nodeChild(node, i); + for (let i = 0; i < nc; i++) { + const c = dom.nodeChild(node, i); cc.collectContent(c, state); } @@ -594,7 +592,7 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas if (isPre) cc.decrementFlag(state, 'preMode'); if (state.localAttribs) { - for (var i = 0; i < state.localAttribs.length; i++) { + for (let i = 0; i < state.localAttribs.length; i++) { cc.decrementAttrib(state, state.localAttribs[i]); } } @@ -608,7 +606,7 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas } _reachBlockPoint(node, 1, state); if (isBlock) { - if (lines.length() - 1 == startLine) { + if (lines.length() - 1 === startLine) { // added additional check to resolve https://github.com/JohnMcLear/ep_copy_paste_images/issues/20 // this does mean that images etc can't be pasted on lists but imho that's fine @@ -625,7 +623,7 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas state.localAttribs = localAttribs; }; // can pass a falsy value for end of doc - cc.notifyNextNode = function (node) { + cc.notifyNextNode = (node) => { // an "empty block" won't end a line; this addresses an issue in IE with // typing into a blank line at the end of the document. typed text // goes into the body, and the empty line div still looks clean. @@ -636,21 +634,15 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas } }; // each returns [line, char] or [-1,-1] - const getSelectionStart = function () { - return selStart; - }; - const getSelectionEnd = function () { - return selEnd; - }; + const getSelectionStart = () => selStart; + const getSelectionEnd = () => selEnd; // returns array of strings for lines found, last entry will be "" if // last line is complete (i.e. if a following span should be on a new line). // can be called at any point - cc.getLines = function () { - return lines.textLines(); - }; + cc.getLines = () => lines.textLines(); - cc.finish = function () { + cc.finish = () => { lines.flush(); const lineAttribs = lines.attribLines(); const lineStrings = cc.getLines(); @@ -661,17 +653,17 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas const ss = getSelectionStart(); const se = getSelectionEnd(); - function fixLongLines() { + const fixLongLines = () => { // design mode does not deal with with really long lines! const lineLimit = 2000; // chars const buffer = 10; // chars allowed over before wrapping let linesWrapped = 0; let numLinesAfter = 0; - for (var i = lineStrings.length - 1; i >= 0; i--) { + for (let i = lineStrings.length - 1; i >= 0; i--) { let oldString = lineStrings[i]; let oldAttribString = lineAttribs[i]; if (oldString.length > lineLimit + buffer) { - var newStrings = []; + const newStrings = []; const newAttribStrings = []; while (oldString.length > lineLimit) { // var semiloc = oldString.lastIndexOf(';', lineLimit-1); @@ -687,13 +679,13 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas newAttribStrings.push(oldAttribString); } - function fixLineNumber(lineChar) { + const fixLineNumber = (lineChar) => { if (lineChar[0] < 0) return; let n = lineChar[0]; let c = lineChar[1]; if (n > i) { n += (newStrings.length - 1); - } else if (n == i) { + } else if (n === i) { let a = 0; while (c > newStrings[a].length) { c -= newStrings[a].length; @@ -703,13 +695,14 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas } lineChar[0] = n; lineChar[1] = c; - } + }; fixLineNumber(ss); fixLineNumber(se); linesWrapped++; numLinesAfter += newStrings.length; newStrings.unshift(i, 1); + // Still to fix linting issue below. lineStrings.splice.apply(lineStrings, newStrings); newAttribStrings.unshift(i, 1); lineAttribs.splice.apply(lineAttribs, newAttribStrings); @@ -719,7 +712,7 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas linesWrapped, numLinesAfter, }; - } + }; const wrapData = fixLongLines(); return { @@ -733,7 +726,7 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas }; return cc; -} +}; exports.sanitizeUnicode = sanitizeUnicode; exports.makeContentCollector = makeContentCollector;