| <!-- |
| 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"> |
| <link rel="import" href="../gr-diff-processor/gr-diff-processor.html"> |
| |
| <dom-module id="gr-diff-builder"> |
| <template> |
| <div class="contentWrapper"> |
| <content></content> |
| </div> |
| <gr-diff-processor |
| id="processor" |
| groups="{{_groups}}"></gr-diff-processor> |
| </template> |
| <script src="../gr-diff/gr-diff-line.js"></script> |
| <script src="../gr-diff/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-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, |
| _groups: Array, |
| }, |
| |
| get diffElement() { |
| return this.queryEffectiveChildren('#diffTable'); |
| }, |
| |
| observers: [ |
| '_groupsChanged(_groups.splices)', |
| ], |
| |
| render: function(diff, comments, prefs) { |
| // Stop the processor (if it's running). |
| this.$.processor.cancel(); |
| |
| this._builder = this._getDiffBuilder(diff, comments, prefs); |
| |
| this.$.processor.context = prefs.context; |
| this.$.processor.keyLocations = this._getCommentLocations(comments); |
| |
| this._clearDiffContent(); |
| |
| this.$.processor.process(diff.content).then(function() { |
| if (this.isImageDiff) { |
| this._builder.renderDiffImages(); |
| } |
| this.fire('render'); |
| }.bind(this)); |
| }, |
| |
| 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; |
| } |
| return null; |
| }, |
| |
| getLineNumberByChild: function(node) { |
| var lineEl = this.getLineElByChild(node); |
| return lineEl ? |
| parseInt(lineEl.getAttribute('data-value'), 10) : null; |
| }, |
| |
| 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) { |
| var root = Polymer.dom(opt_root || this.diffElement); |
| var sideSelector = !!opt_side ? ('.' + opt_side) : ''; |
| return root.querySelector('td.lineNum[data-value="' + lineNumber + |
| '"]' + sideSelector + ' ~ td.content'); |
| }, |
| |
| 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) { |
| var groups = |
| this._builder.getGroupsByLineRange(startLine, endLine, opt_side); |
| // In each group, search Element for lines in range. |
| return groups.reduce((function(acc, group) { |
| for (var line = startLine; line <= endLine; line++) { |
| var content = |
| this.getContentByLine(line, opt_side, group.element); |
| if (content) { |
| acc.push(content); |
| } |
| } |
| return acc; |
| }).bind(this), []); |
| }, |
| |
| getCommentThreadByLine: function(lineNumber, opt_side, opt_root) { |
| var root = Polymer.dom(opt_root || this.diffElement); |
| var sideSelector = !!opt_side ? ('.' + opt_side) : ''; |
| var content = this.getContentByLine(lineNumber, opt_side, opt_root); |
| return this.getCommentThreadByContentEl(content); |
| }, |
| |
| getCommentThreadByContentEl: function(contentEl) { |
| return contentEl.querySelector('gr-diff-comment-thread'); |
| }, |
| |
| getSideByLineEl: function(lineEl) { |
| return lineEl.classList.contains(GrDiffBuilder.Side.RIGHT) ? |
| GrDiffBuilder.Side.RIGHT : GrDiffBuilder.Side.LEFT; |
| }, |
| |
| 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) { |
| var groups = this._builder.groups; |
| // 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); |
| }, this); |
| sectionEl.parentNode.removeChild(sectionEl); |
| |
| this.async(function() { |
| this.fire('render'); |
| }, 1); |
| }, |
| |
| _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; |
| }, |
| |
| _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; |
| }, |
| |
| _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); |
| }, |
| }); |
| })(); |
| </script> |
| </dom-module> |