Merge pull request #3096 from CatoTH/Issue3077-highlight-inline-diff

Add an explicit line highlighting function for the diff
This commit is contained in:
Norman Jäckel 2017-03-20 22:09:18 +01:00 committed by GitHub
commit 42cf987aed
3 changed files with 150 additions and 2 deletions

View File

@ -331,7 +331,7 @@ angular.module('OpenSlidesApp.motions', [
text = ''; text = '';
for (var i = 0; i < changes.length; i++) { for (var i = 0; i < changes.length; i++) {
text += this.getTextBetweenChangeRecommendations(versionId, (i === 0 ? null : changes[i - 1]), changes[i], highlight); text += this.getTextBetweenChangeRecommendations(versionId, (i === 0 ? null : changes[i - 1]), changes[i], highlight);
text += changes[i].getDiff(this, versionId); text += changes[i].getDiff(this, versionId, highlight);
} }
text += this.getTextRemainderAfterLastChangeRecommendation(versionId, changes); text += this.getTextRemainderAfterLastChangeRecommendation(versionId, changes);
break; break;
@ -740,7 +740,13 @@ angular.module('OpenSlidesApp.motions', [
oldText = data.outerContextStart + data.innerContextStart + oldText = data.outerContextStart + data.innerContextStart +
data.html + data.innerContextEnd + data.outerContextEnd; data.html + data.innerContextEnd + data.outerContextEnd;
return diffService.diff(oldText, this.text, lineLength, this.line_from); var diff = diffService.diff(oldText, this.text, lineLength, this.line_from);
if (highlight > 0) {
diff = lineNumberingService.highlightLine(diff, highlight);
}
return diff;
}, },
getType: function(original_full_html) { getType: function(original_full_html) {
return this.type; return this.type;

View File

@ -71,6 +71,32 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
return isLineNumber; return isLineNumber;
}; };
this._getLineNumberNode = function(fragment, lineNumber) {
return fragment.querySelector('.os-line-number.line-number-' + lineNumber);
};
this._htmlToFragment = function(html) {
var fragment = document.createDocumentFragment(),
div = document.createElement('DIV');
div.innerHTML = html;
while (div.childElementCount) {
var child = div.childNodes[0];
div.removeChild(child);
fragment.appendChild(child);
}
return fragment;
};
this._fragmentToHtml = function(fragment) {
var div = document.createElement('DIV');
while (fragment.firstChild) {
var child = fragment.firstChild;
fragment.removeChild(child);
div.appendChild(child);
}
return div.innerHTML;
};
this._createLineBreak = function () { this._createLineBreak = function () {
var br = document.createElement('br'); var br = document.createElement('br');
br.setAttribute('class', 'os-line-break'); br.setAttribute('class', 'os-line-break');
@ -413,6 +439,15 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
return this._insertLineNumbersToNode(root, lineLength, highlight); return this._insertLineNumbersToNode(root, lineLength, highlight);
}; };
/**
*
* @param {string} html
* @param {number} lineLength
* @param {string} highlight - optional
* @param {function} callback
* @param {number} firstLine
* @returns {string}
*/
this.insertLineNumbers = function (html, lineLength, highlight, callback, firstLine) { this.insertLineNumbers = function (html, lineLength, highlight, callback, firstLine) {
var newHtml, newRoot; var newHtml, newRoot;
@ -438,12 +473,81 @@ angular.module('OpenSlidesApp.motions.lineNumbering', [])
return newHtml; return newHtml;
}; };
/**
* @param {string} html
* @returns {string}
*/
this.stripLineNumbers = function (html) { this.stripLineNumbers = function (html) {
var root = document.createElement('div'); var root = document.createElement('div');
root.innerHTML = html; root.innerHTML = html;
this._stripLineNumbers(root); this._stripLineNumbers(root);
return root.innerHTML; return root.innerHTML;
}; };
/**
* Traverses up the DOM tree until it finds a node with a nextSibling, then returns that sibling
*
* @param node
* @private
*/
this._findNextAuntNode = function(node) {
if (node.nextSibling) {
return node.nextSibling;
} else if (node.parentNode) {
return this._findNextAuntNode(node.parentNode);
} else {
return null;
}
};
this._highlightUntilNextLine = function(lineNumberNode) {
var currentNode = lineNumberNode,
foundNextLineNumber = false;
do {
var wasHighlighted = false;
if (currentNode.nodeType === TEXT_NODE) {
var node = document.createElement('span');
node.setAttribute('class', 'highlight');
node.innerHTML = currentNode.nodeValue;
currentNode.parentNode.insertBefore(node, currentNode);
currentNode.parentNode.removeChild(currentNode);
currentNode = node;
wasHighlighted = true;
} else {
wasHighlighted = false;
}
if (currentNode.childNodes.length > 0 && !this._isOsLineNumberNode(currentNode) && !wasHighlighted) {
currentNode = currentNode.childNodes[0];
} else if (currentNode.nextSibling) {
currentNode = currentNode.nextSibling;
} else {
currentNode = this._findNextAuntNode(currentNode);
}
if (this._isOsLineNumberNode(currentNode)) {
foundNextLineNumber = true;
}
} while (!foundNextLineNumber && currentNode !== null);
};
/**
* @param {string} html
* @param {number} lineNumber
* @return {string}
*/
this.highlightLine = function (html, lineNumber) {
var fragment = this._htmlToFragment(html),
lineNumberNode = this._getLineNumberNode(fragment, lineNumber);
if (lineNumberNode) {
this._highlightUntilNextLine(lineNumberNode);
html = this._fragmentToHtml(fragment);
}
return html;
};
} }
]); ]);

