| // 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'; |
| |
| // Maximum length for patch set descriptions. |
| var PATCH_DESC_MAX_LENGTH = 500; |
| |
| var COMMIT_MESSAGE_PATH = '/COMMIT_MSG'; |
| |
| var FileStatus = { |
| A: 'Added', |
| C: 'Copied', |
| D: 'Deleted', |
| R: 'Renamed', |
| W: 'Rewritten', |
| }; |
| |
| Polymer({ |
| is: 'gr-file-list', |
| |
| properties: { |
| patchRange: { |
| type: Object, |
| observer: '_updateSelected', |
| }, |
| patchNum: String, |
| changeNum: String, |
| comments: Object, |
| drafts: Object, |
| revisions: Object, |
| projectConfig: Object, |
| selectedIndex: { |
| type: Number, |
| notify: true, |
| }, |
| keyEventTarget: { |
| type: Object, |
| value: function() { return document.body; }, |
| }, |
| change: Object, |
| diffViewMode: { |
| type: String, |
| notify: true, |
| }, |
| _files: { |
| type: Array, |
| observer: '_filesChanged', |
| value: function() { return []; }, |
| }, |
| _loggedIn: { |
| type: Boolean, |
| value: false, |
| }, |
| _reviewed: { |
| type: Array, |
| value: function() { return []; }, |
| }, |
| _diffAgainst: String, |
| _diffPrefs: Object, |
| _userPrefs: Object, |
| _localPrefs: Object, |
| _showInlineDiffs: Boolean, |
| _numFilesShown: { |
| type: Number, |
| value: 75, |
| }, |
| _patchChange: { |
| type: Object, |
| computed: '_calculatePatchChange(_files)', |
| }, |
| _fileListIncrement: { |
| type: Number, |
| readOnly: true, |
| value: 75, |
| }, |
| _hideChangeTotals: { |
| type: Boolean, |
| computed: '_shouldHideChangeTotals(_patchChange)', |
| }, |
| _hideBinaryChangeTotals: { |
| type: Boolean, |
| computed: '_shouldHideBinaryChangeTotals(_patchChange)', |
| }, |
| _shownFiles: { |
| type: Array, |
| computed: '_computeFilesShown(_numFilesShown, _files.*)', |
| }, |
| // Caps the number of files that can be shown and have the 'show diffs' / |
| // 'hide diffs' buttons still be functional. |
| _maxFilesForBulkActions: { |
| type: Number, |
| readOnly: true, |
| value: 225, |
| }, |
| _expandedFilePaths: { |
| type: Array, |
| value: function() { return []; }, |
| }, |
| }, |
| |
| behaviors: [ |
| Gerrit.KeyboardShortcutBehavior, |
| Gerrit.PatchSetBehavior, |
| Gerrit.URLEncodingBehavior, |
| ], |
| |
| observers: [ |
| '_expandedPathsChanged(_expandedFilePaths.splices)', |
| '_setReviewedFiles(_shownFiles, _files, _reviewed.*, _loggedIn)', |
| ], |
| |
| keyBindings: { |
| 'shift+left': '_handleShiftLeftKey', |
| 'shift+right': '_handleShiftRightKey', |
| 'i': '_handleIKey', |
| 'shift+i': '_handleCapitalIKey', |
| 'down j': '_handleDownKey', |
| 'up k': '_handleUpKey', |
| 'c': '_handleCKey', |
| '[': '_handleLeftBracketKey', |
| ']': '_handleRightBracketKey', |
| 'o enter': '_handleEnterKey', |
| 'n': '_handleNKey', |
| 'p': '_handlePKey', |
| 'shift+a': '_handleCapitalAKey', |
| }, |
| |
| reload: function() { |
| if (!this.changeNum || !this.patchRange.patchNum) { |
| return Promise.resolve(); |
| } |
| this._collapseAllDiffs(); |
| var promises = []; |
| var _this = this; |
| |
| promises.push(this._getFiles().then(function(files) { |
| _this._files = files; |
| })); |
| promises.push(this._getLoggedIn().then(function(loggedIn) { |
| return _this._loggedIn = loggedIn; |
| }).then(function(loggedIn) { |
| if (!loggedIn) { return; } |
| |
| return _this._getReviewedFiles().then(function(reviewed) { |
| _this._reviewed = reviewed; |
| }); |
| })); |
| |
| this._localPrefs = this.$.storage.getPreferences(); |
| promises.push(this._getDiffPreferences().then(function(prefs) { |
| this._diffPrefs = prefs; |
| }.bind(this))); |
| |
| promises.push(this._getPreferences().then(function(prefs) { |
| this._userPrefs = prefs; |
| if (!this.diffViewMode) { |
| this.set('diffViewMode', prefs.default_diff_view); |
| } |
| }.bind(this))); |
| }, |
| |
| get diffs() { |
| return Polymer.dom(this.root).querySelectorAll('gr-diff'); |
| }, |
| |
| _calculatePatchChange: function(files) { |
| var filesNoCommitMsg = files.filter(function(files) { |
| return files.__path !== '/COMMIT_MSG'; |
| }); |
| |
| return filesNoCommitMsg.reduce(function(acc, obj) { |
| var inserted = obj.lines_inserted ? obj.lines_inserted : 0; |
| var deleted = obj.lines_deleted ? obj.lines_deleted : 0; |
| var total_size = (obj.size && obj.binary) ? obj.size : 0; |
| var size_delta_inserted = |
| obj.binary && obj.size_delta > 0 ? obj.size_delta : 0; |
| var size_delta_deleted = |
| obj.binary && obj.size_delta < 0 ? obj.size_delta : 0; |
| |
| return { |
| inserted: acc.inserted + inserted, |
| deleted: acc.deleted + deleted, |
| size_delta_inserted: acc.size_delta_inserted + size_delta_inserted, |
| size_delta_deleted: acc.size_delta_deleted + size_delta_deleted, |
| total_size: acc.total_size + total_size, |
| }; |
| }, {inserted: 0, deleted: 0, size_delta_inserted: 0, |
| size_delta_deleted: 0, total_size: 0}); |
| }, |
| |
| _getDiffPreferences: function() { |
| return this.$.restAPI.getDiffPreferences(); |
| }, |
| |
| _getPreferences: function() { |
| return this.$.restAPI.getPreferences(); |
| }, |
| |
| _computePatchSets: function(revisionRecord) { |
| var revisions = revisionRecord.base; |
| var patchNums = []; |
| for (var commit in revisions) { |
| if (revisions.hasOwnProperty(commit)) { |
| patchNums.push({ |
| num: revisions[commit]._number, |
| desc: revisions[commit].description, |
| }); |
| } |
| } |
| return patchNums.sort(function(a, b) { return a.num - b.num; }); |
| }, |
| |
| _computePatchSetDisabled: function(patchNum, currentPatchNum) { |
| return parseInt(patchNum, 10) >= parseInt(currentPatchNum, 10); |
| }, |
| |
| _handleHiddenChange: function(e) { |
| this._togglePathExpanded(e.model.file.__path); |
| }, |
| |
| _togglePathExpanded: function(path) { |
| // Is the path in the list of expanded diffs? IF so remove it, otherwise |
| // add it to the list. |
| var pathIndex = this._expandedFilePaths.indexOf(path); |
| if (pathIndex === -1) { |
| this.push('_expandedFilePaths', path); |
| } else { |
| this.splice('_expandedFilePaths', pathIndex, 1); |
| } |
| }, |
| |
| _togglePathExpandedByIndex: function(index) { |
| this._togglePathExpanded(this._files[index].__path); |
| }, |
| |
| _handlePatchChange: function(e) { |
| var patchRange = Object.assign({}, this.patchRange); |
| patchRange.basePatchNum = Polymer.dom(e).rootTarget.value; |
| page.show(this.encodeURL('/c/' + this.changeNum + '/' + |
| this._patchRangeStr(patchRange), true)); |
| }, |
| |
| _forEachDiff: function(fn) { |
| var diffs = this.diffs; |
| for (var i = 0; i < diffs.length; i++) { |
| fn(diffs[i]); |
| } |
| }, |
| |
| _expandAllDiffs: function(e) { |
| this._showInlineDiffs = true; |
| |
| // Find the list of paths that are in the file list, but not in the |
| // expanded list. |
| var newPaths = []; |
| var path; |
| for (var i = 0; i < this._shownFiles.length; i++) { |
| path = this._shownFiles[i].__path; |
| if (this._expandedFilePaths.indexOf(path) === -1) { |
| newPaths.push(path); |
| } |
| } |
| |
| this.splice.apply(this, ['_expandedFilePaths', 0, 0].concat(newPaths)); |
| }, |
| |
| _collapseAllDiffs: function(e) { |
| this._showInlineDiffs = false; |
| this._expandedFilePaths = []; |
| this.$.diffCursor.handleDiffUpdate(); |
| }, |
| |
| _computeCommentsString: function(comments, patchNum, path) { |
| return this._computeCountString(comments, patchNum, path, 'comment'); |
| }, |
| |
| _computeDraftsString: function(drafts, patchNum, path) { |
| return this._computeCountString(drafts, patchNum, path, 'draft'); |
| }, |
| |
| _computeDraftsStringMobile: function(drafts, patchNum, path) { |
| var draftCount = this._computeCountString(drafts, patchNum, path); |
| return draftCount ? draftCount + 'd' : ''; |
| }, |
| |
| _computeCommentsStringMobile: function(comments, patchNum, path) { |
| var commentCount = this._computeCountString(comments, patchNum, path); |
| return commentCount ? commentCount + 'c' : ''; |
| }, |
| |
| _getCommentsForPath: function(comments, patchNum, path) { |
| return (comments[path] || []).filter(function(c) { |
| return parseInt(c.patch_set, 10) === parseInt(patchNum, 10); |
| }); |
| }, |
| |
| _computeCountString: function(comments, patchNum, path, opt_noun) { |
| if (!comments) { return ''; } |
| |
| var patchComments = this._getCommentsForPath(comments, patchNum, path); |
| var num = patchComments.length; |
| if (num === 0) { return ''; } |
| if (!opt_noun) { return num; } |
| var output = num + ' ' + opt_noun + (num > 1 ? 's' : ''); |
| return output; |
| }, |
| |
| /** |
| * Computes a string counting the number of unresolved comment threads in a |
| * given file and path. |
| * |
| * @param {Object} comments |
| * @param {Object} drafts |
| * @param {number} patchNum |
| * @param {string} path |
| * @return {string} |
| */ |
| _computeUnresolvedString: function(comments, drafts, patchNum, path) { |
| comments = this._getCommentsForPath(comments, patchNum, path); |
| drafts = this._getCommentsForPath(drafts, patchNum, path); |
| comments = comments.concat(drafts); |
| |
| // Create an object where every comment ID is the key of an unresolved |
| // comment. |
| |
| var idMap = comments.reduce(function(acc, comment) { |
| if (comment.unresolved) { |
| acc[comment.id] = true; |
| } |
| return acc; |
| }, {}); |
| |
| // Set false for the comments that are marked as parents. |
| comments.forEach(function(comment) { |
| idMap[comment.in_reply_to] = false; |
| }); |
| |
| // The unresolved comments are the comments that still have true. |
| var unresolvedLeaves = Object.keys(idMap).filter(function(key) { |
| return idMap[key]; |
| }); |
| |
| return unresolvedLeaves.length === 0 ? |
| '' : '(' + unresolvedLeaves.length + ' unresolved)'; |
| }, |
| |
| _computeReviewed: function(file, _reviewed) { |
| return _reviewed.indexOf(file.__path) !== -1; |
| }, |
| |
| _handleReviewedChange: function(e) { |
| this._reviewFile(Polymer.dom(e).rootTarget.getAttribute('data-path')); |
| }, |
| |
| _reviewFile: function(path) { |
| var index = this._reviewed.indexOf(path); |
| var reviewed = index !== -1; |
| if (reviewed) { |
| this.splice('_reviewed', index, 1); |
| } else { |
| this.push('_reviewed', path); |
| } |
| |
| this._saveReviewedState(path, !reviewed); |
| }, |
| |
| _saveReviewedState: function(path, reviewed) { |
| return this.$.restAPI.saveFileReviewed(this.changeNum, |
| this.patchRange.patchNum, path, reviewed); |
| }, |
| |
| _getLoggedIn: function() { |
| return this.$.restAPI.getLoggedIn(); |
| }, |
| |
| _getReviewedFiles: function() { |
| return this.$.restAPI.getReviewedFiles(this.changeNum, |
| this.patchRange.patchNum); |
| }, |
| |
| _getFiles: function() { |
| return this.$.restAPI.getChangeFilesAsSpeciallySortedArray( |
| this.changeNum, this.patchRange).then(function(files) { |
| // Append UI-specific properties. |
| return files.map(function(file) { |
| return file; |
| }); |
| }); |
| }, |
| |
| _handleFileTap: function(e) { |
| // If the user prefers to expand inline diffs rather than opening the diff |
| // view, intercept the click event. |
| if (e.detail.sourceEvent.metaKey || e.detail.sourceEvent.ctrlKey) { |
| return; |
| } |
| if (this._userPrefs && this._userPrefs.expand_inline_diffs) { |
| e.preventDefault(); |
| this._handleHiddenChange(e); |
| } |
| }, |
| |
| _handleShiftLeftKey: function(e) { |
| if (this.shouldSuppressKeyboardShortcut(e)) { return; } |
| if (!this._showInlineDiffs) { return; } |
| |
| e.preventDefault(); |
| this.$.diffCursor.moveLeft(); |
| }, |
| |
| _handleShiftRightKey: function(e) { |
| if (this.shouldSuppressKeyboardShortcut(e)) { return; } |
| if (!this._showInlineDiffs) { return; } |
| |
| e.preventDefault(); |
| this.$.diffCursor.moveRight(); |
| }, |
| |
| _handleIKey: function(e) { |
| if (this.shouldSuppressKeyboardShortcut(e) || |
| this.modifierPressed(e) || |
| this.$.fileCursor.index === -1) { return; } |
| |
| e.preventDefault(); |
| this._togglePathExpandedByIndex(this.$.fileCursor.index); |
| }, |
| |
| _handleCapitalIKey: function(e) { |
| if (this.shouldSuppressKeyboardShortcut(e)) { return; } |
| |
| e.preventDefault(); |
| this._toggleInlineDiffs(); |
| }, |
| |
| _handleDownKey: function(e) { |
| if (this.shouldSuppressKeyboardShortcut(e)) { return; } |
| e.preventDefault(); |
| if (this._showInlineDiffs) { |
| this.$.diffCursor.moveDown(); |
| } else { |
| this.$.fileCursor.next(); |
| this.selectedIndex = this.$.fileCursor.index; |
| } |
| }, |
| |
| _handleUpKey: function(e) { |
| if (this.shouldSuppressKeyboardShortcut(e)) { return; } |
| |
| e.preventDefault(); |
| if (this._showInlineDiffs) { |
| this.$.diffCursor.moveUp(); |
| } else { |
| this.$.fileCursor.previous(); |
| this.selectedIndex = this.$.fileCursor.index; |
| } |
| }, |
| |
| _handleCKey: function(e) { |
| if (this.shouldSuppressKeyboardShortcut(e) || |
| this.modifierPressed(e)) { return; } |
| |
| var isRangeSelected = this.diffs.some(function(diff) { |
| return diff.isRangeSelected(); |
| }, this); |
| if (this._showInlineDiffs && !isRangeSelected) { |
| e.preventDefault(); |
| this._addDraftAtTarget(); |
| } |
| }, |
| |
| _handleLeftBracketKey: function(e) { |
| if (this.shouldSuppressKeyboardShortcut(e)) { return; } |
| |
| e.preventDefault(); |
| this._openSelectedFile(this._files.length - 1); |
| }, |
| |
| _handleRightBracketKey: function(e) { |
| if (this.shouldSuppressKeyboardShortcut(e)) { return; } |
| |
| e.preventDefault(); |
| this._openSelectedFile(0); |
| }, |
| |
| _handleEnterKey: function(e) { |
| if (this.shouldSuppressKeyboardShortcut(e) || |
| this.modifierPressed(e)) { return; } |
| |
| // Use native handling if an anchor is selected. @see Issue 5754 |
| if (e.detail && e.detail.keyboardEvent && e.detail.keyboardEvent.target && |
| e.detail.keyboardEvent.target.tagName === 'A') { return; } |
| |
| e.preventDefault(); |
| if (this._showInlineDiffs) { |
| this._openCursorFile(); |
| } else { |
| this._openSelectedFile(); |
| } |
| }, |
| |
| _handleNKey: function(e) { |
| if (this.shouldSuppressKeyboardShortcut(e) || |
| this.modifierPressed(e)) { return; } |
| if (!this._showInlineDiffs) { return; } |
| |
| e.preventDefault(); |
| if (e.shiftKey) { |
| this.$.diffCursor.moveToNextCommentThread(); |
| } else { |
| this.$.diffCursor.moveToNextChunk(); |
| } |
| }, |
| |
| _handlePKey: function(e) { |
| if (this.shouldSuppressKeyboardShortcut(e) || |
| this.modifierPressed(e)) { return; } |
| if (!this._showInlineDiffs) { return; } |
| |
| e.preventDefault(); |
| if (e.shiftKey) { |
| this.$.diffCursor.moveToPreviousCommentThread(); |
| } else { |
| this.$.diffCursor.moveToPreviousChunk(); |
| } |
| }, |
| |
| _handleCapitalAKey: function(e) { |
| if (this.shouldSuppressKeyboardShortcut(e)) { return; } |
| |
| e.preventDefault(); |
| this._forEachDiff(function(diff) { |
| diff.toggleLeftDiff(); |
| }); |
| }, |
| |
| _toggleInlineDiffs: function() { |
| if (this._showInlineDiffs) { |
| this._collapseAllDiffs(); |
| } else { |
| this._expandAllDiffs(); |
| } |
| }, |
| |
| _openCursorFile: function() { |
| var diff = this.$.diffCursor.getTargetDiffElement(); |
| page.show(this._computeDiffURL(diff.changeNum, diff.patchRange, |
| diff.path)); |
| }, |
| |
| _openSelectedFile: function(opt_index) { |
| if (opt_index != null) { |
| this.$.fileCursor.setCursorAtIndex(opt_index); |
| } |
| page.show(this._computeDiffURL(this.changeNum, this.patchRange, |
| this._files[this.$.fileCursor.index].__path)); |
| }, |
| |
| _addDraftAtTarget: function() { |
| var diff = this.$.diffCursor.getTargetDiffElement(); |
| var target = this.$.diffCursor.getTargetLineElement(); |
| if (diff && target) { |
| diff.addDraftAtLine(target); |
| } |
| }, |
| |
| _shouldHideChangeTotals: function(_patchChange) { |
| return _patchChange.inserted === 0 && _patchChange.deleted === 0; |
| }, |
| |
| _shouldHideBinaryChangeTotals: function(_patchChange) { |
| return _patchChange.size_delta_inserted === 0 && |
| _patchChange.size_delta_deleted === 0; |
| }, |
| |
| _computeFileStatus: function(status) { |
| return status || 'M'; |
| }, |
| |
| _computeDiffURL: function(changeNum, patchRange, path) { |
| return this.encodeURL('/c/' + changeNum + '/' + |
| this._patchRangeStr(patchRange) + '/' + path, true); |
| }, |
| |
| _patchRangeStr: function(patchRange) { |
| return patchRange.basePatchNum !== 'PARENT' ? |
| patchRange.basePatchNum + '..' + patchRange.patchNum : |
| patchRange.patchNum + ''; |
| }, |
| |
| _computeFileDisplayName: function(path) { |
| return path === COMMIT_MESSAGE_PATH ? 'Commit message' : path; |
| }, |
| |
| _computeTruncatedFileDisplayName: function(path) { |
| return path === COMMIT_MESSAGE_PATH ? |
| 'Commit message' : util.truncatePath(path); |
| }, |
| |
| _formatBytes: function(bytes) { |
| if (bytes == 0) return '+/-0 B'; |
| var bits = 1024; |
| var decimals = 1; |
| var sizes = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']; |
| var exponent = Math.floor(Math.log(Math.abs(bytes)) / Math.log(bits)); |
| var prepend = bytes > 0 ? '+' : ''; |
| return prepend + parseFloat((bytes / Math.pow(bits, exponent)) |
| .toFixed(decimals)) + ' ' + sizes[exponent]; |
| }, |
| |
| _formatPercentage: function(size, delta) { |
| var oldSize = size - delta; |
| |
| if (oldSize === 0) { return ''; } |
| |
| var percentage = Math.round(Math.abs(delta * 100 / oldSize)); |
| return '(' + (delta > 0 ? '+' : '-') + percentage + '%)'; |
| }, |
| |
| _computeBinaryClass: function(delta) { |
| if (delta === 0) { return; } |
| return delta >= 0 ? 'added' : 'removed'; |
| }, |
| |
| _computeClass: function(baseClass, path) { |
| var classes = [baseClass]; |
| if (path === COMMIT_MESSAGE_PATH) { |
| classes.push('invisible'); |
| } |
| return classes.join(' '); |
| }, |
| |
| _computeExpandInlineClass: function(userPrefs) { |
| return userPrefs.expand_inline_diffs ? 'expandInline' : ''; |
| }, |
| |
| _computePathClass: function(path, expandedFilesRecord) { |
| return this._isFileExpanded(path, expandedFilesRecord) ? 'path expanded' : |
| 'path'; |
| }, |
| |
| _computeShowHideText: function(path, expandedFilesRecord) { |
| return this._isFileExpanded(path, expandedFilesRecord) ? 'â–¼' : 'â—€'; |
| }, |
| |
| _computeFilesShown: function(numFilesShown, files) { |
| return files.base.slice(0, numFilesShown); |
| }, |
| |
| _setReviewedFiles: function(shownFiles, files, reviewedRecord, loggedIn) { |
| if (!loggedIn) { return; } |
| var reviewed = reviewedRecord.base; |
| var fileReviewed; |
| for (var i = 0; i < files.length; i++) { |
| fileReviewed = this._computeReviewed(files[i], reviewed); |
| this._files[i].isReviewed = fileReviewed; |
| if (i < shownFiles.length) { |
| this.set(['_shownFiles', i, 'isReviewed'], fileReviewed); |
| } |
| } |
| }, |
| |
| _filesChanged: function() { |
| this.async(function() { |
| var diffElements = Polymer.dom(this.root).querySelectorAll('gr-diff'); |
| |
| // Overwrite the cursor's list of diffs: |
| this.$.diffCursor.splice.apply(this.$.diffCursor, |
| ['diffs', 0, this.$.diffCursor.diffs.length].concat(diffElements)); |
| |
| var files = Polymer.dom(this.root).querySelectorAll('.file-row'); |
| this.$.fileCursor.stops = files; |
| this.$.fileCursor.setCursorAtIndex(this.selectedIndex, true); |
| }.bind(this), 1); |
| }, |
| |
| _incrementNumFilesShown: function() { |
| this._numFilesShown += this._fileListIncrement; |
| }, |
| |
| _computeFileListButtonHidden: function(numFilesShown, files) { |
| return numFilesShown >= files.length; |
| }, |
| |
| _computeIncrementText: function(numFilesShown, files) { |
| if (!files) { return ''; } |
| var text = |
| Math.min(this._fileListIncrement, files.length - numFilesShown); |
| return 'Show ' + text + ' more'; |
| }, |
| |
| _computeShowAllText: function(files) { |
| if (!files) { return ''; } |
| return 'Show all ' + files.length + ' files'; |
| }, |
| |
| _showAllFiles: function() { |
| this._numFilesShown = this._files.length; |
| }, |
| |
| _updateSelected: function(patchRange) { |
| this._diffAgainst = patchRange.basePatchNum; |
| }, |
| |
| /** |
| * _getDiffViewMode: Get the diff view (side-by-side or unified) based on |
| * the current state. |
| * |
| * The expected behavior is to use the mode specified in the user's |
| * preferences unless they have manually chosen the alternative view. |
| * |
| * Use side-by-side if there is no view mode or preferences. |
| * |
| * @return {String} |
| */ |
| _getDiffViewMode: function(diffViewMode, userPrefs) { |
| if (diffViewMode) { |
| return diffViewMode; |
| } else if (userPrefs) { |
| return this.diffViewMode = userPrefs.default_diff_view; |
| } |
| return 'SIDE_BY_SIDE'; |
| }, |
| |
| _fileListActionsVisible: function(shownFilesRecord, |
| maxFilesForBulkActions) { |
| return shownFilesRecord.base.length <= maxFilesForBulkActions; |
| }, |
| |
| _computePatchSetDescription: function(revisions, patchNum) { |
| var rev = this.getRevisionByPatchNum(revisions, patchNum); |
| return (rev && rev.description) ? |
| rev.description.substring(0, PATCH_DESC_MAX_LENGTH) : ''; |
| }, |
| |
| _computeFileStatusLabel: function(status) { |
| var statusCode = this._computeFileStatus(status); |
| return FileStatus.hasOwnProperty(statusCode) ? |
| FileStatus[statusCode] : 'Status Unknown'; |
| }, |
| |
| _isFileExpanded: function(path, expandedFilesRecord) { |
| return expandedFilesRecord.base.indexOf(path) !== -1; |
| }, |
| |
| /** |
| * Handle splices to the list of expanded file paths. If there are any new |
| * entries in the expanded list, then render each diff corresponding in |
| * order by waiting for the previous diff to finish before starting the next |
| * one. |
| * @param {splice} record The splice record in the expanded paths list. |
| */ |
| _expandedPathsChanged: function(record) { |
| if (!record) { return; } |
| |
| // Find the paths introduced by the new index splices: |
| var newPaths = record.indexSplices |
| .map(function(splice) { |
| return splice.object.slice(splice.index, |
| splice.index + splice.addedCount); |
| }) |
| .reduce(function(acc, paths) { return acc.concat(paths); }, []); |
| |
| var timerName = 'Expand ' + newPaths.length + ' diffs'; |
| this.$.reporting.time(timerName); |
| |
| this._renderInOrder(newPaths, this.diffs, newPaths.length) |
| .then(function() { |
| this.$.reporting.timeEnd(timerName); |
| this.$.diffCursor.handleDiffUpdate(); |
| }.bind(this)); |
| }, |
| |
| /** |
| * Given an array of paths and a NodeList of diff elements, render the diff |
| * for each path in order, awaiting the previous render to complete before |
| * continung. |
| * @param {!Array<!String>} paths |
| * @param {!NodeList<!GrDiffElement>} diffElements |
| * @param {Number} initialCount The total number of paths in the pass. This |
| * is used to generate log messages. |
| * @return {!Promise} |
| */ |
| _renderInOrder: function(paths, diffElements, initialCount) { |
| if (!paths.length) { |
| console.log('Finished expanding', initialCount, 'diff(s)'); |
| return Promise.resolve(); |
| } |
| console.log('Expanding diff', 1 + initialCount - paths.length, 'of', |
| initialCount, ':', paths[0]); |
| var diffElem = this._findDiffByPath(paths[0], diffElements); |
| var promises = [diffElem.reload()]; |
| if (this._isLoggedIn) { |
| promises.push(this._reviewFile(paths[0])); |
| } |
| return Promise.all(promises).then(function() { |
| return this._renderInOrder(paths.slice(1), diffElements, initialCount); |
| }.bind(this)); |
| }, |
| |
| /** |
| * In the given NodeList of diff elements, find the diff for the given path. |
| * @param {!String} path |
| * @param {!NodeList<!GrDiffElement>} diffElements |
| * @return {!GrDiffElement} |
| */ |
| _findDiffByPath: function(path, diffElements) { |
| for (var i = 0; i < diffElements.length; i++) { |
| if (diffElements[i].path === path) { |
| return diffElements[i]; |
| } |
| } |
| }, |
| }); |
| })(); |