Changeset View
Changeset View
Standalone View
Standalone View
webroot/rsrc/js/application/diff/DiffChangesetList.js
| /** | /** | ||||
| * @provides phabricator-diff-changeset-list | * @provides phabricator-diff-changeset-list | ||||
| * @requires javelin-install | * @requires javelin-install | ||||
| * phuix-button-view | |||||
| * @javelin | * @javelin | ||||
| */ | */ | ||||
| JX.install('DiffChangesetList', { | JX.install('DiffChangesetList', { | ||||
| construct: function() { | construct: function() { | ||||
| this._changesets = []; | this._changesets = []; | ||||
| ▲ Show 20 Lines • Show All 94 Lines • ▼ Show 20 Lines | members: { | ||||
| _hoverOrigin: null, | _hoverOrigin: null, | ||||
| _hoverTarget: null, | _hoverTarget: null, | ||||
| _rangeActive: false, | _rangeActive: false, | ||||
| _rangeOrigin: null, | _rangeOrigin: null, | ||||
| _rangeTarget: null, | _rangeTarget: null, | ||||
| _bannerNode: null, | _bannerNode: null, | ||||
| _unsavedButton: null, | |||||
| sleep: function() { | sleep: function() { | ||||
| this._asleep = true; | this._asleep = true; | ||||
| this._redrawFocus(); | this._redrawFocus(); | ||||
| this._redrawSelection(); | this._redrawSelection(); | ||||
| this.resetHover(); | this.resetHover(); | ||||
| }, | }, | ||||
| ▲ Show 20 Lines • Show All 131 Lines • ▼ Show 20 Lines | _installKey: function(key, label, handler) { | ||||
| return new JX.KeyboardShortcut(key, label) | return new JX.KeyboardShortcut(key, label) | ||||
| .setHandler(handler) | .setHandler(handler) | ||||
| .register(); | .register(); | ||||
| }, | }, | ||||
| _installJumpKey: function(key, label, delta, filter, show_hidden) { | _installJumpKey: function(key, label, delta, filter, show_hidden) { | ||||
| filter = filter || null; | filter = filter || null; | ||||
| var handler = JX.bind(this, this._onjumpkey, delta, filter, show_hidden); | |||||
| var options = { | |||||
| filter: filter, | |||||
| hidden: show_hidden | |||||
| }; | |||||
| var handler = JX.bind(this, this._onjumpkey, delta, options); | |||||
| return this._installKey(key, label, handler); | return this._installKey(key, label, handler); | ||||
| }, | }, | ||||
| _ontoc: function(manager) { | _ontoc: function(manager) { | ||||
| var toc = JX.$('toc'); | var toc = JX.$('toc'); | ||||
| manager.scrollTo(toc); | manager.scrollTo(toc); | ||||
| }, | }, | ||||
| ▲ Show 20 Lines • Show All 165 Lines • ▼ Show 20 Lines | members: { | ||||
| _warnUser: function(message) { | _warnUser: function(message) { | ||||
| new JX.Notification() | new JX.Notification() | ||||
| .setContent(message) | .setContent(message) | ||||
| .alterClassName('jx-notification-alert', true) | .alterClassName('jx-notification-alert', true) | ||||
| .setDuration(1000) | .setDuration(1000) | ||||
| .show(); | .show(); | ||||
| }, | }, | ||||
| _onjumpkey: function(delta, filter, show_hidden, manager) { | _onjumpkey: function(delta, options) { | ||||
| var state = this._getSelectionState(); | var state = this._getSelectionState(); | ||||
| var filter = options.filter || null; | |||||
| var hidden = options.hidden || false; | |||||
| var wrap = options.wrap || false; | |||||
| var attribute = options.attribute || null; | |||||
| var cursor = state.cursor; | var cursor = state.cursor; | ||||
| var items = state.items; | var items = state.items; | ||||
| // If there's currently no selection and the user tries to go back, | // If there's currently no selection and the user tries to go back, | ||||
| // don't do anything. | // don't do anything. | ||||
| if ((cursor === null) && (delta < 0)) { | if ((cursor === null) && (delta < 0)) { | ||||
| return; | return; | ||||
| } | } | ||||
| var did_wrap = false; | |||||
| while (true) { | while (true) { | ||||
| if (cursor === null) { | if (cursor === null) { | ||||
| cursor = 0; | cursor = 0; | ||||
| } else { | } else { | ||||
| cursor = cursor + delta; | cursor = cursor + delta; | ||||
| } | } | ||||
| // If we've gone backward past the first change, bail out. | // If we've gone backward past the first change, bail out. | ||||
| if (cursor < 0) { | if (cursor < 0) { | ||||
| return; | return; | ||||
| } | } | ||||
| // If we've gone forward off the end of the list, bail out. | // If we've gone forward off the end of the list, figure out where we | ||||
| // should end up. | |||||
| if (cursor >= items.length) { | if (cursor >= items.length) { | ||||
| if (!wrap) { | |||||
| // If we aren't wrapping around, we're done. | |||||
| return; | return; | ||||
| } | } | ||||
| if (did_wrap) { | |||||
| // If we're already wrapped around, we're done. | |||||
| return; | |||||
| } | |||||
| // Otherwise, wrap the cursor back to the top. | |||||
| cursor = 0; | |||||
| did_wrap = true; | |||||
| } | |||||
| // If we're selecting things of a particular type (like only files) | // If we're selecting things of a particular type (like only files) | ||||
| // and the next item isn't of that type, move past it. | // and the next item isn't of that type, move past it. | ||||
| if (filter !== null) { | if (filter !== null) { | ||||
| if (items[cursor].type !== filter) { | if (items[cursor].type !== filter) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| } | } | ||||
| // If the item is hidden, don't select it when iterating with jump | // If the item is hidden, don't select it when iterating with jump | ||||
| // keys. It can still potentially be selected in other ways. | // keys. It can still potentially be selected in other ways. | ||||
| if (!show_hidden) { | if (!hidden) { | ||||
| if (items[cursor].hidden) { | if (items[cursor].hidden) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| } | } | ||||
| // If the item has been deleted, don't select it when iterating. The | |||||
| // cursor may remain on it until it is removed. | |||||
| if (items[cursor].deleted) { | |||||
| continue; | |||||
| } | |||||
| // If we're selecting things with a particular attribute, like | |||||
| // "unsaved", skip items without the attribute. | |||||
| if (attribute !== null) { | |||||
| if (!(items[cursor].attributes || {})[attribute]) { | |||||
| continue; | |||||
| } | |||||
| } | |||||
| // Otherwise, we've found a valid item to select. | // Otherwise, we've found a valid item to select. | ||||
| break; | break; | ||||
| } | } | ||||
| this._setSelectionState(items[cursor], manager); | this._setSelectionState(items[cursor]); | ||||
| }, | }, | ||||
| _getSelectionState: function() { | _getSelectionState: function() { | ||||
| var items = this._getSelectableItems(); | var items = this._getSelectableItems(); | ||||
| var cursor = null; | var cursor = null; | ||||
| if (this._cursorItem !== null) { | if (this._cursorItem !== null) { | ||||
| for (var ii = 0; ii < items.length; ii++) { | for (var ii = 0; ii < items.length; ii++) { | ||||
| var item = items[ii]; | var item = items[ii]; | ||||
| if (this._cursorItem.target === item.target) { | if (this._cursorItem.target === item.target) { | ||||
| cursor = ii; | cursor = ii; | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| return { | return { | ||||
| cursor: cursor, | cursor: cursor, | ||||
| items: items | items: items | ||||
| }; | }; | ||||
| }, | }, | ||||
| _setSelectionState: function(item, manager) { | _setSelectionState: function(item) { | ||||
| this._cursorItem = item; | this._cursorItem = item; | ||||
| this._redrawSelection(manager, true); | this._redrawSelection(true); | ||||
| return this; | return this; | ||||
| }, | }, | ||||
| _redrawSelection: function(manager, scroll) { | _redrawSelection: function(scroll) { | ||||
| var cursor = this._cursorItem; | var cursor = this._cursorItem; | ||||
| if (!cursor) { | if (!cursor) { | ||||
| this.setFocus(null); | this.setFocus(null); | ||||
| return; | return; | ||||
| } | } | ||||
| // If this item has been removed from the document (for example: create | |||||
| // a new empty comment, then use the "Unsaved" button to select it, then | |||||
| // cancel it), we can still keep the cursor here but do not want to show | |||||
| // a selection reticle over an invisible node. | |||||
| if (cursor.deleted) { | |||||
| this.setFocus(null); | |||||
| return; | |||||
| } | |||||
| this.setFocus(cursor.nodes.begin, cursor.nodes.end); | this.setFocus(cursor.nodes.begin, cursor.nodes.end); | ||||
| if (manager && scroll) { | if (scroll) { | ||||
| manager.scrollTo(cursor.nodes.begin); | var pos = JX.$V(cursor.nodes.begin); | ||||
| JX.DOM.scrollToPosition(0, pos.y - 60); | |||||
| } | } | ||||
| return this; | return this; | ||||
| }, | }, | ||||
| redrawCursor: function() { | redrawCursor: function() { | ||||
| // NOTE: This is setting the cursor to the current cursor. Usually, this | // NOTE: This is setting the cursor to the current cursor. Usually, this | ||||
| // would have no effect. | // would have no effect. | ||||
| ▲ Show 20 Lines • Show All 774 Lines • ▼ Show 20 Lines | _redrawBanner: function() { | ||||
| 'diff-banner-has-unsaved', | 'diff-banner-has-unsaved', | ||||
| !!unsaved.length); | !!unsaved.length); | ||||
| JX.DOM.alterClass( | JX.DOM.alterClass( | ||||
| node, | node, | ||||
| 'diff-banner-has-unsubmitted', | 'diff-banner-has-unsubmitted', | ||||
| !!unsubmitted.length); | !!unsubmitted.length); | ||||
| var unsaved_button = this._getUnsavedButton(); | |||||
| var pht = this.getTranslations(); | |||||
| if (unsaved.length) { | |||||
| unsaved_button.setText(unsaved.length + ' ' + pht('Unsaved')); | |||||
| JX.DOM.show(unsaved_button.getNode()); | |||||
| } else { | |||||
| JX.DOM.hide(unsaved_button.getNode()); | |||||
| } | |||||
| var path_view = [icon, ' ', changeset.getDisplayPath()]; | |||||
| var buttons_attrs = { | |||||
| className: 'diff-banner-buttons' | |||||
| }; | |||||
| var buttons_list = [ | |||||
| unsaved_button.getNode() | |||||
| ]; | |||||
| var buttons_view = JX.$N('div', buttons_attrs, buttons_list); | |||||
| var icon = new JX.PHUIXIconView() | var icon = new JX.PHUIXIconView() | ||||
| .setIcon(changeset.getIcon()) | .setIcon(changeset.getIcon()) | ||||
| .getNode(); | .getNode(); | ||||
| JX.DOM.setContent(node, [icon, ' ', changeset.getDisplayPath()]); | JX.DOM.setContent(node, [buttons_view, path_view]); | ||||
| document.body.appendChild(node); | document.body.appendChild(node); | ||||
| }, | }, | ||||
| _getUnsavedButton: function() { | |||||
| if (!this._unsavedButton) { | |||||
| var button = new JX.PHUIXButtonView() | |||||
| .setIcon('fa-commenting-o') | |||||
| .setButtonType(JX.PHUIXButtonView.BUTTONTYPE_SIMPLE); | |||||
| var node = button.getNode(); | |||||
| var onunsaved = JX.bind(this, this._onunsavedclick); | |||||
| JX.DOM.listen(node, 'click', null, onunsaved); | |||||
| this._unsavedButton = button; | |||||
| } | |||||
| return this._unsavedButton; | |||||
| }, | |||||
| _onunsavedclick: function(e) { | |||||
| e.kill(); | |||||
| var options = { | |||||
| filter: 'comment', | |||||
| wrap: true, | |||||
| attribute: 'unsaved' | |||||
| }; | |||||
| this._onjumpkey(1, options); | |||||
| }, | |||||
| _getBannerNode: function() { | _getBannerNode: function() { | ||||
| if (!this._bannerNode) { | if (!this._bannerNode) { | ||||
| var attributes = { | var attributes = { | ||||
| className: 'diff-banner', | className: 'diff-banner', | ||||
| id: 'diff-banner' | id: 'diff-banner' | ||||
| }; | }; | ||||
| this._bannerNode = JX.$N('div', attributes); | this._bannerNode = JX.$N('div', attributes); | ||||
| ▲ Show 20 Lines • Show All 45 Lines • Show Last 20 Lines | |||||