diff --git a/resources/celerity/map.php b/resources/celerity/map.php --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -10,10 +10,10 @@ 'conpherence.pkg.css' => 'ff161f2d', 'conpherence.pkg.js' => 'b5b51108', 'core.pkg.css' => 'd6dc3994', - 'core.pkg.js' => 'a0c8fb20', + 'core.pkg.js' => 'e822b496', 'darkconsole.pkg.js' => '1f9a31bc', - 'differential.pkg.css' => '52b014e7', - 'differential.pkg.js' => '1efe85bf', + 'differential.pkg.css' => 'deae6388', + 'differential.pkg.js' => 'dedee9c8', 'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.js' => '84c8f8fd', 'favicon.ico' => '30672e08', @@ -64,7 +64,7 @@ 'rsrc/css/application/dashboard/dashboard.css' => 'fe5b1869', 'rsrc/css/application/diff/inline-comment-summary.css' => '51efda3a', 'rsrc/css/application/differential/add-comment.css' => 'c47f8c40', - 'rsrc/css/application/differential/changeset-view.css' => 'e7bd2a79', + 'rsrc/css/application/differential/changeset-view.css' => '6b79bdf3', 'rsrc/css/application/differential/core.css' => '5b7b8ff4', 'rsrc/css/application/differential/phui-inline-comment.css' => 'ffd1a542', 'rsrc/css/application/differential/revision-comment.css' => '14b8565a', @@ -390,11 +390,13 @@ 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173', 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', - 'rsrc/js/application/diff/DiffChangeset.js' => '3268dd83', - 'rsrc/js/application/diff/DiffChangesetList.js' => '0a4f7809', - 'rsrc/js/application/diff/DiffInline.js' => '3337c065', + 'rsrc/js/application/diff/DiffChangeset.js' => '20580ec0', + 'rsrc/js/application/diff/DiffChangesetList.js' => '61086d73', + 'rsrc/js/application/diff/DiffInline.js' => '77e14b60', + 'rsrc/js/application/diff/ScrollObjective.js' => '0eee7a00', + 'rsrc/js/application/diff/ScrollObjectiveList.js' => '1ca4d9db', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', - 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', + 'rsrc/js/application/differential/behavior-comment-preview.js' => '51c5ad07', 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', 'rsrc/js/application/differential/behavior-populate.js' => '5e41c819', 'rsrc/js/application/differential/behavior-user-select.js' => 'a8d8459d', @@ -446,7 +448,7 @@ 'rsrc/js/application/transactions/behavior-comment-actions.js' => '9a6dd75c', 'rsrc/js/application/transactions/behavior-reorder-configs.js' => 'd7a74243', 'rsrc/js/application/transactions/behavior-reorder-fields.js' => 'b59e1e96', - 'rsrc/js/application/transactions/behavior-show-older-transactions.js' => '94c65b72', + 'rsrc/js/application/transactions/behavior-show-older-transactions.js' => 'ae95d984', 'rsrc/js/application/transactions/behavior-transaction-comment-form.js' => 'b23b49e6', 'rsrc/js/application/transactions/behavior-transaction-list.js' => '1f6794f6', 'rsrc/js/application/typeahead/behavior-typeahead-browse.js' => '635de1ec', @@ -565,7 +567,7 @@ 'conpherence-thread-manager' => '4d863052', 'conpherence-transaction-css' => '85129c68', 'd3' => 'a11a5ff2', - 'differential-changeset-view-css' => 'e7bd2a79', + 'differential-changeset-view-css' => '6b79bdf3', 'differential-core-view-css' => '5b7b8ff4', 'differential-revision-add-comment-css' => 'c47f8c40', 'differential-revision-comment-css' => '14b8565a', @@ -617,7 +619,7 @@ 'javelin-behavior-device' => 'bb1dd507', 'javelin-behavior-diff-preview-link' => '051c7832', 'javelin-behavior-differential-diff-radios' => 'e1ff79b1', - 'javelin-behavior-differential-feedback-preview' => 'b064af76', + 'javelin-behavior-differential-feedback-preview' => '51c5ad07', 'javelin-behavior-differential-populate' => '5e41c819', 'javelin-behavior-differential-user-select' => 'a8d8459d', 'javelin-behavior-diffusion-browse-file' => '054a0f0b', @@ -665,7 +667,7 @@ 'javelin-behavior-phabricator-remarkup-assist' => 'acd29eee', 'javelin-behavior-phabricator-reveal-content' => '60821bc7', 'javelin-behavior-phabricator-search-typeahead' => 'eded9ee8', - 'javelin-behavior-phabricator-show-older-transactions' => '94c65b72', + 'javelin-behavior-phabricator-show-older-transactions' => 'ae95d984', 'javelin-behavior-phabricator-tooltips' => 'c420b0b9', 'javelin-behavior-phabricator-transaction-comment-form' => 'b23b49e6', 'javelin-behavior-phabricator-transaction-list' => '1f6794f6', @@ -775,9 +777,9 @@ 'phabricator-darklog' => 'c8e1ffe3', 'phabricator-darkmessage' => 'c48cccdd', 'phabricator-dashboard-css' => 'fe5b1869', - 'phabricator-diff-changeset' => '3268dd83', - 'phabricator-diff-changeset-list' => '0a4f7809', - 'phabricator-diff-inline' => '3337c065', + 'phabricator-diff-changeset' => '20580ec0', + 'phabricator-diff-changeset-list' => '61086d73', + 'phabricator-diff-inline' => '77e14b60', 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-draggable-list' => 'bea6e7f4', 'phabricator-fatal-config-template-css' => '8f18fa41', @@ -797,6 +799,8 @@ 'phabricator-phtize' => 'd254d646', 'phabricator-prefab' => 'c5af80a2', 'phabricator-remarkup-css' => 'd1a5e11e', + 'phabricator-scroll-objective' => '0eee7a00', + 'phabricator-scroll-objective-list' => '1ca4d9db', 'phabricator-search-results-css' => 'f87d23ad', 'phabricator-shaped-request' => '7cbe244b', 'phabricator-slowvote-css' => 'a94b7230', @@ -973,8 +977,12 @@ 'javelin-dom', 'javelin-router', ), - '0a4f7809' => array( + '0eee7a00' => array( + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', 'javelin-install', + 'javelin-workflow', ), '0f764c35' => array( 'javelin-install', @@ -1026,6 +1034,14 @@ 'javelin-request', 'javelin-uri', ), + '1ca4d9db' => array( + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-install', + 'javelin-workflow', + 'phabricator-scroll-objective', + ), '1def2711' => array( 'javelin-install', 'javelin-dom', @@ -1054,6 +1070,17 @@ 'javelin-install', 'javelin-dom', ), + '20580ec0' => array( + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-install', + 'javelin-workflow', + 'javelin-router', + 'javelin-behavior-device', + 'javelin-vector', + 'phabricator-diff-inline', + ), '21df4ff5' => array( 'javelin-install', 'javelin-workboard-card', @@ -1112,26 +1139,12 @@ 'javelin-dom', 'javelin-vector', ), - '3268dd83' => array( - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-install', - 'javelin-workflow', - 'javelin-router', - 'javelin-behavior-device', - 'javelin-vector', - 'phabricator-diff-inline', - ), '327a00d1' => array( 'javelin-behavior', 'javelin-stratcom', 'javelin-dom', 'javelin-workflow', ), - '3337c065' => array( - 'javelin-dom', - ), '3ab51e2c' => array( 'javelin-behavior', 'javelin-behavior-device', @@ -1295,6 +1308,14 @@ 'javelin-dom', 'javelin-reactor-dom', ), + '51c5ad07' => array( + 'javelin-behavior', + 'javelin-stratcom', + 'javelin-dom', + 'javelin-request', + 'javelin-util', + 'phabricator-shaped-request', + ), '522431f7' => array( 'javelin-behavior', 'javelin-util', @@ -1386,6 +1407,10 @@ 'javelin-stratcom', 'javelin-dom', ), + '61086d73' => array( + 'javelin-install', + 'phabricator-scroll-objective-list', + ), '61cbc29a' => array( 'javelin-magical-init', 'javelin-util', @@ -1417,6 +1442,9 @@ 'javelin-util', 'javelin-magical-init', ), + '6b79bdf3' => array( + 'phui-inline-comment-view-css', + ), '6b8ef10b' => array( 'javelin-install', ), @@ -1469,6 +1497,9 @@ 'javelin-reactor', 'javelin-util', ), + '77e14b60' => array( + 'javelin-dom', + ), '782ab6e7' => array( 'javelin-behavior', 'javelin-dom', @@ -1629,12 +1660,6 @@ 'javelin-resource', 'javelin-routable', ), - '94c65b72' => array( - 'javelin-behavior', - 'javelin-stratcom', - 'javelin-dom', - 'phabricator-busy', - ), '960f6a39' => array( 'javelin-behavior', 'javelin-dom', @@ -1776,19 +1801,17 @@ 'phuix-autocomplete', 'javelin-mask', ), - 'b003d4fb' => array( + 'ae95d984' => array( 'javelin-behavior', 'javelin-stratcom', 'javelin-dom', - 'phuix-dropdown-menu', + 'phabricator-busy', ), - 'b064af76' => array( + 'b003d4fb' => array( 'javelin-behavior', 'javelin-stratcom', 'javelin-dom', - 'javelin-request', - 'javelin-util', - 'phabricator-shaped-request', + 'phuix-dropdown-menu', ), 'b1f0ccee' => array( 'javelin-install', @@ -2124,9 +2147,6 @@ 'javelin-workflow', 'javelin-magical-init', ), - 'e7bd2a79' => array( - 'phui-inline-comment-view-css', - ), 'e9581f08' => array( 'javelin-behavior', 'javelin-stratcom', @@ -2412,6 +2432,8 @@ 'javelin-behavior-load-blame', 'javelin-behavior-differential-user-select', 'javelin-behavior-aphront-more', + 'phabricator-scroll-objective', + 'phabricator-scroll-objective-list', 'phabricator-diff-inline', 'phabricator-diff-changeset', 'phabricator-diff-changeset-list', diff --git a/resources/celerity/packages.php b/resources/celerity/packages.php --- a/resources/celerity/packages.php +++ b/resources/celerity/packages.php @@ -203,6 +203,9 @@ 'javelin-behavior-differential-user-select', 'javelin-behavior-aphront-more', + 'phabricator-scroll-objective', + 'phabricator-scroll-objective-list', + 'phabricator-diff-inline', 'phabricator-diff-changeset', 'phabricator-diff-changeset-list', diff --git a/src/applications/differential/view/DifferentialChangesetDetailView.php b/src/applications/differential/view/DifferentialChangesetDetailView.php --- a/src/applications/differential/view/DifferentialChangesetDetailView.php +++ b/src/applications/differential/view/DifferentialChangesetDetailView.php @@ -184,6 +184,8 @@ 'loaded' => $this->getLoaded(), 'undoTemplates' => hsprintf('%s', $renderer->renderUndoTemplates()), 'path' => $display_filename, + 'objectiveName' => basename($display_filename), + 'icon' => 'fa-file-text-o', ), 'class' => $class, 'id' => $id, diff --git a/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php b/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php --- a/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php +++ b/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php @@ -99,6 +99,14 @@ 'differential-inline-comment', ); + $is_fixed = false; + switch ($inline->getFixedState()) { + case PhabricatorInlineCommentInterface::STATE_DONE: + case PhabricatorInlineCommentInterface::STATE_DRAFT: + $is_fixed = true; + break; + } + $metadata = array( 'id' => $inline->getID(), 'phid' => $inline->getPHID(), @@ -109,6 +117,9 @@ 'on_right' => $this->getIsOnRight(), 'original' => $inline->getContent(), 'replyToCommentPHID' => $inline->getReplyToCommentPHID(), + 'isDraft' => $inline->isDraft(), + 'isFixed' => $is_fixed, + 'isGhost' => $inline->getIsGhost(), ); $sigil = 'differential-inline-comment'; diff --git a/webroot/rsrc/css/application/differential/changeset-view.css b/webroot/rsrc/css/application/differential/changeset-view.css --- a/webroot/rsrc/css/application/differential/changeset-view.css +++ b/webroot/rsrc/css/application/differential/changeset-view.css @@ -404,3 +404,32 @@ .diff-banner .phui-icon-view { margin-right: 4px; } + +.scroll-objective-list { + position: fixed; + right: 0; + width: 24px; + top: 48px; + bottom: 48px; + z-index: 6; + background: rgba(255, 255, 255, 0.50); + border-style: solid; + border-color: rgba(255, 255, 255, 0.95); + border-width: 1px 0 1px 1px; + box-shadow: -1px 0 2px rgba(255, 255, 255, 0.25); + overflow: hidden; +} + +.scroll-objective { + display: block; + position: absolute; + box-sizing: border-box; + cursor: pointer; + left: 8px; +} + +.scroll-objective .phui-icon-view { + text-shadow: 1px 1px 4px rgba(0, 0, 0, 0.25); + display: block; + height: 14px; +} diff --git a/webroot/rsrc/js/application/diff/DiffChangeset.js b/webroot/rsrc/js/application/diff/DiffChangeset.js --- a/webroot/rsrc/js/application/diff/DiffChangeset.js +++ b/webroot/rsrc/js/application/diff/DiffChangeset.js @@ -32,14 +32,12 @@ this._rightID = data.right; this._path = data.path; + this._objectiveName = data.objectiveName; + this._icon = data.icon; this._inlines = []; }, - properties: { - changesetList: null - }, - members: { _node: null, _loaded: false, @@ -63,6 +61,11 @@ _undoNode: null, _path: null, + _changesetList: null, + _objective: null, + _objectiveName: null, + _icon: null, + getLeftChangesetID: function() { return this._leftID; }, @@ -71,6 +74,49 @@ return this._rightID; }, + setChangesetList: function(list) { + this._changesetList = list; + + var objectives = list.getObjectives(); + this._objective = objectives.newObjective() + .setAnchor(this._node); + + this._updateObjective(); + + return this; + }, + + _updateObjective: function() { + this._objective + .setIcon(this.getIcon()) + .setColor(this.getColor()) + .setTooltip(this.getObjectiveName()); + }, + + getIcon: function() { + if (!this._visible) { + return 'fa-file-o'; + } + + return this._icon; + }, + + getColor: function() { + if (!this._visible) { + return 'grey'; + } + + return 'blue'; + }, + + getObjectiveName: function() { + return this._objectiveName; + }, + + getChangesetList: function() { + return this._changesetList; + }, + /** * Has the content of this changeset been loaded? * @@ -523,6 +569,11 @@ } JX.Stratcom.invoke('differential-inline-comment-refresh'); + + this._objective.show(); + this._rebuildAllInlines(); + + JX.Stratcom.invoke('resize'); }, _getContentFrame: function() { @@ -672,9 +723,18 @@ JX.DOM.appendContent(diff.parentNode, undo); } + this._updateObjective(); + for (var ii = 0; ii < this._inlines.length; ii++) { + this._inlines[ii].updateObjective(); + } + JX.Stratcom.invoke('resize'); }, + isVisible: function() { + return this._visible; + }, + _getUndoNode: function() { if (!this._undoNode) { var pht = this.getChangesetList().getTranslations(); diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js --- a/webroot/rsrc/js/application/diff/DiffChangesetList.js +++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js @@ -1,6 +1,7 @@ /** * @provides phabricator-diff-changeset-list * @requires javelin-install + * phabricator-scroll-objective-list * @javelin */ @@ -8,6 +9,7 @@ construct: function() { this._changesets = []; + this._objectives = new JX.ScrollObjectiveList(); var onload = JX.bind(this, this._ifawake, this._onload); JX.Stratcom.listen('click', 'differential-load', onload); @@ -100,6 +102,7 @@ _initialized: false, _asleep: true, _changesets: null, + _objectives: null, _cursorItem: null, @@ -124,6 +127,8 @@ this._redrawFocus(); this._redrawSelection(); this.resetHover(); + + this._objectives.hide(); }, wake: function() { @@ -132,6 +137,8 @@ this._redrawFocus(); this._redrawSelection(); + this._objectives.show(); + if (this._initialized) { return; } @@ -192,6 +199,10 @@ return this._asleep; }, + getObjectives: function() { + return this._objectives; + }, + newChangesetForNode: function(node) { var changeset = JX.DiffChangeset.getForNode(node); @@ -273,6 +284,18 @@ manager.scrollTo(toc); }, + getSelectedInline: function() { + var cursor = this._cursorItem; + + if (cursor) { + if (cursor.type == 'comment') { + return cursor.target; + } + } + + return null; + }, + _onkeyreply: function(is_quote) { var cursor = this._cursorItem; @@ -772,7 +795,7 @@ } else if (diffs.length == 1) { var diff = diffs[0]; visible_item.setDisabled(false); - if (JX.Stratcom.getData(diff).hidden) { + if (!changeset.isVisible()) { visible_item .setName(pht('Expand File')) .setIcon('fa-expand'); @@ -836,6 +859,10 @@ // event. e.kill(); + this.selectInline(inline); + }, + + selectInline: function(inline) { var selection = this._getSelectionState(); var item; diff --git a/webroot/rsrc/js/application/diff/DiffInline.js b/webroot/rsrc/js/application/diff/DiffInline.js --- a/webroot/rsrc/js/application/diff/DiffInline.js +++ b/webroot/rsrc/js/application/diff/DiffInline.js @@ -9,10 +9,6 @@ construct : function() { }, - properties: { - changeset: null - }, - members: { _id: null, _phid: null, @@ -31,12 +27,18 @@ _isInvisible: false, _isLoading: false, + _changeset: null, + _objective: null, + + _isDraft: null, + _isFixed: null, + bindToRow: function(row) { this._row = row; + this._objective.setAnchor(this._row); var row_data = JX.Stratcom.getData(row); row_data.inline = this; - this._hidden = row_data.hidden || false; // TODO: Get smarter about this once we do more editing, this is pretty @@ -65,6 +67,13 @@ this._replyToCommentPHID = data.replyToCommentPHID; + this._isDraft = data.isDraft; + this._isFixed = data.isFixed; + this._isGhost = data.isGhost; + + this._changesetID = data.changesetID; + + this.updateObjective(); this.setInvisible(false); return this; @@ -152,6 +161,60 @@ return this; }, + setChangeset: function(changeset) { + this._changeset = changeset; + + var objectives = changeset.getChangesetList().getObjectives(); + this._objective = objectives.newObjective() + .setCallback(JX.bind(this, this._onobjective)); + this.updateObjective(); + + return this; + }, + + getChangeset: function() { + return this._changeset; + }, + + _onobjective: function() { + this.getChangeset().getChangesetList().selectInline(this); + }, + + updateObjective: function() { + var objective = this._objective; + + if (this.isHidden() || this._isDeleted) { + objective.hide(); + return; + } + + var changeset = this.getChangeset(); + if (!changeset.isVisible()) { + objective.hide(); + return; + } + + var icon = 'fa-comment'; + var color = 'bluegrey'; + + if (this._isDraft) { + // This inline is an unsubmitted draft. + icon = 'fa-pencil'; + } else if (this._isFixed) { + // This inline has been marked done. + icon = 'fa-check'; + color = 'grey'; + } else if (this._isGhost) { + icon = 'fa-comment-o'; + color = 'grey'; + } + + objective + .setIcon(icon) + .setColor(color) + .show(); + }, + canReply: function() { if (!this._hasAction('reply')) { return false; @@ -202,6 +265,7 @@ JX.Stratcom.getData(row).inline = this; this._row = row; + this._objective.setAnchor(this._row); this._id = null; this._phid = null; @@ -272,6 +336,8 @@ // top-level "draft" state of unsubmitted comments. JX.DOM.alterClass(comment, 'inline-state-is-draft', response.draftState); + this._isFixed = response.isChecked; + this._didUpdate(); }, @@ -633,6 +699,8 @@ this.getChangeset().getChangesetList().redrawPreview(); } + this.updateObjective(); + this.getChangeset().getChangesetList().redrawCursor(); this.getChangeset().getChangesetList().resetHover(); diff --git a/webroot/rsrc/js/application/diff/ScrollObjective.js b/webroot/rsrc/js/application/diff/ScrollObjective.js new file mode 100644 --- /dev/null +++ b/webroot/rsrc/js/application/diff/ScrollObjective.js @@ -0,0 +1,121 @@ +/** + * @provides phabricator-scroll-objective + * @requires javelin-dom + * javelin-util + * javelin-stratcom + * javelin-install + * javelin-workflow + * @javelin + */ + + +JX.install('ScrollObjective', { + + construct : function() { + var node = this.getNode(); + + var onclick = JX.bind(this, this._onclick); + JX.DOM.listen(node, 'click', null, onclick); + }, + + members: { + _list: null, + + _node: null, + _anchor: null, + + _visible: false, + _callback: false, + + getNode: function() { + if (!this._node) { + var attributes = { + className: 'scroll-objective' + }; + + var content = this._getIconObject().getNode(); + + var node = JX.$N('div', attributes, content); + + this._node = node; + } + + return this._node; + }, + + setCallback: function(callback) { + this._callback = callback; + return this; + }, + + setObjectiveList: function(list) { + this._list = list; + return this; + }, + + _getIconObject: function() { + if (!this._iconObject) { + this._iconObject = new JX.PHUIXIconView(); + } + return this._iconObject; + }, + + _onclick: function(e) { + (this._callback && this._callback(e)); + + if (e.getPrevented()) { + return; + } + + e.kill(); + + // This is magic to account for the banner, and should probably be made + // less hard-coded. + var buffer = 48; + + JX.DOM.scrollToPosition(null, JX.$V(this.getAnchor()).y - buffer); + }, + + setAnchor: function(node) { + this._anchor = node; + return this; + }, + + getAnchor: function() { + return this._anchor; + }, + + setIcon: function(icon) { + this._getIconObject().setIcon(icon); + return this; + }, + + setColor: function(color) { + this._getIconObject().setColor(color); + return this; + }, + + setTooltip: function(tip) { + var node = this._getIconObject().getNode(); + JX.Stratcom.addSigil(node, 'has-tooltip'); + JX.Stratcom.getData(node).tip = tip; + JX.Stratcom.getData(node).align = 'W'; + return this; + }, + + show: function() { + this._visible = true; + return this; + }, + + hide: function() { + this._visible = false; + }, + + isVisible: function() { + return this._visible; + } + + } + +}); diff --git a/webroot/rsrc/js/application/diff/ScrollObjectiveList.js b/webroot/rsrc/js/application/diff/ScrollObjectiveList.js new file mode 100644 --- /dev/null +++ b/webroot/rsrc/js/application/diff/ScrollObjectiveList.js @@ -0,0 +1,139 @@ +/** + * @provides phabricator-scroll-objective-list + * @requires javelin-dom + * javelin-util + * javelin-stratcom + * javelin-install + * javelin-workflow + * phabricator-scroll-objective + * @javelin + */ + + +JX.install('ScrollObjectiveList', { + + construct : function() { + this._objectives = []; + + var onresize = JX.bind(this, this._dirty); + JX.Stratcom.listen('resize', null, onresize); + }, + + members: { + _objectives: null, + _visible: false, + _trigger: null, + + newObjective: function() { + var objective = new JX.ScrollObjective() + .setObjectiveList(this); + + this._objectives.push(objective); + this._getNode().appendChild(objective.getNode()); + + this._dirty(); + + return objective; + }, + + show: function() { + this._visible = true; + this._dirty(); + return this; + }, + + hide: function() { + this._visible = false; + this._dirty(); + return this; + }, + + _getNode: function() { + if (!this._node) { + var node = new JX.$N('div', {className: 'scroll-objective-list'}); + this._node = node; + } + return this._node; + }, + + _dirty: function() { + if (this._trigger !== null) { + return; + } + + this._trigger = setTimeout(JX.bind(this, this._redraw), 0); + }, + + _redraw: function() { + this._trigger = null; + + var node = this._getNode(); + + var is_visible = + (this._visible) && + (JX.Device.getDevice() == 'desktop') && + (this._objectives.length); + + if (!is_visible) { + JX.DOM.remove(node); + return; + } + + document.body.appendChild(node); + + var d = JX.Vector.getDocument(); + + var list_dimensions = JX.Vector.getDim(node); + var icon_height = 16; + var list_y = (list_dimensions.y - icon_height); + + var ii; + var offset; + + // First, build a list of all the items we're going to show. + var items = []; + for (ii = 0; ii < this._objectives.length; ii++) { + var objective = this._objectives[ii]; + var objective_node = objective.getNode(); + + var anchor = objective.getAnchor(); + if (!anchor || !objective.isVisible()) { + JX.DOM.remove(objective_node); + continue; + } + + offset = (JX.$V(anchor).y / d.y) * (list_y); + + items.push({ + offset: offset, + node: objective_node + }); + } + + // Now, sort it from top to bottom. + items.sort(function(u, v) { + return u.offset - v.offset; + }); + + // Lay out the items in the objective list, leaving a minimum amount + // of space between them so they do not overlap. + var min = null; + for (ii = 0; ii < items.length; ii++) { + var item = items[ii]; + + offset = item.offset; + + if (min !== null) { + offset = Math.max(offset, min); + } + min = offset + 15; + + item.node.style.top = offset + 'px'; + node.appendChild(item.node); + } + + } + + } + +}); diff --git a/webroot/rsrc/js/application/differential/behavior-comment-preview.js b/webroot/rsrc/js/application/differential/behavior-comment-preview.js --- a/webroot/rsrc/js/application/differential/behavior-comment-preview.js +++ b/webroot/rsrc/js/application/differential/behavior-comment-preview.js @@ -74,6 +74,8 @@ }); updateLinks(); + + JX.Stratcom.invoke('resize'); }) .setTimeout(5000) .send(); diff --git a/webroot/rsrc/js/application/transactions/behavior-show-older-transactions.js b/webroot/rsrc/js/application/transactions/behavior-show-older-transactions.js --- a/webroot/rsrc/js/application/transactions/behavior-show-older-transactions.js +++ b/webroot/rsrc/js/application/transactions/behavior-show-older-transactions.js @@ -60,6 +60,7 @@ var show_older = function(swap, r) { JX.DOM.replace(swap, JX.$H(r.timeline).getFragment()); + JX.Stratcom.invoke('resize'); }; var load_hidden_hash_callback = function(swap, r) {