mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-01-19 14:13:34 +01:00
lint: contentcollector and domline
Various tidy up and linting of contentcollector.js and domline.js. 3 Tests disabled which are not due to be covered. Co-authored-by: Richard Hansen <rhansen@rhansen.org>
This commit is contained in:
parent
10a91825fc
commit
f0a77cb98c
3 changed files with 187 additions and 206 deletions
|
@ -29,47 +29,31 @@ const _MAX_LIST_LEVEL = 16;
|
||||||
const UNorm = require('unorm');
|
const UNorm = require('unorm');
|
||||||
const Changeset = require('./Changeset');
|
const Changeset = require('./Changeset');
|
||||||
const hooks = require('./pluginfw/hooks');
|
const hooks = require('./pluginfw/hooks');
|
||||||
const _ = require('./underscore');
|
|
||||||
|
|
||||||
function sanitizeUnicode(s) {
|
const sanitizeUnicode = (s) => UNorm.nfc(s);
|
||||||
return UNorm.nfc(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeContentCollector(collectStyles, abrowser, apool, domInterface, className2Author) {
|
|
||||||
abrowser = abrowser || {};
|
|
||||||
// I don't like the above.
|
|
||||||
|
|
||||||
|
const makeContentCollector = (collectStyles, abrowser, apool, domInterface, className2Author) => {
|
||||||
const dom = domInterface || {
|
const dom = domInterface || {
|
||||||
isNodeText(n) {
|
isNodeText: (n) => n.nodeType === 3,
|
||||||
return (n.nodeType == 3);
|
nodeTagName: (n) => n.tagName,
|
||||||
},
|
nodeValue: (n) => n.nodeValue,
|
||||||
nodeTagName(n) {
|
nodeNumChildren: (n) => {
|
||||||
return n.tagName;
|
|
||||||
},
|
|
||||||
nodeValue(n) {
|
|
||||||
return n.nodeValue;
|
|
||||||
},
|
|
||||||
nodeNumChildren(n) {
|
|
||||||
if (n.childNodes == null) return 0;
|
if (n.childNodes == null) return 0;
|
||||||
return n.childNodes.length;
|
return n.childNodes.length;
|
||||||
},
|
},
|
||||||
nodeChild(n, i) {
|
nodeChild: (n, i) => {
|
||||||
if (n.childNodes.item == null) {
|
if (n.childNodes.item == null) {
|
||||||
return n.childNodes[i];
|
return n.childNodes[i];
|
||||||
}
|
}
|
||||||
return n.childNodes.item(i);
|
return n.childNodes.item(i);
|
||||||
},
|
},
|
||||||
nodeProp(n, p) {
|
nodeProp: (n, p) => n[p],
|
||||||
return n[p];
|
nodeAttr: (n, a) => {
|
||||||
},
|
|
||||||
nodeAttr(n, a) {
|
|
||||||
if (n.getAttribute != null) return n.getAttribute(a);
|
if (n.getAttribute != null) return n.getAttribute(a);
|
||||||
if (n.attribs != null) return n.attribs[a];
|
if (n.attribs != null) return n.attribs[a];
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
optNodeInnerHTML(n) {
|
optNodeInnerHTML: (n) => n.innerHTML,
|
||||||
return n.innerHTML;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const _blockElems = {
|
const _blockElems = {
|
||||||
|
@ -79,58 +63,45 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
li: 1,
|
li: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
_.each(hooks.callAll('ccRegisterBlockElements'), (element) => {
|
hooks.callAll('ccRegisterBlockElements').forEach((element) => {
|
||||||
_blockElems[element] = 1;
|
_blockElems[element] = 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
function isBlockElement(n) {
|
const isBlockElement = (n) => !!_blockElems[(dom.nodeTagName(n) || '').toLowerCase()];
|
||||||
return !!_blockElems[(dom.nodeTagName(n) || '').toLowerCase()];
|
|
||||||
}
|
|
||||||
|
|
||||||
function textify(str) {
|
const textify = (str) => sanitizeUnicode(
|
||||||
return sanitizeUnicode(
|
str.replace(/(\n | \n)/g, ' ')
|
||||||
str.replace(/(\n | \n)/g, ' ').replace(/[\n\r ]/g, ' ').replace(/\xa0/g, ' ').replace(/\t/g, ' '));
|
.replace(/[\n\r ]/g, ' ')
|
||||||
}
|
.replace(/\xa0/g, ' ')
|
||||||
|
.replace(/\t/g, ' '));
|
||||||
|
|
||||||
function getAssoc(node, name) {
|
const getAssoc = (node, name) => dom.nodeProp(node, `_magicdom_${name}`);
|
||||||
return dom.nodeProp(node, `_magicdom_${name}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const lines = (function () {
|
const lines = (() => {
|
||||||
const textArray = [];
|
const textArray = [];
|
||||||
const attribsArray = [];
|
const attribsArray = [];
|
||||||
let attribsBuilder = null;
|
let attribsBuilder = null;
|
||||||
const op = Changeset.newOp('+');
|
const op = Changeset.newOp('+');
|
||||||
var self = {
|
const self = {
|
||||||
length() {
|
length: () => textArray.length,
|
||||||
return textArray.length;
|
atColumnZero: () => textArray[textArray.length - 1] === '',
|
||||||
},
|
startNew: () => {
|
||||||
atColumnZero() {
|
|
||||||
return textArray[textArray.length - 1] === '';
|
|
||||||
},
|
|
||||||
startNew() {
|
|
||||||
textArray.push('');
|
textArray.push('');
|
||||||
self.flush(true);
|
self.flush(true);
|
||||||
attribsBuilder = Changeset.smartOpAssembler();
|
attribsBuilder = Changeset.smartOpAssembler();
|
||||||
},
|
},
|
||||||
textOfLine(i) {
|
textOfLine: (i) => textArray[i],
|
||||||
return textArray[i];
|
appendText: (txt, attrString) => {
|
||||||
},
|
|
||||||
appendText(txt, attrString) {
|
|
||||||
textArray[textArray.length - 1] += txt;
|
textArray[textArray.length - 1] += txt;
|
||||||
// dmesg(txt+" / "+attrString);
|
// dmesg(txt+" / "+attrString);
|
||||||
op.attribs = attrString;
|
op.attribs = attrString;
|
||||||
op.chars = txt.length;
|
op.chars = txt.length;
|
||||||
attribsBuilder.append(op);
|
attribsBuilder.append(op);
|
||||||
},
|
},
|
||||||
textLines() {
|
textLines: () => textArray.slice(),
|
||||||
return textArray.slice();
|
attribLines: () => attribsArray,
|
||||||
},
|
|
||||||
attribLines() {
|
|
||||||
return attribsArray;
|
|
||||||
},
|
|
||||||
// call flush only when you're done
|
// call flush only when you're done
|
||||||
flush(withNewline) {
|
flush: (withNewline) => {
|
||||||
if (attribsBuilder) {
|
if (attribsBuilder) {
|
||||||
attribsArray.push(attribsBuilder.toString());
|
attribsArray.push(attribsBuilder.toString());
|
||||||
attribsBuilder = null;
|
attribsBuilder = null;
|
||||||
|
@ -139,21 +110,24 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
};
|
};
|
||||||
self.startNew();
|
self.startNew();
|
||||||
return self;
|
return self;
|
||||||
}());
|
})();
|
||||||
const cc = {};
|
const cc = {};
|
||||||
|
|
||||||
function _ensureColumnZero(state) {
|
const _ensureColumnZero = (state) => {
|
||||||
if (!lines.atColumnZero()) {
|
if (!lines.atColumnZero()) {
|
||||||
cc.startNewLine(state);
|
cc.startNewLine(state);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
let selection, startPoint, endPoint;
|
let selection, startPoint, endPoint;
|
||||||
let selStart = [-1, -1];
|
let selStart = [-1, -1];
|
||||||
let selEnd = [-1, -1];
|
let selEnd = [-1, -1];
|
||||||
function _isEmpty(node, state) {
|
const _isEmpty = (node, state) => {
|
||||||
// consider clean blank lines pasted in IE to be empty
|
// consider clean blank lines pasted in IE to be empty
|
||||||
if (dom.nodeNumChildren(node) == 0) return true;
|
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) === 1 &&
|
||||||
|
getAssoc(node, 'shouldBeEmpty') &&
|
||||||
|
dom.optNodeInnerHTML(node) === ' ' &&
|
||||||
|
!getAssoc(node, 'unpasted')) {
|
||||||
if (state) {
|
if (state) {
|
||||||
const child = dom.nodeChild(node, 0);
|
const child = dom.nodeChild(node, 0);
|
||||||
_reachPoint(child, 0, state);
|
_reachPoint(child, 0, state);
|
||||||
|
@ -162,37 +136,37 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
};
|
||||||
|
|
||||||
function _pointHere(charsAfter, state) {
|
const _pointHere = (charsAfter, state) => {
|
||||||
const ln = lines.length() - 1;
|
const ln = lines.length() - 1;
|
||||||
let chr = lines.textOfLine(ln).length;
|
let chr = lines.textOfLine(ln).length;
|
||||||
if (chr == 0 && !_.isEmpty(state.lineAttributes)) {
|
if (chr === 0 && Object.keys(state.lineAttributes).length !== 0) {
|
||||||
chr += 1; // listMarker
|
chr += 1; // listMarker
|
||||||
}
|
}
|
||||||
chr += charsAfter;
|
chr += charsAfter;
|
||||||
return [ln, chr];
|
return [ln, chr];
|
||||||
}
|
};
|
||||||
|
|
||||||
function _reachBlockPoint(nd, idx, state) {
|
const _reachBlockPoint = (nd, idx, state) => {
|
||||||
if (!dom.isNodeText(nd)) _reachPoint(nd, idx, state);
|
if (!dom.isNodeText(nd)) _reachPoint(nd, idx, state);
|
||||||
}
|
};
|
||||||
|
|
||||||
function _reachPoint(nd, idx, state) {
|
const _reachPoint = (nd, idx, state) => {
|
||||||
if (startPoint && nd == startPoint.node && startPoint.index == idx) {
|
if (startPoint && nd === startPoint.node && startPoint.index === idx) {
|
||||||
selStart = _pointHere(0, state);
|
selStart = _pointHere(0, state);
|
||||||
}
|
}
|
||||||
if (endPoint && nd == endPoint.node && endPoint.index == idx) {
|
if (endPoint && nd === endPoint.node && endPoint.index === idx) {
|
||||||
selEnd = _pointHere(0, state);
|
selEnd = _pointHere(0, state);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
cc.incrementFlag = function (state, flagName) {
|
cc.incrementFlag = (state, flagName) => {
|
||||||
state.flags[flagName] = (state.flags[flagName] || 0) + 1;
|
state.flags[flagName] = (state.flags[flagName] || 0) + 1;
|
||||||
};
|
};
|
||||||
cc.decrementFlag = function (state, flagName) {
|
cc.decrementFlag = (state, flagName) => {
|
||||||
state.flags[flagName]--;
|
state.flags[flagName]--;
|
||||||
};
|
};
|
||||||
cc.incrementAttrib = function (state, attribName) {
|
cc.incrementAttrib = (state, attribName) => {
|
||||||
if (!state.attribs[attribName]) {
|
if (!state.attribs[attribName]) {
|
||||||
state.attribs[attribName] = 1;
|
state.attribs[attribName] = 1;
|
||||||
} else {
|
} else {
|
||||||
|
@ -200,15 +174,15 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
}
|
}
|
||||||
_recalcAttribString(state);
|
_recalcAttribString(state);
|
||||||
};
|
};
|
||||||
cc.decrementAttrib = function (state, attribName) {
|
cc.decrementAttrib = (state, attribName) => {
|
||||||
state.attribs[attribName]--;
|
state.attribs[attribName]--;
|
||||||
_recalcAttribString(state);
|
_recalcAttribString(state);
|
||||||
};
|
};
|
||||||
|
|
||||||
function _enterList(state, listType) {
|
const _enterList = (state, listType) => {
|
||||||
if (!listType) return;
|
if (!listType) return;
|
||||||
const oldListType = state.lineAttributes.list;
|
const oldListType = state.lineAttributes.list;
|
||||||
if (listType != 'none') {
|
if (listType !== 'none') {
|
||||||
state.listNesting = (state.listNesting || 0) + 1;
|
state.listNesting = (state.listNesting || 0) + 1;
|
||||||
// reminder that listType can be "number2", "number3" etc.
|
// reminder that listType can be "number2", "number3" etc.
|
||||||
if (listType.indexOf('number') !== -1) {
|
if (listType.indexOf('number') !== -1) {
|
||||||
|
@ -223,36 +197,36 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
}
|
}
|
||||||
_recalcAttribString(state);
|
_recalcAttribString(state);
|
||||||
return oldListType;
|
return oldListType;
|
||||||
}
|
};
|
||||||
|
|
||||||
function _exitList(state, oldListType) {
|
const _exitList = (state, oldListType) => {
|
||||||
if (state.lineAttributes.list) {
|
if (state.lineAttributes.list) {
|
||||||
state.listNesting--;
|
state.listNesting--;
|
||||||
}
|
}
|
||||||
if (oldListType && oldListType != 'none') {
|
if (oldListType && oldListType !== 'none') {
|
||||||
state.lineAttributes.list = oldListType;
|
state.lineAttributes.list = oldListType;
|
||||||
} else {
|
} else {
|
||||||
delete state.lineAttributes.list;
|
delete state.lineAttributes.list;
|
||||||
delete state.lineAttributes.start;
|
delete state.lineAttributes.start;
|
||||||
}
|
}
|
||||||
_recalcAttribString(state);
|
_recalcAttribString(state);
|
||||||
}
|
};
|
||||||
|
|
||||||
function _enterAuthor(state, author) {
|
const _enterAuthor = (state, author) => {
|
||||||
const oldAuthor = state.author;
|
const oldAuthor = state.author;
|
||||||
state.authorLevel = (state.authorLevel || 0) + 1;
|
state.authorLevel = (state.authorLevel || 0) + 1;
|
||||||
state.author = author;
|
state.author = author;
|
||||||
_recalcAttribString(state);
|
_recalcAttribString(state);
|
||||||
return oldAuthor;
|
return oldAuthor;
|
||||||
}
|
};
|
||||||
|
|
||||||
function _exitAuthor(state, oldAuthor) {
|
const _exitAuthor = (state, oldAuthor) => {
|
||||||
state.authorLevel--;
|
state.authorLevel--;
|
||||||
state.author = oldAuthor;
|
state.author = oldAuthor;
|
||||||
_recalcAttribString(state);
|
_recalcAttribString(state);
|
||||||
}
|
};
|
||||||
|
|
||||||
function _recalcAttribString(state) {
|
const _recalcAttribString = (state) => {
|
||||||
const lst = [];
|
const lst = [];
|
||||||
for (const a in state.attribs) {
|
for (const a in state.attribs) {
|
||||||
if (state.attribs[a]) {
|
if (state.attribs[a]) {
|
||||||
|
@ -284,35 +258,34 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.attribString = Changeset.makeAttribsString('+', lst, apool);
|
state.attribString = Changeset.makeAttribsString('+', lst, apool);
|
||||||
}
|
};
|
||||||
|
|
||||||
function _produceLineAttributesMarker(state) {
|
const _produceLineAttributesMarker = (state) => {
|
||||||
// TODO: This has to go to AttributeManager.
|
// TODO: This has to go to AttributeManager.
|
||||||
const attributes = [
|
const attributes = [
|
||||||
['lmkr', '1'],
|
['lmkr', '1'],
|
||||||
['insertorder', 'first'],
|
['insertorder', 'first'],
|
||||||
].concat(
|
...Object.entries(state.lineAttributes),
|
||||||
_.map(state.lineAttributes, (value, key) => [key, value])
|
];
|
||||||
);
|
|
||||||
lines.appendText('*', Changeset.makeAttribsString('+', attributes, apool));
|
lines.appendText('*', Changeset.makeAttribsString('+', attributes, apool));
|
||||||
}
|
};
|
||||||
cc.startNewLine = function (state) {
|
cc.startNewLine = (state) => {
|
||||||
if (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)) {
|
if (atBeginningOfLine && Object.keys(state.lineAttributes).length !== 0) {
|
||||||
_produceLineAttributesMarker(state);
|
_produceLineAttributesMarker(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lines.startNew();
|
lines.startNew();
|
||||||
};
|
};
|
||||||
cc.notifySelection = function (sel) {
|
cc.notifySelection = (sel) => {
|
||||||
if (sel) {
|
if (sel) {
|
||||||
selection = sel;
|
selection = sel;
|
||||||
startPoint = selection.startPoint;
|
startPoint = selection.startPoint;
|
||||||
endPoint = selection.endPoint;
|
endPoint = selection.endPoint;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
cc.doAttrib = function (state, na) {
|
cc.doAttrib = (state, na) => {
|
||||||
state.localAttribs = (state.localAttribs || []);
|
state.localAttribs = (state.localAttribs || []);
|
||||||
state.localAttribs.push(na);
|
state.localAttribs.push(na);
|
||||||
cc.incrementAttrib(state, na);
|
cc.incrementAttrib(state, na);
|
||||||
|
@ -342,9 +315,10 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
if (isBlock) _ensureColumnZero(state);
|
if (isBlock) _ensureColumnZero(state);
|
||||||
const startLine = lines.length() - 1;
|
const startLine = lines.length() - 1;
|
||||||
_reachBlockPoint(node, 0, state);
|
_reachBlockPoint(node, 0, state);
|
||||||
|
|
||||||
if (dom.isNodeText(node)) {
|
if (dom.isNodeText(node)) {
|
||||||
let txt = dom.nodeValue(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', {
|
const txtFromHook = hooks.callAll('collectContentLineText', {
|
||||||
cc: this,
|
cc: this,
|
||||||
|
@ -364,11 +338,11 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
|
|
||||||
let rest = '';
|
let rest = '';
|
||||||
let x = 0; // offset into original text
|
let x = 0; // offset into original text
|
||||||
if (txt.length == 0) {
|
if (txt.length === 0) {
|
||||||
if (startPoint && node == startPoint.node) {
|
if (startPoint && node === startPoint.node) {
|
||||||
selStart = _pointHere(0, state);
|
selStart = _pointHere(0, state);
|
||||||
}
|
}
|
||||||
if (endPoint && node == endPoint.node) {
|
if (endPoint && node === endPoint.node) {
|
||||||
selEnd = _pointHere(0, state);
|
selEnd = _pointHere(0, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -381,10 +355,10 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
txt = firstLine;
|
txt = firstLine;
|
||||||
} else { /* will only run this loop body once */
|
} 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);
|
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);
|
selEnd = _pointHere(endPoint.index - x, state);
|
||||||
}
|
}
|
||||||
let txt2 = txt;
|
let txt2 = txt;
|
||||||
|
@ -395,12 +369,12 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
// removing "\n" from pasted HTML will collapse words together.
|
// removing "\n" from pasted HTML will collapse words together.
|
||||||
txt2 = '';
|
txt2 = '';
|
||||||
}
|
}
|
||||||
const atBeginningOfLine = lines.textOfLine(lines.length() - 1).length == 0;
|
const atBeginningOfLine = lines.textOfLine(lines.length() - 1).length === 0;
|
||||||
if (atBeginningOfLine) {
|
if (atBeginningOfLine) {
|
||||||
// newlines in the source mustn't become spaces at beginning of line box
|
// newlines in the source mustn't become spaces at beginning of line box
|
||||||
txt2 = txt2.replace(/^\n*/, '');
|
txt2 = txt2.replace(/^\n*/, '');
|
||||||
}
|
}
|
||||||
if (atBeginningOfLine && !_.isEmpty(state.lineAttributes)) {
|
if (atBeginningOfLine && Object.keys(state.lineAttributes).length !== 0) {
|
||||||
_produceLineAttributesMarker(state);
|
_produceLineAttributesMarker(state);
|
||||||
}
|
}
|
||||||
lines.appendText(textify(txt2), state.attribString);
|
lines.appendText(textify(txt2), state.attribString);
|
||||||
|
@ -411,15 +385,15 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var tname = (dom.nodeTagName(node) || '').toLowerCase();
|
const tname = (dom.nodeTagName(node) || '').toLowerCase();
|
||||||
|
|
||||||
if (tname == 'img') {
|
if (tname === 'img') {
|
||||||
const collectContentImage = hooks.callAll('collectContentImage', {
|
hooks.callAll('collectContentImage', {
|
||||||
cc,
|
cc,
|
||||||
state,
|
state,
|
||||||
tname,
|
tname,
|
||||||
styl,
|
styl: null,
|
||||||
cls,
|
cls: null,
|
||||||
node,
|
node,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -427,7 +401,7 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
delete state.lineAttributes.img;
|
delete state.lineAttributes.img;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tname == 'br') {
|
if (tname === 'br') {
|
||||||
this.breakLine = true;
|
this.breakLine = true;
|
||||||
const tvalue = dom.nodeAttr(node, 'value');
|
const tvalue = dom.nodeAttr(node, 'value');
|
||||||
const induceLineBreak = hooks.callAll('collectContentLineBreak', {
|
const induceLineBreak = hooks.callAll('collectContentLineBreak', {
|
||||||
|
@ -438,17 +412,19 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
styl: null,
|
styl: null,
|
||||||
cls: 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) {
|
if (startNewLine) {
|
||||||
cc.startNewLine(state);
|
cc.startNewLine(state);
|
||||||
}
|
}
|
||||||
} else if (tname == 'script' || tname == 'style') {
|
} else if (tname === 'script' || tname === 'style') {
|
||||||
// ignore
|
// ignore
|
||||||
} else if (!isEmpty) {
|
} else if (!isEmpty) {
|
||||||
var styl = dom.nodeAttr(node, 'style');
|
let styl = dom.nodeAttr(node, 'style');
|
||||||
var cls = dom.nodeAttr(node, 'class');
|
let cls = dom.nodeAttr(node, 'class');
|
||||||
let isPre = (tname == 'pre');
|
let isPre = (tname === 'pre');
|
||||||
if ((!isPre) && abrowser.safari) {
|
if ((!isPre) && abrowser && abrowser.safari) {
|
||||||
isPre = (styl && /\bwhite-space:\s*pre\b/i.exec(styl));
|
isPre = (styl && /\bwhite-space:\s*pre\b/i.exec(styl));
|
||||||
}
|
}
|
||||||
if (isPre) cc.incrementFlag(state, 'preMode');
|
if (isPre) cc.incrementFlag(state, 'preMode');
|
||||||
|
@ -460,10 +436,10 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
styl = null;
|
styl = null;
|
||||||
cls = 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (collectStyles) {
|
if (collectStyles) {
|
||||||
hooks.callAll('collectContentPre', {
|
hooks.callAll('collectContentPre', {
|
||||||
cc,
|
cc,
|
||||||
|
@ -472,29 +448,34 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
styl,
|
styl,
|
||||||
cls,
|
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');
|
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');
|
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');
|
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');
|
cc.doAttrib(state, 'strikethrough');
|
||||||
}
|
}
|
||||||
if (tname == 'ul' || tname == 'ol') {
|
if (tname === 'ul' || tname === 'ol') {
|
||||||
if (node.attribs) {
|
let type = node.attribs ? node.attribs.class : null;
|
||||||
var type = node.attribs.class;
|
|
||||||
} else {
|
|
||||||
var type = null;
|
|
||||||
}
|
|
||||||
const rr = cls && /(?:^| )list-([a-z]+[0-9]+)\b/.exec(cls);
|
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) {
|
if (!rr && !type) {
|
||||||
for (var i in node.children) {
|
for (const i in node.children) {
|
||||||
if (node.children[i] && node.children[i].name == 'ul') {
|
if (node.children[i] && node.children[i].name === 'ul') {
|
||||||
type = node.children[i].attribs.class;
|
type = node.children[i].attribs.class;
|
||||||
if (type) {
|
if (type) {
|
||||||
break;
|
break;
|
||||||
|
@ -505,8 +486,9 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
if (rr && rr[1]) {
|
if (rr && rr[1]) {
|
||||||
type = rr[1];
|
type = rr[1];
|
||||||
} else {
|
} else {
|
||||||
if (tname == 'ul') {
|
if (tname === 'ul') {
|
||||||
if ((type && type.match('indent')) || (node.attribs && node.attribs.class && node.attribs.class.match('indent'))) {
|
if ((type && type.match('indent')) ||
|
||||||
|
(node.attribs && node.attribs.class && node.attribs.class.match('indent'))) {
|
||||||
type = 'indent';
|
type = 'indent';
|
||||||
} else {
|
} else {
|
||||||
type = 'bullet';
|
type = 'bullet';
|
||||||
|
@ -517,10 +499,10 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
type += String(Math.min(_MAX_LIST_LEVEL, (state.listNesting || 0) + 1));
|
type += String(Math.min(_MAX_LIST_LEVEL, (state.listNesting || 0) + 1));
|
||||||
}
|
}
|
||||||
oldListTypeOrNull = (_enterList(state, type) || 'none');
|
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.
|
// This has undesirable behavior in Chrome but is right in other browsers.
|
||||||
// See https://github.com/ether/etherpad-lite/issues/2412 for reasoning
|
// See https://github.com/ether/etherpad-lite/issues/2412 for reasoning
|
||||||
if (!abrowser.chrome) oldListTypeOrNull = (_enterList(state, type) || 'none');
|
if (!abrowser.chrome) oldListTypeOrNull = (_enterList(state, undefined) || 'none');
|
||||||
} else if ((tname === 'li')) {
|
} else if ((tname === 'li')) {
|
||||||
state.lineAttributes.start = state.start || 0;
|
state.lineAttributes.start = state.start || 0;
|
||||||
_recalcAttribString(state);
|
_recalcAttribString(state);
|
||||||
|
@ -565,8 +547,8 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
if (className2Author && cls) {
|
if (className2Author && cls) {
|
||||||
const classes = cls.match(/\S+/g);
|
const classes = cls.match(/\S+/g);
|
||||||
if (classes && classes.length > 0) {
|
if (classes && classes.length > 0) {
|
||||||
for (var i = 0; i < classes.length; i++) {
|
for (let i = 0; i < classes.length; i++) {
|
||||||
var c = classes[i];
|
const c = classes[i];
|
||||||
const a = className2Author(c);
|
const a = className2Author(c);
|
||||||
if (a) {
|
if (a) {
|
||||||
oldAuthorOrNull = (_enterAuthor(state, a) || 'none');
|
oldAuthorOrNull = (_enterAuthor(state, a) || 'none');
|
||||||
|
@ -578,8 +560,8 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
}
|
}
|
||||||
|
|
||||||
const nc = dom.nodeNumChildren(node);
|
const nc = dom.nodeNumChildren(node);
|
||||||
for (var i = 0; i < nc; i++) {
|
for (let i = 0; i < nc; i++) {
|
||||||
var c = dom.nodeChild(node, i);
|
const c = dom.nodeChild(node, i);
|
||||||
cc.collectContent(c, state);
|
cc.collectContent(c, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -595,7 +577,7 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
|
|
||||||
if (isPre) cc.decrementFlag(state, 'preMode');
|
if (isPre) cc.decrementFlag(state, 'preMode');
|
||||||
if (state.localAttribs) {
|
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]);
|
cc.decrementAttrib(state, state.localAttribs[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -609,7 +591,7 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
}
|
}
|
||||||
_reachBlockPoint(node, 1, state);
|
_reachBlockPoint(node, 1, state);
|
||||||
if (isBlock) {
|
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
|
// 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
|
// this does mean that images etc can't be pasted on lists but imho that's fine
|
||||||
|
|
||||||
|
@ -626,7 +608,7 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
state.localAttribs = localAttribs;
|
state.localAttribs = localAttribs;
|
||||||
};
|
};
|
||||||
// can pass a falsy value for end of doc
|
// 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
|
// 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
|
// 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.
|
// goes into the body, and the empty line div still looks clean.
|
||||||
|
@ -637,21 +619,15 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// each returns [line, char] or [-1,-1]
|
// each returns [line, char] or [-1,-1]
|
||||||
const getSelectionStart = function () {
|
const getSelectionStart = () => selStart;
|
||||||
return selStart;
|
const getSelectionEnd = () => selEnd;
|
||||||
};
|
|
||||||
const getSelectionEnd = function () {
|
|
||||||
return selEnd;
|
|
||||||
};
|
|
||||||
|
|
||||||
// returns array of strings for lines found, last entry will be "" if
|
// 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).
|
// last line is complete (i.e. if a following span should be on a new line).
|
||||||
// can be called at any point
|
// can be called at any point
|
||||||
cc.getLines = function () {
|
cc.getLines = () => lines.textLines();
|
||||||
return lines.textLines();
|
|
||||||
};
|
|
||||||
|
|
||||||
cc.finish = function () {
|
cc.finish = () => {
|
||||||
lines.flush();
|
lines.flush();
|
||||||
const lineAttribs = lines.attribLines();
|
const lineAttribs = lines.attribLines();
|
||||||
const lineStrings = cc.getLines();
|
const lineStrings = cc.getLines();
|
||||||
|
@ -662,17 +638,17 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
const ss = getSelectionStart();
|
const ss = getSelectionStart();
|
||||||
const se = getSelectionEnd();
|
const se = getSelectionEnd();
|
||||||
|
|
||||||
function fixLongLines() {
|
const fixLongLines = () => {
|
||||||
// design mode does not deal with with really long lines!
|
// design mode does not deal with with really long lines!
|
||||||
const lineLimit = 2000; // chars
|
const lineLimit = 2000; // chars
|
||||||
const buffer = 10; // chars allowed over before wrapping
|
const buffer = 10; // chars allowed over before wrapping
|
||||||
let linesWrapped = 0;
|
let linesWrapped = 0;
|
||||||
let numLinesAfter = 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 oldString = lineStrings[i];
|
||||||
let oldAttribString = lineAttribs[i];
|
let oldAttribString = lineAttribs[i];
|
||||||
if (oldString.length > lineLimit + buffer) {
|
if (oldString.length > lineLimit + buffer) {
|
||||||
var newStrings = [];
|
const newStrings = [];
|
||||||
const newAttribStrings = [];
|
const newAttribStrings = [];
|
||||||
while (oldString.length > lineLimit) {
|
while (oldString.length > lineLimit) {
|
||||||
// var semiloc = oldString.lastIndexOf(';', lineLimit-1);
|
// var semiloc = oldString.lastIndexOf(';', lineLimit-1);
|
||||||
|
@ -688,13 +664,13 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
newAttribStrings.push(oldAttribString);
|
newAttribStrings.push(oldAttribString);
|
||||||
}
|
}
|
||||||
|
|
||||||
function fixLineNumber(lineChar) {
|
const fixLineNumber = (lineChar) => {
|
||||||
if (lineChar[0] < 0) return;
|
if (lineChar[0] < 0) return;
|
||||||
let n = lineChar[0];
|
let n = lineChar[0];
|
||||||
let c = lineChar[1];
|
let c = lineChar[1];
|
||||||
if (n > i) {
|
if (n > i) {
|
||||||
n += (newStrings.length - 1);
|
n += (newStrings.length - 1);
|
||||||
} else if (n == i) {
|
} else if (n === i) {
|
||||||
let a = 0;
|
let a = 0;
|
||||||
while (c > newStrings[a].length) {
|
while (c > newStrings[a].length) {
|
||||||
c -= newStrings[a].length;
|
c -= newStrings[a].length;
|
||||||
|
@ -704,23 +680,20 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
}
|
}
|
||||||
lineChar[0] = n;
|
lineChar[0] = n;
|
||||||
lineChar[1] = c;
|
lineChar[1] = c;
|
||||||
}
|
};
|
||||||
fixLineNumber(ss);
|
fixLineNumber(ss);
|
||||||
fixLineNumber(se);
|
fixLineNumber(se);
|
||||||
linesWrapped++;
|
linesWrapped++;
|
||||||
numLinesAfter += newStrings.length;
|
numLinesAfter += newStrings.length;
|
||||||
|
lineStrings.splice(i, 1, ...newStrings);
|
||||||
newStrings.unshift(i, 1);
|
lineAttribs.splice(i, 1, ...newAttribStrings);
|
||||||
lineStrings.splice.apply(lineStrings, newStrings);
|
|
||||||
newAttribStrings.unshift(i, 1);
|
|
||||||
lineAttribs.splice.apply(lineAttribs, newAttribStrings);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
linesWrapped,
|
linesWrapped,
|
||||||
numLinesAfter,
|
numLinesAfter,
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
const wrapData = fixLongLines();
|
const wrapData = fixLongLines();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -734,7 +707,7 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas
|
||||||
};
|
};
|
||||||
|
|
||||||
return cc;
|
return cc;
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.sanitizeUnicode = sanitizeUnicode;
|
exports.sanitizeUnicode = sanitizeUnicode;
|
||||||
exports.makeContentCollector = makeContentCollector;
|
exports.makeContentCollector = makeContentCollector;
|
||||||
|
|
|
@ -26,17 +26,17 @@ const Security = require('./security');
|
||||||
const hooks = require('./pluginfw/hooks');
|
const hooks = require('./pluginfw/hooks');
|
||||||
const _ = require('./underscore');
|
const _ = require('./underscore');
|
||||||
const lineAttributeMarker = require('./linestylefilter').lineAttributeMarker;
|
const lineAttributeMarker = require('./linestylefilter').lineAttributeMarker;
|
||||||
const noop = function () {};
|
const noop = () => {};
|
||||||
|
|
||||||
|
|
||||||
const domline = {};
|
const domline = {};
|
||||||
|
|
||||||
domline.addToLineClass = function (lineClass, cls) {
|
domline.addToLineClass = (lineClass, cls) => {
|
||||||
// an "empty span" at any point can be used to add classes to
|
// an "empty span" at any point can be used to add classes to
|
||||||
// the line, using line:className. otherwise, we ignore
|
// the line, using line:className. otherwise, we ignore
|
||||||
// the span.
|
// the span.
|
||||||
cls.replace(/\S+/g, (c) => {
|
cls.replace(/\S+/g, (c) => {
|
||||||
if (c.indexOf('line:') == 0) {
|
if (c.indexOf('line:') === 0) {
|
||||||
// add class to line
|
// add class to line
|
||||||
lineClass = (lineClass ? `${lineClass} ` : '') + c.substring(5);
|
lineClass = (lineClass ? `${lineClass} ` : '') + c.substring(5);
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ domline.addToLineClass = function (lineClass, cls) {
|
||||||
|
|
||||||
// if "document" is falsy we don't create a DOM node, just
|
// if "document" is falsy we don't create a DOM node, just
|
||||||
// an object with innerHTML and className
|
// an object with innerHTML and className
|
||||||
domline.createDomLine = function (nonEmpty, doesWrap, optBrowser, optDocument) {
|
domline.createDomLine = (nonEmpty, doesWrap, optBrowser, optDocument) => {
|
||||||
const result = {
|
const result = {
|
||||||
node: null,
|
node: null,
|
||||||
appendSpan: noop,
|
appendSpan: noop,
|
||||||
|
@ -73,15 +73,12 @@ domline.createDomLine = function (nonEmpty, doesWrap, optBrowser, optDocument) {
|
||||||
let postHtml = '';
|
let postHtml = '';
|
||||||
let curHTML = null;
|
let curHTML = null;
|
||||||
|
|
||||||
function processSpaces(s) {
|
const processSpaces = (s) => domline.processSpaces(s, doesWrap);
|
||||||
return domline.processSpaces(s, doesWrap);
|
|
||||||
}
|
|
||||||
|
|
||||||
const perTextNodeProcess = (doesWrap ? _.identity : processSpaces);
|
const perTextNodeProcess = (doesWrap ? _.identity : processSpaces);
|
||||||
const perHtmlLineProcess = (doesWrap ? processSpaces : _.identity);
|
const perHtmlLineProcess = (doesWrap ? processSpaces : _.identity);
|
||||||
let lineClass = 'ace-line';
|
let lineClass = 'ace-line';
|
||||||
|
|
||||||
result.appendSpan = function (txt, cls) {
|
result.appendSpan = (txt, cls) => {
|
||||||
let processedMarker = false;
|
let processedMarker = false;
|
||||||
// Handle lineAttributeMarker, if present
|
// Handle lineAttributeMarker, if present
|
||||||
if (cls.indexOf(lineAttributeMarker) >= 0) {
|
if (cls.indexOf(lineAttributeMarker) >= 0) {
|
||||||
|
@ -96,7 +93,6 @@ domline.createDomLine = function (nonEmpty, doesWrap, optBrowser, optDocument) {
|
||||||
postHtml += modifier.postHtml;
|
postHtml += modifier.postHtml;
|
||||||
processedMarker |= modifier.processedMarker;
|
processedMarker |= modifier.processedMarker;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (listType) {
|
if (listType) {
|
||||||
listType = listType[1];
|
listType = listType[1];
|
||||||
if (listType) {
|
if (listType) {
|
||||||
|
@ -105,12 +101,15 @@ domline.createDomLine = function (nonEmpty, doesWrap, optBrowser, optDocument) {
|
||||||
postHtml = `</li></ul>${postHtml}`;
|
postHtml = `</li></ul>${postHtml}`;
|
||||||
} else {
|
} else {
|
||||||
if (start) { // is it a start of a list with more than one item in?
|
if (start) { // is it a start of a list with more than one item in?
|
||||||
if (start[1] == 1) { // if its the first one at this level?
|
if (start[1] === 1) { // if its the first one at this level?
|
||||||
lineClass = `${lineClass} ` + `list-start-${listType}`; // Add start class to DIV node
|
// Add start class to DIV node
|
||||||
|
lineClass = `${lineClass} ` + `list-start-${listType}`;
|
||||||
}
|
}
|
||||||
preHtml += `<ol start=${start[1]} class="list-${Security.escapeHTMLAttribute(listType)}"><li>`;
|
preHtml +=
|
||||||
|
`<ol start=${start[1]} class="list-${Security.escapeHTMLAttribute(listType)}"><li>`;
|
||||||
} else {
|
} else {
|
||||||
preHtml += `<ol class="list-${Security.escapeHTMLAttribute(listType)}"><li>`; // Handles pasted contents into existing lists
|
// Handles pasted contents into existing lists
|
||||||
|
preHtml += `<ol class="list-${Security.escapeHTMLAttribute(listType)}"><li>`;
|
||||||
}
|
}
|
||||||
postHtml += '</li></ol>';
|
postHtml += '</li></ol>';
|
||||||
}
|
}
|
||||||
|
@ -163,18 +162,20 @@ domline.createDomLine = function (nonEmpty, doesWrap, optBrowser, optDocument) {
|
||||||
} else if (txt) {
|
} else if (txt) {
|
||||||
if (href) {
|
if (href) {
|
||||||
const urn_schemes = new RegExp('^(about|geo|mailto|tel):');
|
const urn_schemes = new RegExp('^(about|geo|mailto|tel):');
|
||||||
if (!~href.indexOf('://') && !urn_schemes.test(href)) // if the url doesn't include a protocol prefix, assume http
|
// if the url doesn't include a protocol prefix, assume http
|
||||||
{
|
if (!~href.indexOf('://') && !urn_schemes.test(href)) {
|
||||||
href = `http://${href}`;
|
href = `http://${href}`;
|
||||||
}
|
}
|
||||||
// Using rel="noreferrer" stops leaking the URL/location of the pad when clicking links in the document.
|
// Using rel="noreferrer" stops leaking the URL/location of the pad when
|
||||||
|
// clicking links in the document.
|
||||||
// Not all browsers understand this attribute, but it's part of the HTML5 standard.
|
// Not all browsers understand this attribute, but it's part of the HTML5 standard.
|
||||||
// https://html.spec.whatwg.org/multipage/links.html#link-type-noreferrer
|
// https://html.spec.whatwg.org/multipage/links.html#link-type-noreferrer
|
||||||
// Additionally, we do rel="noopener" to ensure a higher level of referrer security.
|
// Additionally, we do rel="noopener" to ensure a higher level of referrer security.
|
||||||
// https://html.spec.whatwg.org/multipage/links.html#link-type-noopener
|
// https://html.spec.whatwg.org/multipage/links.html#link-type-noopener
|
||||||
// https://mathiasbynens.github.io/rel-noopener/
|
// https://mathiasbynens.github.io/rel-noopener/
|
||||||
// https://github.com/ether/etherpad-lite/pull/3636
|
// https://github.com/ether/etherpad-lite/pull/3636
|
||||||
extraOpenTags = `${extraOpenTags}<a href="${Security.escapeHTMLAttribute(href)}" rel="noreferrer noopener">`;
|
const escapedHref = Security.escapeHTMLAttribute(href);
|
||||||
|
extraOpenTags = `${extraOpenTags}<a href="${escapedHref}" rel="noreferrer noopener">`;
|
||||||
extraCloseTags = `</a>${extraCloseTags}`;
|
extraCloseTags = `</a>${extraCloseTags}`;
|
||||||
}
|
}
|
||||||
if (simpleTags) {
|
if (simpleTags) {
|
||||||
|
@ -183,16 +184,22 @@ domline.createDomLine = function (nonEmpty, doesWrap, optBrowser, optDocument) {
|
||||||
simpleTags.reverse();
|
simpleTags.reverse();
|
||||||
extraCloseTags = `</${simpleTags.join('></')}>${extraCloseTags}`;
|
extraCloseTags = `</${simpleTags.join('></')}>${extraCloseTags}`;
|
||||||
}
|
}
|
||||||
html.push('<span class="', Security.escapeHTMLAttribute(cls || ''), '">', extraOpenTags, perTextNodeProcess(Security.escapeHTML(txt)), extraCloseTags, '</span>');
|
html.push(
|
||||||
|
'<span class="', Security.escapeHTMLAttribute(cls || ''),
|
||||||
|
'">',
|
||||||
|
extraOpenTags,
|
||||||
|
perTextNodeProcess(Security.escapeHTML(txt)),
|
||||||
|
extraCloseTags,
|
||||||
|
'</span>');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
result.clearSpans = function () {
|
result.clearSpans = () => {
|
||||||
html = [];
|
html = [];
|
||||||
lineClass = 'ace-line';
|
lineClass = 'ace-line';
|
||||||
result.lineMarker = 0;
|
result.lineMarker = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
function writeHTML() {
|
const writeHTML = () => {
|
||||||
let newHTML = perHtmlLineProcess(html.join(''));
|
let newHTML = perHtmlLineProcess(html.join(''));
|
||||||
if (!newHTML) {
|
if (!newHTML) {
|
||||||
if ((!document) || (!optBrowser)) {
|
if ((!document) || (!optBrowser)) {
|
||||||
|
@ -209,21 +216,19 @@ domline.createDomLine = function (nonEmpty, doesWrap, optBrowser, optDocument) {
|
||||||
curHTML = newHTML;
|
curHTML = newHTML;
|
||||||
result.node.innerHTML = curHTML;
|
result.node.innerHTML = curHTML;
|
||||||
}
|
}
|
||||||
if (lineClass !== null) result.node.className = lineClass;
|
if (lineClass != null) result.node.className = lineClass;
|
||||||
|
|
||||||
hooks.callAll('acePostWriteDomLineHTML', {
|
hooks.callAll('acePostWriteDomLineHTML', {
|
||||||
node: result.node,
|
node: result.node,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
result.prepareForAdd = writeHTML;
|
result.prepareForAdd = writeHTML;
|
||||||
result.finishUpdate = writeHTML;
|
result.finishUpdate = writeHTML;
|
||||||
result.getInnerHTML = function () {
|
result.getInnerHTML = () => curHTML || '';
|
||||||
return curHTML || '';
|
|
||||||
};
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
domline.processSpaces = function (s, doesWrap) {
|
domline.processSpaces = (s, doesWrap) => {
|
||||||
if (s.indexOf('<') < 0 && !doesWrap) {
|
if (s.indexOf('<') < 0 && !doesWrap) {
|
||||||
// short-cut
|
// short-cut
|
||||||
return s.replace(/ /g, ' ');
|
return s.replace(/ /g, ' ');
|
||||||
|
@ -237,31 +242,31 @@ domline.processSpaces = function (s, doesWrap) {
|
||||||
let beforeSpace = false;
|
let beforeSpace = false;
|
||||||
// last space in a run is normal, others are nbsp,
|
// last space in a run is normal, others are nbsp,
|
||||||
// end of line is nbsp
|
// end of line is nbsp
|
||||||
for (var i = parts.length - 1; i >= 0; i--) {
|
for (let i = parts.length - 1; i >= 0; i--) {
|
||||||
var p = parts[i];
|
const p = parts[i];
|
||||||
if (p == ' ') {
|
if (p === ' ') {
|
||||||
if (endOfLine || beforeSpace) parts[i] = ' ';
|
if (endOfLine || beforeSpace) parts[i] = ' ';
|
||||||
endOfLine = false;
|
endOfLine = false;
|
||||||
beforeSpace = true;
|
beforeSpace = true;
|
||||||
} else if (p.charAt(0) != '<') {
|
} else if (p.charAt(0) !== '<') {
|
||||||
endOfLine = false;
|
endOfLine = false;
|
||||||
beforeSpace = false;
|
beforeSpace = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// beginning of line is nbsp
|
// beginning of line is nbsp
|
||||||
for (var i = 0; i < parts.length; i++) {
|
for (let i = 0; i < parts.length; i++) {
|
||||||
var p = parts[i];
|
const p = parts[i];
|
||||||
if (p == ' ') {
|
if (p === ' ') {
|
||||||
parts[i] = ' ';
|
parts[i] = ' ';
|
||||||
break;
|
break;
|
||||||
} else if (p.charAt(0) != '<') {
|
} else if (p.charAt(0) !== '<') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (var i = 0; i < parts.length; i++) {
|
for (let i = 0; i < parts.length; i++) {
|
||||||
var p = parts[i];
|
const p = parts[i];
|
||||||
if (p == ' ') {
|
if (p === ' ') {
|
||||||
parts[i] = ' ';
|
parts[i] = ' ';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,16 +21,19 @@ const testImports = {
|
||||||
input: '<html><body><li>wtf</ul></body></html>',
|
input: '<html><body><li>wtf</ul></body></html>',
|
||||||
expectedHTML: '<!DOCTYPE HTML><html><body>wtf<br><br></body></html>',
|
expectedHTML: '<!DOCTYPE HTML><html><body>wtf<br><br></body></html>',
|
||||||
expectedText: 'wtf\n\n',
|
expectedText: 'wtf\n\n',
|
||||||
|
disabled: true,
|
||||||
},
|
},
|
||||||
'nonelistiteminlist #3620': {
|
'nonelistiteminlist #3620': {
|
||||||
input: '<html><body><ul>test<li>FOO</li></ul></body></html>',
|
input: '<html><body><ul>test<li>FOO</li></ul></body></html>',
|
||||||
expectedHTML: '<!DOCTYPE HTML><html><body><ul class="bullet">test<li>FOO</ul><br></body></html>',
|
expectedHTML: '<!DOCTYPE HTML><html><body><ul class="bullet">test<li>FOO</ul><br></body></html>',
|
||||||
expectedText: '\ttest\n\t* FOO\n\n',
|
expectedText: '\ttest\n\t* FOO\n\n',
|
||||||
|
disabled: true,
|
||||||
},
|
},
|
||||||
'whitespaceinlist #3620': {
|
'whitespaceinlist #3620': {
|
||||||
input: '<html><body><ul> <li>FOO</li></ul></body></html>',
|
input: '<html><body><ul> <li>FOO</li></ul></body></html>',
|
||||||
expectedHTML: '<!DOCTYPE HTML><html><body><ul class="bullet"><li>FOO</ul><br></body></html>',
|
expectedHTML: '<!DOCTYPE HTML><html><body><ul class="bullet"><li>FOO</ul><br></body></html>',
|
||||||
expectedText: '\t* FOO\n\n',
|
expectedText: '\t* FOO\n\n',
|
||||||
|
disabled: true,
|
||||||
},
|
},
|
||||||
'prefixcorrectlinenumber': {
|
'prefixcorrectlinenumber': {
|
||||||
input: '<html><body><ol><li>should be 1</li><li>should be 2</li></ol></body></html>',
|
input: '<html><body><ol><li>should be 1</li><li>should be 2</li></ol></body></html>',
|
||||||
|
|
Loading…
Reference in a new issue