Refactor directory structure of components
There is no change in functionality. Only moving things around.
+ Separate html from the js.
+ Place the unit test for a component within the same folder.
+ Organize the components in subfolders.
Change-Id: I51fdc510db75fc1b33f040ca63decbbdfd4d5513
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
new file mode 100644
index 0000000..a42a379
--- /dev/null
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
@@ -0,0 +1,354 @@
+// 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';
+
+ Polymer({
+ is: 'gr-change-view',
+
+ /**
+ * Fired when the title of the page should change.
+ *
+ * @event title-change
+ */
+
+ properties: {
+ /**
+ * URL params passed from the router.
+ */
+ params: {
+ type: Object,
+ observer: '_paramsChanged',
+ },
+ viewState: {
+ type: Object,
+ notify: true,
+ value: function() { return {}; },
+ },
+ serverConfig: Object,
+ keyEventTarget: {
+ type: Object,
+ value: function() { return document.body; },
+ },
+
+ _comments: Object,
+ _change: {
+ type: Object,
+ observer: '_changeChanged',
+ },
+ _commitInfo: Object,
+ _changeNum: String,
+ _patchNum: String,
+ _allPatchSets: {
+ type: Array,
+ computed: '_computeAllPatchSets(_change)',
+ },
+ _loggedIn: {
+ type: Boolean,
+ value: false,
+ },
+ _loading: Boolean,
+ _headerContainerEl: Object,
+ _headerEl: Object,
+ _projectConfig: Object,
+ _boundScrollHandler: {
+ type: Function,
+ value: function() { return this._handleBodyScroll.bind(this); },
+ },
+ },
+
+ behaviors: [
+ Gerrit.KeyboardShortcutBehavior,
+ Gerrit.RESTClientBehavior,
+ ],
+
+ ready: function() {
+ app.accountReady.then(function() {
+ this._loggedIn = app.loggedIn;
+ }.bind(this));
+ this._headerEl = this.$$('.header');
+ },
+
+ attached: function() {
+ window.addEventListener('scroll', this._boundScrollHandler);
+ },
+
+ detached: function() {
+ window.removeEventListener('scroll', this._boundScrollHandler);
+ },
+
+ _handleBodyScroll: function(e) {
+ var containerEl = this._headerContainerEl ||
+ this.$$('.headerContainer');
+
+ // Calculate where the header is relative to the window.
+ var top = containerEl.offsetTop;
+ for (var offsetParent = containerEl.offsetParent;
+ offsetParent;
+ offsetParent = offsetParent.offsetParent) {
+ top += offsetParent.offsetTop;
+ }
+ // The element may not be displayed yet, in which case do nothing.
+ if (top == 0) { return; }
+
+ this._headerEl.classList.toggle('pinned', window.scrollY >= top);
+ },
+
+ _resetHeaderEl: function() {
+ var el = this._headerEl || this.$$('.header');
+ this._headerEl = el;
+ el.classList.remove('pinned');
+ },
+
+ _handlePatchChange: function(e) {
+ var patchNum = e.target.value;
+ var currentPatchNum =
+ this._change.revisions[this._change.current_revision]._number;
+ if (patchNum == currentPatchNum) {
+ page.show(this._computeChangePath(this._changeNum));
+ return;
+ }
+ page.show(this._computeChangePath(this._changeNum) + '/' + patchNum);
+ },
+
+ _handleReplyTap: function(e) {
+ e.preventDefault();
+ this.$.replyOverlay.open();
+ },
+
+ _handleDownloadTap: function(e) {
+ e.preventDefault();
+ this.$.downloadOverlay.open();
+ },
+
+ _handleDownloadDialogClose: function(e) {
+ this.$.downloadOverlay.close();
+ },
+
+ _handleMessageReply: function(e) {
+ var msg = e.detail.message.message;
+ var quoteStr = msg.split('\n').map(
+ function(line) { return '> ' + line; }).join('\n') + '\n\n';
+ this.$.replyDialog.draft += quoteStr;
+ this.$.replyOverlay.open();
+ },
+
+ _handleReplyOverlayOpen: function(e) {
+ this.$.replyDialog.reload().then(function() {
+ this.async(function() { this.$.replyOverlay.center() }, 1);
+ }.bind(this));
+ this.$.replyDialog.focus();
+ },
+
+ _handleReplySent: function(e) {
+ this.$.replyOverlay.close();
+ this._reload();
+ },
+
+ _handleReplyCancel: function(e) {
+ this.$.replyOverlay.close();
+ },
+
+ _paramsChanged: function(value) {
+ if (value.view != this.tagName.toLowerCase()) { return; }
+
+ this._changeNum = value.changeNum;
+ this._patchNum = value.patchNum;
+ if (this.viewState.changeNum != this._changeNum ||
+ this.viewState.patchNum != this._patchNum) {
+ this.set('viewState.selectedFileIndex', 0);
+ this.set('viewState.changeNum', this._changeNum);
+ this.set('viewState.patchNum', this._patchNum);
+ }
+ if (!this._changeNum) {
+ return;
+ }
+ this._reload().then(function() {
+ this.$.messageList.topMargin = this._headerEl.offsetHeight;
+
+ // Allow the message list to render before scrolling.
+ this.async(function() {
+ var msgPrefix = '#message-';
+ var hash = window.location.hash;
+ if (hash.indexOf(msgPrefix) == 0) {
+ this.$.messageList.scrollToMessage(hash.substr(msgPrefix.length));
+ }
+ }.bind(this), 1);
+
+ app.accountReady.then(function() {
+ if (!this._loggedIn) { return; }
+
+ if (this.viewState.showReplyDialog) {
+ this.$.replyOverlay.open();
+ this.set('viewState.showReplyDialog', false);
+ }
+ }.bind(this));
+ }.bind(this));
+ },
+
+ _changeChanged: function(change) {
+ if (!change) { return; }
+ this._patchNum = this._patchNum ||
+ change.revisions[change.current_revision]._number;
+
+ var title = change.subject + ' (' + change.change_id.substr(0, 9) + ')';
+ this.fire('title-change', {title: title});
+ },
+
+ _computeChangePath: function(changeNum) {
+ return '/c/' + changeNum;
+ },
+
+ _computeChangePermalink: function(changeNum) {
+ return '/' + changeNum;
+ },
+
+ _computeChangeStatus: function(change, patchNum) {
+ var status = change.status;
+ if (status == this.ChangeStatus.NEW) {
+ var rev = this._getRevisionNumber(change, patchNum);
+ // TODO(davido): Figure out, why sometimes revision is not there
+ if (rev == undefined || !rev.draft) { return ''; }
+ status = this.ChangeStatus.DRAFT;
+ }
+ return '(' + status.toLowerCase() + ')';
+ },
+
+ _computeDetailPath: function(changeNum) {
+ return '/changes/' + changeNum + '/detail';
+ },
+
+ _computeCommitInfoPath: function(changeNum, patchNum) {
+ return this.changeBaseURL(changeNum, patchNum) + '/commit?links';
+ },
+
+ _computeCommentsPath: function(changeNum) {
+ return '/changes/' + changeNum + '/comments';
+ },
+
+ _computeProjectConfigPath: function(project) {
+ return '/projects/' + encodeURIComponent(project) + '/config';
+ },
+
+ _computeDetailQueryParams: function() {
+ var options = this.listChangesOptionsToHex(
+ this.ListChangesOption.ALL_REVISIONS,
+ this.ListChangesOption.CHANGE_ACTIONS,
+ this.ListChangesOption.DOWNLOAD_COMMANDS
+ );
+ return {O: options};
+ },
+
+ _computeLatestPatchNum: function(change) {
+ return change.revisions[change.current_revision]._number;
+ },
+
+ _computeAllPatchSets: function(change) {
+ var patchNums = [];
+ for (var rev in change.revisions) {
+ patchNums.push(change.revisions[rev]._number);
+ }
+ return patchNums.sort(function(a, b) {
+ return a - b;
+ });
+ },
+
+ _getRevisionNumber: function(change, patchNum) {
+ for (var rev in change.revisions) {
+ if (change.revisions[rev]._number == patchNum) {
+ return change.revisions[rev];
+ }
+ }
+ },
+
+ _computePatchIndexIsSelected: function(index, patchNum) {
+ return this._allPatchSets[index] == patchNum;
+ },
+
+ _computeLabelNames: function(labels) {
+ return Object.keys(labels).sort();
+ },
+
+ _computeLabelValues: function(labelName, labels) {
+ var result = [];
+ var t = labels[labelName];
+ if (!t) { return result; }
+ var approvals = t.all || [];
+ approvals.forEach(function(label) {
+ if (label.value && label.value != labels[labelName].default_value) {
+ var labelClassName;
+ var labelValPrefix = '';
+ if (label.value > 0) {
+ labelValPrefix = '+';
+ labelClassName = 'approved';
+ } else if (label.value < 0) {
+ labelClassName = 'notApproved';
+ }
+ result.push({
+ value: labelValPrefix + label.value,
+ className: labelClassName,
+ account: label,
+ });
+ }
+ });
+ return result;
+ },
+
+ _handleKey: function(e) {
+ if (this.shouldSupressKeyboardShortcut(e)) { return; }
+
+ switch (e.keyCode) {
+ case 65: // 'a'
+ e.preventDefault();
+ this.$.replyOverlay.open();
+ break;
+ case 85: // 'u'
+ e.preventDefault();
+ page.show('/');
+ break;
+ }
+ },
+
+ _handleReloadChange: function() {
+ page.show(this._computeChangePath(this._changeNum));
+ },
+
+ _reload: function() {
+ var detailCompletes = this.$.detailXHR.generateRequest().completes;
+ this.$.commentsXHR.generateRequest();
+ var reloadPatchNumDependentResources = function() {
+ return Promise.all([
+ this.$.commitInfoXHR.generateRequest().completes,
+ this.$.actions.reload(),
+ this.$.fileList.reload(),
+ ]);
+ }.bind(this);
+ var reloadDetailDependentResources = function() {
+ return this.$.relatedChanges.reload();
+ }.bind(this);
+
+ this._resetHeaderEl();
+
+ if (this._patchNum) {
+ return reloadPatchNumDependentResources().then(function() {
+ return detailCompletes;
+ }).then(reloadDetailDependentResources);
+ } else {
+ // The patch number is reliant on the change detail request.
+ return detailCompletes.then(reloadPatchNumDependentResources).then(
+ reloadDetailDependentResources);
+ }
+ },
+ });
+})();