Move gr-diff-new to gr-diff
Change-Id: Ifaad016f806c31f3df43143b3238b757faa18b20
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.html b/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.html
index 39477bc..d293060 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.html
@@ -44,7 +44,6 @@
draft="[[comment.__draft]]"
show-actions="[[_showActions]]"
project-config="[[projectConfig]]"
- on-height-change="_handleCommentHeightChange"
on-reply="_handleCommentReply"
on-comment-discard="_handleCommentDiscard"
on-done="_handleCommentDone"></gr-diff-comment>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.js b/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.js
index c7bbff2..b827d26 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.js
@@ -18,12 +18,6 @@
is: 'gr-diff-comment-thread',
/**
- * Fired when the height of the thread changes.
- *
- * @event height-change
- */
-
- /**
* Fired when the thread should be discarded.
*
* @event thread-discard
@@ -44,11 +38,6 @@
},
_showActions: Boolean,
- _boundWindowResizeHandler: {
- type: Function,
- value: function() { return this._handleWindowResize.bind(this); }
- },
- _lastHeight: Number,
_orderedComments: Array,
},
@@ -64,12 +53,6 @@
this._getLoggedIn().then(function(loggedIn) {
this._showActions = loggedIn;
}.bind(this));
-
- window.addEventListener('resize', this._boundWindowResizeHandler);
- },
-
- detached: function() {
- window.removeEventListener('resize', this._boundWindowResizeHandler);
},
addDraft: function(opt_lineNum) {
@@ -88,10 +71,6 @@
return this.$.restAPI.getLoggedIn();
},
- _handleWindowResize: function(e) {
- this._heightChanged();
- },
-
_commentsChanged: function(changeRecord) {
this._orderedComments = this._sortedComments(this.comments);
},
@@ -133,11 +112,6 @@
}
},
- _handleCommentHeightChange: function(e) {
- e.stopPropagation();
- this._heightChanged();
- },
-
_handleCommentReply: function(e) {
var comment = e.detail.comment;
var quoteStr;
@@ -153,7 +127,6 @@
this.async(function() {
var commentEl = this._commentElWithDraftID(reply.__draftID);
commentEl.editing = true;
- this.async(this._heightChanged.bind(this), 1);
}.bind(this), 1);
},
@@ -166,7 +139,6 @@
this.async(function() {
var commentEl = this._commentElWithDraftID(reply.__draftID);
commentEl.save();
- this.async(this._heightChanged.bind(this), 1);
}.bind(this), 1);
},
@@ -215,15 +187,6 @@
this.fire('thread-discard');
return;
}
- this.async(this._heightChanged.bind(this), 1);
- },
-
- _heightChanged: function() {
- var height = this.$.container.offsetHeight;
- if (height == this._lastHeight) { return; }
-
- this.fire('height-change', {height: height}, {bubbles: false});
- this._lastHeight = height;
},
_indexOf: function(comment, arr) {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html b/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html
index ca6815b..a6a8c4d 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html
@@ -129,7 +129,6 @@
disabled="{{disabled}}"
rows="4"
bind-value="{{_editDraft}}"
- on-keyup="_handleTextareaKeyup"
on-keydown="_handleTextareaKeydown"></iron-autogrow-textarea>
<gr-linked-text class="message"
pre
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.js b/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.js
index dcdb881..81449b8 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.js
@@ -18,12 +18,6 @@
is: 'gr-diff-comment',
/**
- * Fired when the height of the comment changes.
- *
- * @event height-change
- */
-
- /**
* Fired when the Reply action is triggered.
*
* @event reply
@@ -75,10 +69,6 @@
this.editing = this._editDraft.length == 0;
},
- attached: function() {
- this._heightChanged();
- },
-
save: function() {
this.comment.message = this._editDraft;
this.disabled = true;
@@ -101,13 +91,6 @@
}.bind(this));
},
- _heightChanged: function() {
- this.async(function() {
- this.fire('height-change', {height: this.offsetHeight},
- {bubbles: false});
- }.bind(this));
- },
-
_draftChanged: function(draft) {
this.$.container.classList.toggle('draft', draft);
},
@@ -126,7 +109,6 @@
if (this.comment && this.comment.id) {
this.$$('.cancel').hidden = !editing;
}
- this._heightChanged();
},
_computeLinkToComment: function(comment) {
@@ -137,12 +119,6 @@
return draft == null || draft.trim() == '';
},
- _handleTextareaKeyup: function(e) {
- // TODO(andybons): This isn't always true, but I can't currently think
- // of a better metric.
- this._heightChanged();
- },
-
_handleTextareaKeydown: function(e) {
if (e.keyCode == 27) { // 'esc'
this._handleCancel(e);
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-side/gr-diff-side.html b/polygerrit-ui/app/elements/diff/gr-diff-side/gr-diff-side.html
deleted file mode 100644
index 972dc2d..0000000
--- a/polygerrit-ui/app/elements/diff/gr-diff-side/gr-diff-side.html
+++ /dev/null
@@ -1,97 +0,0 @@
-<!--
-Copyright (C) 2015 The Android Open Source Project
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
--->
-
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../gr-diff-comment-thread/gr-diff-comment-thread.html">
-
-<dom-module id="gr-diff-side">
- <template>
- <style>
- :host,
- .container {
- display: flex;
- flex: 0 0 auto;
- }
- .lineNum:before,
- .code:before {
- /* To ensure the height is non-zero in these elements, a
- zero-width space is set as its content. The character
- itself doesn't matter. Just that there is something
- there. */
- content: '\200B';
- }
- .lineNum {
- background-color: #eee;
- color: #666;
- padding: 0 .75em;
- text-align: right;
- }
- .canComment .lineNum {
- cursor: pointer;
- text-decoration: underline;
- }
- .canComment .lineNum:hover {
- background-color: #ccc;
- }
- .lightHighlight {
- background-color: var(--light-highlight-color);
- }
- hl,
- .darkHighlight {
- background-color: var(--dark-highlight-color);
- }
- .br:after {
- /* Line feed */
- content: '\A';
- }
- .tab {
- display: inline-block;
- }
- .tab.withIndicator:before {
- color: #C62828;
- /* >> character */
- content: '\00BB';
- }
- .numbers,
- .content {
- white-space: pre;
- }
- .numbers .filler {
- background-color: #eee;
- }
- .contextControl {
- background-color: #fef;
- }
- .contextControl a:link,
- .contextControl a:visited {
- display: block;
- text-decoration: none;
- }
- .numbers .contextControl {
- padding: 0 .75em;
- text-align: right;
- }
- .content .contextControl {
- text-align: center;
- }
- </style>
- <div class$="[[_computeContainerClass(canComment)]]">
- <div class="numbers" id="numbers"></div>
- <div class="content" id="content"></div>
- </div>
- </template>
- <script src="gr-diff-side.js"></script>
-</dom-module>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-side/gr-diff-side.js b/polygerrit-ui/app/elements/diff/gr-diff-side/gr-diff-side.js
deleted file mode 100644
index b97d7b7..0000000
--- a/polygerrit-ui/app/elements/diff/gr-diff-side/gr-diff-side.js
+++ /dev/null
@@ -1,612 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-(function() {
- 'use strict';
-
- var CharCode = {
- LESS_THAN: '<'.charCodeAt(0),
- GREATER_THAN: '>'.charCodeAt(0),
- AMPERSAND: '&'.charCodeAt(0),
- SEMICOLON: ';'.charCodeAt(0),
- };
-
- var TAB_REGEX = /\t/g;
-
- Polymer({
- is: 'gr-diff-side',
-
- /**
- * Fired when an expand context control is clicked.
- *
- * @event expand-context
- */
-
- /**
- * Fired when a thread's height is changed.
- *
- * @event thread-height-change
- */
-
- /**
- * Fired when a draft should be added.
- *
- * @event add-draft
- */
-
- /**
- * Fired when a thread is removed.
- *
- * @event remove-thread
- */
-
- properties: {
- canComment: {
- type: Boolean,
- value: false,
- },
- content: {
- type: Array,
- notify: true,
- observer: '_contentChanged',
- },
- prefs: {
- type: Object,
- value: function() { return {}; },
- },
- changeNum: String,
- patchNum: String,
- path: String,
- projectConfig: {
- type: Object,
- observer: '_projectConfigChanged',
- },
-
- _lineFeedHTML: {
- type: String,
- value: '<span class="style-scope gr-diff-side br"></span>',
- readOnly: true,
- },
- _highlightStartTag: {
- type: String,
- value: '<hl class="style-scope gr-diff-side">',
- readOnly: true,
- },
- _highlightEndTag: {
- type: String,
- value: '</hl>',
- readOnly: true,
- },
- _diffChunkLineNums: {
- type: Array,
- value: function() { return []; },
- },
- _commentThreadLineNums: {
- type: Array,
- value: function() { return []; },
- },
- _focusedLineNum: {
- type: Number,
- value: 1,
- },
- },
-
- listeners: {
- 'tap': '_tapHandler',
- },
-
- observers: [
- '_prefsChanged(prefs.*)',
- ],
-
- rowInserted: function(index) {
- this.renderLineIndexRange(index, index);
- this._updateDOMIndices();
- this._updateJumpIndices();
- },
-
- rowRemoved: function(index) {
- var removedEls = Polymer.dom(this.root).querySelectorAll(
- '[data-index="' + index + '"]');
- for (var i = 0; i < removedEls.length; i++) {
- removedEls[i].parentNode.removeChild(removedEls[i]);
- }
- this._updateDOMIndices();
- this._updateJumpIndices();
- },
-
- rowUpdated: function(index) {
- var removedEls = Polymer.dom(this.root).querySelectorAll(
- '[data-index="' + index + '"]');
- for (var i = 0; i < removedEls.length; i++) {
- removedEls[i].parentNode.removeChild(removedEls[i]);
- }
- this.renderLineIndexRange(index, index);
- },
-
- scrollToLine: function(lineNum) {
- if (isNaN(lineNum) || lineNum < 1) { return; }
-
- var el = this.$$('.numbers .lineNum[data-line-num="' + lineNum + '"]');
- if (!el) { return; }
-
- // Calculate where the line is relative to the window.
- var top = el.offsetTop;
- for (var offsetParent = el.offsetParent;
- offsetParent;
- offsetParent = offsetParent.offsetParent) {
- top += offsetParent.offsetTop;
- }
-
- // Scroll the element to the middle of the window. Dividing by a third
- // instead of half the inner height feels a bit better otherwise the
- // element appears to be below the center of the window even when it
- // isn't.
- window.scrollTo(0, top - (window.innerHeight / 3) - el.offsetHeight);
- },
-
- scrollToNextDiffChunk: function() {
- this._scrollToNextChunkOrThread(this._diffChunkLineNums);
- },
-
- scrollToPreviousDiffChunk: function() {
- this._scrollToPreviousChunkOrThread(this._diffChunkLineNums);
- },
-
- scrollToNextCommentThread: function() {
- this._scrollToNextChunkOrThread(this._commentThreadLineNums);
- },
-
- scrollToPreviousCommentThread: function() {
- this._scrollToPreviousChunkOrThread(this._commentThreadLineNums);
- },
-
- renderLineIndexRange: function(startIndex, endIndex) {
- this._render(this.content, startIndex, endIndex);
- },
-
- hideElementsWithIndex: function(index) {
- var els = Polymer.dom(this.root).querySelectorAll(
- '[data-index="' + index + '"]');
- for (var i = 0; i < els.length; i++) {
- els[i].setAttribute('hidden', true);
- }
- },
-
- getRowHeight: function(index) {
- var row = this.content[index];
- // Filler elements should not be taken into account when determining
- // height calculations.
- if (row.type == 'FILLER') {
- return 0;
- }
- if (row.height != null) {
- return row.height;
- }
-
- var selector = '[data-index="' + index + '"]';
- var els = Polymer.dom(this.root).querySelectorAll(selector);
- if (els.length != 2) {
- throw Error('Rows should only consist of two elements');
- }
- return Math.max(els[0].offsetHeight, els[1].offsetHeight);
- },
-
- getRowNaturalHeight: function(index) {
- var contentEl = this.$$('.content [data-index="' + index + '"]');
- return contentEl.naturalHeight || contentEl.offsetHeight;
- },
-
- setRowNaturalHeight: function(index) {
- var lineEl = this.$$('.numbers [data-index="' + index + '"]');
- var contentEl = this.$$('.content [data-index="' + index + '"]');
- contentEl.style.height = null;
- var height = contentEl.offsetHeight;
- lineEl.style.height = height + 'px';
- this.content[index].height = height;
- return height;
- },
-
- setRowHeight: function(index, height) {
- var selector = '[data-index="' + index + '"]';
- var els = Polymer.dom(this.root).querySelectorAll(selector);
- for (var i = 0; i < els.length; i++) {
- els[i].style.height = height + 'px';
- }
- this.content[index].height = height;
- },
-
- _scrollToNextChunkOrThread: function(lineNums) {
- for (var i = 0; i < lineNums.length; i++) {
- if (lineNums[i] > this._focusedLineNum) {
- this._focusedLineNum = lineNums[i];
- this.scrollToLine(this._focusedLineNum);
- return;
- }
- }
- },
-
- _scrollToPreviousChunkOrThread: function(lineNums) {
- for (var i = lineNums.length - 1; i >= 0; i--) {
- if (this._focusedLineNum > lineNums[i]) {
- this._focusedLineNum = lineNums[i];
- this.scrollToLine(this._focusedLineNum);
- return;
- }
- }
- },
-
- _updateJumpIndices: function() {
- this._commentThreadLineNums = [];
- this._diffChunkLineNums = [];
- var inHighlight = false;
- for (var i = 0; i < this.content.length; i++) {
- switch (this.content[i].type) {
- case 'COMMENT_THREAD':
- this._commentThreadLineNums.push(
- this.content[i].comments[0].line);
- break;
- case 'CODE':
- // Only grab the first line of the highlighted chunk.
- if (!inHighlight && this.content[i].highlight) {
- this._diffChunkLineNums.push(this.content[i].lineNum);
- inHighlight = true;
- } else if (!this.content[i].highlight) {
- inHighlight = false;
- }
- break;
- }
- }
- },
-
- _updateDOMIndices: function() {
- // There is no way to select elements with a data-index greater than a
- // given value. For now, just update all DOM elements.
- var lineEls = Polymer.dom(this.root).querySelectorAll(
- '.numbers [data-index]');
- var contentEls = Polymer.dom(this.root).querySelectorAll(
- '.content [data-index]');
- if (lineEls.length != contentEls.length) {
- throw Error(
- 'There must be the same number of line and content elements');
- }
- var index = 0;
- for (var i = 0; i < this.content.length; i++) {
- if (this.content[i].hidden) { continue; }
-
- lineEls[index].setAttribute('data-index', i);
- contentEls[index].setAttribute('data-index', i);
- index++;
- }
- },
-
- _prefsChanged: function(changeRecord) {
- var prefs = changeRecord.base;
- this.$.content.style.width = prefs.line_length + 'ch';
- },
-
- _projectConfigChanged: function(projectConfig) {
- var threadEls =
- Polymer.dom(this.root).querySelectorAll('gr-diff-comment-thread');
- for (var i = 0; i < threadEls.length; i++) {
- threadEls[i].projectConfig = projectConfig;
- }
- },
-
- _contentChanged: function(diff) {
- this._clearChildren(this.$.numbers);
- this._clearChildren(this.$.content);
- this._render(diff, 0, diff.length - 1);
- this._updateJumpIndices();
- },
-
- _computeContainerClass: function(canComment) {
- return 'container' + (canComment ? ' canComment' : '');
- },
-
- _tapHandler: function(e) {
- var lineEl = Polymer.dom(e).rootTarget;
- if (!this.canComment || !lineEl.classList.contains('lineNum')) {
- return;
- }
-
- e.preventDefault();
- var index = parseInt(lineEl.getAttribute('data-index'), 10);
- var line = parseInt(lineEl.getAttribute('data-line-num'), 10);
- this.fire('add-draft', {
- index: index,
- line: line
- }, {bubbles: false});
- },
-
- _clearChildren: function(el) {
- while (el.firstChild) {
- el.removeChild(el.firstChild);
- }
- },
-
- _handleContextControlClick: function(context, e) {
- e.preventDefault();
- this.fire('expand-context', {context: context}, {bubbles: false});
- },
-
- _render: function(diff, startIndex, endIndex) {
- var beforeLineEl;
- var beforeContentEl;
- if (endIndex != diff.length - 1) {
- beforeLineEl = this.$$('.numbers [data-index="' + endIndex + '"]');
- beforeContentEl = this.$$('.content [data-index="' + endIndex + '"]');
- if (!beforeLineEl && !beforeContentEl) {
- // `endIndex` may be present within the model, but not in the DOM.
- // Insert it before its successive element.
- beforeLineEl = this.$$(
- '.numbers [data-index="' + (endIndex + 1) + '"]');
- beforeContentEl = this.$$(
- '.content [data-index="' + (endIndex + 1) + '"]');
- }
- }
-
- for (var i = startIndex; i <= endIndex; i++) {
- if (diff[i].hidden) { continue; }
-
- switch (diff[i].type) {
- case 'CODE':
- this._renderCode(diff[i], i, beforeLineEl, beforeContentEl);
- break;
- case 'FILLER':
- this._renderFiller(diff[i], i, beforeLineEl, beforeContentEl);
- break;
- case 'CONTEXT_CONTROL':
- this._renderContextControl(diff[i], i, beforeLineEl,
- beforeContentEl);
- break;
- case 'COMMENT_THREAD':
- this._renderCommentThread(diff[i], i, beforeLineEl,
- beforeContentEl);
- break;
- }
- }
- },
-
- _handleCommentThreadHeightChange: function(e) {
- var threadEl = Polymer.dom(e).rootTarget;
- var index = parseInt(threadEl.getAttribute('data-index'), 10);
- this.content[index].height = e.detail.height;
- var lineEl = this.$$('.numbers [data-index="' + index + '"]');
- lineEl.style.height = e.detail.height + 'px';
- this.fire('thread-height-change', {
- index: index,
- height: e.detail.height,
- }, {bubbles: false});
- },
-
- _handleCommentThreadDiscard: function(e) {
- var threadEl = Polymer.dom(e).rootTarget;
- var index = parseInt(threadEl.getAttribute('data-index'), 10);
- this.fire('remove-thread', {index: index}, {bubbles: false});
- },
-
- _renderCommentThread: function(thread, index, beforeLineEl,
- beforeContentEl) {
- var lineEl = this._createElement('div', 'commentThread');
- lineEl.classList.add('filler');
- lineEl.setAttribute('data-index', index);
- var threadEl = document.createElement('gr-diff-comment-thread');
- threadEl.addEventListener('height-change',
- this._handleCommentThreadHeightChange.bind(this));
- threadEl.addEventListener('thread-discard',
- this._handleCommentThreadDiscard.bind(this));
- threadEl.setAttribute('data-index', index);
- threadEl.changeNum = this.changeNum;
- threadEl.patchNum = thread.patchNum || this.patchNum;
- threadEl.path = this.path;
- threadEl.comments = thread.comments;
- threadEl.projectConfig = this.projectConfig;
-
- this.$.numbers.insertBefore(lineEl, beforeLineEl);
- this.$.content.insertBefore(threadEl, beforeContentEl);
- },
-
- _renderContextControl: function(control, index, beforeLineEl,
- beforeContentEl) {
- var lineEl = this._createElement('div', 'contextControl');
- lineEl.setAttribute('data-index', index);
- lineEl.textContent = '@@';
- var contentEl = this._createElement('div', 'contextControl');
- contentEl.setAttribute('data-index', index);
- var a = this._createElement('a');
- a.href = '#';
- a.textContent = 'Show ' + control.numLines + ' common ' +
- (control.numLines == 1 ? 'line' : 'lines') + '...';
- a.addEventListener('click',
- this._handleContextControlClick.bind(this, control));
- contentEl.appendChild(a);
-
- this.$.numbers.insertBefore(lineEl, beforeLineEl);
- this.$.content.insertBefore(contentEl, beforeContentEl);
- },
-
- _renderFiller: function(filler, index, beforeLineEl, beforeContentEl) {
- var lineFillerEl = this._createElement('div', 'filler');
- lineFillerEl.setAttribute('data-index', index);
- var fillerEl = this._createElement('div', 'filler');
- fillerEl.setAttribute('data-index', index);
- var numLines = filler.numLines || 1;
-
- lineFillerEl.textContent = '\n'.repeat(numLines);
- for (var i = 0; i < numLines; i++) {
- var newlineEl = this._createElement('span', 'br');
- fillerEl.appendChild(newlineEl);
- }
-
- this.$.numbers.insertBefore(lineFillerEl, beforeLineEl);
- this.$.content.insertBefore(fillerEl, beforeContentEl);
- },
-
- _renderCode: function(code, index, beforeLineEl, beforeContentEl) {
- var lineNumEl = this._createElement('div', 'lineNum');
- lineNumEl.setAttribute('data-line-num', code.lineNum);
- lineNumEl.setAttribute('data-index', index);
- var numLines = code.numLines || 1;
- lineNumEl.textContent = code.lineNum + '\n'.repeat(numLines);
-
- var contentEl = this._createElement('div', 'code');
- contentEl.setAttribute('data-line-num', code.lineNum);
- contentEl.setAttribute('data-index', index);
-
- if (code.highlight) {
- contentEl.classList.add(code.intraline.length > 0 ?
- 'lightHighlight' : 'darkHighlight');
- }
-
- var html = util.escapeHTML(code.content);
- if (code.highlight && code.intraline.length > 0) {
- html = this._addIntralineHighlights(code.content, html,
- code.intraline);
- }
- if (numLines > 1) {
- html = this._addNewLines(code.content, html, numLines);
- }
- html = this._addTabWrappers(code.content, html);
-
- // If the html is equivalent to the text then it didn't get highlighted
- // or escaped. Use textContent which is faster than innerHTML.
- if (code.content == html) {
- contentEl.textContent = code.content;
- } else {
- contentEl.innerHTML = html;
- }
-
- this.$.numbers.insertBefore(lineNumEl, beforeLineEl);
- this.$.content.insertBefore(contentEl, beforeContentEl);
- },
-
- // Advance `index` by the appropriate number of characters that would
- // represent one source code character and return that index. For
- // example, for source code '<span>' the escaped html string is
- // '<span>'. Advancing from index 0 on the prior html string would
- // return 4, since < maps to one source code character ('<').
- _advanceChar: function(html, index) {
- // Any tags don't count as characters
- while (index < html.length &&
- html.charCodeAt(index) == CharCode.LESS_THAN) {
- while (index < html.length &&
- html.charCodeAt(index) != CharCode.GREATER_THAN) {
- index++;
- }
- index++; // skip the ">" itself
- }
- // An HTML entity (e.g., <) counts as one character.
- if (index < html.length &&
- html.charCodeAt(index) == CharCode.AMPERSAND) {
- while (index < html.length &&
- html.charCodeAt(index) != CharCode.SEMICOLON) {
- index++;
- }
- }
- return index + 1;
- },
-
- _addIntralineHighlights: function(content, html, highlights) {
- var startTag = this._highlightStartTag;
- var endTag = this._highlightEndTag;
-
- for (var i = 0; i < highlights.length; i++) {
- var hl = highlights[i];
-
- var htmlStartIndex = 0;
- for (var j = 0; j < hl.startIndex; j++) {
- htmlStartIndex = this._advanceChar(html, htmlStartIndex);
- }
-
- var htmlEndIndex = 0;
- if (hl.endIndex != null) {
- for (var j = 0; j < hl.endIndex; j++) {
- htmlEndIndex = this._advanceChar(html, htmlEndIndex);
- }
- } else {
- // If endIndex isn't present, continue to the end of the line.
- htmlEndIndex = html.length;
- }
- // The start and end indices could be the same if a highlight is meant
- // to start at the end of a line and continue onto the next one.
- // Ignore it.
- if (htmlStartIndex != htmlEndIndex) {
- html = html.slice(0, htmlStartIndex) + startTag +
- html.slice(htmlStartIndex, htmlEndIndex) + endTag +
- html.slice(htmlEndIndex);
- }
- }
- return html;
- },
-
- _addNewLines: function(content, html, numLines) {
- var htmlIndex = 0;
- var indices = [];
- var numChars = 0;
- for (var i = 0; i < content.length; i++) {
- if (numChars > 0 && numChars % this.prefs.line_length == 0) {
- indices.push(htmlIndex);
- }
- htmlIndex = this._advanceChar(html, htmlIndex);
- if (content[i] == '\t') {
- numChars += this.prefs.tab_size;
- } else {
- numChars++;
- }
- }
- var result = html;
- var linesLeft = numLines;
- // Since the result string is being altered in place, start from the end
- // of the string so that the insertion indices are not affected as the
- // result string changes.
- for (var i = indices.length - 1; i >= 0; i--) {
- result = result.slice(0, indices[i]) + this._lineFeedHTML +
- result.slice(indices[i]);
- linesLeft--;
- }
- // numLines is the total number of lines this code block should take up.
- // Fill in the remaining ones.
- for (var i = 0; i < linesLeft; i++) {
- result += this._lineFeedHTML;
- }
- return result;
- },
-
- _addTabWrappers: function(content, html) {
- // TODO(andybons): CSS tab-size is not supported in IE.
- // Force this to be a number to prevent arbitrary injection.
- var tabSize = +this.prefs.tab_size;
- var htmlStr = '<span class="style-scope gr-diff-side tab ' +
- (this.prefs.show_tabs ? 'withIndicator" ' : '" ') +
- 'style="tab-size:' + tabSize + ';' +
- '-moz-tab-size:' + tabSize + ';">\t</span>';
- return html.replace(TAB_REGEX, htmlStr);
- },
-
- _createElement: function(tagName, className) {
- var el = document.createElement(tagName);
- // When Shady DOM is being used, these classes are added to account for
- // Polymer's polyfill behavior. In order to guarantee sufficient
- // specificity within the CSS rules, these are added to every element.
- // Since the Polymer DOM utility functions (which would do this
- // automatically) are not being used for performance reasons, this is
- // done manually.
- el.classList.add('style-scope', 'gr-diff-side');
- if (!!className) {
- el.classList.add(className);
- }
- return el;
- },
- });
-})();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-side/gr-diff-side_test.html b/polygerrit-ui/app/elements/diff/gr-diff-side/gr-diff-side_test.html
deleted file mode 100644
index dbae6cb..0000000
--- a/polygerrit-ui/app/elements/diff/gr-diff-side/gr-diff-side_test.html
+++ /dev/null
@@ -1,300 +0,0 @@
-<!DOCTYPE html>
-<!--
-Copyright (C) 2015 The Android Open Source Project
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
--->
-
-<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
-<title>gr-diff-side</title>
-
-<script src="../../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
-<script src="../../../scripts/util.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
-<link rel="import" href="gr-diff-side.html">
-
-<test-fixture id="basic">
- <template>
- <gr-diff-side></gr-diff-side>
- </template>
-</test-fixture>
-
-<script>
- suite('gr-diff-side tests', function() {
- var element;
-
- function isVisibleInWindow(el) {
- var rect = el.getBoundingClientRect();
- return rect.top >= 0 && rect.left >= 0 &&
- rect.bottom <= window.innerHeight && rect.right <= window.innerWidth;
- }
-
- setup(function() {
- element = fixture('basic');
- });
-
- test('comments', function() {
- assert.isFalse(element.$$('.container').classList.contains('canComment'));
- element.canComment = true;
- assert.isTrue(element.$$('.container').classList.contains('canComment'));
- // TODO(andybons): Check for comment creation events firing/not firing
- // when implemented.
- });
-
- test('scroll to line', function() {
- var content = [];
- for (var i = 0; i < 300; i++) {
- content.push({
- type: 'CODE',
- content: 'All work and no play makes Jack a dull boy',
- numLines: 1,
- lineNum: i + 1,
- highlight: false,
- intraline: [],
- });
- }
- element.content = content;
-
- window.scrollTo(0, 0);
- element.scrollToLine(-12849);
- assert.equal(window.scrollY, 0);
- element.scrollToLine('sup');
- assert.equal(window.scrollY, 0);
- var lineEl = element.$$('.numbers .lineNum[data-line-num="150"]');
- assert.ok(lineEl);
- element.scrollToLine(150);
- assert.isAbove(window.scrollY, 0);
- assert.isTrue(isVisibleInWindow(lineEl), 'element should be visible');
- });
-
- test('intraline highlights', function() {
- var content = ' <gr-linked-text content="' +
- '[[_computeCurrentRevisionMessage(change)]]"></gr-linked-text>';
- var html = util.escapeHTML(content);
- var highlights = [
- {startIndex: 0, endIndex: 33},
- {startIndex: 75},
- ];
- assert.equal(
- content.slice(highlights[0].startIndex, highlights[0].endIndex),
- ' <gr-linked-text content="');
- assert.equal(content.slice(highlights[1].startIndex),
- '"></gr-linked-text>');
- var result = element._addIntralineHighlights(content, html, highlights);
- var expected = element._highlightStartTag +
- ' <gr-linked-text content="' +
- element._highlightEndTag +
- '[[_computeCurrentRevisionMessage(change)]]' +
- element._highlightStartTag +
- '"></gr-linked-text>' +
- element._highlightEndTag;
- assert.equal(result, expected);
- });
-
- test('newlines', function() {
- element.prefs = {
- line_length: 80,
- tab_size: 4,
- };
-
- element.content = [{
- type: 'CODE',
- content: 'A'.repeat(50),
- numLines: 1,
- lineNum: 1,
- highlight: false,
- intraline: [],
- }];
-
- var lineEl = element.$$('.numbers .lineNum[data-line-num="1"]');
- assert.ok(lineEl);
- var contentEl = element.$$('.content .code[data-line-num="1"]');
- assert.ok(contentEl);
- assert.equal(contentEl.innerHTML, 'A'.repeat(50));
-
- element.content = [{
- type: 'CODE',
- content: 'A'.repeat(100),
- numLines: 2,
- lineNum: 1,
- highlight: false,
- intraline: [],
- }];
-
- lineEl = element.$$('.numbers .lineNum[data-line-num="1"]');
- assert.ok(lineEl);
- contentEl = element.$$('.content .code[data-line-num="1"]');
- assert.ok(contentEl);
- assert.equal(contentEl.innerHTML,
- 'A'.repeat(80) + element._lineFeedHTML +
- 'A'.repeat(20) + element._lineFeedHTML);
- });
-
- test('tabs', function(done) {
- element.prefs = {
- line_length: 80,
- tab_size: 4,
- show_tabs: true,
- };
-
- element.content = [{
- type: 'CODE',
- content: 'A'.repeat(50) + '\t' + 'A'.repeat(50),
- numLines: 2,
- lineNum: 1,
- highlight: false,
- intraline: [],
- }];
-
- var lineEl = element.$$('.numbers .lineNum[data-line-num="1"]');
- assert.ok(lineEl);
- var contentEl = element.$$('.content .code[data-line-num="1"]');
- assert.ok(contentEl);
- var spanEl = contentEl.childNodes[1];
- assert.equal(spanEl.tagName, 'SPAN');
- assert.isTrue(spanEl.classList.contains(
- 'style-scope', 'gr-diff-side', 'tab', 'withIndicator'));
-
- element.prefs.show_tabs = false;
- element.content = [{
- type: 'CODE',
- content: 'A'.repeat(50) + '\t' + 'A'.repeat(50),
- numLines: 2,
- lineNum: 1,
- highlight: false,
- intraline: [],
- }];
- contentEl = element.$$('.content .code[data-line-num="1"]');
- assert.ok(contentEl);
- spanEl = contentEl.childNodes[1];
- assert.equal(spanEl.tagName, 'SPAN');
- assert.isTrue(spanEl.classList.contains(
- 'style-scope', 'gr-diff-side', 'tab'));
-
- var alertStub = sinon.stub(window, 'alert');
- element.prefs.tab_size =
- '"><img src="/" onerror="alert(1);"><span class="';
- element.content = [{
- type: 'CODE',
- content: '\t',
- numLines: 1,
- lineNum: 1,
- highlight: false,
- intraline: [],
- }];
- flush(function() {
- assert.isFalse(alertStub.called);
- alertStub.restore();
- done();
- });
- });
-
- test('diff context', function() {
- var content = [
- {type: 'CODE', hidden: true, content: '<!DOCTYPE html>'},
- {type: 'CODE', hidden: true, content: '<meta charset="utf-8">'},
- {type: 'CODE', hidden: true, content: '<title>My great page</title>'},
- {type: 'CODE', hidden: true, content: '<style>'},
- {type: 'CODE', hidden: true, content: ' *,'},
- {type: 'CODE', hidden: true, content: ' *:before,'},
- {type: 'CODE', hidden: true, content: ' *:after {'},
- {type: 'CODE', hidden: true, content: ' box-sizing: border-box;'},
- {type: 'CONTEXT_CONTROL', numLines: 8, start: 0, end: 8},
- {type: 'CODE', hidden: false, content: ' }'},
- ];
- element.content = content;
-
- // Only the context elements and the following code line elements should
- // be present in the DOM.
- var contextEls =
- Polymer.dom(element.root).querySelectorAll('.contextControl');
- assert.equal(contextEls.length, 2);
- var codeLineEls =
- Polymer.dom(element.root).querySelectorAll('.lineNum, .code');
- assert.equal(codeLineEls.length, 2);
-
- for (var i = 0; i <= 8; i++) {
- element.content[i].hidden = false;
- }
- element.renderLineIndexRange(0, 8);
- element.hideElementsWithIndex(8);
-
- contextEls =
- Polymer.dom(element.root).querySelectorAll('.contextControl');
- for (var i = 0; i < contextEls.length; i++) {
- assert.isTrue(contextEls[i].hasAttribute('hidden'));
- }
-
- codeLineEls =
- Polymer.dom(element.root).querySelectorAll('.lineNum, .code');
-
- // Nine lines should now be present in the DOM.
- assert.equal(codeLineEls.length, 9 * 2);
- });
-
- test('tap line to add a draft', function() {
- var numAddDraftEvents = 0;
- sinon.stub(element, 'fire', function(eventName) {
- if (eventName == 'add-draft') {
- numAddDraftEvents++;
- }
- });
- element.content = [{type: 'CODE', content: '<!DOCTYPE html>'}];
- element.canComment = false;
- flushAsynchronousOperations();
-
- var lineEl = element.$$('.lineNum');
- assert.ok(lineEl);
- MockInteractions.tap(lineEl);
- assert.equal(numAddDraftEvents, 0);
-
- element.canComment = true;
- MockInteractions.tap(lineEl);
- assert.equal(numAddDraftEvents, 1);
- });
-
- test('jump to diff chunk/thread', function() {
- element.content = [
- {type: 'CODE', content: '', intraline: [], lineNum: 1, highlight: true},
- {type: 'CODE', content: '', intraline: [], lineNum: 2, highlight: true},
- {type: 'CODE', content: '', intraline: [], lineNum: 3 },
- {type: 'CODE', content: '', intraline: [], lineNum: 4 },
- {type: 'COMMENT_THREAD', comments: [ { line: 4 }]},
- {type: 'CODE', content: '', intraline: [], lineNum: 5 },
- {type: 'CODE', content: '', intraline: [], lineNum: 6, highlight: true},
- {type: 'CODE', content: '', intraline: [], lineNum: 7, highlight: true},
- {type: 'CODE', content: '', intraline: [], lineNum: 8 },
- {type: 'COMMENT_THREAD', comments: [ { line: 8 }]},
- {type: 'CODE', content: '', intraline: [], lineNum: 9 },
- {type: 'CODE', content: '', intraline: [], lineNum: 10,
- highlight: true},
- ];
-
- var scrollToLineStub = sinon.stub(element, 'scrollToLine');
- element.scrollToNextDiffChunk();
- assert.isTrue(scrollToLineStub.lastCall.calledWithExactly(6));
- element.scrollToPreviousDiffChunk();
- assert.isTrue(scrollToLineStub.lastCall.calledWithExactly(1));
- element.scrollToNextCommentThread();
- assert.isTrue(scrollToLineStub.lastCall.calledWithExactly(4));
- element.scrollToNextCommentThread();
- assert.isTrue(scrollToLineStub.lastCall.calledWithExactly(8));
- element.scrollToPreviousDiffChunk();
- assert.isTrue(scrollToLineStub.lastCall.calledWithExactly(6));
-
- scrollToLineStub.restore();
- });
- });
-</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html
index 0dc18ae..e7ec202 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html
@@ -162,7 +162,6 @@
</h3>
<gr-diff id="diff"
change-num="[[_changeNum]]"
- prefs="{{prefs}}"
patch-range="[[_patchRange]]"
path="[[_path]]"
project-config="[[_projectConfig]]"
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
index 557c213..190a286 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
@@ -26,10 +26,6 @@
*/
properties: {
- prefs: {
- type: Object,
- notify: true,
- },
/**
* URL params passed from the router.
*/
diff --git a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-builder-side-by-side.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-builder-side-by-side.js
similarity index 100%
rename from polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-builder-side-by-side.js
rename to polygerrit-ui/app/elements/diff/gr-diff/gr-diff-builder-side-by-side.js
diff --git a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-builder-unified.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-builder-unified.js
similarity index 100%
rename from polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-builder-unified.js
rename to polygerrit-ui/app/elements/diff/gr-diff/gr-diff-builder-unified.js
diff --git a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-builder.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-builder.js
similarity index 98%
rename from polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-builder.js
rename to polygerrit-ui/app/elements/diff/gr-diff/gr-diff-builder.js
index e49a0a3..338845e 100644
--- a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-builder.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-builder.js
@@ -32,7 +32,7 @@
GrDiffBuilder.TAB_REGEX = /\t/g;
GrDiffBuilder.LINE_FEED_HTML =
- '<span class="style-scope gr-new-diff br"></span>';
+ '<span class="style-scope gr-diff br"></span>';
GrDiffBuilder.GroupType = {
ADDED: 'b',
@@ -509,7 +509,7 @@
GrDiffBuilder.prototype._addIntralineHighlights = function(content, html,
highlights) {
- var START_TAG = '<hl class="style-scope gr-new-diff">';
+ var START_TAG = '<hl class="style-scope gr-diff">';
var END_TAG = '</hl>';
for (var i = 0; i < highlights.length; i++) {
@@ -549,7 +549,7 @@
throw Error('Invalid tab size from preferences.');
}
- var str = '<span class="style-scope gr-new-diff tab ';
+ var str = '<span class="style-scope gr-diff tab ';
if (showTabs) {
str += 'withIndicator';
}
@@ -569,7 +569,7 @@
// Since the Polymer DOM utility functions (which would do this
// automatically) are not being used for performance reasons, this is
// done manually.
- el.classList.add('style-scope', 'gr-new-diff');
+ el.classList.add('style-scope', 'gr-diff');
if (!!className) {
el.classList.add(className);
}
diff --git a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-builder_test.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-builder_test.html
similarity index 100%
rename from polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-builder_test.html
rename to polygerrit-ui/app/elements/diff/gr-diff/gr-diff-builder_test.html
diff --git a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-group.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group.js
similarity index 100%
rename from polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-group.js
rename to polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group.js
diff --git a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-group_test.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group_test.html
similarity index 100%
rename from polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-group_test.html
rename to polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group_test.html
diff --git a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-line.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-line.js
similarity index 100%
rename from polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-line.js
rename to polygerrit-ui/app/elements/diff/gr-diff/gr-diff-line.js
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
index a6d92a2..e0306ad 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
@@ -20,13 +20,19 @@
<link rel="import" href="../../shared/gr-request/gr-request.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
+<link rel="import" href="../gr-diff-comment-thread/gr-diff-comment-thread.html">
<link rel="import" href="../gr-diff-preferences/gr-diff-preferences.html">
-<link rel="import" href="../gr-diff-side/gr-diff-side.html">
<link rel="import" href="../gr-patch-range-select/gr-patch-range-select.html">
<dom-module id="gr-diff">
<template>
<style>
+ :host {
+ --light-remove-highlight-color: #fee;
+ --dark-remove-highlight-color: #ffd4d4;
+ --light-add-highlight-color: #efe;
+ --dark-add-highlight-color: #d4ffd4;
+ }
.loading {
padding: 0 var(--default-horizontal-margin) 1em;
color: #666;
@@ -45,16 +51,99 @@
display: flex;
font: 12px var(--monospace-font-family);
overflow-x: auto;
+ will-change: transform;
}
- gr-diff-side:first-of-type {
- --light-highlight-color: #fee;
- --dark-highlight-color: #ffd4d4;
- }
- gr-diff-side:last-of-type {
- --light-highlight-color: #efe;
- --dark-highlight-color: #d4ffd4;
+ table {
+ border-collapse: collapse;
border-right: 1px solid #ddd;
}
+ .section {
+ background-color: #eee;
+ }
+ .blank,
+ .content {
+ background-color: #fff;
+ }
+ .lineNum,
+ .content {
+ vertical-align: top;
+ white-space: pre;
+ }
+ .contextLineNum:before,
+ .lineNum:before {
+ display: inline-block;
+ color: #666;
+ content: attr(data-value);
+ padding: 0 .75em;
+ text-align: right;
+ width: 100%;
+ }
+ .canComment .lineNum[data-value] {
+ cursor: pointer;
+ }
+ .canComment .lineNum[data-value]:before {
+ text-decoration: underline;
+ }
+ .canComment .lineNum[data-value]:hover:before {
+ background-color: #ccc;
+ }
+ .canComment .lineNum[data-value="FILE"]:before {
+ content: 'File';
+ }
+ .content {
+ overflow: hidden;
+ min-width: var(--content-width, 80ch);
+ }
+ .content.left {
+ -webkit-user-select: var(--left-user-select, text);
+ -moz-user-select: var(--left-user-select, text);
+ -ms-user-select: var(--left-user-select, text);
+ user-select: var(--left-user-select, text);
+ }
+ .content.right {
+ -webkit-user-select: var(--right-user-select, text);
+ -moz-user-select: var(--right-user-select, text);
+ -ms-user-select: var(--right-user-select, text);
+ user-select: var(--right-user-select, text);
+ }
+ .content.add hl,
+ .content.add.darkHighlight {
+ background-color: var(--dark-add-highlight-color);
+ }
+ .content.add.lightHighlight {
+ background-color: var(--light-add-highlight-color);
+ }
+ .content.remove hl,
+ .content.remove.darkHighlight {
+ background-color: var(--dark-remove-highlight-color);
+ }
+ .content.remove.lightHighlight {
+ background-color: var(--light-remove-highlight-color);
+ }
+ .contextControl {
+ color: #849;
+ background-color: #fef;
+ }
+ .contextControl gr-button {
+ display: block;
+ font-family: var(--monospace-font-family);
+ text-decoration: none;
+ }
+ .contextControl td:not(.lineNum) {
+ text-align: center;
+ }
+ .br:after {
+ /* Line feed */
+ content: '\A';
+ }
+ .tab {
+ display: inline-block;
+ }
+ .tab.withIndicator:before {
+ color: #C62828;
+ /* >> character */
+ content: '\00BB';
+ }
</style>
<div class="loading" hidden$="[[!_loading]]">Loading...</div>
<div hidden$="[[_loading]]" hidden>
@@ -67,44 +156,29 @@
<gr-button link
class="prefsButton"
on-tap="_handlePrefsTap"
- hidden$="[[!prefs]]"
+ hidden$="[[_computePrefsButtonHidden(_prefs, _loggedIn)]]"
hidden>Diff View Preferences</gr-button>
</div>
<gr-overlay id="prefsOverlay" with-backdrop>
<gr-diff-preferences
- prefs="{{prefs}}"
+ prefs="{{_prefs}}"
on-save="_handlePrefsSave"
on-cancel="_handlePrefsCancel"></gr-diff-preferences>
</gr-overlay>
- <div class="diffContainer">
- <gr-diff-side id="leftDiff"
- change-num="[[changeNum]]"
- patch-num="[[patchRange.basePatchNum]]"
- path="[[path]]"
- content="{{_diff.leftSide}}"
- prefs="[[prefs]]"
- can-comment="[[_loggedIn]]"
- project-config="[[projectConfig]]"
- on-expand-context="_handleExpandContext"
- on-thread-height-change="_handleThreadHeightChange"
- on-add-draft="_handleAddDraft"
- on-remove-thread="_handleRemoveThread"></gr-diff-side>
- <gr-diff-side id="rightDiff"
- change-num="[[changeNum]]"
- patch-num="[[patchRange.patchNum]]"
- path="[[path]]"
- content="{{_diff.rightSide}}"
- prefs="[[prefs]]"
- can-comment="[[_loggedIn]]"
- project-config="[[projectConfig]]"
- on-expand-context="_handleExpandContext"
- on-thread-height-change="_handleThreadHeightChange"
- on-add-draft="_handleAddDraft"
- on-remove-thread="_handleRemoveThread"></gr-diff-side>
+ <div class$="[[_computeContainerClass(_loggedIn, _viewMode)]]"
+ on-tap="_handleTap"
+ on-mousedown="_handleMouseDown"
+ on-copy="_handleCopy">
+ <table id="diffTable"></table>
</div>
</div>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
</template>
+ <script src="gr-diff-line.js"></script>
+ <script src="gr-diff-group.js"></script>
+ <script src="gr-diff-builder.js"></script>
+ <script src="gr-diff-builder-side-by-side.js"></script>
+ <script src="gr-diff-builder-unified.js"></script>
<script src="gr-diff.js"></script>
</dom-module>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
index cac1f3e..12ceacb 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
@@ -14,6 +14,16 @@
(function() {
'use strict';
+ var DiffViewMode = {
+ SIDE_BY_SIDE: 'SIDE_BY_SIDE',
+ UNIFIED: 'UNIFIED_DIFF',
+ };
+
+ var DiffSide = {
+ LEFT: 'left',
+ RIGHT: 'right',
+ };
+
Polymer({
is: 'gr-diff',
@@ -26,148 +36,379 @@
properties: {
availablePatches: Array,
changeNum: String,
- /*
- * A single object to encompass basePatchNum and patchNum is used
- * so that both can be set at once without incremental observers
- * firing after each property changes.
- */
patchRange: Object,
path: String,
- prefs: {
- type: Object,
- notify: true,
- },
- projectConfig: Object,
- _prefsReady: {
+ projectConfig: {
type: Object,
- readOnly: true,
- value: function() {
- return new Promise(function(resolve) {
- this._resolvePrefsReady = resolve;
- }.bind(this));
- },
+ observer: '_projectConfigChanged',
},
- _baseComments: Array,
- _comments: Array,
- _drafts: Array,
- _baseDrafts: Array,
- /**
- * Base (left side) comments and drafts grouped by line number.
- * Only used for initial rendering.
- */
- _groupedBaseComments: {
- type: Object,
- value: function() { return {}; },
- },
- /**
- * Comments and drafts (right side) grouped by line number.
- * Only used for initial rendering.
- */
- _groupedComments: {
- type: Object,
- value: function() { return {}; },
- },
- _diffResponse: Object,
- _diff: {
- type: Object,
- value: function() { return {}; },
- },
+
_loggedIn: {
type: Boolean,
value: false,
},
- _initialRenderComplete: {
- type: Boolean,
- value: false,
- },
_loading: {
type: Boolean,
value: true,
},
- _savedPrefs: Object,
-
- _diffPreferencesPromise: Object, // Used for testing.
+ _viewMode: {
+ type: String,
+ value: DiffViewMode.SIDE_BY_SIDE,
+ },
+ _diff: Object,
+ _diffBuilder: Object,
+ _prefs: Object,
+ _selectionSide: {
+ type: String,
+ observer: '_selectionSideChanged',
+ },
+ _comments: Object,
+ _focusedSection: {
+ type: Number,
+ value: -1,
+ },
+ _focusedThread: {
+ type: Number,
+ value: -1,
+ },
},
observers: [
- '_prefsChanged(prefs.*)',
+ '_prefsChanged(_prefs.*)',
],
- ready: function() {
- app.accountReady.then(function() {
- this._loggedIn = app.loggedIn;
+ attached: function() {
+ this._getLoggedIn().then(function(loggedIn) {
+ this._loggedIn = loggedIn;
}.bind(this));
- },
- scrollToLine: function(lineNum) {
- // TODO(andybons): Should this always be the right side?
- this.$.rightDiff.scrollToLine(lineNum);
- },
-
- scrollToNextDiffChunk: function() {
- this.$.rightDiff.scrollToNextDiffChunk();
- },
-
- scrollToPreviousDiffChunk: function() {
- this.$.rightDiff.scrollToPreviousDiffChunk();
- },
-
- scrollToNextCommentThread: function() {
- this.$.rightDiff.scrollToNextCommentThread();
- },
-
- scrollToPreviousCommentThread: function() {
- this.$.rightDiff.scrollToPreviousCommentThread();
+ this.addEventListener('thread-discard',
+ this._handleThreadDiscard.bind(this));
+ this.addEventListener('comment-discard',
+ this._handleCommentDiscard.bind(this));
},
reload: function() {
+ this._clearDiffContent();
this._loading = true;
- // If a diff takes a considerable amount of time to render, the previous
- // diff can end up showing up while the DOM is constructed. Clear the
- // content on a reload to prevent this.
- this._diff = {
- leftSide: [],
- rightSide: [],
- };
- var diffLoaded = this._getDiff().then(function(diff) {
- this._diffResponse = diff;
+ var promises = [];
+
+ promises.push(this._getDiff().then(function(diff) {
+ this._diff = diff;
+ this._loading = false;
+ }.bind(this)));
+
+ promises.push(this._getDiffCommentsAndDrafts().then(function(comments) {
+ this._comments = comments;
+ }.bind(this)));
+
+ promises.push(this._getDiffPreferences().then(function(prefs) {
+ this._prefs = prefs;
+ }.bind(this)));
+
+ return Promise.all(promises).then(function() {
+ this._render();
}.bind(this));
+ },
- var promises = [
- this._prefsReady,
- diffLoaded,
- ];
+ showDiffPreferences: function() {
+ this.$.prefsOverlay.open();
+ },
- return app.accountReady.then(function() {
- promises.push(this._getDiffComments().then(function(res) {
- this._baseComments = res.baseComments;
- this._comments = res.comments;
- }.bind(this)));
+ scrollToLine: function(lineNum) {
+ if (isNaN(lineNum) || lineNum < 1) { return; }
- if (!app.loggedIn) {
- this._baseDrafts = [];
- this._drafts = [];
- } else {
- promises.push(this._getDiffDrafts().then(function(res) {
- this._baseDrafts = res.baseComments;
- this._drafts = res.comments;
- }.bind(this)));
- }
+ var lineEls = Polymer.dom(this.root).querySelectorAll(
+ '.lineNum[data-value="' + lineNum + '"]');
- return Promise.all(promises).then(function() {
- this._render();
- this._loading = false;
- }.bind(this)).catch(function(err) {
- this._loading = false;
- alert('Oops. Something went wrong. Check the console and bug the ' +
+ // Always choose the right side.
+ var el = lineEls.length === 2 ? lineEls[1] : lineEls[0];
+ this._scrollToElement(el);
+ },
+
+ scrollToNextDiffChunk: function() {
+ this._focusedSection = this._advanceElementWithinNodeList(
+ this._getDeltaSections(), this._focusedSection, 1);
+ },
+
+ scrollToPreviousDiffChunk: function() {
+ this._focusedSection = this._advanceElementWithinNodeList(
+ this._getDeltaSections(), this._focusedSection, -1);
+ },
+
+ scrollToNextCommentThread: function() {
+ this._focusedThread = this._advanceElementWithinNodeList(
+ this._getCommentThreads(), this._focusedThread, 1);
+ },
+
+ scrollToPreviousCommentThread: function() {
+ this._focusedThread = this._advanceElementWithinNodeList(
+ this._getCommentThreads(), this._focusedThread, -1);
+ },
+
+ _advanceElementWithinNodeList: function(els, curIndex, direction) {
+ var idx = Math.max(0, Math.min(els.length - 1, curIndex + direction));
+ if (curIndex !== idx) {
+ this._scrollToElement(els[idx]);
+ return idx;
+ }
+ return curIndex;
+ },
+
+ _getCommentThreads: function() {
+ return Polymer.dom(this.root).querySelectorAll('gr-diff-comment-thread');
+ },
+
+ _getDeltaSections: function() {
+ return Polymer.dom(this.root).querySelectorAll('.section.delta');
+ },
+
+ _scrollToElement: function(el) {
+ if (!el) { return; }
+
+ // Calculate where the element is relative to the window.
+ var top = el.offsetTop;
+ for (var offsetParent = el.offsetParent;
+ offsetParent;
+ offsetParent = offsetParent.offsetParent) {
+ top += offsetParent.offsetTop;
+ }
+
+ // Scroll the element to the middle of the window. Dividing by a third
+ // instead of half the inner height feels a bit better otherwise the
+ // element appears to be below the center of the window even when it
+ // isn't.
+ window.scrollTo(0, top - (window.innerHeight / 3) +
+ (el.offsetHeight / 2));
+ },
+
+ _computeContainerClass: function(loggedIn, viewMode) {
+ var classes = ['diffContainer'];
+ switch (viewMode) {
+ case DiffViewMode.UNIFIED:
+ classes.push('unified');
+ break;
+ case DiffViewMode.SIDE_BY_SIDE:
+ classes.push('sideBySide');
+ break
+ default:
+ throw Error('Invalid view mode: ', viewMode);
+ }
+ if (loggedIn) {
+ classes.push('canComment');
+ }
+ return classes.join(' ');
+ },
+
+ _computePrefsButtonHidden: function(prefs, loggedIn) {
+ return !loggedIn || !prefs;
+ },
+
+ _handlePrefsTap: function(e) {
+ e.preventDefault();
+ this.$.prefsOverlay.open();
+ },
+
+ _handlePrefsSave: function(e) {
+ e.stopPropagation();
+ var el = Polymer.dom(e).rootTarget;
+ el.disabled = true;
+ this._saveDiffPreferences().then(function() {
+ this.$.prefsOverlay.close();
+ el.disabled = false;
+ }.bind(this)).catch(function(err) {
+ el.disabled = false;
+ alert('Oops. Something went wrong. Check the console and bug the ' +
'PolyGerrit team for assistance.');
- throw err;
- }.bind(this));
+ throw err;
+ });
+ },
+
+ _saveDiffPreferences: function() {
+ return this.$.restAPI.saveDiffPreferences(this._prefs);
+ },
+
+ _handlePrefsCancel: function(e) {
+ e.stopPropagation();
+ this.$.prefsOverlay.close();
+ },
+
+ _handleTap: function(e) {
+ var el = Polymer.dom(e).rootTarget;
+
+ if (el.classList.contains('showContext')) {
+ this._showContext(e.detail.group, e.detail.section);
+ } else if (el.classList.contains('lineNum')) {
+ this._handleLineTap(el);
+ }
+ },
+
+ _handleLineTap: function(el) {
+ this._getLoggedIn().then(function(loggedIn) {
+ if (!loggedIn) { return; }
+
+ var value = el.getAttribute('data-value');
+ if (value === GrDiffLine.FILE) {
+ this._addDraft(el);
+ return;
+ }
+ var lineNum = parseInt(value, 10);
+ if (isNaN(lineNum)) {
+ throw Error('Invalid line number: ' + value);
+ }
+ this._addDraft(el, lineNum);
}.bind(this));
},
+ _addDraft: function(lineEl, opt_lineNum) {
+ var threadEl;
+
+ // Does a thread already exist at this line?
+ var contentEl = lineEl.nextSibling;
+ while (contentEl && !contentEl.classList.contains('content')) {
+ contentEl = contentEl.nextSibling;
+ }
+ if (contentEl.childNodes.length > 0 &&
+ contentEl.lastChild.nodeName === 'GR-DIFF-COMMENT-THREAD') {
+ threadEl = contentEl.lastChild;
+ } else {
+ var patchNum = this.patchRange.patchNum;
+ var side = 'REVISION';
+ if (contentEl.classList.contains(DiffSide.LEFT) ||
+ contentEl.classList.contains('remove')) {
+ if (this.patchRange.basePatchNum === 'PARENT') {
+ side = 'PARENT';
+ } else {
+ patchNum = this.patchRange.basePatchNum;
+ }
+ }
+ threadEl = this._builder.createCommentThread(this.changeNum, patchNum,
+ this.path, side, this.projectConfig);
+ contentEl.appendChild(threadEl);
+ }
+ threadEl.addDraft(opt_lineNum);
+ },
+
+ _handleThreadDiscard: function(e) {
+ var el = Polymer.dom(e).rootTarget;
+ el.parentNode.removeChild(el);
+ },
+
+ _handleCommentDiscard: function(e) {
+ var comment = Polymer.dom(e).rootTarget.comment;
+ this._removeComment(comment);
+ },
+
+ _removeComment: function(comment) {
+ if (!comment.id) { return; }
+ this._removeCommentFromSide(comment, DiffSide.LEFT) ||
+ this._removeCommentFromSide(comment, DiffSide.RIGHT);
+ },
+
+ _removeCommentFromSide: function(comment, side) {
+ var idx = -1;
+ for (var i = 0; i < this._comments[side].length; i++) {
+ if (this._comments[side][i].id === comment.id) {
+ idx = i;
+ break;
+ }
+ }
+ if (idx !== -1) {
+ this.splice('_comments.' + side, idx, 1);
+ return true;
+ }
+ return false;
+ },
+
+ _handleMouseDown: function(e) {
+ var el = Polymer.dom(e).rootTarget;
+ var side;
+ for (var node = el; node != null; node = node.parentNode) {
+ if (!node.classList) { continue; }
+
+ if (node.classList.contains(DiffSide.LEFT)) {
+ side = DiffSide.LEFT;
+ break;
+ } else if (node.classList.contains(DiffSide.RIGHT)) {
+ side = DiffSide.RIGHT;
+ break;
+ }
+ }
+ this._selectionSide = side;
+ },
+
+ _selectionSideChanged: function(side) {
+ if (side) {
+ var oppositeSide = side === DiffSide.RIGHT ?
+ DiffSide.LEFT : DiffSide.RIGHT;
+ this.customStyle['--' + side + '-user-select'] = 'text';
+ this.customStyle['--' + oppositeSide + '-user-select'] = 'none';
+ } else {
+ this.customStyle['--left-user-select'] = 'text';
+ this.customStyle['--right-user-select'] = 'text';
+ }
+ this.updateStyles();
+ },
+
+ _handleCopy: function(e) {
+ var text = this._getSelectedText(this._selectionSide);
+ e.clipboardData.setData('Text', text);
+ e.preventDefault();
+ },
+
+ _getSelectedText: function(opt_side) {
+ var sel = window.getSelection();
+ var range = sel.getRangeAt(0);
+ var doc = range.cloneContents();
+ var selector = '.content';
+ if (opt_side) {
+ selector += '.' + opt_side;
+ }
+ var contentEls = Polymer.dom(doc).querySelectorAll(selector);
+
+ if (contentEls.length === 0) {
+ return doc.textContent;
+ }
+
+ var text = '';
+ for (var i = 0; i < contentEls.length; i++) {
+ text += contentEls[i].textContent + '\n';
+ }
+ return text;
+ },
+
+ _showContext: function(group, sectionEl) {
+ this._builder.emitGroup(group, sectionEl);
+ sectionEl.parentNode.removeChild(sectionEl);
+ },
+
+ _prefsChanged: function(prefsChangeRecord) {
+ var prefs = prefsChangeRecord.base;
+ this.customStyle['--content-width'] = prefs.line_length + 'ch';
+ this.updateStyles();
+
+ if (this._diff && this._comments) {
+ this._render();
+ }
+ },
+
+ _render: function() {
+ this._clearDiffContent();
+ this._builder = this._getDiffBuilder(this._diff, this._comments,
+ this._prefs);
+ this._builder.emitDiff(this._diff.content);
+
+ this.async(function() {
+ this.fire('render', null, {bubbles: false});
+ }.bind(this), 1);
+ },
+
+ _clearDiffContent: function() {
+ this.$.diffTable.innerHTML = null;
+ },
+
_getDiff: function() {
return this.$.restAPI.getDiff(
this.changeNum,
@@ -185,528 +426,95 @@
},
_getDiffDrafts: function() {
- return this.$.restAPI.getDiffDrafts(
- this.changeNum,
- this.patchRange.basePatchNum,
- this.patchRange.patchNum,
- this.path);
- },
-
- showDiffPreferences: function() {
- this.$.prefsOverlay.open();
- },
-
- _prefsChanged: function(changeRecord) {
- if (this._initialRenderComplete) {
- this._render();
- }
- this._resolvePrefsReady(changeRecord.base);
- },
-
- _render: function() {
- this._groupCommentsAndDrafts();
- this._processContent();
-
- // Allow for the initial rendering to complete before firing the event.
- this.async(function() {
- this.fire('render', null, {bubbles: false});
- }.bind(this), 1);
-
- this._initialRenderComplete = true;
- },
-
- _handlePrefsTap: function(e) {
- e.preventDefault();
-
- // TODO(andybons): This is not supported in IE. Implement a polyfill.
- // NOTE: Object.assign is NOT automatically a deep copy. If prefs adds
- // an object as a value, it must be marked enumerable.
- this._savedPrefs = Object.assign({}, this.prefs);
- this.$.prefsOverlay.open();
- },
-
- _handlePrefsSave: function(e) {
- e.stopPropagation();
- var el = Polymer.dom(e).rootTarget;
- el.disabled = true;
- app.accountReady.then(function() {
- if (!this._loggedIn) {
- el.disabled = false;
- this.$.prefsOverlay.close();
- return;
+ return this._getLoggedIn().then(function(loggedIn) {
+ if (!loggedIn) {
+ return Promise.resolve({baseComments: [], comments: []});
}
- this._saveDiffPreferences().then(function() {
- this.$.prefsOverlay.close();
- el.disabled = false;
- }.bind(this)).catch(function(err) {
- el.disabled = false;
- alert('Oops. Something went wrong. Check the console and bug the ' +
- 'PolyGerrit team for assistance.');
- throw err;
- });
+ return this.$.restAPI.getDiffDrafts(
+ this.changeNum,
+ this.patchRange.basePatchNum,
+ this.patchRange.patchNum,
+ this.path);
}.bind(this));
},
- _saveDiffPreferences: function() {
- var xhr = document.createElement('gr-request');
- this._diffPreferencesPromise = xhr.send({
- method: 'PUT',
- url: '/accounts/self/preferences.diff',
- body: this.prefs,
- });
- return this._diffPreferencesPromise;
+ _getDiffCommentsAndDrafts: function() {
+ var promises = [];
+ promises.push(this._getDiffComments());
+ promises.push(this._getDiffDrafts());
+ return Promise.all(promises).then(function(results) {
+ return Promise.resolve({
+ comments: results[0],
+ drafts: results[1],
+ });
+ }).then(this._normalizeDiffCommentsAndDrafts.bind(this));
},
- _handlePrefsCancel: function(e) {
- e.stopPropagation();
- this.prefs = this._savedPrefs;
- this.$.prefsOverlay.close();
- },
-
- _handleExpandContext: function(e) {
- var ctx = e.detail.context;
- var contextControlIndex = -1;
- for (var i = ctx.start; i <= ctx.end; i++) {
- this._diff.leftSide[i].hidden = false;
- this._diff.rightSide[i].hidden = false;
- if (this._diff.leftSide[i].type == 'CONTEXT_CONTROL' &&
- this._diff.rightSide[i].type == 'CONTEXT_CONTROL') {
- contextControlIndex = i;
+ _getDiffPreferences: function() {
+ return this._getLoggedIn().then(function(loggedIn) {
+ if (!loggedIn) {
+ // These defaults should match the defaults in
+ // gerrit-extension-api/src/main/jcg/gerrit/extensions/client/DiffPreferencesInfo.java
+ // NOTE: There are some settings that don't apply to PolyGerrit
+ // (Render mode being at least one of them).
+ return Promise.resolve({
+ auto_hide_diff_table_header: true,
+ context: 10,
+ cursor_blink_rate: 0,
+ ignore_whitespace: 'IGNORE_NONE',
+ intraline_difference: true,
+ line_length: 100,
+ show_line_endings: true,
+ show_tabs: true,
+ show_whitespace_errors: true,
+ syntax_highlighting: true,
+ tab_size: 8,
+ theme: 'DEFAULT',
+ });
}
- }
- this._diff.leftSide[contextControlIndex].hidden = true;
- this._diff.rightSide[contextControlIndex].hidden = true;
-
- this.$.leftDiff.hideElementsWithIndex(contextControlIndex);
- this.$.rightDiff.hideElementsWithIndex(contextControlIndex);
-
- this.$.leftDiff.renderLineIndexRange(ctx.start, ctx.end);
- this.$.rightDiff.renderLineIndexRange(ctx.start, ctx.end);
+ return this.$.restAPI.getDiffPreferences();
+ }.bind(this));
},
- _handleThreadHeightChange: function(e) {
- var index = e.detail.index;
- var diffEl = Polymer.dom(e).rootTarget;
- var otherSide = diffEl == this.$.leftDiff ?
- this.$.rightDiff : this.$.leftDiff;
-
- var threadHeight = e.detail.height;
- var otherSideHeight;
- if (otherSide.content[index].type == 'COMMENT_THREAD') {
- otherSideHeight = otherSide.getRowNaturalHeight(index);
- } else {
- otherSideHeight = otherSide.getRowHeight(index);
+ _normalizeDiffCommentsAndDrafts: function(results) {
+ function markAsDraft(d) {
+ d.__draft = true;
+ return d;
}
- var maxHeight = Math.max(threadHeight, otherSideHeight);
- this.$.leftDiff.setRowHeight(index, maxHeight);
- this.$.rightDiff.setRowHeight(index, maxHeight);
- },
-
- _handleAddDraft: function(e) {
- var insertIndex = e.detail.index + 1;
- var diffEl = Polymer.dom(e).rootTarget;
- var content = diffEl.content;
- if (content[insertIndex] &&
- content[insertIndex].type == 'COMMENT_THREAD') {
- // A thread is already here. Do nothing.
- return;
- }
- var comment = {
- type: 'COMMENT_THREAD',
- comments: [{
- __draft: true,
- __draftID: Math.random().toString(36),
- line: e.detail.line,
+ var baseDrafts = results.drafts.baseComments.map(markAsDraft);
+ var drafts = results.drafts.comments.map(markAsDraft);
+ return Promise.resolve({
+ meta: {
path: this.path,
- }]
- };
- if (diffEl == this.$.leftDiff &&
- this.patchRange.basePatchNum == 'PARENT') {
- comment.comments[0].side = 'PARENT';
- comment.patchNum = this.patchRange.patchNum;
- }
-
- if (content[insertIndex] &&
- content[insertIndex].type == 'FILLER') {
- content[insertIndex] = comment;
- diffEl.rowUpdated(insertIndex);
- } else {
- content.splice(insertIndex, 0, comment);
- diffEl.rowInserted(insertIndex);
- }
-
- var otherSide = diffEl == this.$.leftDiff ?
- this.$.rightDiff : this.$.leftDiff;
- if (otherSide.content[insertIndex] == null ||
- otherSide.content[insertIndex].type != 'COMMENT_THREAD') {
- otherSide.content.splice(insertIndex, 0, {
- type: 'FILLER',
- });
- otherSide.rowInserted(insertIndex);
- }
- },
-
- _handleRemoveThread: function(e) {
- var diffEl = Polymer.dom(e).rootTarget;
- var otherSide = diffEl == this.$.leftDiff ?
- this.$.rightDiff : this.$.leftDiff;
- var index = e.detail.index;
-
- if (otherSide.content[index].type == 'FILLER') {
- otherSide.content.splice(index, 1);
- otherSide.rowRemoved(index);
- diffEl.content.splice(index, 1);
- diffEl.rowRemoved(index);
- } else if (otherSide.content[index].type == 'COMMENT_THREAD') {
- diffEl.content[index] = {type: 'FILLER'};
- diffEl.rowUpdated(index);
- var height = otherSide.setRowNaturalHeight(index);
- diffEl.setRowHeight(index, height);
- } else {
- throw Error('A thread cannot be opposite anything but filler or ' +
- 'another thread');
- }
- },
-
- _processContent: function() {
- var leftSide = [];
- var rightSide = [];
- var initialLineNum = 0 + (this._diffResponse.content.skip || 0);
- var ctx = {
- hidingLines: false,
- lastNumLinesHidden: 0,
- left: {
- lineNum: initialLineNum,
+ changeNum: this.changeNum,
+ patchRange: this.patchRange,
+ projectConfig: this.projectConfig,
},
- right: {
- lineNum: initialLineNum,
- }
- };
- var content = this._breakUpCommonChunksWithComments(ctx,
- this._diffResponse.content);
- var context = this.prefs.context;
- if (context == -1) {
- // Show the entire file.
- context = Infinity;
- }
- for (var i = 0; i < content.length; i++) {
- if (i == 0) {
- ctx.skipRange = [0, context];
- } else if (i == content.length - 1) {
- ctx.skipRange = [context, 0];
- } else {
- ctx.skipRange = [context, context];
- }
- ctx.diffChunkIndex = i;
- this._addDiffChunk(ctx, content[i], leftSide, rightSide);
- }
-
- this._diff = {
- leftSide: leftSide,
- rightSide: rightSide,
- };
+ left: results.comments.baseComments.concat(baseDrafts),
+ right: results.comments.comments.concat(drafts),
+ });
},
- // In order to show comments out of the bounds of the selected context,
- // treat them as diffs within the model so that the content (and context
- // surrounding it) renders correctly.
- _breakUpCommonChunksWithComments: function(ctx, content) {
- var result = [];
- var leftLineNum = ctx.left.lineNum;
- var rightLineNum = ctx.right.lineNum;
- for (var i = 0; i < content.length; i++) {
- if (!content[i].ab) {
- result.push(content[i]);
- if (content[i].a) {
- leftLineNum += content[i].a.length;
- }
- if (content[i].b) {
- rightLineNum += content[i].b.length;
- }
- continue;
- }
- var chunk = content[i].ab;
- var currentChunk = {ab: []};
- for (var j = 0; j < chunk.length; j++) {
- leftLineNum++;
- rightLineNum++;
- if (this._groupedBaseComments[leftLineNum] == null &&
- this._groupedComments[rightLineNum] == null) {
- currentChunk.ab.push(chunk[j]);
- } else {
- if (currentChunk.ab && currentChunk.ab.length > 0) {
- result.push(currentChunk);
- currentChunk = {ab: []};
- }
- // Append an annotation to indicate that this line should not be
- // highlighted even though it's implied with both `a` and `b`
- // defined. This is needed since there may be two lines that
- // should be highlighted but are equal (blank lines, for example).
- result.push({
- __noHighlight: true,
- a: [chunk[j]],
- b: [chunk[j]],
- });
- }
- }
- if (currentChunk.ab != null && currentChunk.ab.length > 0) {
- result.push(currentChunk);
- }
- }
- return result;
+ _getLoggedIn: function() {
+ return this.$.restAPI.getLoggedIn();
},
- _groupCommentsAndDrafts: function() {
- this._baseDrafts.forEach(function(d) { d.__draft = true; });
- this._drafts.forEach(function(d) { d.__draft = true; });
- var allLeft = this._baseComments.concat(this._baseDrafts);
- var allRight = this._comments.concat(this._drafts);
-
- var leftByLine = {};
- var rightByLine = {};
- var mapFunc = function(byLine) {
- return function(c) {
- // File comments/drafts are grouped with line 1 for now.
- var line = c.line || 1;
- if (byLine[line] == null) {
- byLine[line] = [];
- }
- byLine[line].push(c);
- };
- };
- allLeft.forEach(mapFunc(leftByLine));
- allRight.forEach(mapFunc(rightByLine));
-
- this._groupedBaseComments = leftByLine;
- this._groupedComments = rightByLine;
+ _getDiffBuilder: function(diff, comments, prefs) {
+ if (this._viewMode === DiffViewMode.SIDE_BY_SIDE) {
+ return new GrDiffBuilderSideBySide(diff, comments, prefs,
+ this.$.diffTable);
+ } else if (this._viewMode === DiffViewMode.UNIFIED) {
+ return new GrDiffBuilderUnified(diff, comments, prefs,
+ this.$.diffTable);
+ }
+ throw Error('Unsupported diff view mode: ' + this._viewMode);
},
- _addContextControl: function(ctx, leftSide, rightSide) {
- var numLinesHidden = ctx.lastNumLinesHidden;
- var leftStart = leftSide.length - numLinesHidden;
- var leftEnd = leftSide.length;
- var rightStart = rightSide.length - numLinesHidden;
- var rightEnd = rightSide.length;
- if (leftStart != rightStart || leftEnd != rightEnd) {
- throw Error(
- 'Left and right ranges for context control should be equal:' +
- 'Left: [' + leftStart + ', ' + leftEnd + '] ' +
- 'Right: [' + rightStart + ', ' + rightEnd + ']');
+ _projectConfigChanged: function(projectConfig) {
+ var threadEls = this._getCommentThreads();
+ for (var i = 0; i < threadEls.length; i++) {
+ threadEls[i].projectConfig = projectConfig;
}
- var obj = {
- type: 'CONTEXT_CONTROL',
- numLines: numLinesHidden,
- start: leftStart,
- end: leftEnd,
- };
- // NOTE: Be careful, here. This object is meant to be immutable. If the
- // object is altered within one side's array it will reflect the
- // alterations in another.
- leftSide.push(obj);
- rightSide.push(obj);
- },
-
- _addCommonDiffChunk: function(ctx, chunk, leftSide, rightSide) {
- for (var i = 0; i < chunk.ab.length; i++) {
- var numLines = Math.ceil(
- this._visibleLineLength(chunk.ab[i]) / this.prefs.line_length);
- var hidden = i >= ctx.skipRange[0] &&
- i < chunk.ab.length - ctx.skipRange[1];
- if (ctx.hidingLines && hidden == false) {
- // No longer hiding lines. Add a context control.
- this._addContextControl(ctx, leftSide, rightSide);
- ctx.lastNumLinesHidden = 0;
- }
- ctx.hidingLines = hidden;
- if (hidden) {
- ctx.lastNumLinesHidden++;
- }
-
- // Blank lines within a diff content array indicate a newline.
- leftSide.push({
- type: 'CODE',
- hidden: hidden,
- content: chunk.ab[i] || '\n',
- numLines: numLines,
- lineNum: ++ctx.left.lineNum,
- });
- rightSide.push({
- type: 'CODE',
- hidden: hidden,
- content: chunk.ab[i] || '\n',
- numLines: numLines,
- lineNum: ++ctx.right.lineNum,
- });
-
- this._addCommentsIfPresent(ctx, leftSide, rightSide);
- }
- if (ctx.lastNumLinesHidden > 0) {
- this._addContextControl(ctx, leftSide, rightSide);
- }
- },
-
- _addDiffChunk: function(ctx, chunk, leftSide, rightSide) {
- if (chunk.ab) {
- this._addCommonDiffChunk(ctx, chunk, leftSide, rightSide);
- return;
- }
-
- var leftHighlights = [];
- if (chunk.edit_a) {
- leftHighlights =
- this._normalizeIntralineHighlights(chunk.a, chunk.edit_a);
- }
- var rightHighlights = [];
- if (chunk.edit_b) {
- rightHighlights =
- this._normalizeIntralineHighlights(chunk.b, chunk.edit_b);
- }
-
- var aLen = (chunk.a && chunk.a.length) || 0;
- var bLen = (chunk.b && chunk.b.length) || 0;
- var maxLen = Math.max(aLen, bLen);
- for (var i = 0; i < maxLen; i++) {
- var hasLeftContent = chunk.a && i < chunk.a.length;
- var hasRightContent = chunk.b && i < chunk.b.length;
- var leftContent = hasLeftContent ? chunk.a[i] : '';
- var rightContent = hasRightContent ? chunk.b[i] : '';
- var highlight = !chunk.__noHighlight;
- var maxNumLines = this._maxLinesSpanned(leftContent, rightContent);
- if (hasLeftContent) {
- leftSide.push({
- type: 'CODE',
- content: leftContent || '\n',
- numLines: maxNumLines,
- lineNum: ++ctx.left.lineNum,
- highlight: highlight,
- intraline: highlight && leftHighlights.filter(function(hl) {
- return hl.contentIndex == i;
- }),
- });
- } else {
- leftSide.push({
- type: 'FILLER',
- numLines: maxNumLines,
- });
- }
- if (hasRightContent) {
- rightSide.push({
- type: 'CODE',
- content: rightContent || '\n',
- numLines: maxNumLines,
- lineNum: ++ctx.right.lineNum,
- highlight: highlight,
- intraline: highlight && rightHighlights.filter(function(hl) {
- return hl.contentIndex == i;
- }),
- });
- } else {
- rightSide.push({
- type: 'FILLER',
- numLines: maxNumLines,
- });
- }
- this._addCommentsIfPresent(ctx, leftSide, rightSide);
- }
- },
-
- _addCommentsIfPresent: function(ctx, leftSide, rightSide) {
- var leftComments = this._groupedBaseComments[ctx.left.lineNum];
- var rightComments = this._groupedComments[ctx.right.lineNum];
- if (leftComments) {
- var thread = {
- type: 'COMMENT_THREAD',
- comments: leftComments,
- };
- if (this.patchRange.basePatchNum == 'PARENT') {
- thread.patchNum = this.patchRange.patchNum;
- }
- leftSide.push(thread);
- }
- if (rightComments) {
- rightSide.push({
- type: 'COMMENT_THREAD',
- comments: rightComments,
- });
- }
- if (leftComments && !rightComments) {
- rightSide.push({type: 'FILLER'});
- } else if (!leftComments && rightComments) {
- leftSide.push({type: 'FILLER'});
- }
- this._groupedBaseComments[ctx.left.lineNum] = null;
- this._groupedComments[ctx.right.lineNum] = null;
- },
-
- // The `highlights` array consists of a list of <skip length, mark length>
- // pairs, where the skip length is the number of characters between the
- // end of the previous edit and the start of this edit, and the mark
- // length is the number of edited characters following the skip. The start
- // of the edits is from the beginning of the related diff content lines.
- //
- // Note that the implied newline character at the end of each line is
- // included in the length calculation, and thus it is possible for the
- // edits to span newlines.
- //
- // A line highlight object consists of three fields:
- // - contentIndex: The index of the diffChunk `content` field (the line
- // being referred to).
- // - startIndex: Where the highlight should begin.
- // - endIndex: (optional) Where the highlight should end. If omitted, the
- // highlight is meant to be a continuation onto the next line.
- _normalizeIntralineHighlights: function(content, highlights) {
- var contentIndex = 0;
- var idx = 0;
- var normalized = [];
- for (var i = 0; i < highlights.length; i++) {
- var line = content[contentIndex] + '\n';
- var hl = highlights[i];
- var j = 0;
- while (j < hl[0]) {
- if (idx == line.length) {
- idx = 0;
- line = content[++contentIndex] + '\n';
- continue;
- }
- idx++;
- j++;
- }
- var lineHighlight = {
- contentIndex: contentIndex,
- startIndex: idx,
- };
-
- j = 0;
- while (line && j < hl[1]) {
- if (idx == line.length) {
- idx = 0;
- line = content[++contentIndex] + '\n';
- normalized.push(lineHighlight);
- lineHighlight = {
- contentIndex: contentIndex,
- startIndex: idx,
- };
- continue;
- }
- idx++;
- j++;
- }
- lineHighlight.endIndex = idx;
- normalized.push(lineHighlight);
- }
- return normalized;
- },
-
- _visibleLineLength: function(contents) {
- // http://jsperf.com/performance-of-match-vs-split
- var numTabs = contents.split('\t').length - 1;
- return contents.length - numTabs + (this.prefs.tab_size * numTabs);
- },
-
- _maxLinesSpanned: function(left, right) {
- return Math.max(
- Math.ceil(this._visibleLineLength(left) / this.prefs.line_length),
- Math.ceil(this._visibleLineLength(right) / this.prefs.line_length));
},
});
})();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html
index e46bbb8..579957e 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html
@@ -20,8 +20,6 @@
<script src="../../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
<script src="../../../bower_components/web-component-tester/browser.js"></script>
-<script src="../../../test/fake-app.js"></script>
-<script src="../../../scripts/util.js"></script>
<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
<link rel="import" href="gr-diff.html">
@@ -35,458 +33,214 @@
<script>
suite('gr-diff tests', function() {
var element;
- var server;
- var getDiffStub;
- var getCommentsStub;
setup(function() {
+ stub('gr-rest-api-interface', {
+ getLoggedIn: function() { return Promise.resolve(false); },
+ })
element = fixture('basic');
- element.changeNum = 42;
- element.path = 'sieve.go';
- element.prefs = {
- context: 10,
- tab_size: 8,
- };
-
- getDiffStub = sinon.stub(element.$.restAPI, 'getDiff', function() {
- return Promise.resolve({
- change_type: 'MODIFIED',
- content: [
- {
- ab: [
- '<!DOCTYPE html>',
- '<meta charset="utf-8">',
- '<title>My great page</title>',
- '<style>',
- ' *,',
- ' *:before,',
- ' *:after {',
- ' box-sizing: border-box;',
- ' }',
- '</style>',
- '<header>',
- ]
- },
- {
- a: [
- ' Welcome ',
- ' to the wooorld of tomorrow!',
- ],
- b: [
- ' Hello, world!',
- ],
- },
- {
- ab: [
- '</header>',
- '<body>',
- 'Leela: This is the only place the ship can’t hear us, so ',
- 'everyone pretend to shower.',
- 'Fry: Same as every day. Got it.',
- ]
- },
- ]
- });
- });
-
- getCommentsStub = sinon.stub(element.$.restAPI, 'getDiffComments',
- function() {
- return Promise.resolve({
- baseComments: [
- {
- author: {
- _account_id: 1000000,
- name: 'Andrew Bonventre',
- email: 'andybons@gmail.com',
- },
- id: '9af53d3f_5f2b8b82',
- line: 1,
- message: 'this isn’t quite right',
- updated: '2015-12-10 02:50:21.627000000',
- }
- ],
- comments: [
- {
- author: {
- _account_id: 1010008,
- name: 'Dave Borowitz',
- email: 'dborowitz@google.com',
- },
- id: '001a2067_f30f3048',
- line: 12,
- message: 'What on earth are you thinking, here?',
- updated: '2015-12-12 02:51:37.973000000',
- },
- {
- author: {
- _account_id: 1000000,
- name: 'Andrew Bonventre',
- email: 'andybons@gmail.com',
- },
- id: 'a0407443_30dfe8fb',
- in_reply_to: '001a2067_f30f3048',
- line: 12,
- message: '¯\\_(ツ)_/¯',
- updated: '2015-12-12 18:50:21.627000000',
- },
- ],
- });
- }
- );
-
- server = sinon.fakeServer.create();
- server.respondWith(
- 'PUT',
- '/accounts/self/preferences.diff',
- [
- 200,
- {'Content-Type': 'application/json'},
- ')]}\'\n' +
- JSON.stringify({context: 25}),
- ]
- );
-
});
- teardown(function() {
- getDiffStub.restore();
- getCommentsStub.restore();
- server.restore();
- });
+ test('get drafts logged out', function(done) {
+ element.patchRange = {basePatchNum: 0, patchNum: 0};
- test('comment rendering', function(done) {
- element.prefs.context = -1;
- element._loggedIn = true;
- element.patchRange = {
- basePatchNum: 1,
- patchNum: 2,
- };
-
- element.reload().then(function() {
- flush(function() {
- var leftThreadEls =
- Polymer.dom(element.$.leftDiff.root).querySelectorAll(
- 'gr-diff-comment-thread');
- assert.equal(leftThreadEls.length, 1);
- assert.equal(leftThreadEls[0].comments.length, 1);
-
- var rightThreadEls =
- Polymer.dom(element.$.rightDiff.root).querySelectorAll(
- 'gr-diff-comment-thread');
- assert.equal(rightThreadEls.length, 1);
- assert.equal(rightThreadEls[0].comments.length, 2);
-
- var index = leftThreadEls[0].getAttribute('data-index');
- var leftFillerEls =
- Polymer.dom(element.$.leftDiff.root).querySelectorAll(
- '.commentThread.filler[data-index="' + index + '"]');
- assert.equal(leftFillerEls.length, 1);
- var rightFillerEls =
- Polymer.dom(element.$.rightDiff.root).querySelectorAll(
- '[data-index="' + index + '"]');
- assert.equal(rightFillerEls.length, 2);
-
- for (var i = 0; i < rightFillerEls.length; i++) {
- assert.isTrue(rightFillerEls[i].classList.contains('filler'));
- }
- var originalHeight = rightFillerEls[0].offsetHeight;
- assert.equal(rightFillerEls[1].offsetHeight, originalHeight);
- assert.equal(leftThreadEls[0].offsetHeight, originalHeight);
- assert.equal(leftFillerEls[0].offsetHeight, originalHeight);
-
- // Create a comment on the opposite side of the first comment.
- var rightLineEL = element.$.rightDiff.$$(
- '.lineNum[data-index="' + (index - 1) + '"]');
- assert.ok(rightLineEL);
- MockInteractions.tap(rightLineEL);
- flush(function() {
- var newThreadEls =
- Polymer.dom(element.$.rightDiff.root).querySelectorAll(
- '[data-index="' + index + '"]');
- assert.equal(newThreadEls.length, 2);
- for (var i = 0; i < newThreadEls.length; i++) {
- assert.isTrue(
- newThreadEls[i].classList.contains('commentThread') ||
- newThreadEls[i].tagName == 'GR-DIFF-COMMENT-THREAD');
- }
- var newHeight = newThreadEls[0].offsetHeight;
- assert.equal(newThreadEls[1].offsetHeight, newHeight);
- assert.equal(leftFillerEls[0].offsetHeight, newHeight);
- assert.equal(leftThreadEls[0].offsetHeight, newHeight);
-
- // The editing mode height of the right comment will be greater than
- // the non-editing mode height of the left comment.
- assert.isAbove(newHeight, originalHeight);
-
- // Discard the right thread and ensure the left comment heights are
- // back to their original values.
- newThreadEls[1].addEventListener('thread-discard', function() {
- rightFillerEls =
- Polymer.dom(element.$.rightDiff.root).querySelectorAll(
- '[data-index="' + index + '"]');
- assert.equal(rightFillerEls.length, 2);
-
- for (var i = 0; i < rightFillerEls.length; i++) {
- assert.isTrue(rightFillerEls[i].classList.contains('filler'));
- }
- var originalHeight = rightFillerEls[0].offsetHeight;
- assert.equal(rightFillerEls[1].offsetHeight, originalHeight);
- assert.equal(leftThreadEls[0].offsetHeight, originalHeight);
- assert.equal(leftFillerEls[0].offsetHeight, originalHeight);
- done();
- });
- var commentEl = newThreadEls[1].$$('gr-diff-comment');
- commentEl.fire('comment-discard');
- });
- });
- });
- server.respond();
- });
-
- test('intraline normalization', function() {
- // The content and highlights are in the format returned by the Gerrit
- // REST API.
- var content = [
- ' <section class="summary">',
- ' <gr-linked-text content="' +
- '[[_computeCurrentRevisionMessage(change)]]"></gr-linked-text>',
- ' </section>',
- ];
- var highlights = [
- [31, 34], [42, 26]
- ];
- var results = element._normalizeIntralineHighlights(content, highlights);
- assert.deepEqual(results, [
- {
- contentIndex: 0,
- startIndex: 31,
- },
- {
- contentIndex: 1,
- startIndex: 0,
- endIndex: 33,
- },
- {
- contentIndex: 1,
- startIndex: 75,
- },
- {
- contentIndex: 2,
- startIndex: 0,
- endIndex: 6,
- }
- ]);
-
- content = [
- ' this._path = value.path;',
- '',
- ' // When navigating away from the page, there is a possibility that the',
- ' // patch number is no longer a part of the URL (say when navigating to',
- ' // the top-level change info view) and therefore undefined in `params`.',
- ' if (!this._patchRange.patchNum) {',
- ];
- highlights = [
- [14, 17],
- [11, 70],
- [12, 67],
- [12, 67],
- [14, 29],
- ];
- results = element._normalizeIntralineHighlights(content, highlights);
- assert.deepEqual(results, [
- {
- contentIndex: 0,
- startIndex: 14,
- endIndex: 31,
- },
- {
- contentIndex: 2,
- startIndex: 8,
- endIndex: 78,
- },
- {
- contentIndex: 3,
- startIndex: 11,
- endIndex: 78,
- },
- {
- contentIndex: 4,
- startIndex: 11,
- endIndex: 78,
- },
- {
- contentIndex: 5,
- startIndex: 12,
- endIndex: 41,
- }
- ]);
- });
-
- test('context', function() {
- element.prefs.context = 3;
- element._diffResponse = {
- content: [
- {
- ab: [
- '<!DOCTYPE html>',
- '<meta charset="utf-8">',
- '<title>My great page</title>',
- '<style>',
- ' *,',
- ' *:before,',
- ' *:after {',
- ' box-sizing: border-box;',
- ' }',
- '</style>',
- '<header>',
- ]
- },
- {
- a: [
- ' Welcome ',
- ' to the wooorld of tomorrow!',
- ],
- b: [
- ' Hello, world!',
- ],
- },
- {
- ab: [
- '</header>',
- '<body>',
- 'Leela: This is the only place the ship can’t hear us, so ',
- 'everyone pretend to shower.',
- 'Fry: Same as every day. Got it.',
- ]
- },
- ]
- };
- element._processContent();
-
- // First eight lines should be hidden on both sides.
- for (var i = 0; i < 8; i++) {
- assert.isTrue(element._diff.leftSide[i].hidden);
- assert.isTrue(element._diff.rightSide[i].hidden);
- }
- // A context control should be at index 8 on both sides.
- var leftContext = element._diff.leftSide[8];
- var rightContext = element._diff.rightSide[8];
- assert.deepEqual(leftContext, rightContext);
- assert.equal(leftContext.numLines, 8);
- assert.equal(leftContext.start, 0);
- assert.equal(leftContext.end, 8);
-
- // Line indices 9-16 should be shown.
- for (var i = 9; i <= 16; i++) {
- // notOk (falsy) because the `hidden` attribute may not be present.
- assert.notOk(element._diff.leftSide[i].hidden);
- assert.notOk(element._diff.rightSide[i].hidden);
- }
-
- // Lines at indices 17 and 18 should be hidden.
- assert.isTrue(element._diff.leftSide[17].hidden);
- assert.isTrue(element._diff.rightSide[17].hidden);
- assert.isTrue(element._diff.leftSide[18].hidden);
- assert.isTrue(element._diff.rightSide[18].hidden);
-
- // Context control at index 19.
- leftContext = element._diff.leftSide[19];
- rightContext = element._diff.rightSide[19];
- assert.deepEqual(leftContext, rightContext);
- assert.equal(leftContext.numLines, 2);
- assert.equal(leftContext.start, 17);
- assert.equal(leftContext.end, 19);
- });
-
- test('save prefs', function(done) {
- element._loggedIn = false;
-
- element.prefs = {
- tab_size: 4,
- context: 50,
- };
- element.fire('save', {}, {node: element.$$('gr-diff-preferences')});
- assert.isTrue(element._diffPreferencesPromise == null);
-
- element._loggedIn = true;
- element.fire('save', {}, {node: element.$$('gr-diff-preferences')});
- server.respond();
-
- element._diffPreferencesPromise.then(function(req) {
- assert.equal(req.xhr.requestBody, JSON.stringify(element.prefs));
+ var getDraftsStub = sinon.stub(element.$.restAPI, 'getDiffDrafts');
+ var loggedInStub = sinon.stub(element, '_getLoggedIn',
+ function() { return Promise.resolve(false); });
+ element._getDiffDrafts().then(function(result) {
+ assert.deepEqual(result, {baseComments: [], comments: []});
+ sinon.assert.notCalled(getDraftsStub);
+ loggedInStub.restore();
+ getDraftsStub.restore();
done();
});
});
- test('visible line length', function() {
- assert.equal(element._visibleLineLength('A'.repeat(5)), 5);
- assert.equal(
- element._visibleLineLength('A'.repeat(5) + '\t' + 'A'.repeat(5)), 18);
+ test('get drafts logged in', function(done) {
+ element.patchRange = {basePatchNum: 0, patchNum: 0};
+ var draftsResponse = {
+ baseComments: [{id: 'foo'}],
+ comments: [{id: 'bar'}],
+ };
+ var getDraftsStub = sinon.stub(element.$.restAPI, 'getDiffDrafts',
+ function() { return Promise.resolve(draftsResponse); });
+ var loggedInStub = sinon.stub(element, '_getLoggedIn',
+ function() { return Promise.resolve(true); });
+ element._getDiffDrafts().then(function(result) {
+ assert.deepEqual(result, draftsResponse);
+ loggedInStub.restore();
+ getDraftsStub.restore();
+ done();
+ });
});
- test('break up common diff chunks', function() {
- element._groupedBaseComments = {
- 1: {},
+ test('get comments and drafts', function(done) {
+ var loggedInStub = sinon.stub(element, '_getLoggedIn',
+ function() { return Promise.resolve(true); });
+ var comments = {
+ baseComments: [
+ {id: 'bc1'},
+ {id: 'bc2'},
+ ],
+ comments: [
+ {id: 'c1'},
+ {id: 'c2'},
+ ],
};
- element._groupedComments = {
- 10: {},
+ var diffCommentsStub = sinon.stub(element, '_getDiffComments',
+ function() { return Promise.resolve(comments); });
+
+ var drafts = {
+ baseComments: [
+ {id: 'bd1'},
+ {id: 'bd2'},
+ ],
+ comments: [
+ {id: 'd1'},
+ {id: 'd2'},
+ ],
};
- var ctx = {
- left: {lineNum: 0},
- right: {lineNum: 0},
+ var diffDraftsStub = sinon.stub(element, '_getDiffDrafts',
+ function() { return Promise.resolve(drafts); });
+
+ element.changeNum = '42';
+ element.patchRange = {
+ basePatchNum: 'PARENT',
+ patchNum: 3,
};
- var content = [
- {
- ab: [
- 'Copyright (C) 2015 The Android Open Source Project',
- '',
- 'Licensed under the Apache License, Version 2.0 (the "License");',
- 'you may not use this file except in compliance with the License.',
- 'You may obtain a copy of the License at',
- '',
- 'http://www.apache.org/licenses/LICENSE-2.0',
- '',
- 'Unless required by applicable law or agreed to in writing, ',
- 'software distributed under the License is distributed on an ',
- '"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, ',
- 'either express or implied. See the License for the specific ',
- 'language governing permissions and limitations under the License.',
- ]
- }
- ];
- var result = element._breakUpCommonChunksWithComments(ctx, content);
- assert.deepEqual(result, [
- {
- __noHighlight: true,
- a: ['Copyright (C) 2015 The Android Open Source Project'],
- b: ['Copyright (C) 2015 The Android Open Source Project'],
+ element.path = '/path/to/foo';
+ element.projectConfig = {foo: 'bar'};
+
+ element._getDiffCommentsAndDrafts().then(function(result) {
+ assert.deepEqual(result, {
+ meta: {
+ changeNum: '42',
+ patchRange: {
+ basePatchNum: 'PARENT',
+ patchNum: 3,
+ },
+ path: '/path/to/foo',
+ projectConfig: {foo: 'bar'},
+ },
+ left: [
+ {id: 'bc1'},
+ {id: 'bc2'},
+ {id: 'bd1', __draft: true},
+ {id: 'bd2', __draft: true},
+ ],
+ right: [
+ {id: 'c1'},
+ {id: 'c2'},
+ {id: 'd1', __draft: true},
+ {id: 'd2', __draft: true},
+ ],
+ });
+
+ diffCommentsStub.restore();
+ diffDraftsStub.restore();
+ loggedInStub.restore();
+ done();
+ });
+ });
+
+ test('remove comment', function() {
+ element._comments = {
+ meta: {
+ changeNum: '42',
+ patchRange: {
+ basePatchNum: 'PARENT',
+ patchNum: 3,
+ },
+ path: '/path/to/foo',
+ projectConfig: {foo: 'bar'},
},
- {
- ab: [
- '',
- 'Licensed under the Apache License, Version 2.0 (the "License");',
- 'you may not use this file except in compliance with the License.',
- 'You may obtain a copy of the License at',
- '',
- 'http://www.apache.org/licenses/LICENSE-2.0',
- '',
- 'Unless required by applicable law or agreed to in writing, ',
- ]
+ left: [
+ {id: 'bc1'},
+ {id: 'bc2'},
+ {id: 'bd1', __draft: true},
+ {id: 'bd2', __draft: true},
+ ],
+ right: [
+ {id: 'c1'},
+ {id: 'c2'},
+ {id: 'd1', __draft: true},
+ {id: 'd2', __draft: true},
+ ],
+ };
+
+ element._removeComment({});
+ // Using JSON.stringify because Safari 9.1 (11601.5.17.1) doesn’t seem to
+ // believe that one object deepEquals another even when they do :-/.
+ assert.equal(JSON.stringify(element._comments), JSON.stringify({
+ meta: {
+ changeNum: '42',
+ patchRange: {
+ basePatchNum: 'PARENT',
+ patchNum: 3,
+ },
+ path: '/path/to/foo',
+ projectConfig: {foo: 'bar'},
},
- {
- __noHighlight: true,
- a: ['software distributed under the License is distributed on an '],
- b: ['software distributed under the License is distributed on an ']
+ left: [
+ {id: 'bc1'},
+ {id: 'bc2'},
+ {id: 'bd1', __draft: true},
+ {id: 'bd2', __draft: true},
+ ],
+ right: [
+ {id: 'c1'},
+ {id: 'c2'},
+ {id: 'd1', __draft: true},
+ {id: 'd2', __draft: true},
+ ],
+ }));
+
+ element._removeComment({id: 'bc2'});
+ assert.equal(JSON.stringify(element._comments), JSON.stringify({
+ meta: {
+ changeNum: '42',
+ patchRange: {
+ basePatchNum: 'PARENT',
+ patchNum: 3,
+ },
+ path: '/path/to/foo',
+ projectConfig: {foo: 'bar'},
},
- {
- ab: [
- '"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, ',
- 'either express or implied. See the License for the specific ',
- 'language governing permissions and limitations under the License.',
- ]
- }
- ]);
+ left: [
+ {id: 'bc1'},
+ {id: 'bd1', __draft: true},
+ {id: 'bd2', __draft: true},
+ ],
+ right: [
+ {id: 'c1'},
+ {id: 'c2'},
+ {id: 'd1', __draft: true},
+ {id: 'd2', __draft: true},
+ ],
+ }));
+
+ element._removeComment({id: 'd2'});
+ assert.deepEqual(JSON.stringify(element._comments), JSON.stringify({
+ meta: {
+ changeNum: '42',
+ patchRange: {
+ basePatchNum: 'PARENT',
+ patchNum: 3,
+ },
+ path: '/path/to/foo',
+ projectConfig: {foo: 'bar'},
+ },
+ left: [
+ {id: 'bc1'},
+ {id: 'bd1', __draft: true},
+ {id: 'bd2', __draft: true},
+ ],
+ right: [
+ {id: 'c1'},
+ {id: 'c2'},
+ {id: 'd1', __draft: true},
+ ],
+ }));
});
});
-
</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-new-diff.html b/polygerrit-ui/app/elements/diff/gr-new-diff/gr-new-diff.html
deleted file mode 100644
index 352092c..0000000
--- a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-new-diff.html
+++ /dev/null
@@ -1,184 +0,0 @@
-<!--
-Copyright (C) 2015 The Android Open Source Project
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
--->
-
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../shared/gr-button/gr-button.html">
-<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
-<link rel="import" href="../../shared/gr-request/gr-request.html">
-<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
-
-<link rel="import" href="../gr-diff-comment-thread/gr-diff-comment-thread.html">
-<link rel="import" href="../gr-diff-preferences/gr-diff-preferences.html">
-<link rel="import" href="../gr-patch-range-select/gr-patch-range-select.html">
-
-<dom-module id="gr-new-diff">
- <template>
- <style>
- :host {
- --light-remove-highlight-color: #fee;
- --dark-remove-highlight-color: #ffd4d4;
- --light-add-highlight-color: #efe;
- --dark-add-highlight-color: #d4ffd4;
- }
- .loading {
- padding: 0 var(--default-horizontal-margin) 1em;
- color: #666;
- }
- .header {
- display: flex;
- justify-content: space-between;
- margin: 0 var(--default-horizontal-margin) .75em;
- }
- .prefsButton {
- text-align: right;
- }
- .diffContainer {
- border-bottom: 1px solid #eee;
- border-top: 1px solid #eee;
- display: flex;
- font: 12px var(--monospace-font-family);
- overflow-x: auto;
- will-change: transform;
- }
- table {
- border-collapse: collapse;
- border-right: 1px solid #ddd;
- }
- .section {
- background-color: #eee;
- }
- .blank,
- .content {
- background-color: #fff;
- }
- .lineNum,
- .content {
- vertical-align: top;
- white-space: pre;
- }
- .contextLineNum:before,
- .lineNum:before {
- display: inline-block;
- color: #666;
- content: attr(data-value);
- padding: 0 .75em;
- text-align: right;
- width: 100%;
- }
- .canComment .lineNum[data-value] {
- cursor: pointer;
- }
- .canComment .lineNum[data-value]:before {
- text-decoration: underline;
- }
- .canComment .lineNum[data-value]:hover:before {
- background-color: #ccc;
- }
- .canComment .lineNum[data-value="FILE"]:before {
- content: 'File';
- }
- .content {
- overflow: hidden;
- min-width: var(--content-width, 80ch);
- }
- .content.left {
- -webkit-user-select: var(--left-user-select, text);
- -moz-user-select: var(--left-user-select, text);
- -ms-user-select: var(--left-user-select, text);
- user-select: var(--left-user-select, text);
- }
- .content.right {
- -webkit-user-select: var(--right-user-select, text);
- -moz-user-select: var(--right-user-select, text);
- -ms-user-select: var(--right-user-select, text);
- user-select: var(--right-user-select, text);
- }
- .content.add hl,
- .content.add.darkHighlight {
- background-color: var(--dark-add-highlight-color);
- }
- .content.add.lightHighlight {
- background-color: var(--light-add-highlight-color);
- }
- .content.remove hl,
- .content.remove.darkHighlight {
- background-color: var(--dark-remove-highlight-color);
- }
- .content.remove.lightHighlight {
- background-color: var(--light-remove.highlight-color);
- }
- .contextControl {
- color: #849;
- background-color: #fef;
- }
- .contextControl gr-button {
- display: block;
- font-family: var(--monospace-font-family);
- text-decoration: none;
- }
- .contextControl td:not(.lineNum) {
- text-align: center;
- }
- .br:after {
- /* Line feed */
- content: '\A';
- }
- .tab {
- display: inline-block;
- }
- .tab.withIndicator:before {
- color: #C62828;
- /* >> character */
- content: '\00BB';
- }
- </style>
- <div class="loading" hidden$="[[!_loading]]">Loading...</div>
- <div hidden$="[[_loading]]" hidden>
- <div class="header">
- <gr-patch-range-select
- path="[[path]]"
- change-num="[[changeNum]]"
- patch-range="[[patchRange]]"
- available-patches="[[availablePatches]]"></gr-patch-range-select>
- <gr-button link
- class="prefsButton"
- on-tap="_handlePrefsTap"
- hidden$="[[_computePrefsButtonHidden(_prefs, _loggedIn)]]"
- hidden>Diff View Preferences</gr-button>
- </div>
- <gr-overlay id="prefsOverlay" with-backdrop>
- <gr-diff-preferences
- prefs="{{_prefs}}"
- on-save="_handlePrefsSave"
- on-cancel="_handlePrefsCancel"></gr-diff-preferences>
- </gr-overlay>
-
- <div class$="[[_computeContainerClass(_loggedIn, _viewMode)]]"
- on-tap="_handleTap"
- on-mousedown="_handleMouseDown"
- on-copy="_handleCopy">
- <table id="diffTable"></table>
- </div>
- </div>
- <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
- </template>
- <script src="gr-diff-line.js"></script>
- <script src="gr-diff-group.js"></script>
- <script src="gr-diff-builder.js"></script>
- <script src="gr-diff-builder-side-by-side.js"></script>
- <script src="gr-diff-builder-unified.js"></script>
- <script src="gr-new-diff.js"></script>
-</dom-module>
diff --git a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-new-diff.js b/polygerrit-ui/app/elements/diff/gr-new-diff/gr-new-diff.js
deleted file mode 100644
index aafea43..0000000
--- a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-new-diff.js
+++ /dev/null
@@ -1,516 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-(function() {
- 'use strict';
-
- var DiffViewMode = {
- SIDE_BY_SIDE: 'SIDE_BY_SIDE',
- UNIFIED: 'UNIFIED_DIFF',
- };
-
- var DiffSide = {
- LEFT: 'left',
- RIGHT: 'right',
- };
-
- Polymer({
- is: 'gr-new-diff',
-
- /**
- * Fired when the diff is rendered.
- *
- * @event render
- */
-
- properties: {
- availablePatches: Array,
- changeNum: String,
- patchRange: Object,
- path: String,
-
- projectConfig: {
- type: Object,
- observer: '_projectConfigChanged',
- },
-
- _loggedIn: {
- type: Boolean,
- value: false,
- },
- _loading: {
- type: Boolean,
- value: true,
- },
- _viewMode: {
- type: String,
- value: DiffViewMode.SIDE_BY_SIDE,
- },
- _diff: Object,
- _diffBuilder: Object,
- _prefs: Object,
- _selectionSide: {
- type: String,
- observer: '_selectionSideChanged',
- },
- _comments: Object,
- _focusedSection: {
- type: Number,
- value: -1,
- },
- _focusedThread: {
- type: Number,
- value: -1,
- },
- },
-
- observers: [
- '_prefsChanged(_prefs.*)',
- ],
-
- attached: function() {
- this._getLoggedIn().then(function(loggedIn) {
- this._loggedIn = loggedIn;
- }.bind(this));
-
- this.addEventListener('thread-discard',
- this._handleThreadDiscard.bind(this));
- this.addEventListener('comment-discard',
- this._handleCommentDiscard.bind(this));
- },
-
- reload: function() {
- this._clearDiffContent();
- this._loading = true;
-
- var promises = [];
-
- promises.push(this._getDiff().then(function(diff) {
- this._diff = diff;
- this._loading = false;
- }.bind(this)));
-
- promises.push(this._getDiffCommentsAndDrafts().then(function(comments) {
- this._comments = comments;
- }.bind(this)));
-
- promises.push(this._getDiffPreferences().then(function(prefs) {
- this._prefs = prefs;
- }.bind(this)));
-
- return Promise.all(promises).then(function() {
- this._render();
- }.bind(this));
- },
-
- scrollToLine: function(lineNum) {
- if (isNaN(lineNum) || lineNum < 1) { return; }
-
- var lineEls = Polymer.dom(this.root).querySelectorAll(
- '.lineNum[data-value="' + lineNum + '"]');
-
- // Always choose the right side.
- var el = lineEls.length === 2 ? lineEls[1] : lineEls[0];
- this._scrollToElement(el);
- },
-
- scrollToNextDiffChunk: function() {
- this._focusedSection = this._advanceElementWithinNodeList(
- this._getDeltaSections(), this._focusedSection, 1);
- },
-
- scrollToPreviousDiffChunk: function() {
- this._focusedSection = this._advanceElementWithinNodeList(
- this._getDeltaSections(), this._focusedSection, -1);
- },
-
- scrollToNextCommentThread: function() {
- this._focusedThread = this._advanceElementWithinNodeList(
- this._getCommentThreads(), this._focusedThread, 1);
- },
-
- scrollToPreviousCommentThread: function() {
- this._focusedThread = this._advanceElementWithinNodeList(
- this._getCommentThreads(), this._focusedThread, -1);
- },
-
- _advanceElementWithinNodeList: function(els, curIndex, direction) {
- var idx = Math.max(0, Math.min(els.length - 1, curIndex + direction));
- if (curIndex !== idx) {
- this._scrollToElement(els[idx]);
- return idx;
- }
- return curIndex;
- },
-
- _getCommentThreads: function() {
- return Polymer.dom(this.root).querySelectorAll('gr-diff-comment-thread');
- },
-
- _getDeltaSections: function() {
- return Polymer.dom(this.root).querySelectorAll('.section.delta');
- },
-
- _scrollToElement: function(el) {
- if (!el) { return; }
-
- // Calculate where the element is relative to the window.
- var top = el.offsetTop;
- for (var offsetParent = el.offsetParent;
- offsetParent;
- offsetParent = offsetParent.offsetParent) {
- top += offsetParent.offsetTop;
- }
-
- // Scroll the element to the middle of the window. Dividing by a third
- // instead of half the inner height feels a bit better otherwise the
- // element appears to be below the center of the window even when it
- // isn't.
- window.scrollTo(0, top - (window.innerHeight / 3) +
- (el.offsetHeight / 2));
- },
-
- _computeContainerClass: function(loggedIn, viewMode) {
- var classes = ['diffContainer'];
- switch (viewMode) {
- case DiffViewMode.UNIFIED:
- classes.push('unified');
- break;
- case DiffViewMode.SIDE_BY_SIDE:
- classes.push('sideBySide');
- break
- default:
- throw Error('Invalid view mode: ', viewMode);
- }
- if (loggedIn) {
- classes.push('canComment');
- }
- return classes.join(' ');
- },
-
- _computePrefsButtonHidden: function(prefs, loggedIn) {
- return !loggedIn || !prefs;
- },
-
- _handlePrefsTap: function(e) {
- e.preventDefault();
- this.$.prefsOverlay.open();
- },
-
- _handlePrefsSave: function(e) {
- e.stopPropagation();
- var el = Polymer.dom(e).rootTarget;
- el.disabled = true;
- this._saveDiffPreferences().then(function() {
- this.$.prefsOverlay.close();
- el.disabled = false;
- }.bind(this)).catch(function(err) {
- el.disabled = false;
- alert('Oops. Something went wrong. Check the console and bug the ' +
- 'PolyGerrit team for assistance.');
- throw err;
- });
- },
-
- _saveDiffPreferences: function() {
- return this.$.restAPI.saveDiffPreferences(this._prefs);
- },
-
- _handlePrefsCancel: function(e) {
- e.stopPropagation();
- this.$.prefsOverlay.close();
- },
-
- _handleTap: function(e) {
- var el = Polymer.dom(e).rootTarget;
-
- if (el.classList.contains('showContext')) {
- this._showContext(e.detail.group, e.detail.section);
- } else if (el.classList.contains('lineNum')) {
- this._handleLineTap(el);
- }
- },
-
- _handleLineTap: function(el) {
- this._getLoggedIn().then(function(loggedIn) {
- if (!loggedIn) { return; }
-
- var value = el.getAttribute('data-value');
- if (value === GrDiffLine.FILE) {
- this._addDraft(el);
- return;
- }
- var lineNum = parseInt(value, 10);
- if (isNaN(lineNum)) {
- throw Error('Invalid line number: ' + value);
- }
- this._addDraft(el, lineNum);
- }.bind(this));
- },
-
- _addDraft: function(lineEl, opt_lineNum) {
- var threadEl;
-
- // Does a thread already exist at this line?
- var contentEl = lineEl.nextSibling;
- while (contentEl && !contentEl.classList.contains('content')) {
- contentEl = contentEl.nextSibling;
- }
- if (contentEl.childNodes.length > 0 &&
- contentEl.lastChild.nodeName === 'GR-DIFF-COMMENT-THREAD') {
- threadEl = contentEl.lastChild;
- } else {
- var patchNum = this.patchRange.patchNum;
- var side = 'REVISION';
- if (contentEl.classList.contains(DiffSide.LEFT) ||
- contentEl.classList.contains('remove')) {
- if (this.patchRange.basePatchNum === 'PARENT') {
- side = 'PARENT';
- } else {
- patchNum = this.patchRange.basePatchNum;
- }
- }
- threadEl = this._builder.createCommentThread(this.changeNum, patchNum,
- this.path, side, this.projectConfig);
- contentEl.appendChild(threadEl);
- }
- threadEl.addDraft(opt_lineNum);
- },
-
- _handleThreadDiscard: function(e) {
- var el = Polymer.dom(e).rootTarget;
- el.parentNode.removeChild(el);
- },
-
- _handleCommentDiscard: function(e) {
- var comment = Polymer.dom(e).rootTarget.comment;
- this._removeComment(comment);
- },
-
- _removeComment: function(comment) {
- if (!comment.id) { return; }
- this._removeCommentFromSide(comment, DiffSide.LEFT) ||
- this._removeCommentFromSide(comment, DiffSide.RIGHT);
- },
-
- _removeCommentFromSide: function(comment, side) {
- var idx = -1;
- for (var i = 0; i < this._comments[side].length; i++) {
- if (this._comments[side][i].id === comment.id) {
- idx = i;
- break;
- }
- }
- if (idx !== -1) {
- this.splice('_comments.' + side, idx, 1);
- return true;
- }
- return false;
- },
-
- _handleMouseDown: function(e) {
- var el = Polymer.dom(e).rootTarget;
- var side;
- for (var node = el; node != null; node = node.parentNode) {
- if (!node.classList) { continue; }
-
- if (node.classList.contains(DiffSide.LEFT)) {
- side = DiffSide.LEFT;
- break;
- } else if (node.classList.contains(DiffSide.RIGHT)) {
- side = DiffSide.RIGHT;
- break;
- }
- }
- this._selectionSide = side;
- },
-
- _selectionSideChanged: function(side) {
- if (side) {
- var oppositeSide = side === DiffSide.RIGHT ?
- DiffSide.LEFT : DiffSide.RIGHT;
- this.customStyle['--' + side + '-user-select'] = 'text';
- this.customStyle['--' + oppositeSide + '-user-select'] = 'none';
- } else {
- this.customStyle['--left-user-select'] = 'text';
- this.customStyle['--right-user-select'] = 'text';
- }
- this.updateStyles();
- },
-
- _handleCopy: function(e) {
- var text = this._getSelectedText(this._selectionSide);
- e.clipboardData.setData('Text', text);
- e.preventDefault();
- },
-
- _getSelectedText: function(opt_side) {
- var sel = window.getSelection();
- var range = sel.getRangeAt(0);
- var doc = range.cloneContents();
- var selector = '.content';
- if (opt_side) {
- selector += '.' + opt_side;
- }
- var contentEls = Polymer.dom(doc).querySelectorAll(selector);
-
- if (contentEls.length === 0) {
- return doc.textContent;
- }
-
- var text = '';
- for (var i = 0; i < contentEls.length; i++) {
- text += contentEls[i].textContent + '\n';
- }
- return text;
- },
-
- _showContext: function(group, sectionEl) {
- this._builder.emitGroup(group, sectionEl);
- sectionEl.parentNode.removeChild(sectionEl);
- },
-
- _prefsChanged: function(prefsChangeRecord) {
- var prefs = prefsChangeRecord.base;
- this.customStyle['--content-width'] = prefs.line_length + 'ch';
- this.updateStyles();
-
- if (this._diff && this._comments) {
- this._render();
- }
- },
-
- _render: function() {
- this._clearDiffContent();
- this._builder = this._getDiffBuilder(this._diff, this._comments,
- this._prefs);
- this._builder.emitDiff(this._diff.content);
-
- this.async(function() {
- this.fire('render', null, {bubbles: false});
- }.bind(this), 1);
- },
-
- _clearDiffContent: function() {
- this.$.diffTable.innerHTML = null;
- },
-
- _getDiff: function() {
- return this.$.restAPI.getDiff(
- this.changeNum,
- this.patchRange.basePatchNum,
- this.patchRange.patchNum,
- this.path);
- },
-
- _getDiffComments: function() {
- return this.$.restAPI.getDiffComments(
- this.changeNum,
- this.patchRange.basePatchNum,
- this.patchRange.patchNum,
- this.path);
- },
-
- _getDiffDrafts: function() {
- return this._getLoggedIn().then(function(loggedIn) {
- if (!loggedIn) {
- return Promise.resolve({baseComments: [], comments: []});
- }
- return this.$.restAPI.getDiffDrafts(
- this.changeNum,
- this.patchRange.basePatchNum,
- this.patchRange.patchNum,
- this.path);
- }.bind(this));
- },
-
- _getDiffCommentsAndDrafts: function() {
- var promises = [];
- promises.push(this._getDiffComments());
- promises.push(this._getDiffDrafts());
- return Promise.all(promises).then(function(results) {
- return Promise.resolve({
- comments: results[0],
- drafts: results[1],
- });
- }).then(this._normalizeDiffCommentsAndDrafts.bind(this));
- },
-
- _getDiffPreferences: function() {
- return this._getLoggedIn().then(function(loggedIn) {
- if (!loggedIn) {
- // These defaults should match the defaults in
- // gerrit-extension-api/src/main/jcg/gerrit/extensions/client/DiffPreferencesInfo.java
- // NOTE: There are some settings that don't apply to PolyGerrit
- // (Render mode being at least one of them).
- return Promise.resolve({
- auto_hide_diff_table_header: true,
- context: 10,
- cursor_blink_rate: 0,
- ignore_whitespace: 'IGNORE_NONE',
- intraline_difference: true,
- line_length: 100,
- show_line_endings: true,
- show_tabs: true,
- show_whitespace_errors: true,
- syntax_highlighting: true,
- tab_size: 8,
- theme: 'DEFAULT',
- });
- }
- return this.$.restAPI.getDiffPreferences();
- }.bind(this));
- },
-
- _normalizeDiffCommentsAndDrafts: function(results) {
- function markAsDraft(d) {
- d.__draft = true;
- return d;
- }
- var baseDrafts = results.drafts.baseComments.map(markAsDraft);
- var drafts = results.drafts.comments.map(markAsDraft);
- return Promise.resolve({
- meta: {
- path: this.path,
- changeNum: this.changeNum,
- patchRange: this.patchRange,
- projectConfig: this.projectConfig,
- },
- left: results.comments.baseComments.concat(baseDrafts),
- right: results.comments.comments.concat(drafts),
- });
- },
-
- _getLoggedIn: function() {
- return this.$.restAPI.getLoggedIn();
- },
-
- _getDiffBuilder: function(diff, comments, prefs) {
- if (this._viewMode === DiffViewMode.SIDE_BY_SIDE) {
- return new GrDiffBuilderSideBySide(diff, comments, prefs,
- this.$.diffTable);
- } else if (this._viewMode === DiffViewMode.UNIFIED) {
- return new GrDiffBuilderUnified(diff, comments, prefs,
- this.$.diffTable);
- }
- throw Error('Unsupported diff view mode: ' + this._viewMode);
- },
-
- _projectConfigChanged: function(projectConfig) {
- var threadEls = this._getCommentThreads();
- for (var i = 0; i < threadEls.length; i++) {
- threadEls[i].projectConfig = projectConfig;
- }
- },
- });
-})();
diff --git a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-new-diff_test.html b/polygerrit-ui/app/elements/diff/gr-new-diff/gr-new-diff_test.html
deleted file mode 100644
index b015df4..0000000
--- a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-new-diff_test.html
+++ /dev/null
@@ -1,244 +0,0 @@
-<!DOCTYPE html>
-<!--
-Copyright (C) 2015 The Android Open Source Project
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
--->
-
-<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
-<title>gr-new-diff</title>
-
-<script src="../../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
-<link rel="import" href="gr-new-diff.html">
-
-<test-fixture id="basic">
- <template>
- <gr-new-diff></gr-new-diff>
- </template>
-</test-fixture>
-
-<script>
- suite('gr-new-diff tests', function() {
- var element;
-
- setup(function() {
- stub('gr-rest-api-interface', {
- getLoggedIn: function() { return Promise.resolve(false); },
- })
- element = fixture('basic');
- });
-
- test('get drafts logged out', function(done) {
- element.patchRange = {basePatchNum: 0, patchNum: 0};
-
- var getDraftsStub = sinon.stub(element.$.restAPI, 'getDiffDrafts');
- var loggedInStub = sinon.stub(element, '_getLoggedIn',
- function() { return Promise.resolve(false); });
- element._getDiffDrafts().then(function(result) {
- assert.deepEqual(result, {baseComments: [], comments: []});
- sinon.assert.notCalled(getDraftsStub);
- loggedInStub.restore();
- getDraftsStub.restore();
- done();
- });
- });
-
- test('get drafts logged in', function(done) {
- element.patchRange = {basePatchNum: 0, patchNum: 0};
- var draftsResponse = {
- baseComments: [{id: 'foo'}],
- comments: [{id: 'bar'}],
- };
- var getDraftsStub = sinon.stub(element.$.restAPI, 'getDiffDrafts',
- function() { return Promise.resolve(draftsResponse); });
- var loggedInStub = sinon.stub(element, '_getLoggedIn',
- function() { return Promise.resolve(true); });
- element._getDiffDrafts().then(function(result) {
- assert.deepEqual(result, draftsResponse);
- loggedInStub.restore();
- getDraftsStub.restore();
- done();
- });
- });
-
- test('get comments and drafts', function(done) {
- var loggedInStub = sinon.stub(element, '_getLoggedIn',
- function() { return Promise.resolve(true); });
- var comments = {
- baseComments: [
- {id: 'bc1'},
- {id: 'bc2'},
- ],
- comments: [
- {id: 'c1'},
- {id: 'c2'},
- ],
- };
- var diffCommentsStub = sinon.stub(element, '_getDiffComments',
- function() { return Promise.resolve(comments); });
-
- var drafts = {
- baseComments: [
- {id: 'bd1'},
- {id: 'bd2'},
- ],
- comments: [
- {id: 'd1'},
- {id: 'd2'},
- ],
- };
- var diffDraftsStub = sinon.stub(element, '_getDiffDrafts',
- function() { return Promise.resolve(drafts); });
-
- element.changeNum = '42';
- element.patchRange = {
- basePatchNum: 'PARENT',
- patchNum: 3,
- };
- element.path = '/path/to/foo';
- element.projectConfig = {foo: 'bar'};
-
- element._getDiffCommentsAndDrafts().then(function(result) {
- assert.deepEqual(result, {
- meta: {
- changeNum: '42',
- patchRange: {
- basePatchNum: 'PARENT',
- patchNum: 3,
- },
- path: '/path/to/foo',
- projectConfig: {foo: 'bar'},
- },
- left: [
- {id: 'bc1'},
- {id: 'bc2'},
- {id: 'bd1', __draft: true},
- {id: 'bd2', __draft: true},
- ],
- right: [
- {id: 'c1'},
- {id: 'c2'},
- {id: 'd1', __draft: true},
- {id: 'd2', __draft: true},
- ],
- });
-
- diffCommentsStub.restore();
- diffDraftsStub.restore();
- loggedInStub.restore();
- done();
- });
- });
-
- test('remove comment', function() {
- element._comments = {
- meta: {
- changeNum: '42',
- patchRange: {
- basePatchNum: 'PARENT',
- patchNum: 3,
- },
- path: '/path/to/foo',
- projectConfig: {foo: 'bar'},
- },
- left: [
- {id: 'bc1'},
- {id: 'bc2'},
- {id: 'bd1', __draft: true},
- {id: 'bd2', __draft: true},
- ],
- right: [
- {id: 'c1'},
- {id: 'c2'},
- {id: 'd1', __draft: true},
- {id: 'd2', __draft: true},
- ],
- };
-
- element._removeComment({});
- assert.deepEqual(element._comments, {
- meta: {
- changeNum: '42',
- patchRange: {
- basePatchNum: 'PARENT',
- patchNum: 3,
- },
- path: '/path/to/foo',
- projectConfig: {foo: 'bar'},
- },
- left: [
- {id: 'bc1'},
- {id: 'bc2'},
- {id: 'bd1', __draft: true},
- {id: 'bd2', __draft: true},
- ],
- right: [
- {id: 'c1'},
- {id: 'c2'},
- {id: 'd1', __draft: true},
- {id: 'd2', __draft: true},
- ],
- });
-
- element._removeComment({id: 'bc2'});
- assert.deepEqual(element._comments, {
- meta: {
- changeNum: '42',
- patchRange: {
- basePatchNum: 'PARENT',
- patchNum: 3,
- },
- path: '/path/to/foo',
- projectConfig: {foo: 'bar'},
- },
- left: [
- {id: 'bc1'},
- {id: 'bd1', __draft: true},
- {id: 'bd2', __draft: true},
- ],
- right: [
- {id: 'c1'},
- {id: 'c2'},
- {id: 'd1', __draft: true},
- {id: 'd2', __draft: true},
- ],
- });
-
- element._removeComment({id: 'd2'});
- assert.deepEqual(element._comments, {
- meta: {
- changeNum: '42',
- patchRange: {
- basePatchNum: 'PARENT',
- patchNum: 3,
- },
- path: '/path/to/foo',
- projectConfig: {foo: 'bar'},
- },
- left: [
- {id: 'bc1'},
- {id: 'bd1', __draft: true},
- {id: 'bd2', __draft: true},
- ],
- right: [
- {id: 'c1'},
- {id: 'c2'},
- {id: 'd1', __draft: true},
- ],
- });
- });
- });
-</script>