2016-06-01 11:41:47 -07:00
|
|
|
<!--
|
|
|
|
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.
|
|
|
|
-->
|
|
|
|
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
2016-06-27 12:19:21 -07:00
|
|
|
<link rel="import" href="../gr-diff-processor/gr-diff-processor.html">
|
2016-06-01 11:41:47 -07:00
|
|
|
|
|
|
|
<dom-module id="gr-diff-builder">
|
|
|
|
<template>
|
|
|
|
<div class="contentWrapper">
|
|
|
|
<content></content>
|
|
|
|
</div>
|
2016-06-28 16:40:46 -07:00
|
|
|
<gr-diff-processor
|
|
|
|
id="processor"
|
|
|
|
groups="{{_groups}}"></gr-diff-processor>
|
2016-06-01 11:41:47 -07:00
|
|
|
</template>
|
|
|
|
<script src="../gr-diff/gr-diff-line.js"></script>
|
|
|
|
<script src="../gr-diff/gr-diff-group.js"></script>
|
2016-07-13 10:59:10 -07:00
|
|
|
<script src="../gr-diff-highlight/gr-annotation.js"></script>
|
2016-06-01 11:41:47 -07:00
|
|
|
<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-builder-image.js"></script>
|
|
|
|
<script>
|
|
|
|
(function() {
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
var DiffViewMode = {
|
|
|
|
SIDE_BY_SIDE: 'SIDE_BY_SIDE',
|
|
|
|
UNIFIED: 'UNIFIED_DIFF',
|
|
|
|
};
|
|
|
|
|
|
|
|
Polymer({
|
|
|
|
is: 'gr-diff-builder',
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fired when the diff is rendered.
|
|
|
|
*
|
|
|
|
* @event render
|
|
|
|
*/
|
|
|
|
|
|
|
|
properties: {
|
|
|
|
viewMode: String,
|
|
|
|
isImageDiff: Boolean,
|
|
|
|
baseImage: Object,
|
|
|
|
revisionImage: Object,
|
|
|
|
_builder: Object,
|
2016-06-28 16:40:46 -07:00
|
|
|
_groups: Array,
|
2016-06-01 11:41:47 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
get diffElement() {
|
|
|
|
return this.queryEffectiveChildren('#diffTable');
|
|
|
|
},
|
|
|
|
|
2016-06-28 16:40:46 -07:00
|
|
|
observers: [
|
|
|
|
'_groupsChanged(_groups.splices)',
|
|
|
|
],
|
|
|
|
|
2016-06-01 11:41:47 -07:00
|
|
|
render: function(diff, comments, prefs) {
|
2016-06-28 16:40:46 -07:00
|
|
|
// Stop the processor (if it's running).
|
|
|
|
this.$.processor.cancel();
|
|
|
|
|
2016-06-01 11:41:47 -07:00
|
|
|
this._builder = this._getDiffBuilder(diff, comments, prefs);
|
2016-06-27 12:19:21 -07:00
|
|
|
|
|
|
|
this.$.processor.context = prefs.context;
|
|
|
|
this.$.processor.keyLocations = this._getCommentLocations(comments);
|
2016-06-28 16:40:46 -07:00
|
|
|
|
|
|
|
this._clearDiffContent();
|
|
|
|
|
2016-07-12 14:11:01 -07:00
|
|
|
console.time('diff render');
|
2016-06-28 16:40:46 -07:00
|
|
|
this.$.processor.process(diff.content).then(function() {
|
|
|
|
if (this.isImageDiff) {
|
|
|
|
this._builder.renderDiffImages();
|
|
|
|
}
|
2016-07-12 14:11:01 -07:00
|
|
|
console.timeEnd('diff render');
|
2016-06-28 16:40:46 -07:00
|
|
|
this.fire('render');
|
|
|
|
}.bind(this));
|
2016-06-01 11:41:47 -07:00
|
|
|
},
|
|
|
|
|
2016-06-09 16:39:17 -07:00
|
|
|
getLineElByChild: function(node) {
|
|
|
|
while (node) {
|
|
|
|
if (node instanceof Element) {
|
|
|
|
if (node.classList.contains('lineNum')) {
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
if (node.classList.contains('section')) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
node = node.previousSibling || node.parentElement;
|
2016-06-15 11:57:36 -07:00
|
|
|
}
|
2016-06-09 16:39:17 -07:00
|
|
|
return null;
|
|
|
|
},
|
|
|
|
|
2016-06-06 12:20:31 -07:00
|
|
|
getLineNumberByChild: function(node) {
|
|
|
|
var lineEl = this.getLineElByChild(node);
|
|
|
|
return lineEl ?
|
|
|
|
parseInt(lineEl.getAttribute('data-value'), 10) : null;
|
|
|
|
},
|
|
|
|
|
2016-06-09 16:08:04 -07:00
|
|
|
renderLineRange: function(startLine, endLine, opt_side) {
|
|
|
|
var groups =
|
|
|
|
this._builder.getGroupsByLineRange(startLine, endLine, opt_side);
|
|
|
|
groups.forEach(function(group) {
|
|
|
|
var newElement = this._builder.buildSectionElement(group);
|
|
|
|
var oldElement = group.element;
|
|
|
|
|
|
|
|
// Transfer comment threads from existing section to new one.
|
|
|
|
var threads = Polymer.dom(newElement).querySelectorAll(
|
|
|
|
'gr-diff-comment-thread');
|
|
|
|
threads.forEach(function(threadEl) {
|
|
|
|
var lineEl = this.getLineElByChild(threadEl, oldElement);
|
|
|
|
if (!lineEl) { // New comment thread.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var side = this.getSideByLineEl(lineEl);
|
|
|
|
var line = lineEl.getAttribute('data-value');
|
|
|
|
var oldThreadEl =
|
|
|
|
this.getCommentThreadByLine(line, side, oldElement);
|
|
|
|
threadEl.parentNode.replaceChild(oldThreadEl, threadEl);
|
|
|
|
}, this);
|
|
|
|
|
|
|
|
// Replace old group elements with new ones.
|
|
|
|
group.element.parentNode.replaceChild(newElement, group.element);
|
|
|
|
group.element = newElement;
|
|
|
|
}, this);
|
|
|
|
|
|
|
|
this.async(function() {
|
|
|
|
this.fire('render');
|
|
|
|
}, 1);
|
|
|
|
},
|
|
|
|
|
|
|
|
getContentByLine: function(lineNumber, opt_side, opt_root) {
|
2016-07-15 17:08:22 -07:00
|
|
|
return this._builder.getContentByLine(lineNumber, opt_side, opt_root);
|
2016-06-09 16:08:04 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
getContentByLineEl: function(lineEl) {
|
|
|
|
var root = Polymer.dom(lineEl.parentElement);
|
|
|
|
var side = this.getSideByLineEl(lineEl);
|
|
|
|
var line = lineEl.getAttribute('data-value');
|
|
|
|
return this.getContentByLine(line, side, root);
|
|
|
|
},
|
|
|
|
|
|
|
|
getLineElByNumber: function(lineNumber, opt_side) {
|
|
|
|
var sideSelector = !!opt_side ? ('.' + opt_side) : '';
|
|
|
|
return this.diffElement.querySelector(
|
|
|
|
'.lineNum[data-value="' + lineNumber + '"]' + sideSelector);
|
|
|
|
},
|
|
|
|
|
|
|
|
getContentsByLineRange: function(startLine, endLine, opt_side) {
|
2016-07-15 17:08:22 -07:00
|
|
|
var result = [];
|
|
|
|
this._builder.findLinesByRange(startLine, endLine, opt_side, null,
|
|
|
|
result);
|
|
|
|
return result;
|
2016-06-09 16:08:04 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
getCommentThreadByLine: function(lineNumber, opt_side, opt_root) {
|
|
|
|
var content = this.getContentByLine(lineNumber, opt_side, opt_root);
|
2016-06-22 10:32:00 -07:00
|
|
|
return this.getCommentThreadByContentEl(content);
|
|
|
|
},
|
|
|
|
|
|
|
|
getCommentThreadByContentEl: function(contentEl) {
|
2016-07-01 09:30:44 -07:00
|
|
|
if (contentEl.classList.contains('contentText')) {
|
|
|
|
contentEl = contentEl.parentElement;
|
|
|
|
}
|
2016-06-22 10:32:00 -07:00
|
|
|
return contentEl.querySelector('gr-diff-comment-thread');
|
2016-06-09 16:08:04 -07:00
|
|
|
},
|
|
|
|
|
2016-06-09 16:39:17 -07:00
|
|
|
getSideByLineEl: function(lineEl) {
|
|
|
|
return lineEl.classList.contains(GrDiffBuilder.Side.RIGHT) ?
|
2016-06-09 16:08:04 -07:00
|
|
|
GrDiffBuilder.Side.RIGHT : GrDiffBuilder.Side.LEFT;
|
2016-06-09 16:39:17 -07:00
|
|
|
},
|
|
|
|
|
2016-06-01 11:41:47 -07:00
|
|
|
createCommentThread: function(changeNum, patchNum, path, side,
|
|
|
|
projectConfig) {
|
|
|
|
return this._builder.createCommentThread(changeNum, patchNum, path,
|
|
|
|
side, projectConfig);
|
|
|
|
},
|
|
|
|
|
|
|
|
emitGroup: function(group, sectionEl) {
|
|
|
|
this._builder.emitGroup(group, sectionEl);
|
|
|
|
},
|
|
|
|
|
|
|
|
showContext: function(newGroups, sectionEl) {
|
2016-06-27 12:19:21 -07:00
|
|
|
var groups = this._builder.groups;
|
2016-06-01 11:41:47 -07:00
|
|
|
// TODO(viktard): Polyfill findIndex for IE10.
|
|
|
|
var contextIndex = groups.findIndex(function(group) {
|
|
|
|
return group.element == sectionEl;
|
|
|
|
});
|
|
|
|
groups.splice.apply(groups, [contextIndex, 1].concat(newGroups));
|
|
|
|
|
|
|
|
newGroups.forEach(function(newGroup) {
|
|
|
|
this._builder.emitGroup(newGroup, sectionEl);
|
2016-06-09 16:08:04 -07:00
|
|
|
}, this);
|
2016-06-01 11:41:47 -07:00
|
|
|
sectionEl.parentNode.removeChild(sectionEl);
|
|
|
|
|
|
|
|
this.async(function() {
|
|
|
|
this.fire('render');
|
2016-06-09 16:08:04 -07:00
|
|
|
}, 1);
|
2016-06-01 11:41:47 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
_getDiffBuilder: function(diff, comments, prefs) {
|
|
|
|
if (this.isImageDiff) {
|
|
|
|
return new GrDiffBuilderImage(diff, comments, prefs,
|
|
|
|
this.diffElement, this.baseImage, this.revisionImage);
|
|
|
|
} else if (this.viewMode === DiffViewMode.SIDE_BY_SIDE) {
|
|
|
|
return new GrDiffBuilderSideBySide(
|
|
|
|
diff, comments, prefs, this.diffElement);
|
|
|
|
} else if (this.viewMode === DiffViewMode.UNIFIED) {
|
|
|
|
return new GrDiffBuilderUnified(
|
|
|
|
diff, comments, prefs, this.diffElement);
|
|
|
|
}
|
|
|
|
throw Error('Unsupported diff view mode: ' + this.viewMode);
|
|
|
|
},
|
|
|
|
|
|
|
|
_clearDiffContent: function() {
|
|
|
|
this.diffElement.innerHTML = null;
|
|
|
|
},
|
2016-06-27 12:19:21 -07:00
|
|
|
|
|
|
|
_getCommentLocations: function(comments) {
|
|
|
|
var result = {
|
|
|
|
left: {},
|
|
|
|
right: {},
|
|
|
|
};
|
|
|
|
for (var side in comments) {
|
|
|
|
if (side !== GrDiffBuilder.Side.LEFT &&
|
|
|
|
side !== GrDiffBuilder.Side.RIGHT) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
comments[side].forEach(function(c) {
|
|
|
|
result[side][c.line || GrDiffLine.FILE] = true;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
},
|
2016-06-28 16:40:46 -07:00
|
|
|
|
|
|
|
_groupsChanged: function(changeRecord) {
|
|
|
|
if (!changeRecord) { return; }
|
|
|
|
changeRecord.indexSplices.forEach(function(splice) {
|
|
|
|
var group;
|
|
|
|
for (var i = 0; i < splice.addedCount; i++) {
|
|
|
|
group = splice.object[splice.index + i];
|
|
|
|
this._builder.groups.push(group);
|
|
|
|
this._builder.emitGroup(group);
|
|
|
|
}
|
|
|
|
}, this);
|
|
|
|
},
|
2016-07-19 10:59:31 -07:00
|
|
|
|
|
|
|
_createIntralineLayer: function() {
|
|
|
|
return {
|
|
|
|
addListener: function() {},
|
|
|
|
|
|
|
|
// Take a DIV.contentText element and a line object with intraline
|
|
|
|
// differences to highlight and apply them to the element as
|
|
|
|
// annotations.
|
|
|
|
annotate: function(el, line, GrAnnotation) {
|
|
|
|
var HL_CLASS = 'style-scope gr-diff';
|
|
|
|
line.highlights.forEach(function(highlight) {
|
|
|
|
// 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 (highlight.startIndex === highlight.endIndex) { return; }
|
|
|
|
|
|
|
|
// If endIndex isn't present, continue to the end of the line.
|
|
|
|
var endIndex = highlight.endIndex === undefined ?
|
|
|
|
line.text.length : highlight.endIndex;
|
|
|
|
|
|
|
|
GrAnnotation.annotateElement(
|
|
|
|
el,
|
|
|
|
highlight.startIndex,
|
|
|
|
endIndex - highlight.startIndex,
|
|
|
|
HL_CLASS);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
};
|
|
|
|
},
|
2016-06-01 11:41:47 -07:00
|
|
|
});
|
|
|
|
})();
|
|
|
|
</script>
|
|
|
|
</dom-module>
|