| <!-- |
| 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="../behaviors/keyboard-shortcut-behavior.html"> |
| <link rel="import" href="../behaviors/rest-client-behavior.html"> |
| <link rel="import" href="gr-ajax.html"> |
| |
| <dom-module id="gr-file-list"> |
| <template> |
| <style> |
| :host { |
| display: block; |
| } |
| .tableContainer { |
| overflow-x: auto; |
| } |
| table { |
| border-collapse: collapse; |
| width: 100%; |
| } |
| table a { |
| display: block; |
| } |
| td { |
| padding: 2px 0; |
| white-space: nowrap; |
| } |
| th { |
| text-align: left; |
| } |
| .positionIndicator { |
| padding-left: .25em; |
| visibility: hidden; |
| } |
| tr[selected] { |
| background-color: #ebf5fb; |
| } |
| tr[selected] .positionIndicator { |
| visibility: visible; |
| } |
| .status { |
| width: 20px; |
| } |
| .drafts { |
| color: #C62828; |
| font-weight: bold; |
| } |
| </style> |
| <gr-ajax id="filesXHR" |
| url="[[_computeFilesURL(changeNum, patchNum)]]" |
| on-response="_handleResponse"></gr-ajax> |
| <gr-ajax id="draftsXHR" |
| url="[[_computeDraftsURL(changeNum, patchNum)]]" |
| last-response="{{_drafts}}"></gr-ajax> |
| </gr-ajax> |
| <div class="tableContainer"> |
| <table> |
| <tr> |
| <th></th> |
| <th></th> |
| <th>Path</th> |
| <th>Comments</th> |
| <th>Stats</th> |
| </tr> |
| <template is="dom-repeat" items="{{files}}" as="file"> |
| <tr class="fileRow" selected$="[[_computeFileSelected(index, selectedIndex)]]"> |
| <td class="positionIndicator">▶</td> |
| <td class="status">[[file.status]]</td> |
| <td class="path"> |
| <a class="file" |
| href$="[[_computeDiffURL(changeNum, patchNum, file.__path)]]">[[file.__path]]</a> |
| </td> |
| <td> |
| <span class="drafts">[[_computeDraftsString(_drafts, file.__path)]]</span> |
| <span class="comments">[[_computeCommentsString(comments, patchNum, file.__path)]]</span> |
| </td> |
| <td> |
| +<span>[[file.lines_inserted]]</span> lines, |
| -<span>[[file.lines_deleted]]</span> lines |
| </td> |
| </tr> |
| </template> |
| </table> |
| </div> |
| </template> |
| <script> |
| (function() { |
| 'use strict'; |
| |
| Polymer({ |
| is: 'gr-file-list', |
| |
| properties: { |
| patchNum: String, |
| changeNum: String, |
| comments: Object, |
| files: Array, |
| selectedIndex: { |
| type: Number, |
| notify: true, |
| }, |
| keyEventTarget: { |
| type: Object, |
| value: function() { return document.body; }, |
| }, |
| |
| _drafts: Object, |
| }, |
| |
| behaviors: [ |
| Gerrit.KeyboardShortcutBehavior, |
| Gerrit.RESTClientBehavior, |
| ], |
| |
| reload: function() { |
| if (!this.changeNum || !this.patchNum) { |
| return Promise.resolve(); |
| } |
| return Promise.all([ |
| this.$.filesXHR.generateRequest().completes, |
| app.accountReady.then(function() { |
| if (!app.loggedIn) { return; } |
| this.$.draftsXHR.generateRequest(); |
| }.bind(this)), |
| ]); |
| }, |
| |
| _computeFilesURL: function(changeNum, patchNum) { |
| return this.changeBaseURL(changeNum, patchNum) + '/files'; |
| }, |
| |
| _computeCommentsString: function(comments, patchNum, path) { |
| var patchComments = (comments[path] || []).filter(function(c) { |
| return c.patch_set == patchNum; |
| }); |
| var num = patchComments.length; |
| if (num == 0) { return ''; } |
| if (num == 1) { return '1 comment'; } |
| if (num > 1) { return num + ' comments'; }; |
| }, |
| |
| _computeDraftsURL: function(changeNum, patchNum) { |
| return this.changeBaseURL(changeNum, patchNum) + '/drafts'; |
| }, |
| |
| _computeDraftsString: function(drafts, path) { |
| var num = (drafts[path] || []).length; |
| if (num == 0) { return ''; } |
| if (num == 1) { return '1 draft'; } |
| if (num > 1) { return num + ' drafts'; }; |
| }, |
| |
| _handleResponse: function(e, req) { |
| var result = e.detail.response; |
| var paths = Object.keys(result).sort(); |
| var files = []; |
| for (var i = 0; i < paths.length; i++) { |
| var info = result[paths[i]]; |
| info.__path = paths[i]; |
| info.lines_inserted = info.lines_inserted || 0; |
| info.lines_deleted = info.lines_deleted || 0; |
| files.push(info) |
| } |
| this.files = files; |
| }, |
| |
| _handleKey: function(e) { |
| if (this.shouldSupressKeyboardShortcut(e)) { return; } |
| |
| switch(e.keyCode) { |
| case 74: // 'j' |
| e.preventDefault(); |
| this.selectedIndex = |
| Math.min(this.files.length - 1, this.selectedIndex + 1); |
| break; |
| case 75: // 'k' |
| e.preventDefault(); |
| this.selectedIndex = Math.max(0, this.selectedIndex - 1); |
| break; |
| case 219: // '[' |
| e.preventDefault(); |
| this._openSelectedFile(this.files.length - 1); |
| break; |
| case 221: // ']' |
| e.preventDefault(); |
| this._openSelectedFile(0); |
| break; |
| case 13: // <enter> |
| case 79: // 'o' |
| e.preventDefault(); |
| this._openSelectedFile(); |
| break; |
| } |
| }, |
| |
| _openSelectedFile: function(opt_index) { |
| if (opt_index != null) { |
| this.selectedIndex = opt_index; |
| } |
| page(this._computeDiffURL(this.changeNum, this.patchNum, |
| this.files[this.selectedIndex].__path)); |
| }, |
| |
| _computeFileSelected: function(index, selectedIndex) { |
| return index == selectedIndex; |
| }, |
| |
| _computeDiffURL: function(changeNum, patchNum, path) { |
| return '/c/' + changeNum + '/' + patchNum + '/' + path; |
| }, |
| }); |
| })(); |
| </script> |
| </dom-module> |