diff --git a/client/src/app/site/motions/services/diff.service.spec.ts b/client/src/app/site/motions/services/diff.service.spec.ts index 7b5ea48be..df8cfa809 100644 --- a/client/src/app/site/motions/services/diff.service.spec.ts +++ b/client/src/app/site/motions/services/diff.service.spec.ts @@ -805,7 +805,7 @@ describe('DiffService', () => { after = ''; const diff = service.diff(before, after); expect(diff).toBe( - '

Ihr könnt ohne Sorge fortgehen.\'Da meckerte die Alte und machte sich getrost auf den Weg.

' + '

Ihr könnt ohne Sorge fortgehen.\'Da meckerte die Alte und machte sich getrost auf den Weg.

' ); })); @@ -953,6 +953,20 @@ describe('DiffService', () => { 'Gegenüber

' ); })); + + it('does not delete a paragraph before an inserted one', inject([DiffService], (service: DiffService) => { + const inHtml = '', + outHtml = ''; + const diff = service.diff(inHtml, outHtml); + expect(diff).toBe(''); + })); }); describe('ignoring line numbers', () => { @@ -1028,6 +1042,17 @@ describe('DiffService', () => { } )); + it('works with a replaced list item', inject([DiffService], (service: DiffService) => { + const before = "", + after = "\n", + expected = '' + + ""; + const diff = service.diff(before, after, 80, 1); + const diffNormalized = service.normalizeHtmlForDiff(diff).toLowerCase(); + const expectedNormalized = service.normalizeHtmlForDiff(expected).toLowerCase(); + expect(diffNormalized).toBe(expectedNormalized); + })); + it('detects broken HTML and lowercases class names', inject([DiffService], (service: DiffService) => { const before = '

 holen, da rief sie alle sieben herbei und sprach:

\n\n

 "Liebe Kinder, ich will hinaus in den Wald, seid auf der Hut vor dem Wolf! Wenn er
 hereinkommt, frisst er euch alle mit Haut und Haar. Der Bösewicht verstellt sich oft, aber
 an der rauen Stimme und an seinen schwarzen Füßen werdet ihr ihn schon erkennen."

\n\n

 Die Geißlein sagten: " Liebe Mutter, wir wollen uns schon in acht nehmen, du kannst ohne

', diff --git a/client/src/app/site/motions/services/diff.service.ts b/client/src/app/site/motions/services/diff.service.ts index 39a2ae4ea..844fede67 100644 --- a/client/src/app/site/motions/services/diff.service.ts +++ b/client/src/app/site/motions/services/diff.service.ts @@ -1709,18 +1709,14 @@ export class DiffService { null, firstLineNumber ); - newTextWithBreaks = this.lineNumberingService.insertLineNumbersNode( - newText, - lineLength, - null, - firstLineNumber - ); + newText = this.lineNumberingService.insertLineBreaksWithoutNumbers(newText, lineLength); } else { oldTextWithBreaks = document.createElement('div'); oldTextWithBreaks.innerHTML = oldText; - newTextWithBreaks = document.createElement('div'); - newTextWithBreaks.innerHTML = newText; } + newText = newText.replace(/^\s+/g, '').replace(/\s+$/g, ''); + newTextWithBreaks = document.createElement('div'); + newTextWithBreaks.innerHTML = newText; for (let i = 0; i < oldTextWithBreaks.childNodes.length; i++) { currChild = oldTextWithBreaks.childNodes[i]; @@ -1923,6 +1919,31 @@ export class DiffService { } ); + //
  • ...
  • =>
  • ...
  • + diffUnnormalized = diffUnnormalized.replace( + /<(ins|del)><(p|div|blockquote|li)([^>]*)>([\s\S]*)<\/\2><\/\1>/gi, + (whole: string, insDel: string, block: string, blockArguments: string, content: string): string => { + // Prevent accidental matches like

    ...

    ...
    ...

    + if (content.match(/<(ins|del)>/gi)) { + return whole; + } + // Add the CSS-class to the existing "class"-attribute, or add one + let newArguments = blockArguments; + const modificationClass = (insDel.toLowerCase() === 'ins' ? 'insert' : 'delete'); + if (newArguments.match(/class="/gi)) { + // class="someclass" => class="someclass insert" + newArguments = newArguments.replace(/(class\s*=\s*)(["'])([^\2]*)\2/gi, + (classWhole: string, attr: string, para: string, classContent: string): string => { + return attr + para + classContent + ' ' + modificationClass + para; + } + ); + } else { + newArguments += ' class="' + modificationClass + '"'; + } + return '<' + block + newArguments + '>' + content + ''; + } + ); + if (diffUnnormalized.substr(0, workaroundPrepend.length) === workaroundPrepend) { diffUnnormalized = diffUnnormalized.substring(workaroundPrepend.length); } @@ -1931,49 +1952,6 @@ export class DiffService { if (this.diffDetectBrokenDiffHtml(diffUnnormalized)) { diff = this.diffParagraphs(htmlOld, htmlNew, lineLength, firstLineNumber); } else { - diffUnnormalized = diffUnnormalized.replace( - /.*?(\n.*?)*<\/ins>/gi, - (found: string): string => { - found = found.replace( - /<(div|p|li)[^>]*>/gi, - (match: string): string => { - return match + ''; - } - ); - found = found.replace( - /<\/(div|p|li)[^>]*>/gi, - (match: string): string => { - return '' + match; - } - ); - return found; - } - ); - diffUnnormalized = diffUnnormalized.replace( - /.*?(\n.*?)*<\/del>/gi, - (found: string): string => { - found = found.replace( - /<(div|p|li)[^>]*>/gi, - (match: string): string => { - return match + ''; - } - ); - found = found.replace( - /<\/(div|p|li)[^>]*>/gi, - (match: string): string => { - return '' + match; - } - ); - return found; - } - ); - diffUnnormalized = diffUnnormalized.replace( - /^

    (.*)<\/p><\/del>$/gi, - (match: string, inner: string): string => { - return '

    ' + inner + '

    '; - } - ); - let node: Element = document.createElement('div'); node.innerHTML = diffUnnormalized; diff = node.innerHTML; diff --git a/client/src/app/site/motions/services/linenumbering.service.spec.ts b/client/src/app/site/motions/services/linenumbering.service.spec.ts index 44471be19..2ba77af7b 100644 --- a/client/src/app/site/motions/services/linenumbering.service.spec.ts +++ b/client/src/app/site/motions/services/linenumbering.service.spec.ts @@ -491,6 +491,16 @@ describe('LinenumberingService', () => { expect(service.insertLineBreaksWithoutNumbers(outHtml, 80)).toBe(outHtml); })); + it('does not count within .insert nodes', inject( + [LinenumberingService], + (service: LinenumberingService) => { + const inHtml = "

    1234

    • 1234

    1234 1234

    "; + const outHtml = service.insertLineNumbers(inHtml, 10); + expect(outHtml).toBe('

    ' + noMarkup(1) + '1234

    • 1234

    ' + noMarkup(2) + '1234 1234

    '); + expect(service.stripLineNumbers(outHtml)).toBe(inHtml); + expect(service.insertLineBreaksWithoutNumbers(outHtml, 80)).toBe(outHtml); + })); + it('does not create a new line for a trailing INS', inject( [LinenumberingService], (service: LinenumberingService) => { diff --git a/client/src/app/site/motions/services/linenumbering.service.ts b/client/src/app/site/motions/services/linenumbering.service.ts index d5af49ade..4fe81e352 100644 --- a/client/src/app/site/motions/services/linenumbering.service.ts +++ b/client/src/app/site/motions/services/linenumbering.service.ts @@ -511,6 +511,8 @@ export class LinenumberingService { return this.ignoreInsertedText; } else if (this.isOsLineNumberNode(element)) { return true; + } else if (element.classList && element.classList.contains('insert')) { + return true; } else { return false; } @@ -808,7 +810,7 @@ export class LinenumberingService { throw new Error('This method may only be called for ELEMENT-nodes: ' + element.nodeValue); } if (this.isIgnoredByLineNumbering(element)) { - if (this.currentInlineOffset === 0 && this.currentLineNumber !== null) { + if (this.currentInlineOffset === 0 && this.currentLineNumber !== null && this.isInlineElement(element)) { const lineNumberNode = this.createLineNumber(); if (lineNumberNode) { element.insertBefore(lineNumberNode, element.firstChild);