diff --git a/CHANGELOG b/CHANGELOG index 92b31043d..65889089a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -37,7 +37,7 @@ Motions: - Added config value for pagenumber alignment in PDF [#3327]. - Bugfix: Several bugfixes regarding splitting list items in change recommendations [#3288]. -- Bugfix: Several bugfixes regarding diff version [#3407, #3408]. +- Bugfix: Several bugfixes regarding diff version [#3407, #3408, #3410]. - Added inline Editing for motion reason [#3361]. - Added multiselect filter for motion comments [#3372]. - Added support for pinning personal notes to the window [#3360]. diff --git a/openslides/motions/static/js/motions/diff.js b/openslides/motions/static/js/motions/diff.js index 6e2a6d7d0..2f2af8bbc 100644 --- a/openslides/motions/static/js/motions/diff.js +++ b/openslides/motions/static/js/motions/diff.js @@ -671,6 +671,8 @@ angular.module('OpenSlidesApp.motions.diff', ['OpenSlidesApp.motions.lineNumberi for (var ent in entities) { html = html.replace(new RegExp(ent, 'g'), entities[ent]); } + html = html.replace(/[ \n\t]+/gi, ' '); + html = html.replace(/(<\/(div|p|ul|li|blockquote>)>) /gi, "$1\n"); return html; }; @@ -1049,14 +1051,16 @@ angular.module('OpenSlidesApp.motions.diff', ['OpenSlidesApp.motions.lineNumberi // The "!!(found=...)"-construction is only used to make jshint happy :) var findDel = /(.*?)<\/del>/gi, findIns = /(.*?)<\/ins>/gi, - found; + found, inner; while (!!(found = findDel.exec(html))) { - if (found[1].match(/<[^>]*>/)) { + inner = found[1].replace(/]*>/gi, ''); + if (inner.match(/<[^>]*>/)) { return true; } } while (!!(found = findIns.exec(html))) { - if (found[1].match(/<[^>]*>/)) { + inner = found[1].replace(/]*>/gi, ''); + if (inner.match(/<[^>]*>/)) { return true; } } diff --git a/openslides/motions/static/js/motions/linenumbering.js b/openslides/motions/static/js/motions/linenumbering.js index 93faed1e9..4ef1eb5b6 100644 --- a/openslides/motions/static/js/motions/linenumbering.js +++ b/openslides/motions/static/js/motions/linenumbering.js @@ -202,7 +202,7 @@ angular.module('OpenSlidesApp.motions.lineNumbering', []) lineBreakAt = i - 1; } } - if (lineBreakAt !== null && node.nodeValue[i] != ' ') { + if (lineBreakAt !== null && (node.nodeValue[i] !== ' ' && node.nodeValue[i] !== "\n")) { var currLine = node.nodeValue.substring(currLineStart, lineBreakAt + 1); addLine(currLine, highlight); @@ -211,7 +211,7 @@ angular.module('OpenSlidesApp.motions.lineNumbering', []) lastBreakableIndex = null; } - if (node.nodeValue[i] == ' ' || node.nodeValue[i] == '-') { + if (node.nodeValue[i] === ' ' || node.nodeValue[i] === '-' || node.nodeValue[i] === "\n") { lastBreakableIndex = i; } @@ -413,6 +413,12 @@ angular.module('OpenSlidesApp.motions.lineNumbering', []) this._stripLineNumbers = function (node) { for (var i = 0; i < node.childNodes.length; i++) { if (this._isOsLineBreakNode(node.childNodes[i]) || this._isOsLineNumberNode(node.childNodes[i])) { + // If a newline character follows a line break, it's been very likely inserted by the WYSIWYG-editor + if (node.childNodes.length > (i + 1) && node.childNodes[i + 1].nodeType === TEXT_NODE) { + if (node.childNodes[i + 1].nodeValue[0] === "\n") { + node.childNodes[i + 1].nodeValue = " " + node.childNodes[i + 1].nodeValue.substring(1); + } + } node.removeChild(node.childNodes[i]); i--; } else { @@ -439,6 +445,9 @@ angular.module('OpenSlidesApp.motions.lineNumbering', []) * @param {number|null} firstLine */ this.insertLineNumbersNode = function (html, lineLength, highlight, firstLine) { + // Removing newlines after BRs, as they lead to problems like #3410 + html = html.replace(/(]*>)[\n\r]+/gi, '$1'); + var root = document.createElement('div'); root.innerHTML = html; diff --git a/tests/karma/motions/diff.service.test.js b/tests/karma/motions/diff.service.test.js index e4a678d82..08ac0accf 100644 --- a/tests/karma/motions/diff.service.test.js +++ b/tests/karma/motions/diff.service.test.js @@ -399,6 +399,12 @@ describe('linenumbering', function () { normalized = diffService._normalizeHtmlForDiff(unnormalized); expect(normalized).toBe("

Test

"); }); + + it('treats newlines like spaces', function () { + var unnormalized = "

Test line\n\t 2

", + normalized = diffService._normalizeHtmlForDiff(unnormalized); + expect(normalized).toBe("

Test line 2

"); + }); }); describe('the core diff algorithm', function () { @@ -521,7 +527,7 @@ describe('linenumbering', function () { after = "

sem. Nulla consequat massa quis enim. TEST
\nTEST

"; var diff = diffService.diff(before, after); - expect(diff).toBe('

sem. Nulla consequat massa quis enim. TEST
' + "\n" + "TEST

"); + expect(diff).toBe('

sem. Nulla consequat massa quis enim. TEST
TEST

'); }); it('does not repeat the last word (2)', function () { diff --git a/tests/karma/motions/linenumbering.service.test.js b/tests/karma/motions/linenumbering.service.test.js index 4c905b190..17692519c 100644 --- a/tests/karma/motions/linenumbering.service.test.js +++ b/tests/karma/motions/linenumbering.service.test.js @@ -145,6 +145,12 @@ describe('linenumbering', function () { expect(lineNumberingService.stripLineNumbers(outHtml)).toBe(inHtml); expect(lineNumberingService.insertLineBreaksWithoutNumbers(outHtml, 80)).toBe(outHtml); }); + + it('treats ascii newline characters like spaces', function () { + var inHtml = "

Test 123\nTest1

"; + var outHtml = lineNumberingService.insertLineNumbers(inHtml, 5); + expect(outHtml).toBe('

' + noMarkup(1) + 'Test ' + brMarkup(2) + "123\n" + brMarkup(3) + 'Test1

'); + }); }); @@ -291,6 +297,12 @@ describe('linenumbering', function () { expect(lineNumberingService.stripLineNumbers(outHtml)).toBe(inHtml); expect(lineNumberingService.insertLineBreaksWithoutNumbers(outHtml, 80)).toBe(outHtml); }); + + it('cancels newlines after br-elements', function () { + var inHtml = "

Test 123
\nTest 456

"; + var outHtml = lineNumberingService.insertLineNumbers(inHtml, 80); + expect(outHtml).toBe("

" + noMarkup(1) + "Test 123
" + noMarkup(2) + "Test 456

"); + }); }); describe('line breaking without adding line numbers', function() {