Break lines in nodes that have already been parsed
This commit is contained in:
parent
a2b8f84c73
commit
e18f22f200
@ -21,10 +21,27 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
|
|||||||
var ELEMENT_NODE = 1,
|
var ELEMENT_NODE = 1,
|
||||||
TEXT_NODE = 3;
|
TEXT_NODE = 3;
|
||||||
|
|
||||||
|
// Counts the number of characters in the current line, beyond singe nodes.
|
||||||
|
// Needs to be resetted after each line break and after entering a new block node.
|
||||||
this._currentInlineOffset = null;
|
this._currentInlineOffset = null;
|
||||||
|
|
||||||
|
// The last position of a point suitable for breaking the line. null or an object with the following values:
|
||||||
|
// - node: the node that contains the position. Guaranteed to be a TextNode
|
||||||
|
// - offset: the offset of the breaking characters (like the space)
|
||||||
|
// Needs to be resetted after each line break and after entering a new block node.
|
||||||
|
this._lastInlineBreakablePoint = null;
|
||||||
|
|
||||||
|
// The line number counter
|
||||||
this._currentLineNumber = null;
|
this._currentLineNumber = null;
|
||||||
|
|
||||||
|
// Indicates that we just entered a block element and we want to add a line number without line break at the beginning.
|
||||||
this._prependLineNumberToFirstText = false;
|
this._prependLineNumberToFirstText = false;
|
||||||
|
|
||||||
|
// A workaround to prevent double line numbers
|
||||||
this._ignoreNextRegularLineNumber = false;
|
this._ignoreNextRegularLineNumber = false;
|
||||||
|
|
||||||
|
// Decides if the content of inserted nodes should count as well. This is used so we can use the algorithm on a
|
||||||
|
// text with inline diff annotations and get the same line numbering as with the original text (when set to false)
|
||||||
this._ignoreInsertedText = false;
|
this._ignoreInsertedText = false;
|
||||||
|
|
||||||
var lineNumberCache = $cacheFactory('linenumbering.service');
|
var lineNumberCache = $cacheFactory('linenumbering.service');
|
||||||
@ -141,7 +158,6 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
|
|||||||
currLineStart = 0,
|
currLineStart = 0,
|
||||||
i = 0,
|
i = 0,
|
||||||
firstTextNode = true,
|
firstTextNode = true,
|
||||||
lastBreakableIndex = null,
|
|
||||||
service = this;
|
service = this;
|
||||||
var addLine = function (text, highlight) {
|
var addLine = function (text, highlight) {
|
||||||
var node;
|
var node;
|
||||||
@ -171,9 +187,19 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
out.push(node);
|
out.push(node);
|
||||||
|
return node;
|
||||||
|
};
|
||||||
|
var addLinebreakToPreviousNode = function (node, offset, highlight) {
|
||||||
|
var firstText = node.nodeValue.substr(0, offset + 1),
|
||||||
|
secondText = node.nodeValue.substr(offset + 1);
|
||||||
|
var lineBreak = service._createLineBreak();
|
||||||
|
var firstNode = document.createTextNode(firstText);
|
||||||
|
node.parentNode.insertBefore(firstNode, node);
|
||||||
|
node.parentNode.insertBefore(lineBreak, node);
|
||||||
|
node.nodeValue = secondText;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (node.nodeValue == "\n") {
|
if (node.nodeValue === "\n") {
|
||||||
out.push(node);
|
out.push(node);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@ -184,6 +210,7 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
|
|||||||
out.push(service._createLineNumber());
|
out.push(service._createLineNumber());
|
||||||
}
|
}
|
||||||
this._currentInlineOffset = 0;
|
this._currentInlineOffset = 0;
|
||||||
|
this._lastInlineBreakablePoint = null;
|
||||||
} else if (this._prependLineNumberToFirstText) {
|
} else if (this._prependLineNumberToFirstText) {
|
||||||
if (this._ignoreNextRegularLineNumber) {
|
if (this._ignoreNextRegularLineNumber) {
|
||||||
this._ignoreNextRegularLineNumber = false;
|
this._ignoreNextRegularLineNumber = false;
|
||||||
@ -196,30 +223,50 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
|
|||||||
while (i < node.nodeValue.length) {
|
while (i < node.nodeValue.length) {
|
||||||
var lineBreakAt = null;
|
var lineBreakAt = null;
|
||||||
if (this._currentInlineOffset >= length) {
|
if (this._currentInlineOffset >= length) {
|
||||||
if (lastBreakableIndex !== null) {
|
if (this._lastInlineBreakablePoint !== null) {
|
||||||
lineBreakAt = lastBreakableIndex;
|
lineBreakAt = this._lastInlineBreakablePoint;
|
||||||
} else {
|
} else {
|
||||||
lineBreakAt = i - 1;
|
lineBreakAt = {
|
||||||
|
'node': node,
|
||||||
|
'offset': i - 1
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (lineBreakAt !== null && (node.nodeValue[i] !== ' ' && node.nodeValue[i] !== "\n")) {
|
if (lineBreakAt !== null && (node.nodeValue[i] !== ' ' && node.nodeValue[i] !== "\n")) {
|
||||||
var currLine = node.nodeValue.substring(currLineStart, lineBreakAt + 1);
|
if (lineBreakAt.node === node) {
|
||||||
addLine(currLine, highlight);
|
// The last possible breaking point is in this text node
|
||||||
|
var currLine = node.nodeValue.substring(currLineStart, lineBreakAt.offset + 1);
|
||||||
|
addLine(currLine, highlight);
|
||||||
|
|
||||||
|
currLineStart = lineBreakAt.offset + 1;
|
||||||
|
this._currentInlineOffset = i - lineBreakAt.offset - 1;
|
||||||
|
this._lastInlineBreakablePoint = null;
|
||||||
|
} else {
|
||||||
|
// The last possible breaking point was not in this text not, but one we have already passed
|
||||||
|
var remainderOfPrev = lineBreakAt.node.nodeValue.length - lineBreakAt.offset - 1;
|
||||||
|
addLinebreakToPreviousNode(lineBreakAt.node, lineBreakAt.offset, highlight);
|
||||||
|
|
||||||
|
this._currentInlineOffset = i + remainderOfPrev;
|
||||||
|
this._lastInlineBreakablePoint = null;
|
||||||
|
}
|
||||||
|
|
||||||
currLineStart = lineBreakAt + 1;
|
|
||||||
this._currentInlineOffset = i - lineBreakAt - 1;
|
|
||||||
lastBreakableIndex = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.nodeValue[i] === ' ' || node.nodeValue[i] === '-' || node.nodeValue[i] === "\n") {
|
if (node.nodeValue[i] === ' ' || node.nodeValue[i] === '-' || node.nodeValue[i] === "\n") {
|
||||||
lastBreakableIndex = i;
|
this._lastInlineBreakablePoint = {
|
||||||
|
'node': node,
|
||||||
|
'offset': i
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this._currentInlineOffset++;
|
this._currentInlineOffset++;
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
}
|
}
|
||||||
addLine(node.nodeValue.substring(currLineStart), highlight);
|
var lastLine = addLine(node.nodeValue.substring(currLineStart), highlight);
|
||||||
|
if (this._lastInlineBreakablePoint !== null) {
|
||||||
|
this._lastInlineBreakablePoint.node = lastLine;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
};
|
};
|
||||||
@ -280,6 +327,7 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
|
|||||||
overlength = ((this._currentInlineOffset + firstword) > length && this._currentInlineOffset > 0);
|
overlength = ((this._currentInlineOffset + firstword) > length && this._currentInlineOffset > 0);
|
||||||
if (overlength && this._isInlineElement(oldChildren[i])) {
|
if (overlength && this._isInlineElement(oldChildren[i])) {
|
||||||
this._currentInlineOffset = 0;
|
this._currentInlineOffset = 0;
|
||||||
|
this._lastInlineBreakablePoint = null;
|
||||||
node.appendChild(this._createLineBreak());
|
node.appendChild(this._createLineBreak());
|
||||||
if (this._currentLineNumber !== null) {
|
if (this._currentLineNumber !== null) {
|
||||||
node.appendChild(this._createLineNumber());
|
node.appendChild(this._createLineNumber());
|
||||||
@ -338,6 +386,7 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
|
|||||||
|
|
||||||
this._insertLineNumbersToBlockNode = function (node, length, highlight) {
|
this._insertLineNumbersToBlockNode = function (node, length, highlight) {
|
||||||
this._currentInlineOffset = 0;
|
this._currentInlineOffset = 0;
|
||||||
|
this._lastInlineBreakablePoint = null;
|
||||||
this._prependLineNumberToFirstText = true;
|
this._prependLineNumberToFirstText = true;
|
||||||
|
|
||||||
var oldChildren = [], i;
|
var oldChildren = [], i;
|
||||||
@ -369,6 +418,7 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
|
|||||||
overlength = ((this._currentInlineOffset + firstword) > length && this._currentInlineOffset > 0);
|
overlength = ((this._currentInlineOffset + firstword) > length && this._currentInlineOffset > 0);
|
||||||
if (overlength && this._isInlineElement(oldChildren[i]) && !this._isIgnoredByLineNumbering(oldChildren[i])) {
|
if (overlength && this._isInlineElement(oldChildren[i]) && !this._isIgnoredByLineNumbering(oldChildren[i])) {
|
||||||
this._currentInlineOffset = 0;
|
this._currentInlineOffset = 0;
|
||||||
|
this._lastInlineBreakablePoint = null;
|
||||||
node.appendChild(this._createLineBreak());
|
node.appendChild(this._createLineBreak());
|
||||||
if (this._currentLineNumber !== null) {
|
if (this._currentLineNumber !== null) {
|
||||||
node.appendChild(this._createLineNumber());
|
node.appendChild(this._createLineNumber());
|
||||||
@ -383,6 +433,7 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._currentInlineOffset = 0;
|
this._currentInlineOffset = 0;
|
||||||
|
this._lastInlineBreakablePoint = null;
|
||||||
this._prependLineNumberToFirstText = true;
|
this._prependLineNumberToFirstText = true;
|
||||||
this._ignoreNextRegularLineNumber = false;
|
this._ignoreNextRegularLineNumber = false;
|
||||||
|
|
||||||
@ -454,6 +505,7 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
|
|||||||
root.innerHTML = html;
|
root.innerHTML = html;
|
||||||
|
|
||||||
this._currentInlineOffset = 0;
|
this._currentInlineOffset = 0;
|
||||||
|
this._lastInlineBreakablePoint = null;
|
||||||
if (firstLine) {
|
if (firstLine) {
|
||||||
this._currentLineNumber = parseInt(firstLine);
|
this._currentLineNumber = parseInt(firstLine);
|
||||||
} else {
|
} else {
|
||||||
@ -513,6 +565,7 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
|
|||||||
root.innerHTML = html;
|
root.innerHTML = html;
|
||||||
|
|
||||||
this._currentInlineOffset = 0;
|
this._currentInlineOffset = 0;
|
||||||
|
this._lastInlineBreakablePoint = null;
|
||||||
this._currentLineNumber = null;
|
this._currentLineNumber = null;
|
||||||
this._prependLineNumberToFirstText = true;
|
this._prependLineNumberToFirstText = true;
|
||||||
this._ignoreNextRegularLineNumber = false;
|
this._ignoreNextRegularLineNumber = false;
|
||||||
|
@ -303,6 +303,12 @@ describe('linenumbering', function () {
|
|||||||
var outHtml = lineNumberingService.insertLineNumbers(inHtml, 80);
|
var outHtml = lineNumberingService.insertLineNumbers(inHtml, 80);
|
||||||
expect(outHtml).toBe("<p>" + noMarkup(1) + "Test 123<br>" + noMarkup(2) + "Test 456</p>");
|
expect(outHtml).toBe("<p>" + noMarkup(1) + "Test 123<br>" + noMarkup(2) + "Test 456</p>");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not force-break words right after an INS', function () {
|
||||||
|
var inHtml = "<p>" + noMarkup(1) + "012345 <ins>78 01 34567</ins>8901234567890123456789</p>";
|
||||||
|
var outHtml = lineNumberingService.insertLineBreaksWithoutNumbers(inHtml, 20, true);
|
||||||
|
expect(outHtml).toBe("<p>" + noMarkup(1) + "012345 <ins>78 01 <br class=\"os-line-break\">34567</ins>890123456789012<br class=\"os-line-break\">3456789</p>");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('line breaking without adding line numbers', function() {
|
describe('line breaking without adding line numbers', function() {
|
||||||
|
Loading…
Reference in New Issue
Block a user