Changeset View
Changeset View
Standalone View
Standalone View
webroot/rsrc/js/application/diff/DiffChangesetList.js
| Show First 20 Lines • Show All 111 Lines • ▼ Show 20 Lines | members: { | ||||
| _rangeTarget: null, | _rangeTarget: null, | ||||
| _bannerNode: null, | _bannerNode: null, | ||||
| _unsavedButton: null, | _unsavedButton: null, | ||||
| _unsubmittedButton: null, | _unsubmittedButton: null, | ||||
| _doneButton: null, | _doneButton: null, | ||||
| _doneMode: null, | _doneMode: null, | ||||
| _dropdownMenu: null, | |||||
| _menuButton: null, | |||||
| _menuItems: 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 325 Lines • ▼ Show 20 Lines | members: { | ||||
| _onjumpkey: function(delta, options) { | _onjumpkey: function(delta, options) { | ||||
| var state = this._getSelectionState(); | var state = this._getSelectionState(); | ||||
| var filter = options.filter || null; | var filter = options.filter || null; | ||||
| var collapsed = options.collapsed || false; | var collapsed = options.collapsed || false; | ||||
| var wrap = options.wrap || false; | var wrap = options.wrap || false; | ||||
| var attribute = options.attribute || null; | var attribute = options.attribute || null; | ||||
| var show = options.show || false; | |||||
| 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; | ||||
| ▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | _onjumpkey: function(delta, options) { | ||||
| // If we're selecting things with a particular attribute, like | // If we're selecting things with a particular attribute, like | ||||
| // "unsaved", skip items without the attribute. | // "unsaved", skip items without the attribute. | ||||
| if (attribute !== null) { | if (attribute !== null) { | ||||
| if (!(items[cursor].attributes || {})[attribute]) { | if (!(items[cursor].attributes || {})[attribute]) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| } | } | ||||
| // If this item is a hidden inline but we're clicking a button which | |||||
| // selects inlines of a particular type, make it visible again. | |||||
| if (items[cursor].hidden) { | |||||
| if (!show) { | |||||
| continue; | |||||
| } | |||||
| items[cursor].target.setHidden(false); | |||||
| } | |||||
| // Otherwise, we've found a valid item to select. | // Otherwise, we've found a valid item to select. | ||||
| break; | break; | ||||
| } | } | ||||
| this._setSelectionState(items[cursor], true); | this._setSelectionState(items[cursor], true); | ||||
| }, | }, | ||||
| _getSelectionState: function() { | _getSelectionState: function() { | ||||
| ▲ Show 20 Lines • Show All 769 Lines • ▼ Show 20 Lines | _onrangeup: function(e) { | ||||
| this._rangeActive = false; | this._rangeActive = false; | ||||
| this._rangeOrigin = null; | this._rangeOrigin = null; | ||||
| this._rangeTarget = null; | this._rangeTarget = null; | ||||
| this.resetHover(); | this.resetHover(); | ||||
| }, | }, | ||||
| _redrawBanner: function() { | _redrawBanner: function() { | ||||
| // If the inline comment menu is open and we've done a redraw, close it. | |||||
| // In particular, this makes it close when you scroll the document: | |||||
| // otherwise, it stays open but the banner moves underneath it. | |||||
| if (this._dropdownMenu) { | |||||
| this._dropdownMenu.close(); | |||||
| } | |||||
| var node = this._getBannerNode(); | var node = this._getBannerNode(); | ||||
| var changeset = this._getVisibleChangeset(); | var changeset = this._getVisibleChangeset(); | ||||
| // Don't do anything if nothing has changed. This seems to avoid some | // Don't do anything if nothing has changed. This seems to avoid some | ||||
| // flickering issues in Safari, at least. | // flickering issues in Safari, at least. | ||||
| if (this._bannerChangeset === changeset) { | if (this._bannerChangeset === changeset) { | ||||
| return; | return; | ||||
| } | } | ||||
| this._bannerChangeset = changeset; | this._bannerChangeset = changeset; | ||||
| if (!changeset) { | if (!changeset) { | ||||
| JX.DOM.remove(node); | JX.DOM.remove(node); | ||||
| return; | return; | ||||
| } | } | ||||
| var changesets = this._changesets; | var inlines = this._getInlinesByType(); | ||||
| var unsaved = []; | |||||
| var unsubmitted = []; | |||||
| var draft_done = []; | |||||
| var undone = []; | |||||
| var done = []; | |||||
| for (var ii = 0; ii < changesets.length; ii++) { | |||||
| var inlines = changesets[ii].getInlines(); | |||||
| for (var jj = 0; jj < inlines.length; jj++) { | |||||
| var inline = inlines[jj]; | |||||
| if (inline.isDeleted()) { | |||||
| continue; | |||||
| } | |||||
| if (inline.isSynthetic()) { | |||||
| continue; | |||||
| } | |||||
| if (inline.isEditing()) { | |||||
| unsaved.push(inline); | |||||
| } else if (!inline.getID()) { | |||||
| // These are new comments which have been cancelled, and do not | |||||
| // count as anything. | |||||
| continue; | |||||
| } else if (inline.isDraft()) { | |||||
| unsubmitted.push(inline); | |||||
| } else { | |||||
| // NOTE: Unlike other states, an inline may be marked with a | |||||
| // draft checkmark and still be a "done" or "undone" comment. | |||||
| if (inline.isDraftDone()) { | |||||
| draft_done.push(inline); | |||||
| } | |||||
| if (!inline.isDone()) { | var unsaved = inlines.unsaved; | ||||
| undone.push(inline); | var unsubmitted = inlines.unsubmitted; | ||||
| } else { | var undone = inlines.undone; | ||||
| done.push(inline); | var done = inlines.done; | ||||
| } | var draft_done = inlines.draftDone; | ||||
| } | |||||
| } | |||||
| } | |||||
| JX.DOM.alterClass( | JX.DOM.alterClass( | ||||
| node, | node, | ||||
| '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); | ||||
| JX.DOM.alterClass( | JX.DOM.alterClass( | ||||
| node, | node, | ||||
| 'diff-banner-has-draft-done', | 'diff-banner-has-draft-done', | ||||
| !!draft_done.length); | !!draft_done.length); | ||||
| var pht = this.getTranslations(); | var pht = this.getTranslations(); | ||||
| var unsaved_button = this._getUnsavedButton(); | var unsaved_button = this._getUnsavedButton(); | ||||
| var unsubmitted_button = this._getUnsubmittedButton(); | var unsubmitted_button = this._getUnsubmittedButton(); | ||||
| var done_button = this._getDoneButton(); | var done_button = this._getDoneButton(); | ||||
| var menu_button = this._getMenuButton(); | |||||
| if (unsaved.length) { | if (unsaved.length) { | ||||
| unsaved_button.setText(unsaved.length + ' ' + pht('Unsaved')); | unsaved_button.setText(unsaved.length + ' ' + pht('Unsaved')); | ||||
| JX.DOM.show(unsaved_button.getNode()); | JX.DOM.show(unsaved_button.getNode()); | ||||
| } else { | } else { | ||||
| JX.DOM.hide(unsaved_button.getNode()); | JX.DOM.hide(unsaved_button.getNode()); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | _redrawBanner: function() { | ||||
| var buttons_attrs = { | var buttons_attrs = { | ||||
| className: 'diff-banner-buttons' | className: 'diff-banner-buttons' | ||||
| }; | }; | ||||
| var buttons_list = [ | var buttons_list = [ | ||||
| unsaved_button.getNode(), | unsaved_button.getNode(), | ||||
| unsubmitted_button.getNode(), | unsubmitted_button.getNode(), | ||||
| done_button.getNode() | done_button.getNode(), | ||||
| menu_button.getNode() | |||||
| ]; | ]; | ||||
| var buttons_view = JX.$N('div', buttons_attrs, buttons_list); | 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, [buttons_view, path_view]); | JX.DOM.setContent(node, [buttons_view, path_view]); | ||||
| document.body.appendChild(node); | document.body.appendChild(node); | ||||
| }, | }, | ||||
| _getInlinesByType: function() { | |||||
| var changesets = this._changesets; | |||||
| var unsaved = []; | |||||
| var unsubmitted = []; | |||||
| var undone = []; | |||||
| var done = []; | |||||
| var draft_done = []; | |||||
| var visible_done = []; | |||||
| var visible_collapsed = []; | |||||
| var visible_ghosts = []; | |||||
| var visible = []; | |||||
| var hidden = []; | |||||
| for (var ii = 0; ii < changesets.length; ii++) { | |||||
| var inlines = changesets[ii].getInlines(); | |||||
| var inline; | |||||
| var jj; | |||||
| for (jj = 0; jj < inlines.length; jj++) { | |||||
| inline = inlines[jj]; | |||||
| if (inline.isDeleted()) { | |||||
| continue; | |||||
| } | |||||
| if (inline.isSynthetic()) { | |||||
| continue; | |||||
| } | |||||
| if (inline.isEditing()) { | |||||
| unsaved.push(inline); | |||||
| } else if (!inline.getID()) { | |||||
| // These are new comments which have been cancelled, and do not | |||||
| // count as anything. | |||||
| continue; | |||||
| } else if (inline.isDraft()) { | |||||
| unsubmitted.push(inline); | |||||
| } else { | |||||
| // NOTE: Unlike other states, an inline may be marked with a | |||||
| // draft checkmark and still be a "done" or "undone" comment. | |||||
| if (inline.isDraftDone()) { | |||||
| draft_done.push(inline); | |||||
| } | |||||
| if (!inline.isDone()) { | |||||
| undone.push(inline); | |||||
| } else { | |||||
| done.push(inline); | |||||
| } | |||||
| } | |||||
| } | |||||
| for (jj = 0; jj < inlines.length; jj++) { | |||||
| inline = inlines[jj]; | |||||
| if (inline.isDeleted()) { | |||||
| continue; | |||||
| } | |||||
| if (inline.isEditing()) { | |||||
| continue; | |||||
| } | |||||
| if (inline.isHidden()) { | |||||
| hidden.push(inline); | |||||
| continue; | |||||
| } | |||||
| visible.push(inline); | |||||
| if (inline.isDone()) { | |||||
| visible_done.push(inline); | |||||
| } | |||||
| if (inline.isCollapsed()) { | |||||
| visible_collapsed.push(inline); | |||||
| } | |||||
| if (inline.isGhost()) { | |||||
| visible_ghosts.push(inline); | |||||
| } | |||||
| } | |||||
| } | |||||
| return { | |||||
| unsaved: unsaved, | |||||
| unsubmitted: unsubmitted, | |||||
| undone: undone, | |||||
| done: done, | |||||
| draftDone: draft_done, | |||||
| visibleDone: visible_done, | |||||
| visibleGhosts: visible_ghosts, | |||||
| visibleCollapsed: visible_collapsed, | |||||
| visible: visible, | |||||
| hidden: hidden | |||||
| }; | |||||
| }, | |||||
| _getUnsavedButton: function() { | _getUnsavedButton: function() { | ||||
| if (!this._unsavedButton) { | if (!this._unsavedButton) { | ||||
| var button = new JX.PHUIXButtonView() | var button = new JX.PHUIXButtonView() | ||||
| .setIcon('fa-commenting-o') | .setIcon('fa-commenting-o') | ||||
| .setButtonType(JX.PHUIXButtonView.BUTTONTYPE_SIMPLE); | .setButtonType(JX.PHUIXButtonView.BUTTONTYPE_SIMPLE); | ||||
| var node = button.getNode(); | var node = button.getNode(); | ||||
| Show All 34 Lines | _getDoneButton: function() { | ||||
| var ondone = JX.bind(this, this._ondoneclick); | var ondone = JX.bind(this, this._ondoneclick); | ||||
| JX.DOM.listen(node, 'click', null, ondone); | JX.DOM.listen(node, 'click', null, ondone); | ||||
| this._doneButton = button; | this._doneButton = button; | ||||
| } | } | ||||
| return this._doneButton; | return this._doneButton; | ||||
| }, | }, | ||||
| _getMenuButton: function() { | |||||
| if (!this._menuButton) { | |||||
| var button = new JX.PHUIXButtonView() | |||||
| .setIcon('fa-gear') | |||||
| .setButtonType(JX.PHUIXButtonView.BUTTONTYPE_SIMPLE); | |||||
| var dropdown = new JX.PHUIXDropdownMenu(button.getNode()); | |||||
| this._menuItems = {}; | |||||
| var list = new JX.PHUIXActionListView(); | |||||
| dropdown.setContent(list.getNode()); | |||||
| var map = { | |||||
| hideDone: { | |||||
| type: 'done' | |||||
| }, | |||||
| hideCollapsed: { | |||||
| type: 'collapsed' | |||||
| }, | |||||
| hideGhosts: { | |||||
| type: 'ghosts' | |||||
| }, | |||||
| hideAll: { | |||||
| type: 'all' | |||||
| }, | |||||
| showAll: { | |||||
| type: 'show' | |||||
| } | |||||
| }; | |||||
| for (var k in map) { | |||||
| var spec = map[k]; | |||||
| var handler = JX.bind(this, this._onhideinlines, spec.type); | |||||
| var item = new JX.PHUIXActionView() | |||||
| .setHandler(handler); | |||||
| list.addItem(item); | |||||
| this._menuItems[k] = item; | |||||
| } | |||||
| dropdown.listen('open', JX.bind(this, this._ondropdown)); | |||||
| this._menuButton = button; | |||||
| this._dropdownMenu = dropdown; | |||||
| } | |||||
| return this._menuButton; | |||||
| }, | |||||
| _ondropdown: function() { | |||||
| var inlines = this._getInlinesByType(); | |||||
| var items = this._menuItems; | |||||
| var pht = this.getTranslations(); | |||||
| items.hideDone | |||||
| .setName(pht('Hide "Done" Inlines')) | |||||
| .setDisabled(!inlines.visibleDone.length); | |||||
| items.hideCollapsed | |||||
| .setName(pht('Hide Collapsed Inlines')) | |||||
| .setDisabled(!inlines.visibleCollapsed.length); | |||||
| items.hideGhosts | |||||
| .setName(pht('Hide Older Inlines')) | |||||
| .setDisabled(!inlines.visibleGhosts.length); | |||||
| items.hideAll | |||||
| .setName(pht('Hide All Inlines')) | |||||
| .setDisabled(!inlines.visible.length); | |||||
| items.showAll | |||||
| .setName(pht('Show All Inlines')) | |||||
| .setDisabled(!inlines.hidden.length); | |||||
| }, | |||||
| _onhideinlines: function(type, e) { | |||||
| this._dropdownMenu.close(); | |||||
| e.prevent(); | |||||
| var inlines = this._getInlinesByType(); | |||||
| // Clear the selection state since we end up in a weird place if the | |||||
| // user hides the selected inline. | |||||
| this._setSelectionState(null); | |||||
| var targets; | |||||
| var mode = true; | |||||
| switch (type) { | |||||
| case 'done': | |||||
| targets = inlines.visibleDone; | |||||
| break; | |||||
| case 'collapsed': | |||||
| targets = inlines.visibleCollapsed; | |||||
| break; | |||||
| case 'ghosts': | |||||
| targets = inlines.visibleGhosts; | |||||
| break; | |||||
| case 'all': | |||||
| targets = inlines.visible; | |||||
| break; | |||||
| case 'show': | |||||
| targets = inlines.hidden; | |||||
| mode = false; | |||||
| break; | |||||
| } | |||||
| for (var ii = 0; ii < targets.length; ii++) { | |||||
| targets[ii].setHidden(mode); | |||||
| } | |||||
| }, | |||||
| _onunsavedclick: function(e) { | _onunsavedclick: function(e) { | ||||
| e.kill(); | e.kill(); | ||||
| var options = { | var options = { | ||||
| filter: 'comment', | filter: 'comment', | ||||
| wrap: true, | wrap: true, | ||||
| show: true, | |||||
| attribute: 'unsaved' | attribute: 'unsaved' | ||||
| }; | }; | ||||
| this._onjumpkey(1, options); | this._onjumpkey(1, options); | ||||
| }, | }, | ||||
| _onunsubmittedclick: function(e) { | _onunsubmittedclick: function(e) { | ||||
| e.kill(); | e.kill(); | ||||
| var options = { | var options = { | ||||
| filter: 'comment', | filter: 'comment', | ||||
| wrap: true, | wrap: true, | ||||
| show: true, | |||||
| attribute: 'anyDraft' | attribute: 'anyDraft' | ||||
| }; | }; | ||||
| this._onjumpkey(1, options); | this._onjumpkey(1, options); | ||||
| }, | }, | ||||
| _ondoneclick: function(e) { | _ondoneclick: function(e) { | ||||
| e.kill(); | e.kill(); | ||||
| var options = { | var options = { | ||||
| filter: 'comment', | filter: 'comment', | ||||
| wrap: true, | wrap: true, | ||||
| show: true, | |||||
| attribute: this._doneMode | attribute: this._doneMode | ||||
| }; | }; | ||||
| this._onjumpkey(1, options); | this._onjumpkey(1, options); | ||||
| }, | }, | ||||
| _getBannerNode: function() { | _getBannerNode: function() { | ||||
| if (!this._bannerNode) { | if (!this._bannerNode) { | ||||
| ▲ Show 20 Lines • Show All 51 Lines • Show Last 20 Lines | |||||