ES6ify /gr-diff-selection/*

Bug: Issue 6179
Change-Id: I2c929b863c22dc28df7b571002d6ea927b8e631e
This commit is contained in:
Kasper Nilsson
2017-05-16 14:31:51 -07:00
parent 858f5bee35
commit 2f07624fd4
2 changed files with 93 additions and 97 deletions

View File

@@ -18,13 +18,13 @@
* Possible CSS classes indicating the state of selection. Dynamically added/ * Possible CSS classes indicating the state of selection. Dynamically added/
* removed based on where the user clicks within the diff. * removed based on where the user clicks within the diff.
*/ */
var SelectionClass = { const SelectionClass = {
COMMENT: 'selected-comment', COMMENT: 'selected-comment',
LEFT: 'selected-left', LEFT: 'selected-left',
RIGHT: 'selected-right', RIGHT: 'selected-right',
}; };
var getNewCache = function() { return {left: null, right: null}; }; const getNewCache = () => { return {left: null, right: null}; };
Polymer({ Polymer({
is: 'gr-diff-selection', is: 'gr-diff-selection',
@@ -43,11 +43,11 @@
], ],
listeners: { listeners: {
'copy': '_handleCopy', copy: '_handleCopy',
'down': '_handleDown', down: '_handleDown',
}, },
attached: function() { attached() {
this.classList.add(SelectionClass.RIGHT); this.classList.add(SelectionClass.RIGHT);
}, },
@@ -59,19 +59,19 @@
return this._cachedDiffBuilder; return this._cachedDiffBuilder;
}, },
_diffChanged: function() { _diffChanged() {
this._linesCache = getNewCache(); this._linesCache = getNewCache();
}, },
_handleDown: function(e) { _handleDown(e) {
var lineEl = this.diffBuilder.getLineElByChild(e.target); const lineEl = this.diffBuilder.getLineElByChild(e.target);
if (!lineEl) { if (!lineEl) {
return; return;
} }
var commentSelected = const commentSelected =
this._elementDescendedFromClass(e.target, 'gr-diff-comment'); this._elementDescendedFromClass(e.target, 'gr-diff-comment');
var side = this.diffBuilder.getSideByLineEl(lineEl); const side = this.diffBuilder.getSideByLineEl(lineEl);
var targetClasses = []; const targetClasses = [];
targetClasses.push(side === 'left' ? targetClasses.push(side === 'left' ?
SelectionClass.LEFT : SelectionClass.LEFT :
SelectionClass.RIGHT); SelectionClass.RIGHT);
@@ -80,23 +80,23 @@
targetClasses.push(SelectionClass.COMMENT); targetClasses.push(SelectionClass.COMMENT);
} }
// Remove any selection classes that do not belong. // Remove any selection classes that do not belong.
for (var key in SelectionClass) { for (const key in SelectionClass) {
if (SelectionClass.hasOwnProperty(key)) { if (SelectionClass.hasOwnProperty(key)) {
var className = SelectionClass[key]; const className = SelectionClass[key];
if (targetClasses.indexOf(className) === -1) { if (!targetClasses.includes(className)) {
this.classList.remove(SelectionClass[key]); this.classList.remove(SelectionClass[key]);
} }
} }
} }
// Add new selection classes iff they are not already present. // Add new selection classes iff they are not already present.
for (var i = 0; i < targetClasses.length; i++) { for (const _class of targetClasses) {
if (!this.classList.contains(targetClasses[i])) { if (!this.classList.contains(_class)) {
this.classList.add(targetClasses[i]); this.classList.add(_class);
} }
} }
}, },
_getCopyEventTarget: function(e) { _getCopyEventTarget(e) {
return Polymer.dom(e).rootTarget; return Polymer.dom(e).rootTarget;
}, },
@@ -108,7 +108,7 @@
* @param {!string} className * @param {!string} className
* @return {boolean} * @return {boolean}
*/ */
_elementDescendedFromClass: function(element, className) { _elementDescendedFromClass(element, className) {
while (!element.classList.contains(className)) { while (!element.classList.contains(className)) {
if (!element.parentElement || if (!element.parentElement ||
element === this.diffBuilder.diffElement) { element === this.diffBuilder.diffElement) {
@@ -119,20 +119,20 @@
return true; return true;
}, },
_handleCopy: function(e) { _handleCopy(e) {
var commentSelected = false; let commentSelected = false;
var target = this._getCopyEventTarget(e); const target = this._getCopyEventTarget(e);
if (target.type === 'textarea') { return; } if (target.type === 'textarea') { return; }
if (!this._elementDescendedFromClass(target, 'diff-row')) { return; } if (!this._elementDescendedFromClass(target, 'diff-row')) { return; }
if (this.classList.contains(SelectionClass.COMMENT)) { if (this.classList.contains(SelectionClass.COMMENT)) {
commentSelected = true; commentSelected = true;
} }
var lineEl = this.diffBuilder.getLineElByChild(target); const lineEl = this.diffBuilder.getLineElByChild(target);
if (!lineEl) { if (!lineEl) {
return; return;
} }
var side = this.diffBuilder.getSideByLineEl(lineEl); const side = this.diffBuilder.getSideByLineEl(lineEl);
var text = this._getSelectedText(side, commentSelected); const text = this._getSelectedText(side, commentSelected);
if (text) { if (text) {
e.clipboardData.setData('Text', text); e.clipboardData.setData('Text', text);
e.preventDefault(); e.preventDefault();
@@ -148,19 +148,20 @@
* @param {boolean} Whether or not a comment is selected. * @param {boolean} Whether or not a comment is selected.
* @return {string} The selected text. * @return {string} The selected text.
*/ */
_getSelectedText: function(side, commentSelected) { _getSelectedText(side, commentSelected) {
var sel = window.getSelection(); const sel = window.getSelection();
if (sel.rangeCount != 1) { if (sel.rangeCount != 1) {
return; // No multi-select support yet. return; // No multi-select support yet.
} }
if (commentSelected) { if (commentSelected) {
return this._getCommentLines(sel, side); return this._getCommentLines(sel, side);
} }
var range = GrRangeNormalizer.normalize(sel.getRangeAt(0)); const range = GrRangeNormalizer.normalize(sel.getRangeAt(0));
var startLineEl = this.diffBuilder.getLineElByChild(range.startContainer); const startLineEl =
var endLineEl = this.diffBuilder.getLineElByChild(range.endContainer); this.diffBuilder.getLineElByChild(range.startContainer);
var startLineNum = parseInt(startLineEl.getAttribute('data-value'), 10); const endLineEl = this.diffBuilder.getLineElByChild(range.endContainer);
var endLineNum = parseInt(endLineEl.getAttribute('data-value'), 10); const startLineNum = parseInt(startLineEl.getAttribute('data-value'), 10);
const endLineNum = parseInt(endLineEl.getAttribute('data-value'), 10);
return this._getRangeFromDiff(startLineNum, range.startOffset, endLineNum, return this._getRangeFromDiff(startLineNum, range.startOffset, endLineNum,
range.endOffset, side); range.endOffset, side);
@@ -176,9 +177,9 @@
* @param {!string} side The side that is currently selected. * @param {!string} side The side that is currently selected.
* @return {string} The selected diff text. * @return {string} The selected diff text.
*/ */
_getRangeFromDiff: function(startLineNum, startOffset, endLineNum, _getRangeFromDiff(startLineNum, startOffset, endLineNum, endOffset, side) {
endOffset, side) { const lines =
var lines = this._getDiffLines(side).slice(startLineNum - 1, endLineNum); this._getDiffLines(side).slice(startLineNum - 1, endLineNum);
if (lines.length) { if (lines.length) {
lines[lines.length - 1] = lines[lines.length - 1] lines[lines.length - 1] = lines[lines.length - 1]
.substring(0, endOffset); .substring(0, endOffset);
@@ -193,17 +194,13 @@
* @param {!string} side The side that is currently selected. * @param {!string} side The side that is currently selected.
* @return {Array.string} An array of strings indexed by line number. * @return {Array.string} An array of strings indexed by line number.
*/ */
_getDiffLines: function(side) { _getDiffLines(side) {
if (this._linesCache[side]) { if (this._linesCache[side]) {
return this._linesCache[side]; return this._linesCache[side];
} }
var lines = []; let lines = [];
var chunk; const key = side === 'left' ? 'a' : 'b';
var key = side === 'left' ? 'a' : 'b'; for (const chunk of this.diff.content) {
for (var chunkIndex = 0;
chunkIndex < this.diff.content.length;
chunkIndex++) {
chunk = this.diff.content[chunkIndex];
if (chunk.ab) { if (chunk.ab) {
lines = lines.concat(chunk.ab); lines = lines.concat(chunk.ab);
} else if (chunk[key]) { } else if (chunk[key]) {
@@ -222,16 +219,16 @@
* @param {!string} side The side that is currently selected. * @param {!string} side The side that is currently selected.
* @return {string} The selected comment text. * @return {string} The selected comment text.
*/ */
_getCommentLines: function(sel, side) { _getCommentLines(sel, side) {
var range = GrRangeNormalizer.normalize(sel.getRangeAt(0)); const range = GrRangeNormalizer.normalize(sel.getRangeAt(0));
var content = []; const content = [];
// Query the diffElement for comments. // Query the diffElement for comments.
var messages = this.diffBuilder.diffElement.querySelectorAll( const messages = this.diffBuilder.diffElement.querySelectorAll(
'.side-by-side [data-side="' + side + `.side-by-side [data-side="${side
'"] .message *, .unified .message *'); }"] .message *, .unified .message *`);
for (var i = 0; i < messages.length; i++) { for (let i = 0; i < messages.length; i++) {
var el = messages[i]; const el = messages[i];
// Check if the comment element exists inside the selection. // Check if the comment element exists inside the selection.
if (sel.containsNode(el, true)) { if (sel.containsNode(el, true)) {
// Padded elements require newlines for accurate spacing. // Padded elements require newlines for accurate spacing.
@@ -262,10 +259,10 @@
* @param {Range} range The normalized selection range. * @param {Range} range The normalized selection range.
* @return {string} The text within the selection. * @return {string} The text within the selection.
*/ */
_getTextContentForRange: function(domNode, sel, range) { _getTextContentForRange(domNode, sel, range) {
if (!sel.containsNode(domNode, true)) { return ''; } if (!sel.containsNode(domNode, true)) { return ''; }
var text = ''; let text = '';
if (domNode instanceof Text) { if (domNode instanceof Text) {
text = domNode.textContent; text = domNode.textContent;
if (domNode === range.endContainer) { if (domNode === range.endContainer) {
@@ -275,9 +272,8 @@
text = text.substring(range.startOffset); text = text.substring(range.startOffset);
} }
} else { } else {
for (var i = 0; i < domNode.childNodes.length; i++) { for (const childNode of domNode.childNodes) {
text += this._getTextContentForRange(domNode.childNodes[i], text += this._getTextContentForRange(childNode, sel, range);
sel, range);
} }
} }
return text; return text;

View File

@@ -101,13 +101,13 @@ limitations under the License.
</test-fixture> </test-fixture>
<script> <script>
suite('gr-diff-selection', function() { suite('gr-diff-selection', () => {
var element; let element;
var sandbox; let sandbox;
var emulateCopyOn = function(target) { const emulateCopyOn = function(target) {
var fakeEvent = { const fakeEvent = {
target: target, target,
preventDefault: sandbox.stub(), preventDefault: sandbox.stub(),
clipboardData: { clipboardData: {
setData: sandbox.stub(), setData: sandbox.stub(),
@@ -118,7 +118,7 @@ limitations under the License.
return fakeEvent; return fakeEvent;
}; };
setup(function() { setup(() => {
element = fixture('basic'); element = fixture('basic');
sandbox = sinon.sandbox.create(); sandbox = sinon.sandbox.create();
sandbox.stub(element, '_getCopyEventTarget'); sandbox.stub(element, '_getCopyEventTarget');
@@ -145,11 +145,11 @@ limitations under the License.
}; };
}); });
teardown(function() { teardown(() => {
sandbox.restore(); sandbox.restore();
}); });
test('applies selected-left on left side click', function() { test('applies selected-left on left side click', () => {
element.classList.add('selected-right'); element.classList.add('selected-right');
element._cachedDiffBuilder.getSideByLineEl.returns('left'); element._cachedDiffBuilder.getSideByLineEl.returns('left');
MockInteractions.down(element); MockInteractions.down(element);
@@ -160,7 +160,7 @@ limitations under the License.
'removes selected-right'); 'removes selected-right');
}); });
test('applies selected-right on right side click', function() { test('applies selected-right on right side click', () => {
element.classList.add('selected-left'); element.classList.add('selected-left');
element._cachedDiffBuilder.getSideByLineEl.returns('right'); element._cachedDiffBuilder.getSideByLineEl.returns('right');
MockInteractions.down(element); MockInteractions.down(element);
@@ -170,41 +170,41 @@ limitations under the License.
element.classList.contains('selected-left'), 'removes selected-left'); element.classList.contains('selected-left'), 'removes selected-left');
}); });
test('ignores copy for non-content Element', function() { test('ignores copy for non-content Element', () => {
sandbox.stub(element, '_getSelectedText'); sandbox.stub(element, '_getSelectedText');
emulateCopyOn(element.querySelector('.not-diff-row')); emulateCopyOn(element.querySelector('.not-diff-row'));
assert.isFalse(element._getSelectedText.called); assert.isFalse(element._getSelectedText.called);
}); });
test('asks for text for left side Elements', function() { test('asks for text for left side Elements', () => {
element._cachedDiffBuilder.getSideByLineEl.returns('left'); element._cachedDiffBuilder.getSideByLineEl.returns('left');
sandbox.stub(element, '_getSelectedText'); sandbox.stub(element, '_getSelectedText');
emulateCopyOn(element.querySelector('div.contentText')); emulateCopyOn(element.querySelector('div.contentText'));
assert.deepEqual(['left', false], element._getSelectedText.lastCall.args); assert.deepEqual(['left', false], element._getSelectedText.lastCall.args);
}); });
test('reacts to copy for content Elements', function() { test('reacts to copy for content Elements', () => {
sandbox.stub(element, '_getSelectedText'); sandbox.stub(element, '_getSelectedText');
emulateCopyOn(element.querySelector('div.contentText')); emulateCopyOn(element.querySelector('div.contentText'));
assert.isTrue(element._getSelectedText.called); assert.isTrue(element._getSelectedText.called);
}); });
test('copy event is prevented for content Elements', function() { test('copy event is prevented for content Elements', () => {
sandbox.stub(element, '_getSelectedText'); sandbox.stub(element, '_getSelectedText');
element._cachedDiffBuilder.getSideByLineEl.returns('left'); element._cachedDiffBuilder.getSideByLineEl.returns('left');
element._getSelectedText.returns('test'); element._getSelectedText.returns('test');
var event = emulateCopyOn(element.querySelector('div.contentText')); const event = emulateCopyOn(element.querySelector('div.contentText'));
assert.isTrue(event.preventDefault.called); assert.isTrue(event.preventDefault.called);
}); });
test('inserts text into clipboard on copy', function() { test('inserts text into clipboard on copy', () => {
sandbox.stub(element, '_getSelectedText').returns('the text'); sandbox.stub(element, '_getSelectedText').returns('the text');
var event = emulateCopyOn(element.querySelector('div.contentText')); const event = emulateCopyOn(element.querySelector('div.contentText'));
assert.deepEqual( assert.deepEqual(
['Text', 'the text'], event.clipboardData.setData.lastCall.args); ['Text', 'the text'], event.clipboardData.setData.lastCall.args);
}); });
test('copies content correctly', function() { test('copies content correctly', () => {
// Fetch the line number. // Fetch the line number.
element._cachedDiffBuilder.getLineElByChild = function(child) { element._cachedDiffBuilder.getLineElByChild = function(child) {
while (!child.classList.contains('content') && child.parentElement) { while (!child.classList.contains('content') && child.parentElement) {
@@ -216,9 +216,9 @@ limitations under the License.
element.classList.add('selected-left'); element.classList.add('selected-left');
element.classList.remove('selected-right'); element.classList.remove('selected-right');
var selection = window.getSelection(); const selection = window.getSelection();
selection.removeAllRanges(); selection.removeAllRanges();
var range = document.createRange(); const range = document.createRange();
range.setStart(element.querySelector('div.contentText').firstChild, 3); range.setStart(element.querySelector('div.contentText').firstChild, 3);
range.setEnd( range.setEnd(
element.querySelectorAll('div.contentText')[4].firstChild, 2); element.querySelectorAll('div.contentText')[4].firstChild, 2);
@@ -226,13 +226,13 @@ limitations under the License.
assert.equal(element._getSelectedText('left'), 'ba\nzin\nga'); assert.equal(element._getSelectedText('left'), 'ba\nzin\nga');
}); });
test('copies comments', function() { test('copies comments', () => {
element.classList.add('selected-left'); element.classList.add('selected-left');
element.classList.add('selected-comment'); element.classList.add('selected-comment');
element.classList.remove('selected-right'); element.classList.remove('selected-right');
var selection = window.getSelection(); const selection = window.getSelection();
selection.removeAllRanges(); selection.removeAllRanges();
var range = document.createRange(); const range = document.createRange();
range.setStart( range.setStart(
element.querySelector('.gr-formatted-text *').firstChild, 3); element.querySelector('.gr-formatted-text *').firstChild, 3);
range.setEnd( range.setEnd(
@@ -242,14 +242,14 @@ limitations under the License.
element._getSelectedText('left', true)); element._getSelectedText('left', true));
}); });
test('respects astral chars in comments', function() { test('respects astral chars in comments', () => {
element.classList.add('selected-left'); element.classList.add('selected-left');
element.classList.add('selected-comment'); element.classList.add('selected-comment');
element.classList.remove('selected-right'); element.classList.remove('selected-right');
var selection = window.getSelection(); const selection = window.getSelection();
selection.removeAllRanges(); selection.removeAllRanges();
var range = document.createRange(); const range = document.createRange();
var nodes = element.querySelectorAll('.gr-formatted-text *'); const nodes = element.querySelectorAll('.gr-formatted-text *');
range.setStart(nodes[2].childNodes[2], 13); range.setStart(nodes[2].childNodes[2], 13);
range.setEnd(nodes[2].childNodes[2], 23); range.setEnd(nodes[2].childNodes[2], 23);
selection.addRange(range); selection.addRange(range);
@@ -257,15 +257,15 @@ limitations under the License.
element._getSelectedText('left', true)); element._getSelectedText('left', true));
}); });
test('defers to default behavior for textarea', function() { test('defers to default behavior for textarea', () => {
element.classList.add('selected-left'); element.classList.add('selected-left');
element.classList.remove('selected-right'); element.classList.remove('selected-right');
var selectedTextSpy = sandbox.spy(element, '_getSelectedText'); const selectedTextSpy = sandbox.spy(element, '_getSelectedText');
emulateCopyOn(element.querySelector('textarea')); emulateCopyOn(element.querySelector('textarea'));
assert.isFalse(selectedTextSpy.called); assert.isFalse(selectedTextSpy.called);
}); });
test('regression test for 4794', function() { test('regression test for 4794', () => {
element._cachedDiffBuilder.getLineElByChild = function(child) { element._cachedDiffBuilder.getLineElByChild = function(child) {
while (!child.classList.contains('content') && child.parentElement) { while (!child.classList.contains('content') && child.parentElement) {
child = child.parentElement; child = child.parentElement;
@@ -276,9 +276,9 @@ limitations under the License.
element.classList.add('selected-right'); element.classList.add('selected-right');
element.classList.remove('selected-left'); element.classList.remove('selected-left');
var selection = window.getSelection(); const selection = window.getSelection();
selection.removeAllRanges(); selection.removeAllRanges();
var range = document.createRange(); const range = document.createRange();
range.setStart( range.setStart(
element.querySelectorAll('div.contentText')[1].firstChild, 4); element.querySelectorAll('div.contentText')[1].firstChild, 4);
range.setEnd( range.setEnd(
@@ -287,12 +287,12 @@ limitations under the License.
assert.equal(element._getSelectedText('right'), ' other'); assert.equal(element._getSelectedText('right'), ' other');
}); });
suite('_getTextContentForRange', function() { suite('_getTextContentForRange', () => {
var selection; let selection;
var range; let range;
var nodes; let nodes;
setup(function() { setup(() => {
element.classList.add('selected-left'); element.classList.add('selected-left');
element.classList.add('selected-comment'); element.classList.add('selected-comment');
element.classList.remove('selected-right'); element.classList.remove('selected-right');
@@ -302,7 +302,7 @@ limitations under the License.
nodes = element.querySelectorAll('.gr-formatted-text *'); nodes = element.querySelectorAll('.gr-formatted-text *');
}); });
test('multi level element contained in range', function() { test('multi level element contained in range', () => {
range.setStart(nodes[2].childNodes[0], 1); range.setStart(nodes[2].childNodes[0], 1);
range.setEnd(nodes[2].childNodes[2], 7); range.setEnd(nodes[2].childNodes[2], 7);
selection.addRange(range); selection.addRange(range);
@@ -311,7 +311,7 @@ limitations under the License.
}); });
test('multi level element as startContainer of range', function() { test('multi level element as startContainer of range', () => {
range.setStart(nodes[2].childNodes[1], 0); range.setStart(nodes[2].childNodes[1], 0);
range.setEnd(nodes[2].childNodes[2], 7); range.setEnd(nodes[2].childNodes[2], 7);
selection.addRange(range); selection.addRange(range);
@@ -319,7 +319,7 @@ limitations under the License.
'a differ'); 'a differ');
}); });
test('startContainer === endContainer', function() { test('startContainer === endContainer', () => {
range.setStart(nodes[0].firstChild, 2); range.setStart(nodes[0].firstChild, 2);
range.setEnd(nodes[0].firstChild, 12); range.setEnd(nodes[0].firstChild, 12);
selection.addRange(range); selection.addRange(range);
@@ -328,7 +328,7 @@ limitations under the License.
}); });
}); });
test('cache is reset when diff changes', function() { test('cache is reset when diff changes', () => {
element._linesCache = {left: 'test', right: 'test'}; element._linesCache = {left: 'test', right: 'test'};
element.diff = {}; element.diff = {};
flushAsynchronousOperations(); flushAsynchronousOperations();