View File

@ -286,4 +286,42 @@ describe('linenumbering', function () {
expect(outHtml).toBe("<ul>\n\n<li>" + noMarkup(1) + "Point 1</li>\n\n</ul>"); expect(outHtml).toBe("<ul>\n\n<li>" + noMarkup(1) + "Point 1</li>\n\n</ul>");
}); });
}); });
describe('line highlighting', function() {
it('highlights a simple line', function () {
var inHtml = lineNumberingService.insertLineNumbers("<span>Lorem ipsum dolorsit amet</span>", 5);
var highlighted = lineNumberingService.highlightLine(inHtml, 2);
expect(highlighted).toBe(noMarkup(1) + '<span>Lorem ' + brMarkup(2) + '<span class="highlight">ipsum </span>' + brMarkup(3) + 'dolor' + brMarkup(4) + 'sit ' + brMarkup(5) + 'amet</span>');
});
it('highlights a simple line with formattings', function () {
var inHtml = lineNumberingService.insertLineNumbers("<span>Lorem ipsum <strong>dolorsit amet Lorem</strong><em> ipsum dolorsit amet</em> Lorem ipsum dolorsit amet</span>", 20);
expect(inHtml).toBe(noMarkup(1) + '<span>Lorem ipsum <strong>dolorsit ' +
brMarkup(2) + 'amet Lorem</strong><em> ipsum ' +
brMarkup(3) + 'dolorsit amet</em> Lorem ' + brMarkup(4) + 'ipsum dolorsit amet</span>');
var highlighted = lineNumberingService.highlightLine(inHtml, 2);
expect(highlighted).toBe(noMarkup(1) + '<span>Lorem ipsum <strong>dolorsit ' +
brMarkup(2) + '<span class="highlight">amet Lorem</span></strong><em><span class="highlight"> ipsum </span>' +
brMarkup(3) + 'dolorsit amet</em> Lorem ' + brMarkup(4) + 'ipsum dolorsit amet</span>');
});
it('highlights the last line', function () {
var inHtml = lineNumberingService.insertLineNumbers("<span>Lorem ipsum dolorsit amet</span>", 5);
var highlighted = lineNumberingService.highlightLine(inHtml, 5);
expect(highlighted).toBe(noMarkup(1) + '<span>Lorem ' + brMarkup(2) + 'ipsum ' + brMarkup(3) + 'dolor' + brMarkup(4) + 'sit ' + brMarkup(5) + '<span class="highlight">amet</span></span>');
});
it('highlights the first line', function () {
var inHtml = lineNumberingService.insertLineNumbers("<span>Lorem ipsum dolorsit amet</span>", 5);
var highlighted = lineNumberingService.highlightLine(inHtml, 1);
expect(highlighted).toBe(noMarkup(1) + '<span><span class="highlight">Lorem </span>' + brMarkup(2) + 'ipsum ' + brMarkup(3) + 'dolor' + brMarkup(4) + 'sit ' + brMarkup(5) + 'amet</span>');
});
it('does not change the string if the line number is not found', function () {
var inHtml = lineNumberingService.insertLineNumbers("<span>Lorem ipsum dolorsit amet</span>", 5);
var highlighted = lineNumberingService.highlightLine(inHtml, 8);
expect(highlighted).toBe(noMarkup(1) + '<span>Lorem ' + brMarkup(2) + 'ipsum ' + brMarkup(3) + 'dolor' + brMarkup(4) + 'sit ' + brMarkup(5) + 'amet</span>');
});
});
}); });