Page MenuHomestyx hydra

No OneTemporary

diff --git a/resources/celerity/map.php b/resources/celerity/map.php
index 086cb1b8b3..04036e70e4 100644
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -1,2365 +1,2365 @@
<?php
/**
* This file is automatically generated. Use 'bin/celerity map' to rebuild it.
*
* @generated
*/
return array(
'names' => array(
'conpherence.pkg.css' => '3c8a0668',
'conpherence.pkg.js' => '020aebcf',
'core.pkg.css' => '261ee8cf',
- 'core.pkg.js' => '5ba0b6d7',
- 'differential.pkg.css' => 'd1b29c9c',
- 'differential.pkg.js' => '0e2b0e2c',
+ 'core.pkg.js' => 'e368deda',
+ 'differential.pkg.css' => '249b542d',
+ 'differential.pkg.js' => '53f8d00c',
'diffusion.pkg.css' => '42c75c37',
'diffusion.pkg.js' => '91192d85',
'maniphest.pkg.css' => '35995d6d',
'maniphest.pkg.js' => '286955ae',
'rsrc/audio/basic/alert.mp3' => '17889334',
'rsrc/audio/basic/bing.mp3' => 'a817a0c3',
'rsrc/audio/basic/pock.mp3' => '0fa843d0',
'rsrc/audio/basic/tap.mp3' => '02d16994',
'rsrc/audio/basic/ting.mp3' => 'a6b6540e',
'rsrc/css/aphront/aphront-bars.css' => '4a327b4a',
'rsrc/css/aphront/dark-console.css' => '7f06cda2',
'rsrc/css/aphront/dialog-view.css' => 'b70c70df',
'rsrc/css/aphront/list-filter-view.css' => 'feb64255',
'rsrc/css/aphront/multi-column.css' => 'fbc00ba3',
'rsrc/css/aphront/notification.css' => '30240bd2',
'rsrc/css/aphront/panel-view.css' => '46923d46',
'rsrc/css/aphront/phabricator-nav-view.css' => 'f8a0c1bf',
'rsrc/css/aphront/table-view.css' => '205053cd',
'rsrc/css/aphront/tokenizer.css' => 'b52d0668',
'rsrc/css/aphront/tooltip.css' => 'e3f2412f',
'rsrc/css/aphront/typeahead-browse.css' => 'b7ed02d2',
'rsrc/css/aphront/typeahead.css' => '8779483d',
'rsrc/css/application/almanac/almanac.css' => '2e050f4f',
'rsrc/css/application/auth/auth.css' => 'add92fd8',
'rsrc/css/application/base/main-menu-view.css' => '8e2d9a28',
'rsrc/css/application/base/notification-menu.css' => 'e6962e89',
'rsrc/css/application/base/phui-theme.css' => '35883b37',
'rsrc/css/application/base/standard-page-view.css' => '8a295cb9',
'rsrc/css/application/chatlog/chatlog.css' => 'abdc76ee',
'rsrc/css/application/conduit/conduit-api.css' => 'ce2cfc41',
'rsrc/css/application/config/config-options.css' => '16c920ae',
'rsrc/css/application/config/config-template.css' => '20babf50',
'rsrc/css/application/config/setup-issue.css' => '5eed85b2',
'rsrc/css/application/config/unhandled-exception.css' => '9ecfc00d',
'rsrc/css/application/conpherence/color.css' => 'b17746b0',
'rsrc/css/application/conpherence/durable-column.css' => '2d57072b',
'rsrc/css/application/conpherence/header-pane.css' => 'c9a3db8e',
'rsrc/css/application/conpherence/menu.css' => '67f4680d',
'rsrc/css/application/conpherence/message-pane.css' => 'd244db1e',
'rsrc/css/application/conpherence/notification.css' => '6a3d4e58',
'rsrc/css/application/conpherence/participant-pane.css' => '69e0058a',
'rsrc/css/application/conpherence/transaction.css' => '3a3f5e7e',
'rsrc/css/application/contentsource/content-source-view.css' => 'cdf0d579',
'rsrc/css/application/countdown/timer.css' => 'bff8012f',
'rsrc/css/application/daemon/bulk-job.css' => '73af99f5',
'rsrc/css/application/dashboard/dashboard.css' => '4267d6c6',
'rsrc/css/application/diff/inline-comment-summary.css' => '81eb368d',
'rsrc/css/application/differential/add-comment.css' => '7e5900d9',
- 'rsrc/css/application/differential/changeset-view.css' => 'e2b81e85',
+ 'rsrc/css/application/differential/changeset-view.css' => 'cc3fd795',
'rsrc/css/application/differential/core.css' => 'bdb93065',
'rsrc/css/application/differential/phui-inline-comment.css' => '48acce5b',
'rsrc/css/application/differential/revision-comment.css' => '7dbc8d1d',
'rsrc/css/application/differential/revision-history.css' => '8aa3eac5',
'rsrc/css/application/differential/revision-list.css' => '93d2df7d',
'rsrc/css/application/differential/table-of-contents.css' => '0e3364c7',
'rsrc/css/application/diffusion/diffusion-icons.css' => '23b31a1b',
'rsrc/css/application/diffusion/diffusion-readme.css' => 'b68a76e4',
'rsrc/css/application/diffusion/diffusion-repository.css' => 'b89e8c6c',
'rsrc/css/application/diffusion/diffusion.css' => 'b54c77b0',
'rsrc/css/application/feed/feed.css' => 'd8b6e3f8',
'rsrc/css/application/files/global-drag-and-drop.css' => '1d2713a4',
'rsrc/css/application/flag/flag.css' => '2b77be8d',
'rsrc/css/application/harbormaster/harbormaster.css' => '8dfe16b2',
'rsrc/css/application/herald/herald-test.css' => 'e004176f',
'rsrc/css/application/herald/herald.css' => '648d39e2',
'rsrc/css/application/maniphest/report.css' => '3d53188b',
'rsrc/css/application/maniphest/task-edit.css' => '272daa84',
'rsrc/css/application/maniphest/task-summary.css' => '61d1667e',
'rsrc/css/application/objectselector/object-selector.css' => 'ee77366f',
'rsrc/css/application/owners/owners-path-editor.css' => 'fa7c13ef',
'rsrc/css/application/paste/paste.css' => 'b37bcd38',
'rsrc/css/application/people/people-picture-menu-item.css' => 'fe8e07cf',
'rsrc/css/application/people/people-profile.css' => '2ea2daa1',
'rsrc/css/application/phame/phame.css' => '799febf9',
'rsrc/css/application/pholio/pholio-edit.css' => '4df55b3b',
'rsrc/css/application/pholio/pholio-inline-comments.css' => '722b48c2',
'rsrc/css/application/pholio/pholio.css' => '88ef5ef1',
'rsrc/css/application/phortune/phortune-credit-card-form.css' => '3b9868a8',
'rsrc/css/application/phortune/phortune-invoice.css' => '4436b241',
'rsrc/css/application/phortune/phortune.css' => '12e8251a',
'rsrc/css/application/phrequent/phrequent.css' => 'bd79cc67',
'rsrc/css/application/phriction/phriction-document-css.css' => '03380da0',
'rsrc/css/application/policy/policy-edit.css' => '8794e2ed',
'rsrc/css/application/policy/policy-transaction-detail.css' => 'c02b8384',
'rsrc/css/application/policy/policy.css' => 'ceb56a08',
'rsrc/css/application/ponder/ponder-view.css' => '05a09d0a',
'rsrc/css/application/project/project-card-view.css' => '3b1f7b20',
'rsrc/css/application/project/project-view.css' => '567858b3',
'rsrc/css/application/releeph/releeph-core.css' => 'f81ff2db',
'rsrc/css/application/releeph/releeph-preview-branch.css' => '22db5c07',
'rsrc/css/application/releeph/releeph-request-differential-create-dialog.css' => '0ac1ea31',
'rsrc/css/application/releeph/releeph-request-typeahead.css' => 'bce37359',
'rsrc/css/application/search/application-search-view.css' => '0f7c06d8',
'rsrc/css/application/search/search-results.css' => '9ea70ace',
'rsrc/css/application/slowvote/slowvote.css' => '1694baed',
'rsrc/css/application/tokens/tokens.css' => 'ce5a50bd',
'rsrc/css/application/uiexample/example.css' => 'b4795059',
'rsrc/css/core/core.css' => '1b29ed61',
'rsrc/css/core/remarkup.css' => '9e627d41',
'rsrc/css/core/syntax.css' => '8a16f91b',
'rsrc/css/core/z-index.css' => '99c0f5eb',
'rsrc/css/diviner/diviner-shared.css' => '4bd263b0',
'rsrc/css/font/font-awesome.css' => '3883938a',
'rsrc/css/font/font-lato.css' => '23631304',
'rsrc/css/font/phui-font-icon-base.css' => 'd7994e06',
'rsrc/css/layout/phabricator-filetree-view.css' => '56cdd875',
'rsrc/css/layout/phabricator-source-code-view.css' => '03d7ac28',
'rsrc/css/phui/button/phui-button-bar.css' => 'a4aa75c4',
'rsrc/css/phui/button/phui-button-simple.css' => '1ff278aa',
'rsrc/css/phui/button/phui-button.css' => 'ea704902',
'rsrc/css/phui/calendar/phui-calendar-day.css' => '9597d706',
'rsrc/css/phui/calendar/phui-calendar-list.css' => 'ccd7e4e2',
'rsrc/css/phui/calendar/phui-calendar-month.css' => 'cb758c42',
'rsrc/css/phui/calendar/phui-calendar.css' => 'f11073aa',
'rsrc/css/phui/object-item/phui-oi-big-ui.css' => '9e037c7a',
'rsrc/css/phui/object-item/phui-oi-color.css' => 'b517bfa0',
'rsrc/css/phui/object-item/phui-oi-drag-ui.css' => 'da15d3dc',
'rsrc/css/phui/object-item/phui-oi-flush-ui.css' => '490e2e2e',
'rsrc/css/phui/object-item/phui-oi-list-view.css' => '909f3844',
'rsrc/css/phui/object-item/phui-oi-simple-ui.css' => '6a30fa46',
'rsrc/css/phui/phui-action-list.css' => 'c4972757',
'rsrc/css/phui/phui-action-panel.css' => '6c386cbf',
'rsrc/css/phui/phui-badge.css' => '666e25ad',
'rsrc/css/phui/phui-basic-nav-view.css' => '56ebd66d',
'rsrc/css/phui/phui-big-info-view.css' => '362ad37b',
'rsrc/css/phui/phui-box.css' => '5ed3b8cb',
'rsrc/css/phui/phui-bulk-editor.css' => '374d5e30',
'rsrc/css/phui/phui-chart.css' => '7853a69b',
'rsrc/css/phui/phui-cms.css' => '8c05c41e',
'rsrc/css/phui/phui-comment-form.css' => '68a2d99a',
'rsrc/css/phui/phui-comment-panel.css' => 'ec4e31c0',
'rsrc/css/phui/phui-crumbs-view.css' => '614f43cf',
'rsrc/css/phui/phui-curtain-view.css' => '68c5efb6',
'rsrc/css/phui/phui-document-pro.css' => 'b9613a10',
'rsrc/css/phui/phui-document-summary.css' => 'b068eed1',
'rsrc/css/phui/phui-document.css' => '52b748a5',
'rsrc/css/phui/phui-feed-story.css' => 'a0c05029',
'rsrc/css/phui/phui-fontkit.css' => '9b714a5e',
'rsrc/css/phui/phui-form-view.css' => '01b796c0',
'rsrc/css/phui/phui-form.css' => '159e2d9c',
'rsrc/css/phui/phui-head-thing.css' => 'd7f293df',
'rsrc/css/phui/phui-header-view.css' => '285c9139',
'rsrc/css/phui/phui-hovercard.css' => '6ca90fa0',
'rsrc/css/phui/phui-icon-set-selector.css' => '7aa5f3ec',
'rsrc/css/phui/phui-icon.css' => '4cbc684a',
'rsrc/css/phui/phui-image-mask.css' => '62c7f4d2',
'rsrc/css/phui/phui-info-view.css' => '37b8d9ce',
'rsrc/css/phui/phui-invisible-character-view.css' => 'c694c4a4',
'rsrc/css/phui/phui-left-right.css' => '68513c34',
'rsrc/css/phui/phui-lightbox.css' => '4ebf22da',
'rsrc/css/phui/phui-list.css' => '470b1adb',
'rsrc/css/phui/phui-object-box.css' => 'f434b6be',
'rsrc/css/phui/phui-pager.css' => 'd022c7ad',
'rsrc/css/phui/phui-pinboard-view.css' => '1f08f5d8',
'rsrc/css/phui/phui-property-list-view.css' => 'cad62236',
'rsrc/css/phui/phui-remarkup-preview.css' => '91767007',
'rsrc/css/phui/phui-segment-bar-view.css' => '5166b370',
'rsrc/css/phui/phui-spacing.css' => 'b05cadc3',
'rsrc/css/phui/phui-status.css' => 'e5ff8be0',
'rsrc/css/phui/phui-tag-view.css' => '29409667',
'rsrc/css/phui/phui-timeline-view.css' => '1e348e4b',
'rsrc/css/phui/phui-two-column-view.css' => '01e6991e',
'rsrc/css/phui/workboards/phui-workboard-color.css' => 'e86de308',
'rsrc/css/phui/workboards/phui-workboard.css' => '74fc9d98',
'rsrc/css/phui/workboards/phui-workcard.css' => '8c536f90',
'rsrc/css/phui/workboards/phui-workpanel.css' => 'bd546a49',
'rsrc/css/sprite-login.css' => '18b368a6',
'rsrc/css/sprite-tokens.css' => 'f1896dc5',
'rsrc/css/syntax/syntax-default.css' => '055fc231',
'rsrc/externals/d3/d3.min.js' => 'd67475f5',
'rsrc/externals/font/fontawesome/fontawesome-webfont.eot' => '23f8c698',
'rsrc/externals/font/fontawesome/fontawesome-webfont.ttf' => '70983df0',
'rsrc/externals/font/fontawesome/fontawesome-webfont.woff' => 'cd02f93b',
'rsrc/externals/font/fontawesome/fontawesome-webfont.woff2' => '351fd46a',
'rsrc/externals/font/lato/lato-bold.eot' => '7367aa5e',
'rsrc/externals/font/lato/lato-bold.svg' => '681aa4f5',
'rsrc/externals/font/lato/lato-bold.ttf' => '66d3c296',
'rsrc/externals/font/lato/lato-bold.woff' => '89d9fba7',
'rsrc/externals/font/lato/lato-bold.woff2' => '389fcdb1',
'rsrc/externals/font/lato/lato-bolditalic.eot' => '03eeb4da',
'rsrc/externals/font/lato/lato-bolditalic.svg' => 'f56fa11c',
'rsrc/externals/font/lato/lato-bolditalic.ttf' => '9c3aec21',
'rsrc/externals/font/lato/lato-bolditalic.woff' => 'bfbd0616',
'rsrc/externals/font/lato/lato-bolditalic.woff2' => 'bc7d1274',
'rsrc/externals/font/lato/lato-italic.eot' => '7db5b247',
'rsrc/externals/font/lato/lato-italic.svg' => 'b1ae496f',
'rsrc/externals/font/lato/lato-italic.ttf' => '43eed813',
'rsrc/externals/font/lato/lato-italic.woff' => 'c28975e1',
'rsrc/externals/font/lato/lato-italic.woff2' => 'fffc0d8c',
'rsrc/externals/font/lato/lato-regular.eot' => '06e0c291',
'rsrc/externals/font/lato/lato-regular.svg' => '3ad95f53',
'rsrc/externals/font/lato/lato-regular.ttf' => 'e2e9c398',
'rsrc/externals/font/lato/lato-regular.woff' => '0b13d332',
'rsrc/externals/font/lato/lato-regular.woff2' => '8f846797',
'rsrc/externals/javelin/core/Event.js' => 'c03f2fb4',
'rsrc/externals/javelin/core/Stratcom.js' => '0889b835',
'rsrc/externals/javelin/core/__tests__/event-stop-and-kill.js' => '048472d2',
'rsrc/externals/javelin/core/__tests__/install.js' => '14a7e671',
'rsrc/externals/javelin/core/__tests__/stratcom.js' => 'a28464bb',
'rsrc/externals/javelin/core/__tests__/util.js' => 'e29a4354',
'rsrc/externals/javelin/core/init.js' => '98e6504a',
'rsrc/externals/javelin/core/init_node.js' => '16961339',
'rsrc/externals/javelin/core/install.js' => '5902260c',
'rsrc/externals/javelin/core/util.js' => '22ae1776',
'rsrc/externals/javelin/docs/Base.js' => '5a401d7d',
'rsrc/externals/javelin/docs/onload.js' => 'ee58fb62',
'rsrc/externals/javelin/ext/fx/Color.js' => '78f811c9',
'rsrc/externals/javelin/ext/fx/FX.js' => '34450586',
'rsrc/externals/javelin/ext/reactor/core/DynVal.js' => '202a2e85',
'rsrc/externals/javelin/ext/reactor/core/Reactor.js' => '1c850a26',
'rsrc/externals/javelin/ext/reactor/core/ReactorNode.js' => '72960bc1',
'rsrc/externals/javelin/ext/reactor/core/ReactorNodeCalmer.js' => '225bbb98',
'rsrc/externals/javelin/ext/reactor/dom/RDOM.js' => '6cfa0008',
'rsrc/externals/javelin/ext/view/HTMLView.js' => 'f8c4e135',
'rsrc/externals/javelin/ext/view/View.js' => '289bf236',
'rsrc/externals/javelin/ext/view/ViewInterpreter.js' => '876506b6',
'rsrc/externals/javelin/ext/view/ViewPlaceholder.js' => 'a9942052',
'rsrc/externals/javelin/ext/view/ViewRenderer.js' => '9aae2b66',
'rsrc/externals/javelin/ext/view/ViewVisitor.js' => '308f9fe4',
'rsrc/externals/javelin/ext/view/__tests__/HTMLView.js' => '6e50a13f',
'rsrc/externals/javelin/ext/view/__tests__/View.js' => 'd284be5d',
'rsrc/externals/javelin/ext/view/__tests__/ViewInterpreter.js' => 'a9f35511',
'rsrc/externals/javelin/ext/view/__tests__/ViewRenderer.js' => '3a1b81f6',
'rsrc/externals/javelin/lib/Cookie.js' => '05d290ef',
'rsrc/externals/javelin/lib/DOM.js' => '94681e22',
'rsrc/externals/javelin/lib/History.js' => '030b4f7a',
'rsrc/externals/javelin/lib/JSON.js' => '541f81c3',
'rsrc/externals/javelin/lib/Leader.js' => '0d2490ce',
'rsrc/externals/javelin/lib/Mask.js' => '7c4d8998',
'rsrc/externals/javelin/lib/Quicksand.js' => 'd3799cb4',
'rsrc/externals/javelin/lib/Request.js' => '91863989',
'rsrc/externals/javelin/lib/Resource.js' => '740956e1',
'rsrc/externals/javelin/lib/Routable.js' => '6a18c42e',
'rsrc/externals/javelin/lib/Router.js' => '32755edb',
'rsrc/externals/javelin/lib/Scrollbar.js' => 'a43ae2ae',
'rsrc/externals/javelin/lib/Sound.js' => 'e562708c',
'rsrc/externals/javelin/lib/URI.js' => '2e255291',
'rsrc/externals/javelin/lib/Vector.js' => 'e9c80beb',
'rsrc/externals/javelin/lib/WebSocket.js' => 'fdc13e4e',
'rsrc/externals/javelin/lib/Workflow.js' => '958e9045',
'rsrc/externals/javelin/lib/__tests__/Cookie.js' => 'ca686f71',
'rsrc/externals/javelin/lib/__tests__/DOM.js' => '4566e249',
'rsrc/externals/javelin/lib/__tests__/JSON.js' => '710377ae',
'rsrc/externals/javelin/lib/__tests__/URI.js' => '6fff0c2b',
'rsrc/externals/javelin/lib/__tests__/behavior.js' => '8426ebeb',
'rsrc/externals/javelin/lib/behavior.js' => 'fce5d170',
'rsrc/externals/javelin/lib/control/tokenizer/Tokenizer.js' => '89a1ae3a',
'rsrc/externals/javelin/lib/control/typeahead/Typeahead.js' => 'a4356cde',
'rsrc/externals/javelin/lib/control/typeahead/normalizer/TypeaheadNormalizer.js' => 'a241536a',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadCompositeSource.js' => '22ee68a5',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadOnDemandSource.js' => '23387297',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadPreloadedSource.js' => '5a79f6c3',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadSource.js' => '8badee71',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadStaticSource.js' => '80bff3af',
'rsrc/favicons/favicon-16x16.png' => '4c51a03a',
'rsrc/favicons/mask-icon.svg' => 'db699fe1',
'rsrc/image/BFCFDA.png' => '74b5c88b',
'rsrc/image/actions/edit.png' => 'fd987dff',
'rsrc/image/avatar.png' => '0d17c6c4',
'rsrc/image/checker_dark.png' => '7fc8fa7b',
'rsrc/image/checker_light.png' => '3157a202',
'rsrc/image/checker_lighter.png' => 'c45928c1',
'rsrc/image/chevron-in.png' => '1aa2f88f',
'rsrc/image/chevron-out.png' => 'c815e272',
'rsrc/image/controls/checkbox-checked.png' => '1770d7a0',
'rsrc/image/controls/checkbox-unchecked.png' => 'e1deba0a',
'rsrc/image/d5d8e1.png' => '6764616e',
'rsrc/image/darkload.gif' => '5bd41a89',
'rsrc/image/divot.png' => '0fbe2453',
'rsrc/image/examples/hero.png' => '5d8c4b21',
'rsrc/image/grippy_texture.png' => 'a7d222b5',
'rsrc/image/icon/fatcow/arrow_branch.png' => '98149d9f',
'rsrc/image/icon/fatcow/arrow_merge.png' => 'e142f4f8',
'rsrc/image/icon/fatcow/calendar_edit.png' => '5ff44a08',
'rsrc/image/icon/fatcow/document_black.png' => 'd3515fa5',
'rsrc/image/icon/fatcow/flag_blue.png' => '54db2e5c',
'rsrc/image/icon/fatcow/flag_finish.png' => '2953a51b',
'rsrc/image/icon/fatcow/flag_ghost.png' => '7d9ada92',
'rsrc/image/icon/fatcow/flag_green.png' => '010f7161',
'rsrc/image/icon/fatcow/flag_orange.png' => '6c384ca5',
'rsrc/image/icon/fatcow/flag_pink.png' => '11ac6b12',
'rsrc/image/icon/fatcow/flag_purple.png' => 'c4f423a4',
'rsrc/image/icon/fatcow/flag_red.png' => '9e6d8817',
'rsrc/image/icon/fatcow/flag_yellow.png' => '906733f4',
'rsrc/image/icon/fatcow/key_question.png' => 'c10c26db',
'rsrc/image/icon/fatcow/link.png' => '8edbf327',
'rsrc/image/icon/fatcow/page_white_edit.png' => '17ef5625',
'rsrc/image/icon/fatcow/page_white_put.png' => '82430c91',
'rsrc/image/icon/fatcow/source/conduit.png' => '5b55130c',
'rsrc/image/icon/fatcow/source/email.png' => '8a32b77f',
'rsrc/image/icon/fatcow/source/fax.png' => '8bc2a49b',
'rsrc/image/icon/fatcow/source/mobile.png' => '0a918412',
'rsrc/image/icon/fatcow/source/tablet.png' => 'fc50b050',
'rsrc/image/icon/fatcow/source/web.png' => '70433af3',
'rsrc/image/icon/subscribe.png' => '07ef454e',
'rsrc/image/icon/tango/attachment.png' => 'bac9032d',
'rsrc/image/icon/tango/edit.png' => 'e6296206',
'rsrc/image/icon/tango/go-down.png' => '0b903712',
'rsrc/image/icon/tango/log.png' => '86b6a6f4',
'rsrc/image/icon/tango/upload.png' => '3fe6b92d',
'rsrc/image/icon/unsubscribe.png' => 'db04378a',
'rsrc/image/lightblue-header.png' => 'e6d483c6',
'rsrc/image/logo/light-eye.png' => '72337472',
'rsrc/image/main_texture.png' => '894d03c4',
'rsrc/image/menu_texture.png' => '896c9ade',
'rsrc/image/people/harding.png' => '95b2db63',
'rsrc/image/people/jefferson.png' => 'e883a3a2',
'rsrc/image/people/lincoln.png' => 'be2c07c5',
'rsrc/image/people/mckinley.png' => '6af510a0',
'rsrc/image/people/taft.png' => 'b15ab07e',
'rsrc/image/people/user0.png' => '4bc64b40',
'rsrc/image/people/user1.png' => '8063f445',
'rsrc/image/people/user2.png' => 'd28246c0',
'rsrc/image/people/user3.png' => 'fb1ac12d',
'rsrc/image/people/user4.png' => 'fe4fac8f',
'rsrc/image/people/user5.png' => '3d07065c',
'rsrc/image/people/user6.png' => 'e4bd47c8',
'rsrc/image/people/user7.png' => '71d8fe8b',
'rsrc/image/people/user8.png' => '85f86bf7',
'rsrc/image/people/user9.png' => '523db8aa',
'rsrc/image/people/washington.png' => '86159e68',
'rsrc/image/phrequent_active.png' => 'de66dc50',
'rsrc/image/phrequent_inactive.png' => '79c61baf',
'rsrc/image/resize.png' => '9cc83373',
'rsrc/image/sprite-login-X2.png' => '604545f6',
'rsrc/image/sprite-login.png' => '7a001a9a',
'rsrc/image/sprite-tokens-X2.png' => '21621dd9',
'rsrc/image/sprite-tokens.png' => 'bede2580',
'rsrc/image/texture/card-gradient.png' => 'e6892cb4',
'rsrc/image/texture/dark-menu-hover.png' => '390a4fa1',
'rsrc/image/texture/dark-menu.png' => '542f699c',
'rsrc/image/texture/grip.png' => 'bc80753a',
'rsrc/image/texture/panel-header-gradient.png' => '65004dbf',
'rsrc/image/texture/phlnx-bg.png' => '6c9cd31d',
'rsrc/image/texture/pholio-background.gif' => '84910bfc',
'rsrc/image/texture/table_header.png' => '7652d1ad',
'rsrc/image/texture/table_header_hover.png' => '12ea5236',
'rsrc/image/texture/table_header_tall.png' => '5cc420c4',
'rsrc/js/application/aphlict/Aphlict.js' => '022516b4',
'rsrc/js/application/aphlict/behavior-aphlict-dropdown.js' => 'e9a2940f',
'rsrc/js/application/aphlict/behavior-aphlict-listen.js' => '4e61fa88',
'rsrc/js/application/aphlict/behavior-aphlict-status.js' => 'c3703a16',
'rsrc/js/application/aphlict/behavior-desktop-notifications-control.js' => '070679fe',
'rsrc/js/application/calendar/behavior-day-view.js' => '727a5a61',
'rsrc/js/application/calendar/behavior-event-all-day.js' => '0b1bc990',
'rsrc/js/application/calendar/behavior-month-view.js' => '158c64e0',
'rsrc/js/application/config/behavior-reorder-fields.js' => '2539f834',
'rsrc/js/application/conpherence/ConpherenceThreadManager.js' => 'aec8e38c',
'rsrc/js/application/conpherence/behavior-conpherence-search.js' => '91befbcc',
'rsrc/js/application/conpherence/behavior-durable-column.js' => 'fa6f30b2',
'rsrc/js/application/conpherence/behavior-menu.js' => '8c2ed2bf',
'rsrc/js/application/conpherence/behavior-participant-pane.js' => '43ba89a2',
'rsrc/js/application/conpherence/behavior-pontificate.js' => '4ae58b5a',
'rsrc/js/application/conpherence/behavior-quicksand-blacklist.js' => '5a6f6a06',
'rsrc/js/application/conpherence/behavior-toggle-widget.js' => '8f959ad0',
'rsrc/js/application/countdown/timer.js' => '6a162524',
'rsrc/js/application/daemon/behavior-bulk-job-reload.js' => '3829a3cf',
'rsrc/js/application/dashboard/behavior-dashboard-async-panel.js' => '09ecf50c',
'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '076bd092',
'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '1e413dc9',
'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => '9b1cbd76',
'rsrc/js/application/diff/DiffChangeset.js' => 'd0a85a85',
- 'rsrc/js/application/diff/DiffChangesetList.js' => '26fb79ba',
+ 'rsrc/js/application/diff/DiffChangesetList.js' => '04023d82',
'rsrc/js/application/diff/DiffInline.js' => 'a4a14a94',
'rsrc/js/application/diff/behavior-preview-link.js' => 'f51e9c17',
'rsrc/js/application/differential/behavior-diff-radios.js' => '925fe8cd',
'rsrc/js/application/differential/behavior-populate.js' => 'dfa1d313',
'rsrc/js/application/differential/behavior-user-select.js' => 'e18685c0',
'rsrc/js/application/diffusion/DiffusionLocateFileSource.js' => '94243d89',
'rsrc/js/application/diffusion/behavior-audit-preview.js' => 'b7b73831',
'rsrc/js/application/diffusion/behavior-commit-branches.js' => '4b671572',
'rsrc/js/application/diffusion/behavior-commit-graph.js' => '1c88f154',
'rsrc/js/application/diffusion/behavior-locate-file.js' => '87428eb2',
'rsrc/js/application/diffusion/behavior-pull-lastmodified.js' => 'c715c123',
'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => '6a85bc5a',
'rsrc/js/application/drydock/drydock-live-operation-status.js' => '47a0728b',
'rsrc/js/application/files/behavior-document-engine.js' => '243d6c22',
'rsrc/js/application/files/behavior-icon-composer.js' => '38a6cedb',
'rsrc/js/application/files/behavior-launch-icon-composer.js' => 'a17b84f1',
'rsrc/js/application/harbormaster/behavior-harbormaster-log.js' => 'b347a301',
'rsrc/js/application/herald/HeraldRuleEditor.js' => '27daef73',
'rsrc/js/application/herald/PathTypeahead.js' => 'ad486db3',
'rsrc/js/application/herald/herald-rule-editor.js' => '0922e81d',
'rsrc/js/application/maniphest/behavior-batch-selector.js' => 'cffd39b4',
'rsrc/js/application/maniphest/behavior-line-chart.js' => 'c8147a20',
'rsrc/js/application/maniphest/behavior-list-edit.js' => 'c687e867',
'rsrc/js/application/maniphest/behavior-subpriorityeditor.js' => '8400307c',
'rsrc/js/application/owners/OwnersPathEditor.js' => '2a8b62d9',
'rsrc/js/application/owners/owners-path-editor.js' => 'ff688a7a',
'rsrc/js/application/passphrase/passphrase-credential-control.js' => '48fe33d0',
'rsrc/js/application/pholio/behavior-pholio-mock-edit.js' => '3eed1f2b',
'rsrc/js/application/pholio/behavior-pholio-mock-view.js' => '5aa1544e',
'rsrc/js/application/phortune/behavior-stripe-payment-form.js' => '02cb4398',
'rsrc/js/application/phortune/behavior-test-payment-form.js' => '4a7fb02b',
'rsrc/js/application/phortune/phortune-credit-card-form.js' => 'd12d214f',
'rsrc/js/application/policy/behavior-policy-control.js' => '0eaa33a9',
'rsrc/js/application/policy/behavior-policy-rule-editor.js' => '9347f172',
'rsrc/js/application/projects/WorkboardBoard.js' => '45d0b2b1',
'rsrc/js/application/projects/WorkboardCard.js' => '9a513421',
'rsrc/js/application/projects/WorkboardColumn.js' => '8573dc1b',
'rsrc/js/application/projects/WorkboardController.js' => '42c7a5a7',
'rsrc/js/application/projects/behavior-project-boards.js' => '05c74d65',
'rsrc/js/application/projects/behavior-project-create.js' => '34c53422',
'rsrc/js/application/projects/behavior-reorder-columns.js' => '8ac32fd9',
'rsrc/js/application/releeph/releeph-preview-branch.js' => '75184d68',
'rsrc/js/application/releeph/releeph-request-state-change.js' => '9f081f05',
'rsrc/js/application/releeph/releeph-request-typeahead.js' => 'aa3a100c',
'rsrc/js/application/repository/repository-crossreference.js' => 'c15122b4',
'rsrc/js/application/search/behavior-reorder-profile-menu-items.js' => 'e5bdb730',
'rsrc/js/application/search/behavior-reorder-queries.js' => 'b86f297f',
'rsrc/js/application/transactions/behavior-comment-actions.js' => '4dffaeb2',
'rsrc/js/application/transactions/behavior-reorder-configs.js' => '4842f137',
'rsrc/js/application/transactions/behavior-reorder-fields.js' => '0ad8d31f',
'rsrc/js/application/transactions/behavior-show-older-transactions.js' => '600f440c',
'rsrc/js/application/transactions/behavior-transaction-comment-form.js' => '2bdadf1a',
'rsrc/js/application/transactions/behavior-transaction-list.js' => '9cec214e',
'rsrc/js/application/typeahead/behavior-typeahead-browse.js' => '70245195',
'rsrc/js/application/typeahead/behavior-typeahead-search.js' => '7b139193',
'rsrc/js/application/uiexample/gesture-example.js' => '242dedd0',
'rsrc/js/application/uiexample/notification-example.js' => '29819b75',
'rsrc/js/core/Busy.js' => '5202e831',
'rsrc/js/core/DragAndDropFileUpload.js' => '4370900d',
'rsrc/js/core/DraggableList.js' => '3c6bd549',
'rsrc/js/core/Favicon.js' => '7930776a',
'rsrc/js/core/FileUpload.js' => 'ab85e184',
'rsrc/js/core/Hovercard.js' => '074f0783',
'rsrc/js/core/KeyboardShortcut.js' => 'c9749dcd',
'rsrc/js/core/KeyboardShortcutManager.js' => '37b8a04a',
'rsrc/js/core/MultirowRowManager.js' => '5b54c823',
'rsrc/js/core/Notification.js' => 'a9b91e3f',
'rsrc/js/core/Prefab.js' => '5793d835',
'rsrc/js/core/ShapedRequest.js' => 'abf88db8',
'rsrc/js/core/TextAreaUtils.js' => 'f340a484',
'rsrc/js/core/Title.js' => '43bc9360',
'rsrc/js/core/ToolTip.js' => '83754533',
'rsrc/js/core/behavior-active-nav.js' => '7353f43d',
'rsrc/js/core/behavior-audio-source.js' => '3dc5ad43',
'rsrc/js/core/behavior-autofocus.js' => '65bb0011',
'rsrc/js/core/behavior-badge-view.js' => '92cdd7b6',
'rsrc/js/core/behavior-bulk-editor.js' => 'aa6d2308',
'rsrc/js/core/behavior-choose-control.js' => '04f8a1e3',
'rsrc/js/core/behavior-copy.js' => 'cf32921f',
'rsrc/js/core/behavior-detect-timezone.js' => '78bc5d94',
'rsrc/js/core/behavior-device.js' => '0cf79f45',
'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '7ad020a5',
'rsrc/js/core/behavior-fancy-datepicker.js' => '956f3eeb',
'rsrc/js/core/behavior-file-tree.js' => 'ee82cedb',
'rsrc/js/core/behavior-form.js' => '55d7b788',
'rsrc/js/core/behavior-gesture.js' => 'b58d1a2a',
'rsrc/js/core/behavior-global-drag-and-drop.js' => '1cab0e9a',
'rsrc/js/core/behavior-high-security-warning.js' => 'dae2d55b',
'rsrc/js/core/behavior-history-install.js' => '6a1583a8',
'rsrc/js/core/behavior-hovercard.js' => '6c379000',
'rsrc/js/core/behavior-keyboard-pager.js' => '1325b731',
'rsrc/js/core/behavior-keyboard-shortcuts.js' => '2cc87f49',
'rsrc/js/core/behavior-lightbox-attachments.js' => 'c7e748bf',
'rsrc/js/core/behavior-line-linker.js' => 'e15c8b1f',
'rsrc/js/core/behavior-linked-container.js' => '74446546',
'rsrc/js/core/behavior-more.js' => '506aa3f4',
'rsrc/js/core/behavior-object-selector.js' => 'a4af0b4a',
- 'rsrc/js/core/behavior-oncopy.js' => 'f20d66c1',
+ 'rsrc/js/core/behavior-oncopy.js' => 'de59bf15',
'rsrc/js/core/behavior-phabricator-nav.js' => 'f166c949',
'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '2f80333f',
'rsrc/js/core/behavior-read-only-warning.js' => 'b9109f8f',
'rsrc/js/core/behavior-redirect.js' => '407ee861',
'rsrc/js/core/behavior-refresh-csrf.js' => '46116c01',
'rsrc/js/core/behavior-remarkup-load-image.js' => '202bfa3f',
'rsrc/js/core/behavior-remarkup-preview.js' => 'd8a86cfb',
'rsrc/js/core/behavior-reorder-applications.js' => 'aa371860',
'rsrc/js/core/behavior-reveal-content.js' => 'b105a3a6',
'rsrc/js/core/behavior-scrollbar.js' => '92388bae',
'rsrc/js/core/behavior-search-typeahead.js' => '1cb7d027',
'rsrc/js/core/behavior-select-content.js' => 'e8240b50',
'rsrc/js/core/behavior-select-on-click.js' => '66365ee2',
'rsrc/js/core/behavior-setup-check-https.js' => '01384686',
'rsrc/js/core/behavior-time-typeahead.js' => '5803b9e7',
'rsrc/js/core/behavior-toggle-class.js' => 'f5c78ae3',
'rsrc/js/core/behavior-tokenizer.js' => '3b4899b0',
'rsrc/js/core/behavior-tooltip.js' => '73ecc1f8',
'rsrc/js/core/behavior-user-menu.js' => '60cd9241',
'rsrc/js/core/behavior-watch-anchor.js' => '0e6d261f',
'rsrc/js/core/behavior-workflow.js' => '9623adc1',
'rsrc/js/core/darkconsole/DarkLog.js' => '3b869402',
'rsrc/js/core/darkconsole/DarkMessage.js' => '26cd4b73',
'rsrc/js/core/darkconsole/behavior-dark-console.js' => 'f39d968b',
'rsrc/js/core/phtize.js' => '2f1db1ed',
'rsrc/js/phui/behavior-phui-dropdown-menu.js' => '5cf0501a',
'rsrc/js/phui/behavior-phui-file-upload.js' => 'e150bd50',
'rsrc/js/phui/behavior-phui-selectable-list.js' => 'b26a41e4',
'rsrc/js/phui/behavior-phui-submenu.js' => 'b5e9bff9',
'rsrc/js/phui/behavior-phui-tab-group.js' => '242aa08b',
'rsrc/js/phui/behavior-phui-timer-control.js' => 'f84bcbf4',
'rsrc/js/phuix/PHUIXActionListView.js' => 'c68f183f',
'rsrc/js/phuix/PHUIXActionView.js' => 'aaa08f3b',
'rsrc/js/phuix/PHUIXAutocomplete.js' => '8f139ef0',
'rsrc/js/phuix/PHUIXButtonView.js' => '55a24e84',
'rsrc/js/phuix/PHUIXDropdownMenu.js' => 'bdce4d78',
'rsrc/js/phuix/PHUIXExample.js' => 'c2c500a7',
'rsrc/js/phuix/PHUIXFormControl.js' => '38c1f3fb',
'rsrc/js/phuix/PHUIXIconView.js' => 'a5257c4e',
),
'symbols' => array(
'almanac-css' => '2e050f4f',
'aphront-bars' => '4a327b4a',
'aphront-dark-console-css' => '7f06cda2',
'aphront-dialog-view-css' => 'b70c70df',
'aphront-list-filter-view-css' => 'feb64255',
'aphront-multi-column-view-css' => 'fbc00ba3',
'aphront-panel-view-css' => '46923d46',
'aphront-table-view-css' => '205053cd',
'aphront-tokenizer-control-css' => 'b52d0668',
'aphront-tooltip-css' => 'e3f2412f',
'aphront-typeahead-control-css' => '8779483d',
'application-search-view-css' => '0f7c06d8',
'auth-css' => 'add92fd8',
'bulk-job-css' => '73af99f5',
'conduit-api-css' => 'ce2cfc41',
'config-options-css' => '16c920ae',
'conpherence-color-css' => 'b17746b0',
'conpherence-durable-column-view' => '2d57072b',
'conpherence-header-pane-css' => 'c9a3db8e',
'conpherence-menu-css' => '67f4680d',
'conpherence-message-pane-css' => 'd244db1e',
'conpherence-notification-css' => '6a3d4e58',
'conpherence-participant-pane-css' => '69e0058a',
'conpherence-thread-manager' => 'aec8e38c',
'conpherence-transaction-css' => '3a3f5e7e',
'd3' => 'd67475f5',
- 'differential-changeset-view-css' => 'e2b81e85',
+ 'differential-changeset-view-css' => 'cc3fd795',
'differential-core-view-css' => 'bdb93065',
'differential-revision-add-comment-css' => '7e5900d9',
'differential-revision-comment-css' => '7dbc8d1d',
'differential-revision-history-css' => '8aa3eac5',
'differential-revision-list-css' => '93d2df7d',
'differential-table-of-contents-css' => '0e3364c7',
'diffusion-css' => 'b54c77b0',
'diffusion-icons-css' => '23b31a1b',
'diffusion-readme-css' => 'b68a76e4',
'diffusion-repository-css' => 'b89e8c6c',
'diviner-shared-css' => '4bd263b0',
'font-fontawesome' => '3883938a',
'font-lato' => '23631304',
'global-drag-and-drop-css' => '1d2713a4',
'harbormaster-css' => '8dfe16b2',
'herald-css' => '648d39e2',
'herald-rule-editor' => '27daef73',
'herald-test-css' => 'e004176f',
'inline-comment-summary-css' => '81eb368d',
'javelin-aphlict' => '022516b4',
'javelin-behavior' => 'fce5d170',
'javelin-behavior-aphlict-dropdown' => 'e9a2940f',
'javelin-behavior-aphlict-listen' => '4e61fa88',
'javelin-behavior-aphlict-status' => 'c3703a16',
'javelin-behavior-aphront-basic-tokenizer' => '3b4899b0',
'javelin-behavior-aphront-drag-and-drop-textarea' => '7ad020a5',
'javelin-behavior-aphront-form-disable-on-submit' => '55d7b788',
'javelin-behavior-aphront-more' => '506aa3f4',
'javelin-behavior-audio-source' => '3dc5ad43',
'javelin-behavior-audit-preview' => 'b7b73831',
'javelin-behavior-badge-view' => '92cdd7b6',
'javelin-behavior-bulk-editor' => 'aa6d2308',
'javelin-behavior-bulk-job-reload' => '3829a3cf',
'javelin-behavior-calendar-month-view' => '158c64e0',
'javelin-behavior-choose-control' => '04f8a1e3',
'javelin-behavior-comment-actions' => '4dffaeb2',
'javelin-behavior-config-reorder-fields' => '2539f834',
'javelin-behavior-conpherence-menu' => '8c2ed2bf',
'javelin-behavior-conpherence-participant-pane' => '43ba89a2',
'javelin-behavior-conpherence-pontificate' => '4ae58b5a',
'javelin-behavior-conpherence-search' => '91befbcc',
'javelin-behavior-countdown-timer' => '6a162524',
'javelin-behavior-dark-console' => 'f39d968b',
'javelin-behavior-dashboard-async-panel' => '09ecf50c',
'javelin-behavior-dashboard-move-panels' => '076bd092',
'javelin-behavior-dashboard-query-panel-select' => '1e413dc9',
'javelin-behavior-dashboard-tab-panel' => '9b1cbd76',
'javelin-behavior-day-view' => '727a5a61',
'javelin-behavior-desktop-notifications-control' => '070679fe',
'javelin-behavior-detect-timezone' => '78bc5d94',
'javelin-behavior-device' => '0cf79f45',
'javelin-behavior-diff-preview-link' => 'f51e9c17',
'javelin-behavior-differential-diff-radios' => '925fe8cd',
'javelin-behavior-differential-populate' => 'dfa1d313',
'javelin-behavior-differential-user-select' => 'e18685c0',
'javelin-behavior-diffusion-commit-branches' => '4b671572',
'javelin-behavior-diffusion-commit-graph' => '1c88f154',
'javelin-behavior-diffusion-locate-file' => '87428eb2',
'javelin-behavior-diffusion-pull-lastmodified' => 'c715c123',
'javelin-behavior-document-engine' => '243d6c22',
'javelin-behavior-doorkeeper-tag' => '6a85bc5a',
'javelin-behavior-drydock-live-operation-status' => '47a0728b',
'javelin-behavior-durable-column' => 'fa6f30b2',
'javelin-behavior-editengine-reorder-configs' => '4842f137',
'javelin-behavior-editengine-reorder-fields' => '0ad8d31f',
'javelin-behavior-event-all-day' => '0b1bc990',
'javelin-behavior-fancy-datepicker' => '956f3eeb',
'javelin-behavior-global-drag-and-drop' => '1cab0e9a',
'javelin-behavior-harbormaster-log' => 'b347a301',
'javelin-behavior-herald-rule-editor' => '0922e81d',
'javelin-behavior-high-security-warning' => 'dae2d55b',
'javelin-behavior-history-install' => '6a1583a8',
'javelin-behavior-icon-composer' => '38a6cedb',
'javelin-behavior-launch-icon-composer' => 'a17b84f1',
'javelin-behavior-lightbox-attachments' => 'c7e748bf',
'javelin-behavior-line-chart' => 'c8147a20',
'javelin-behavior-linked-container' => '74446546',
'javelin-behavior-maniphest-batch-selector' => 'cffd39b4',
'javelin-behavior-maniphest-list-editor' => 'c687e867',
'javelin-behavior-maniphest-subpriority-editor' => '8400307c',
'javelin-behavior-owners-path-editor' => 'ff688a7a',
'javelin-behavior-passphrase-credential-control' => '48fe33d0',
'javelin-behavior-phabricator-active-nav' => '7353f43d',
'javelin-behavior-phabricator-autofocus' => '65bb0011',
'javelin-behavior-phabricator-clipboard-copy' => 'cf32921f',
'javelin-behavior-phabricator-file-tree' => 'ee82cedb',
'javelin-behavior-phabricator-gesture' => 'b58d1a2a',
'javelin-behavior-phabricator-gesture-example' => '242dedd0',
'javelin-behavior-phabricator-keyboard-pager' => '1325b731',
'javelin-behavior-phabricator-keyboard-shortcuts' => '2cc87f49',
'javelin-behavior-phabricator-line-linker' => 'e15c8b1f',
'javelin-behavior-phabricator-nav' => 'f166c949',
'javelin-behavior-phabricator-notification-example' => '29819b75',
'javelin-behavior-phabricator-object-selector' => 'a4af0b4a',
- 'javelin-behavior-phabricator-oncopy' => 'f20d66c1',
+ 'javelin-behavior-phabricator-oncopy' => 'de59bf15',
'javelin-behavior-phabricator-remarkup-assist' => '2f80333f',
'javelin-behavior-phabricator-reveal-content' => 'b105a3a6',
'javelin-behavior-phabricator-search-typeahead' => '1cb7d027',
'javelin-behavior-phabricator-show-older-transactions' => '600f440c',
'javelin-behavior-phabricator-tooltips' => '73ecc1f8',
'javelin-behavior-phabricator-transaction-comment-form' => '2bdadf1a',
'javelin-behavior-phabricator-transaction-list' => '9cec214e',
'javelin-behavior-phabricator-watch-anchor' => '0e6d261f',
'javelin-behavior-pholio-mock-edit' => '3eed1f2b',
'javelin-behavior-pholio-mock-view' => '5aa1544e',
'javelin-behavior-phui-dropdown-menu' => '5cf0501a',
'javelin-behavior-phui-file-upload' => 'e150bd50',
'javelin-behavior-phui-hovercards' => '6c379000',
'javelin-behavior-phui-selectable-list' => 'b26a41e4',
'javelin-behavior-phui-submenu' => 'b5e9bff9',
'javelin-behavior-phui-tab-group' => '242aa08b',
'javelin-behavior-phui-timer-control' => 'f84bcbf4',
'javelin-behavior-phuix-example' => 'c2c500a7',
'javelin-behavior-policy-control' => '0eaa33a9',
'javelin-behavior-policy-rule-editor' => '9347f172',
'javelin-behavior-project-boards' => '05c74d65',
'javelin-behavior-project-create' => '34c53422',
'javelin-behavior-quicksand-blacklist' => '5a6f6a06',
'javelin-behavior-read-only-warning' => 'b9109f8f',
'javelin-behavior-redirect' => '407ee861',
'javelin-behavior-refresh-csrf' => '46116c01',
'javelin-behavior-releeph-preview-branch' => '75184d68',
'javelin-behavior-releeph-request-state-change' => '9f081f05',
'javelin-behavior-releeph-request-typeahead' => 'aa3a100c',
'javelin-behavior-remarkup-load-image' => '202bfa3f',
'javelin-behavior-remarkup-preview' => 'd8a86cfb',
'javelin-behavior-reorder-applications' => 'aa371860',
'javelin-behavior-reorder-columns' => '8ac32fd9',
'javelin-behavior-reorder-profile-menu-items' => 'e5bdb730',
'javelin-behavior-repository-crossreference' => 'c15122b4',
'javelin-behavior-scrollbar' => '92388bae',
'javelin-behavior-search-reorder-queries' => 'b86f297f',
'javelin-behavior-select-content' => 'e8240b50',
'javelin-behavior-select-on-click' => '66365ee2',
'javelin-behavior-setup-check-https' => '01384686',
'javelin-behavior-stripe-payment-form' => '02cb4398',
'javelin-behavior-test-payment-form' => '4a7fb02b',
'javelin-behavior-time-typeahead' => '5803b9e7',
'javelin-behavior-toggle-class' => 'f5c78ae3',
'javelin-behavior-toggle-widget' => '8f959ad0',
'javelin-behavior-typeahead-browse' => '70245195',
'javelin-behavior-typeahead-search' => '7b139193',
'javelin-behavior-user-menu' => '60cd9241',
'javelin-behavior-view-placeholder' => 'a9942052',
'javelin-behavior-workflow' => '9623adc1',
'javelin-color' => '78f811c9',
'javelin-cookie' => '05d290ef',
'javelin-diffusion-locate-file-source' => '94243d89',
'javelin-dom' => '94681e22',
'javelin-dynval' => '202a2e85',
'javelin-event' => 'c03f2fb4',
'javelin-fx' => '34450586',
'javelin-history' => '030b4f7a',
'javelin-install' => '5902260c',
'javelin-json' => '541f81c3',
'javelin-leader' => '0d2490ce',
'javelin-magical-init' => '98e6504a',
'javelin-mask' => '7c4d8998',
'javelin-quicksand' => 'd3799cb4',
'javelin-reactor' => '1c850a26',
'javelin-reactor-dom' => '6cfa0008',
'javelin-reactor-node-calmer' => '225bbb98',
'javelin-reactornode' => '72960bc1',
'javelin-request' => '91863989',
'javelin-resource' => '740956e1',
'javelin-routable' => '6a18c42e',
'javelin-router' => '32755edb',
'javelin-scrollbar' => 'a43ae2ae',
'javelin-sound' => 'e562708c',
'javelin-stratcom' => '0889b835',
'javelin-tokenizer' => '89a1ae3a',
'javelin-typeahead' => 'a4356cde',
'javelin-typeahead-composite-source' => '22ee68a5',
'javelin-typeahead-normalizer' => 'a241536a',
'javelin-typeahead-ondemand-source' => '23387297',
'javelin-typeahead-preloaded-source' => '5a79f6c3',
'javelin-typeahead-source' => '8badee71',
'javelin-typeahead-static-source' => '80bff3af',
'javelin-uri' => '2e255291',
'javelin-util' => '22ae1776',
'javelin-vector' => 'e9c80beb',
'javelin-view' => '289bf236',
'javelin-view-html' => 'f8c4e135',
'javelin-view-interpreter' => '876506b6',
'javelin-view-renderer' => '9aae2b66',
'javelin-view-visitor' => '308f9fe4',
'javelin-websocket' => 'fdc13e4e',
'javelin-workboard-board' => '45d0b2b1',
'javelin-workboard-card' => '9a513421',
'javelin-workboard-column' => '8573dc1b',
'javelin-workboard-controller' => '42c7a5a7',
'javelin-workflow' => '958e9045',
'maniphest-report-css' => '3d53188b',
'maniphest-task-edit-css' => '272daa84',
'maniphest-task-summary-css' => '61d1667e',
'multirow-row-manager' => '5b54c823',
'owners-path-editor' => '2a8b62d9',
'owners-path-editor-css' => 'fa7c13ef',
'paste-css' => 'b37bcd38',
'path-typeahead' => 'ad486db3',
'people-picture-menu-item-css' => 'fe8e07cf',
'people-profile-css' => '2ea2daa1',
'phabricator-action-list-view-css' => 'c4972757',
'phabricator-busy' => '5202e831',
'phabricator-chatlog-css' => 'abdc76ee',
'phabricator-content-source-view-css' => 'cdf0d579',
'phabricator-core-css' => '1b29ed61',
'phabricator-countdown-css' => 'bff8012f',
'phabricator-darklog' => '3b869402',
'phabricator-darkmessage' => '26cd4b73',
'phabricator-dashboard-css' => '4267d6c6',
'phabricator-diff-changeset' => 'd0a85a85',
- 'phabricator-diff-changeset-list' => '26fb79ba',
+ 'phabricator-diff-changeset-list' => '04023d82',
'phabricator-diff-inline' => 'a4a14a94',
'phabricator-drag-and-drop-file-upload' => '4370900d',
'phabricator-draggable-list' => '3c6bd549',
'phabricator-fatal-config-template-css' => '20babf50',
'phabricator-favicon' => '7930776a',
'phabricator-feed-css' => 'd8b6e3f8',
'phabricator-file-upload' => 'ab85e184',
'phabricator-filetree-view-css' => '56cdd875',
'phabricator-flag-css' => '2b77be8d',
'phabricator-keyboard-shortcut' => 'c9749dcd',
'phabricator-keyboard-shortcut-manager' => '37b8a04a',
'phabricator-main-menu-view' => '8e2d9a28',
'phabricator-nav-view-css' => 'f8a0c1bf',
'phabricator-notification' => 'a9b91e3f',
'phabricator-notification-css' => '30240bd2',
'phabricator-notification-menu-css' => 'e6962e89',
'phabricator-object-selector-css' => 'ee77366f',
'phabricator-phtize' => '2f1db1ed',
'phabricator-prefab' => '5793d835',
'phabricator-remarkup-css' => '9e627d41',
'phabricator-search-results-css' => '9ea70ace',
'phabricator-shaped-request' => 'abf88db8',
'phabricator-slowvote-css' => '1694baed',
'phabricator-source-code-view-css' => '03d7ac28',
'phabricator-standard-page-view' => '8a295cb9',
'phabricator-textareautils' => 'f340a484',
'phabricator-title' => '43bc9360',
'phabricator-tooltip' => '83754533',
'phabricator-ui-example-css' => 'b4795059',
'phabricator-zindex-css' => '99c0f5eb',
'phame-css' => '799febf9',
'pholio-css' => '88ef5ef1',
'pholio-edit-css' => '4df55b3b',
'pholio-inline-comments-css' => '722b48c2',
'phortune-credit-card-form' => 'd12d214f',
'phortune-credit-card-form-css' => '3b9868a8',
'phortune-css' => '12e8251a',
'phortune-invoice-css' => '4436b241',
'phrequent-css' => 'bd79cc67',
'phriction-document-css' => '03380da0',
'phui-action-panel-css' => '6c386cbf',
'phui-badge-view-css' => '666e25ad',
'phui-basic-nav-view-css' => '56ebd66d',
'phui-big-info-view-css' => '362ad37b',
'phui-box-css' => '5ed3b8cb',
'phui-bulk-editor-css' => '374d5e30',
'phui-button-bar-css' => 'a4aa75c4',
'phui-button-css' => 'ea704902',
'phui-button-simple-css' => '1ff278aa',
'phui-calendar-css' => 'f11073aa',
'phui-calendar-day-css' => '9597d706',
'phui-calendar-list-css' => 'ccd7e4e2',
'phui-calendar-month-css' => 'cb758c42',
'phui-chart-css' => '7853a69b',
'phui-cms-css' => '8c05c41e',
'phui-comment-form-css' => '68a2d99a',
'phui-comment-panel-css' => 'ec4e31c0',
'phui-crumbs-view-css' => '614f43cf',
'phui-curtain-view-css' => '68c5efb6',
'phui-document-summary-view-css' => 'b068eed1',
'phui-document-view-css' => '52b748a5',
'phui-document-view-pro-css' => 'b9613a10',
'phui-feed-story-css' => 'a0c05029',
'phui-font-icon-base-css' => 'd7994e06',
'phui-fontkit-css' => '9b714a5e',
'phui-form-css' => '159e2d9c',
'phui-form-view-css' => '01b796c0',
'phui-head-thing-view-css' => 'd7f293df',
'phui-header-view-css' => '285c9139',
'phui-hovercard' => '074f0783',
'phui-hovercard-view-css' => '6ca90fa0',
'phui-icon-set-selector-css' => '7aa5f3ec',
'phui-icon-view-css' => '4cbc684a',
'phui-image-mask-css' => '62c7f4d2',
'phui-info-view-css' => '37b8d9ce',
'phui-inline-comment-view-css' => '48acce5b',
'phui-invisible-character-view-css' => 'c694c4a4',
'phui-left-right-css' => '68513c34',
'phui-lightbox-css' => '4ebf22da',
'phui-list-view-css' => '470b1adb',
'phui-object-box-css' => 'f434b6be',
'phui-oi-big-ui-css' => '9e037c7a',
'phui-oi-color-css' => 'b517bfa0',
'phui-oi-drag-ui-css' => 'da15d3dc',
'phui-oi-flush-ui-css' => '490e2e2e',
'phui-oi-list-view-css' => '909f3844',
'phui-oi-simple-ui-css' => '6a30fa46',
'phui-pager-css' => 'd022c7ad',
'phui-pinboard-view-css' => '1f08f5d8',
'phui-property-list-view-css' => 'cad62236',
'phui-remarkup-preview-css' => '91767007',
'phui-segment-bar-view-css' => '5166b370',
'phui-spacing-css' => 'b05cadc3',
'phui-status-list-view-css' => 'e5ff8be0',
'phui-tag-view-css' => '29409667',
'phui-theme-css' => '35883b37',
'phui-timeline-view-css' => '1e348e4b',
'phui-two-column-view-css' => '01e6991e',
'phui-workboard-color-css' => 'e86de308',
'phui-workboard-view-css' => '74fc9d98',
'phui-workcard-view-css' => '8c536f90',
'phui-workpanel-view-css' => 'bd546a49',
'phuix-action-list-view' => 'c68f183f',
'phuix-action-view' => 'aaa08f3b',
'phuix-autocomplete' => '8f139ef0',
'phuix-button-view' => '55a24e84',
'phuix-dropdown-menu' => 'bdce4d78',
'phuix-form-control-view' => '38c1f3fb',
'phuix-icon-view' => 'a5257c4e',
'policy-css' => 'ceb56a08',
'policy-edit-css' => '8794e2ed',
'policy-transaction-detail-css' => 'c02b8384',
'ponder-view-css' => '05a09d0a',
'project-card-view-css' => '3b1f7b20',
'project-view-css' => '567858b3',
'releeph-core' => 'f81ff2db',
'releeph-preview-branch' => '22db5c07',
'releeph-request-differential-create-dialog' => '0ac1ea31',
'releeph-request-typeahead-css' => 'bce37359',
'setup-issue-css' => '5eed85b2',
'sprite-login-css' => '18b368a6',
'sprite-tokens-css' => 'f1896dc5',
'syntax-default-css' => '055fc231',
'syntax-highlighting-css' => '8a16f91b',
'tokens-css' => 'ce5a50bd',
'typeahead-browse-css' => 'b7ed02d2',
'unhandled-exception-css' => '9ecfc00d',
),
'requires' => array(
'01384686' => array(
'javelin-behavior',
'javelin-uri',
'phabricator-notification',
),
'022516b4' => array(
'javelin-install',
'javelin-util',
'javelin-websocket',
'javelin-leader',
'javelin-json',
),
'02cb4398' => array(
'javelin-behavior',
'javelin-dom',
'phortune-credit-card-form',
),
'030b4f7a' => array(
'javelin-stratcom',
'javelin-install',
'javelin-uri',
'javelin-util',
),
+ '04023d82' => array(
+ 'javelin-install',
+ 'phuix-button-view',
+ ),
'04f8a1e3' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-workflow',
),
'05c74d65' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-vector',
'javelin-stratcom',
'javelin-workflow',
'javelin-workboard-controller',
),
'05d290ef' => array(
'javelin-install',
'javelin-util',
),
'070679fe' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-uri',
'phabricator-notification',
),
'074f0783' => array(
'javelin-install',
'javelin-dom',
'javelin-vector',
'javelin-request',
'javelin-uri',
),
'076bd092' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-stratcom',
'javelin-workflow',
'phabricator-draggable-list',
),
'0889b835' => array(
'javelin-install',
'javelin-event',
'javelin-util',
'javelin-magical-init',
),
'0922e81d' => array(
'herald-rule-editor',
'javelin-behavior',
),
'09ecf50c' => array(
'javelin-behavior',
'javelin-dom',
'javelin-workflow',
),
'0ad8d31f' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
'phabricator-draggable-list',
),
'0cf79f45' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-vector',
'javelin-install',
),
'0d2490ce' => array(
'javelin-install',
),
'0e6d261f' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-vector',
),
'0eaa33a9' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'phuix-dropdown-menu',
'phuix-action-list-view',
'phuix-action-view',
'javelin-workflow',
'phuix-icon-view',
),
'1325b731' => array(
'javelin-behavior',
'javelin-uri',
'phabricator-keyboard-shortcut',
),
'1c850a26' => array(
'javelin-install',
'javelin-util',
),
'1c88f154' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
),
'1cab0e9a' => array(
'javelin-behavior',
'javelin-dom',
'javelin-uri',
'javelin-mask',
'phabricator-drag-and-drop-file-upload',
),
'1cb7d027' => array(
'javelin-behavior',
'javelin-typeahead-ondemand-source',
'javelin-typeahead',
'javelin-dom',
'javelin-uri',
'javelin-util',
'javelin-stratcom',
'phabricator-prefab',
'phuix-icon-view',
),
'1e413dc9' => array(
'javelin-behavior',
'javelin-dom',
),
'1ff278aa' => array(
'phui-button-css',
),
'202a2e85' => array(
'javelin-install',
'javelin-reactornode',
'javelin-util',
'javelin-reactor',
),
'202bfa3f' => array(
'javelin-behavior',
'javelin-request',
),
'225bbb98' => array(
'javelin-install',
'javelin-reactor',
'javelin-util',
),
'22ee68a5' => array(
'javelin-install',
'javelin-typeahead-source',
'javelin-util',
),
23387297 => array(
'javelin-install',
'javelin-util',
'javelin-request',
'javelin-typeahead-source',
),
23631304 => array(
'phui-fontkit-css',
),
'242aa08b' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'242dedd0' => array(
'javelin-stratcom',
'javelin-behavior',
'javelin-vector',
'javelin-dom',
),
'243d6c22' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
),
'2539f834' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-json',
'phabricator-draggable-list',
),
- '26fb79ba' => array(
- 'javelin-install',
- 'phuix-button-view',
- ),
'27daef73' => array(
'multirow-row-manager',
'javelin-install',
'javelin-util',
'javelin-dom',
'javelin-stratcom',
'javelin-json',
'phabricator-prefab',
),
'289bf236' => array(
'javelin-install',
'javelin-util',
),
'29819b75' => array(
'phabricator-notification',
'javelin-stratcom',
'javelin-behavior',
),
'2a8b62d9' => array(
'multirow-row-manager',
'javelin-install',
'path-typeahead',
'javelin-dom',
'javelin-util',
'phabricator-prefab',
'phuix-form-control-view',
),
'2bdadf1a' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-request',
'phabricator-shaped-request',
),
'2cc87f49' => array(
'javelin-behavior',
'javelin-workflow',
'javelin-json',
'javelin-dom',
'phabricator-keyboard-shortcut',
),
'2e255291' => array(
'javelin-install',
'javelin-util',
'javelin-stratcom',
),
'2f1db1ed' => array(
'javelin-util',
),
'2f80333f' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'phabricator-phtize',
'phabricator-textareautils',
'javelin-workflow',
'javelin-vector',
'phuix-autocomplete',
'javelin-mask',
),
'308f9fe4' => array(
'javelin-install',
'javelin-util',
),
'32755edb' => array(
'javelin-install',
'javelin-util',
),
34450586 => array(
'javelin-color',
'javelin-install',
'javelin-util',
),
'34c53422' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-workflow',
),
'37b8a04a' => array(
'javelin-install',
'javelin-util',
'javelin-stratcom',
'javelin-dom',
'javelin-vector',
),
'3829a3cf' => array(
'javelin-behavior',
'javelin-uri',
),
'38a6cedb' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
),
'38c1f3fb' => array(
'javelin-install',
'javelin-dom',
),
'3b4899b0' => array(
'javelin-behavior',
'phabricator-prefab',
),
'3c6bd549' => array(
'javelin-install',
'javelin-dom',
'javelin-stratcom',
'javelin-util',
'javelin-vector',
'javelin-magical-init',
),
'3dc5ad43' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-vector',
'javelin-dom',
),
'3eed1f2b' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-workflow',
'javelin-quicksand',
'phabricator-phtize',
'phabricator-drag-and-drop-file-upload',
'phabricator-draggable-list',
),
'407ee861' => array(
'javelin-behavior',
'javelin-uri',
),
'42c7a5a7' => array(
'javelin-install',
'javelin-dom',
'javelin-util',
'javelin-vector',
'javelin-stratcom',
'javelin-workflow',
'phabricator-drag-and-drop-file-upload',
'javelin-workboard-board',
),
'4370900d' => array(
'javelin-install',
'javelin-util',
'javelin-request',
'javelin-dom',
'javelin-uri',
'phabricator-file-upload',
),
'43ba89a2' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-workflow',
'javelin-util',
'phabricator-notification',
'conpherence-thread-manager',
),
'43bc9360' => array(
'javelin-install',
),
'45d0b2b1' => array(
'javelin-install',
'javelin-dom',
'javelin-util',
'javelin-stratcom',
'javelin-workflow',
'phabricator-draggable-list',
'javelin-workboard-column',
),
'46116c01' => array(
'javelin-request',
'javelin-behavior',
'javelin-dom',
'javelin-router',
'javelin-util',
'phabricator-busy',
),
'47a0728b' => array(
'javelin-behavior',
'javelin-dom',
'javelin-request',
),
'4842f137' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
'phabricator-draggable-list',
),
'48fe33d0' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-workflow',
'javelin-util',
'javelin-uri',
),
'490e2e2e' => array(
'phui-oi-list-view-css',
),
'4a7fb02b' => array(
'javelin-behavior',
'javelin-dom',
'phortune-credit-card-form',
),
'4ae58b5a' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-workflow',
'javelin-stratcom',
'conpherence-thread-manager',
),
'4b671572' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-request',
),
'4dffaeb2' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
'phuix-form-control-view',
'phuix-icon-view',
'javelin-behavior-phabricator-gesture',
),
'4e61fa88' => array(
'javelin-behavior',
'javelin-aphlict',
'javelin-stratcom',
'javelin-request',
'javelin-uri',
'javelin-dom',
'javelin-json',
'javelin-router',
'javelin-util',
'javelin-leader',
'javelin-sound',
'phabricator-notification',
),
'506aa3f4' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'5202e831' => array(
'javelin-install',
'javelin-dom',
'javelin-fx',
),
'541f81c3' => array(
'javelin-install',
),
'55a24e84' => array(
'javelin-install',
'javelin-dom',
),
'55d7b788' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'5793d835' => array(
'javelin-install',
'javelin-util',
'javelin-dom',
'javelin-typeahead',
'javelin-tokenizer',
'javelin-typeahead-preloaded-source',
'javelin-typeahead-ondemand-source',
'javelin-dom',
'javelin-stratcom',
'javelin-util',
),
'5803b9e7' => array(
'javelin-behavior',
'javelin-util',
'javelin-dom',
'javelin-stratcom',
'javelin-vector',
'javelin-typeahead-static-source',
),
'5902260c' => array(
'javelin-util',
'javelin-magical-init',
),
'5a6f6a06' => array(
'javelin-behavior',
'javelin-quicksand',
),
'5a79f6c3' => array(
'javelin-install',
'javelin-util',
'javelin-request',
'javelin-typeahead-source',
),
'5aa1544e' => array(
'javelin-behavior',
'javelin-util',
'javelin-stratcom',
'javelin-dom',
'javelin-vector',
'javelin-magical-init',
'javelin-request',
'javelin-history',
'javelin-workflow',
'javelin-mask',
'javelin-behavior-device',
'phabricator-keyboard-shortcut',
),
'5b54c823' => array(
'javelin-install',
'javelin-stratcom',
'javelin-dom',
'javelin-util',
),
'5cf0501a' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'phuix-dropdown-menu',
),
'600f440c' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'phabricator-busy',
),
'60cd9241' => array(
'javelin-behavior',
),
'65bb0011' => array(
'javelin-behavior',
'javelin-dom',
),
'66365ee2' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'6a1583a8' => array(
'javelin-behavior',
'javelin-history',
),
'6a162524' => array(
'javelin-behavior',
'javelin-dom',
),
'6a18c42e' => array(
'javelin-install',
),
'6a30fa46' => array(
'phui-oi-list-view-css',
),
'6a85bc5a' => array(
'javelin-behavior',
'javelin-dom',
'javelin-json',
'javelin-workflow',
'javelin-magical-init',
),
'6c379000' => array(
'javelin-behavior',
'javelin-behavior-device',
'javelin-stratcom',
'javelin-vector',
'phui-hovercard',
),
'6cfa0008' => array(
'javelin-dom',
'javelin-dynval',
'javelin-reactor',
'javelin-reactornode',
'javelin-install',
'javelin-util',
),
70245195 => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
),
'727a5a61' => array(
'phuix-icon-view',
),
'72960bc1' => array(
'javelin-install',
'javelin-reactor',
'javelin-util',
'javelin-reactor-node-calmer',
),
'7353f43d' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-vector',
'javelin-dom',
'javelin-uri',
),
'73ecc1f8' => array(
'javelin-behavior',
'javelin-behavior-device',
'javelin-stratcom',
'phabricator-tooltip',
),
'740956e1' => array(
'javelin-util',
'javelin-uri',
'javelin-install',
),
74446546 => array(
'javelin-behavior',
'javelin-dom',
),
'75184d68' => array(
'javelin-behavior',
'javelin-dom',
'javelin-uri',
'javelin-request',
),
'78bc5d94' => array(
'javelin-behavior',
'javelin-uri',
'phabricator-notification',
),
'78f811c9' => array(
'javelin-install',
),
'7930776a' => array(
'javelin-install',
'javelin-dom',
),
'7ad020a5' => array(
'javelin-behavior',
'javelin-dom',
'phabricator-drag-and-drop-file-upload',
'phabricator-textareautils',
),
'7b139193' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
),
'7c4d8998' => array(
'javelin-install',
'javelin-dom',
),
'80bff3af' => array(
'javelin-install',
'javelin-typeahead-source',
),
83754533 => array(
'javelin-install',
'javelin-util',
'javelin-dom',
'javelin-vector',
),
'8400307c' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-workflow',
'phabricator-draggable-list',
),
'8573dc1b' => array(
'javelin-install',
'javelin-workboard-card',
),
'87428eb2' => array(
'javelin-behavior',
'javelin-diffusion-locate-file-source',
'javelin-dom',
'javelin-typeahead',
'javelin-uri',
),
'876506b6' => array(
'javelin-view',
'javelin-install',
'javelin-dom',
),
'89a1ae3a' => array(
'javelin-dom',
'javelin-util',
'javelin-stratcom',
'javelin-install',
),
'8a16f91b' => array(
'syntax-default-css',
),
'8ac32fd9' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
'phabricator-draggable-list',
),
'8badee71' => array(
'javelin-install',
'javelin-util',
'javelin-dom',
'javelin-typeahead-normalizer',
),
'8c2ed2bf' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-stratcom',
'javelin-workflow',
'javelin-behavior-device',
'javelin-history',
'javelin-vector',
'javelin-scrollbar',
'phabricator-title',
'phabricator-shaped-request',
'conpherence-thread-manager',
),
'8e2d9a28' => array(
'phui-theme-css',
),
'8f139ef0' => array(
'javelin-install',
'javelin-dom',
'phuix-icon-view',
'phabricator-prefab',
),
'8f959ad0' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-workflow',
'javelin-stratcom',
),
91863989 => array(
'javelin-install',
'javelin-stratcom',
'javelin-util',
'javelin-behavior',
'javelin-json',
'javelin-dom',
'javelin-resource',
'javelin-routable',
),
'91befbcc' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-workflow',
'javelin-stratcom',
),
'92388bae' => array(
'javelin-behavior',
'javelin-scrollbar',
),
'925fe8cd' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'92cdd7b6' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'9347f172' => array(
'javelin-behavior',
'multirow-row-manager',
'javelin-dom',
'javelin-util',
'phabricator-prefab',
'javelin-json',
),
'94243d89' => array(
'javelin-install',
'javelin-dom',
'javelin-typeahead-preloaded-source',
'javelin-util',
),
'94681e22' => array(
'javelin-magical-init',
'javelin-install',
'javelin-util',
'javelin-vector',
'javelin-stratcom',
),
'956f3eeb' => array(
'javelin-behavior',
'javelin-util',
'javelin-dom',
'javelin-stratcom',
'javelin-vector',
),
'958e9045' => array(
'javelin-stratcom',
'javelin-request',
'javelin-dom',
'javelin-vector',
'javelin-install',
'javelin-util',
'javelin-mask',
'javelin-uri',
'javelin-routable',
),
'9623adc1' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
'javelin-router',
),
'9a513421' => array(
'javelin-install',
),
'9aae2b66' => array(
'javelin-install',
'javelin-util',
),
'9b1cbd76' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
),
'9cec214e' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
'javelin-uri',
'phabricator-textareautils',
),
'9e037c7a' => array(
'phui-oi-list-view-css',
),
'9f081f05' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-workflow',
'javelin-util',
'phabricator-keyboard-shortcut',
),
'a17b84f1' => array(
'javelin-behavior',
'javelin-dom',
'javelin-workflow',
),
'a241536a' => array(
'javelin-install',
),
'a4356cde' => array(
'javelin-install',
'javelin-dom',
'javelin-vector',
'javelin-util',
),
'a43ae2ae' => array(
'javelin-install',
'javelin-dom',
'javelin-stratcom',
'javelin-vector',
),
'a4a14a94' => array(
'javelin-dom',
),
'a4aa75c4' => array(
'phui-button-css',
'phui-button-simple-css',
),
'a4af0b4a' => array(
'javelin-behavior',
'javelin-dom',
'javelin-request',
'javelin-util',
),
'a5257c4e' => array(
'javelin-install',
'javelin-dom',
),
'a9942052' => array(
'javelin-behavior',
'javelin-dom',
'javelin-view-renderer',
'javelin-install',
),
'a9b91e3f' => array(
'javelin-install',
'javelin-dom',
'javelin-stratcom',
'javelin-util',
'phabricator-notification-css',
),
'aa371860' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
'phabricator-draggable-list',
),
'aa3a100c' => array(
'javelin-behavior',
'javelin-dom',
'javelin-typeahead',
'javelin-typeahead-ondemand-source',
'javelin-dom',
),
'aa6d2308' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'multirow-row-manager',
'javelin-json',
'phuix-form-control-view',
),
'aaa08f3b' => array(
'javelin-install',
'javelin-dom',
'javelin-util',
),
'ab85e184' => array(
'javelin-install',
'javelin-dom',
'phabricator-notification',
),
'abf88db8' => array(
'javelin-install',
'javelin-util',
'javelin-request',
'javelin-router',
),
'ad486db3' => array(
'javelin-install',
'javelin-typeahead',
'javelin-dom',
'javelin-request',
'javelin-typeahead-ondemand-source',
'javelin-util',
),
'aec8e38c' => array(
'javelin-dom',
'javelin-util',
'javelin-stratcom',
'javelin-install',
'javelin-aphlict',
'javelin-workflow',
'javelin-router',
'javelin-behavior-device',
'javelin-vector',
),
'b105a3a6' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'b26a41e4' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'b347a301' => array(
'javelin-behavior',
),
'b517bfa0' => array(
'phui-oi-list-view-css',
),
'b52d0668' => array(
'aphront-typeahead-control-css',
'phui-tag-view-css',
),
'b58d1a2a' => array(
'javelin-behavior',
'javelin-behavior-device',
'javelin-stratcom',
'javelin-vector',
'javelin-dom',
'javelin-magical-init',
),
'b5e9bff9' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'b7b73831' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'phabricator-shaped-request',
),
'b86f297f' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
'phabricator-draggable-list',
),
'b9109f8f' => array(
'javelin-behavior',
'javelin-uri',
'phabricator-notification',
),
'bd546a49' => array(
'phui-workcard-view-css',
),
'bdce4d78' => array(
'javelin-install',
'javelin-util',
'javelin-dom',
'javelin-vector',
'javelin-stratcom',
),
'c03f2fb4' => array(
'javelin-install',
),
'c15122b4' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-uri',
),
'c2c500a7' => array(
'javelin-install',
'javelin-dom',
'phuix-button-view',
),
'c3703a16' => array(
'javelin-behavior',
'javelin-aphlict',
'phabricator-phtize',
'javelin-dom',
),
'c687e867' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-workflow',
'javelin-fx',
'javelin-util',
),
'c68f183f' => array(
'javelin-install',
'javelin-dom',
),
'c715c123' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'javelin-workflow',
'javelin-json',
),
'c7e748bf' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-mask',
'javelin-util',
'phuix-icon-view',
'phabricator-busy',
),
'c8147a20' => array(
'javelin-behavior',
'javelin-dom',
'javelin-vector',
'phui-chart-css',
),
'c9749dcd' => array(
'javelin-install',
'javelin-util',
'phabricator-keyboard-shortcut-manager',
),
+ 'cc3fd795' => array(
+ 'phui-inline-comment-view-css',
+ ),
'cf32921f' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
),
'cffd39b4' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-util',
),
'd0a85a85' => array(
'javelin-dom',
'javelin-util',
'javelin-stratcom',
'javelin-install',
'javelin-workflow',
'javelin-router',
'javelin-behavior-device',
'javelin-vector',
'phabricator-diff-inline',
),
'd12d214f' => array(
'javelin-install',
'javelin-dom',
'javelin-json',
'javelin-workflow',
'javelin-util',
),
'd3799cb4' => array(
'javelin-install',
),
'd8a86cfb' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'phabricator-shaped-request',
),
'da15d3dc' => array(
'phui-oi-list-view-css',
),
'dae2d55b' => array(
'javelin-behavior',
'javelin-uri',
'phabricator-notification',
),
+ 'de59bf15' => array(
+ 'javelin-behavior',
+ 'javelin-dom',
+ ),
'dfa1d313' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'phabricator-tooltip',
'phabricator-diff-changeset-list',
'phabricator-diff-changeset',
),
'e150bd50' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'phuix-dropdown-menu',
),
'e15c8b1f' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-history',
),
'e18685c0' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
),
- 'e2b81e85' => array(
- 'phui-inline-comment-view-css',
- ),
'e562708c' => array(
'javelin-install',
),
'e5bdb730' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-workflow',
'javelin-dom',
'phabricator-draggable-list',
),
'e8240b50' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'e9a2940f' => array(
'javelin-behavior',
'javelin-request',
'javelin-stratcom',
'javelin-vector',
'javelin-dom',
'javelin-uri',
'javelin-behavior-device',
'phabricator-title',
'phabricator-favicon',
),
'e9c80beb' => array(
'javelin-install',
'javelin-event',
),
'ec4e31c0' => array(
'phui-timeline-view-css',
),
'ee77366f' => array(
'aphront-dialog-view-css',
),
'ee82cedb' => array(
'javelin-behavior',
'phabricator-keyboard-shortcut',
'javelin-stratcom',
),
'f166c949' => array(
'javelin-behavior',
'javelin-behavior-device',
'javelin-stratcom',
'javelin-dom',
'javelin-magical-init',
'javelin-vector',
'javelin-request',
'javelin-util',
),
- 'f20d66c1' => array(
- 'javelin-behavior',
- 'javelin-dom',
- ),
'f340a484' => array(
'javelin-install',
'javelin-dom',
'javelin-vector',
),
'f39d968b' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-util',
'javelin-dom',
'javelin-request',
'phabricator-keyboard-shortcut',
'phabricator-darklog',
'phabricator-darkmessage',
),
'f51e9c17' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'f5c78ae3' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'f84bcbf4' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'f8c4e135' => array(
'javelin-install',
'javelin-dom',
'javelin-view-visitor',
'javelin-util',
),
'fa6f30b2' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-behavior-device',
'javelin-scrollbar',
'javelin-quicksand',
'phabricator-keyboard-shortcut',
'conpherence-thread-manager',
),
'fce5d170' => array(
'javelin-magical-init',
'javelin-util',
),
'fdc13e4e' => array(
'javelin-install',
),
'ff688a7a' => array(
'owners-path-editor',
'javelin-behavior',
),
),
'packages' => array(
'conpherence.pkg.css' => array(
'conpherence-durable-column-view',
'conpherence-menu-css',
'conpherence-color-css',
'conpherence-message-pane-css',
'conpherence-notification-css',
'conpherence-transaction-css',
'conpherence-participant-pane-css',
'conpherence-header-pane-css',
),
'conpherence.pkg.js' => array(
'javelin-behavior-conpherence-menu',
'javelin-behavior-conpherence-participant-pane',
'javelin-behavior-conpherence-pontificate',
'javelin-behavior-toggle-widget',
),
'core.pkg.css' => array(
'phabricator-core-css',
'phabricator-zindex-css',
'phui-button-css',
'phui-button-simple-css',
'phui-theme-css',
'phabricator-standard-page-view',
'aphront-dialog-view-css',
'phui-form-view-css',
'aphront-panel-view-css',
'aphront-table-view-css',
'aphront-tokenizer-control-css',
'aphront-typeahead-control-css',
'aphront-list-filter-view-css',
'application-search-view-css',
'phabricator-remarkup-css',
'syntax-highlighting-css',
'syntax-default-css',
'phui-pager-css',
'aphront-tooltip-css',
'phabricator-flag-css',
'phui-info-view-css',
'phabricator-main-menu-view',
'phabricator-notification-css',
'phabricator-notification-menu-css',
'phui-lightbox-css',
'phui-comment-panel-css',
'phui-header-view-css',
'phabricator-nav-view-css',
'phui-basic-nav-view-css',
'phui-crumbs-view-css',
'phui-oi-list-view-css',
'phui-oi-color-css',
'phui-oi-big-ui-css',
'phui-oi-drag-ui-css',
'phui-oi-simple-ui-css',
'phui-oi-flush-ui-css',
'global-drag-and-drop-css',
'phui-spacing-css',
'phui-form-css',
'phui-icon-view-css',
'phabricator-action-list-view-css',
'phui-property-list-view-css',
'phui-tag-view-css',
'phui-list-view-css',
'font-fontawesome',
'font-lato',
'phui-font-icon-base-css',
'phui-fontkit-css',
'phui-box-css',
'phui-object-box-css',
'phui-timeline-view-css',
'phui-two-column-view-css',
'phui-curtain-view-css',
'sprite-login-css',
'sprite-tokens-css',
'tokens-css',
'auth-css',
'phui-status-list-view-css',
'phui-feed-story-css',
'phabricator-feed-css',
'phabricator-dashboard-css',
'aphront-multi-column-view-css',
),
'core.pkg.js' => array(
'javelin-util',
'javelin-install',
'javelin-event',
'javelin-stratcom',
'javelin-behavior',
'javelin-resource',
'javelin-request',
'javelin-vector',
'javelin-dom',
'javelin-json',
'javelin-uri',
'javelin-workflow',
'javelin-mask',
'javelin-typeahead',
'javelin-typeahead-normalizer',
'javelin-typeahead-source',
'javelin-typeahead-preloaded-source',
'javelin-typeahead-ondemand-source',
'javelin-tokenizer',
'javelin-history',
'javelin-router',
'javelin-routable',
'javelin-behavior-aphront-basic-tokenizer',
'javelin-behavior-workflow',
'javelin-behavior-aphront-form-disable-on-submit',
'phabricator-keyboard-shortcut-manager',
'phabricator-keyboard-shortcut',
'javelin-behavior-phabricator-keyboard-shortcuts',
'javelin-behavior-refresh-csrf',
'javelin-behavior-phabricator-watch-anchor',
'javelin-behavior-phabricator-autofocus',
'phuix-dropdown-menu',
'phuix-action-list-view',
'phuix-action-view',
'phuix-icon-view',
'phabricator-phtize',
'javelin-behavior-phabricator-oncopy',
'phabricator-tooltip',
'javelin-behavior-phabricator-tooltips',
'phabricator-prefab',
'javelin-behavior-device',
'javelin-behavior-toggle-class',
'javelin-behavior-lightbox-attachments',
'phabricator-busy',
'javelin-sound',
'javelin-aphlict',
'phabricator-notification',
'javelin-behavior-aphlict-listen',
'javelin-behavior-phabricator-search-typeahead',
'javelin-behavior-aphlict-dropdown',
'javelin-behavior-history-install',
'javelin-behavior-phabricator-gesture',
'javelin-behavior-phabricator-active-nav',
'javelin-behavior-phabricator-nav',
'javelin-behavior-phabricator-remarkup-assist',
'phabricator-textareautils',
'phabricator-file-upload',
'javelin-behavior-global-drag-and-drop',
'javelin-behavior-phabricator-reveal-content',
'phui-hovercard',
'javelin-behavior-phui-hovercards',
'javelin-color',
'javelin-fx',
'phabricator-draggable-list',
'javelin-behavior-phabricator-transaction-list',
'javelin-behavior-phabricator-show-older-transactions',
'javelin-behavior-phui-dropdown-menu',
'javelin-behavior-doorkeeper-tag',
'phabricator-title',
'javelin-leader',
'javelin-websocket',
'javelin-behavior-dashboard-async-panel',
'javelin-behavior-dashboard-tab-panel',
'javelin-quicksand',
'javelin-behavior-quicksand-blacklist',
'javelin-behavior-high-security-warning',
'javelin-behavior-read-only-warning',
'javelin-scrollbar',
'javelin-behavior-scrollbar',
'javelin-behavior-durable-column',
'conpherence-thread-manager',
'javelin-behavior-detect-timezone',
'javelin-behavior-setup-check-https',
'javelin-behavior-aphlict-status',
'javelin-behavior-user-menu',
'phabricator-favicon',
),
'differential.pkg.css' => array(
'differential-core-view-css',
'differential-changeset-view-css',
'differential-revision-history-css',
'differential-revision-list-css',
'differential-table-of-contents-css',
'differential-revision-comment-css',
'differential-revision-add-comment-css',
'phabricator-object-selector-css',
'phabricator-content-source-view-css',
'inline-comment-summary-css',
'phui-inline-comment-view-css',
'phabricator-filetree-view-css',
),
'differential.pkg.js' => array(
'phabricator-drag-and-drop-file-upload',
'phabricator-shaped-request',
'javelin-behavior-differential-populate',
'javelin-behavior-differential-diff-radios',
'javelin-behavior-aphront-drag-and-drop-textarea',
'javelin-behavior-phabricator-object-selector',
'javelin-behavior-repository-crossreference',
'javelin-behavior-differential-user-select',
'javelin-behavior-aphront-more',
'phabricator-diff-inline',
'phabricator-diff-changeset',
'phabricator-diff-changeset-list',
),
'diffusion.pkg.css' => array(
'diffusion-icons-css',
),
'diffusion.pkg.js' => array(
'javelin-behavior-diffusion-pull-lastmodified',
'javelin-behavior-diffusion-commit-graph',
'javelin-behavior-audit-preview',
),
'maniphest.pkg.css' => array(
'maniphest-task-summary-css',
),
'maniphest.pkg.js' => array(
'javelin-behavior-maniphest-batch-selector',
'javelin-behavior-maniphest-subpriority-editor',
'javelin-behavior-maniphest-list-editor',
),
),
);
diff --git a/src/applications/differential/render/DifferentialChangesetHTMLRenderer.php b/src/applications/differential/render/DifferentialChangesetHTMLRenderer.php
index e4320b712b..df59db9228 100644
--- a/src/applications/differential/render/DifferentialChangesetHTMLRenderer.php
+++ b/src/applications/differential/render/DifferentialChangesetHTMLRenderer.php
@@ -1,614 +1,624 @@
<?php
abstract class DifferentialChangesetHTMLRenderer
extends DifferentialChangesetRenderer {
public static function getHTMLRendererByKey($key) {
switch ($key) {
case '1up':
return new DifferentialChangesetOneUpRenderer();
case '2up':
default:
return new DifferentialChangesetTwoUpRenderer();
}
throw new Exception(pht('Unknown HTML renderer "%s"!', $key));
}
abstract protected function getRendererTableClass();
abstract public function getRowScaffoldForInline(
PHUIDiffInlineCommentView $view);
protected function renderChangeTypeHeader($force) {
$changeset = $this->getChangeset();
$change = $changeset->getChangeType();
$file = $changeset->getFileType();
$messages = array();
switch ($change) {
case DifferentialChangeType::TYPE_ADD:
switch ($file) {
case DifferentialChangeType::FILE_TEXT:
$messages[] = pht('This file was added.');
break;
case DifferentialChangeType::FILE_IMAGE:
$messages[] = pht('This image was added.');
break;
case DifferentialChangeType::FILE_DIRECTORY:
$messages[] = pht('This directory was added.');
break;
case DifferentialChangeType::FILE_BINARY:
$messages[] = pht('This binary file was added.');
break;
case DifferentialChangeType::FILE_SYMLINK:
$messages[] = pht('This symlink was added.');
break;
case DifferentialChangeType::FILE_SUBMODULE:
$messages[] = pht('This submodule was added.');
break;
}
break;
case DifferentialChangeType::TYPE_DELETE:
switch ($file) {
case DifferentialChangeType::FILE_TEXT:
$messages[] = pht('This file was deleted.');
break;
case DifferentialChangeType::FILE_IMAGE:
$messages[] = pht('This image was deleted.');
break;
case DifferentialChangeType::FILE_DIRECTORY:
$messages[] = pht('This directory was deleted.');
break;
case DifferentialChangeType::FILE_BINARY:
$messages[] = pht('This binary file was deleted.');
break;
case DifferentialChangeType::FILE_SYMLINK:
$messages[] = pht('This symlink was deleted.');
break;
case DifferentialChangeType::FILE_SUBMODULE:
$messages[] = pht('This submodule was deleted.');
break;
}
break;
case DifferentialChangeType::TYPE_MOVE_HERE:
$from = phutil_tag('strong', array(), $changeset->getOldFile());
switch ($file) {
case DifferentialChangeType::FILE_TEXT:
$messages[] = pht('This file was moved from %s.', $from);
break;
case DifferentialChangeType::FILE_IMAGE:
$messages[] = pht('This image was moved from %s.', $from);
break;
case DifferentialChangeType::FILE_DIRECTORY:
$messages[] = pht('This directory was moved from %s.', $from);
break;
case DifferentialChangeType::FILE_BINARY:
$messages[] = pht('This binary file was moved from %s.', $from);
break;
case DifferentialChangeType::FILE_SYMLINK:
$messages[] = pht('This symlink was moved from %s.', $from);
break;
case DifferentialChangeType::FILE_SUBMODULE:
$messages[] = pht('This submodule was moved from %s.', $from);
break;
}
break;
case DifferentialChangeType::TYPE_COPY_HERE:
$from = phutil_tag('strong', array(), $changeset->getOldFile());
switch ($file) {
case DifferentialChangeType::FILE_TEXT:
$messages[] = pht('This file was copied from %s.', $from);
break;
case DifferentialChangeType::FILE_IMAGE:
$messages[] = pht('This image was copied from %s.', $from);
break;
case DifferentialChangeType::FILE_DIRECTORY:
$messages[] = pht('This directory was copied from %s.', $from);
break;
case DifferentialChangeType::FILE_BINARY:
$messages[] = pht('This binary file was copied from %s.', $from);
break;
case DifferentialChangeType::FILE_SYMLINK:
$messages[] = pht('This symlink was copied from %s.', $from);
break;
case DifferentialChangeType::FILE_SUBMODULE:
$messages[] = pht('This submodule was copied from %s.', $from);
break;
}
break;
case DifferentialChangeType::TYPE_MOVE_AWAY:
$paths = phutil_tag(
'strong',
array(),
implode(', ', $changeset->getAwayPaths()));
switch ($file) {
case DifferentialChangeType::FILE_TEXT:
$messages[] = pht('This file was moved to %s.', $paths);
break;
case DifferentialChangeType::FILE_IMAGE:
$messages[] = pht('This image was moved to %s.', $paths);
break;
case DifferentialChangeType::FILE_DIRECTORY:
$messages[] = pht('This directory was moved to %s.', $paths);
break;
case DifferentialChangeType::FILE_BINARY:
$messages[] = pht('This binary file was moved to %s.', $paths);
break;
case DifferentialChangeType::FILE_SYMLINK:
$messages[] = pht('This symlink was moved to %s.', $paths);
break;
case DifferentialChangeType::FILE_SUBMODULE:
$messages[] = pht('This submodule was moved to %s.', $paths);
break;
}
break;
case DifferentialChangeType::TYPE_COPY_AWAY:
$paths = phutil_tag(
'strong',
array(),
implode(', ', $changeset->getAwayPaths()));
switch ($file) {
case DifferentialChangeType::FILE_TEXT:
$messages[] = pht('This file was copied to %s.', $paths);
break;
case DifferentialChangeType::FILE_IMAGE:
$messages[] = pht('This image was copied to %s.', $paths);
break;
case DifferentialChangeType::FILE_DIRECTORY:
$messages[] = pht('This directory was copied to %s.', $paths);
break;
case DifferentialChangeType::FILE_BINARY:
$messages[] = pht('This binary file was copied to %s.', $paths);
break;
case DifferentialChangeType::FILE_SYMLINK:
$messages[] = pht('This symlink was copied to %s.', $paths);
break;
case DifferentialChangeType::FILE_SUBMODULE:
$messages[] = pht('This submodule was copied to %s.', $paths);
break;
}
break;
case DifferentialChangeType::TYPE_MULTICOPY:
$paths = phutil_tag(
'strong',
array(),
implode(', ', $changeset->getAwayPaths()));
switch ($file) {
case DifferentialChangeType::FILE_TEXT:
$messages[] = pht(
'This file was deleted after being copied to %s.',
$paths);
break;
case DifferentialChangeType::FILE_IMAGE:
$messages[] = pht(
'This image was deleted after being copied to %s.',
$paths);
break;
case DifferentialChangeType::FILE_DIRECTORY:
$messages[] = pht(
'This directory was deleted after being copied to %s.',
$paths);
break;
case DifferentialChangeType::FILE_BINARY:
$messages[] = pht(
'This binary file was deleted after being copied to %s.',
$paths);
break;
case DifferentialChangeType::FILE_SYMLINK:
$messages[] = pht(
'This symlink was deleted after being copied to %s.',
$paths);
break;
case DifferentialChangeType::FILE_SUBMODULE:
$messages[] = pht(
'This submodule was deleted after being copied to %s.',
$paths);
break;
}
break;
default:
switch ($file) {
case DifferentialChangeType::FILE_TEXT:
// This is the default case, so we only render this header if
// forced to since it's not very useful.
if ($force) {
$messages[] = pht('This file was not modified.');
}
break;
case DifferentialChangeType::FILE_IMAGE:
$messages[] = pht('This is an image.');
break;
case DifferentialChangeType::FILE_DIRECTORY:
$messages[] = pht('This is a directory.');
break;
case DifferentialChangeType::FILE_BINARY:
$messages[] = pht('This is a binary file.');
break;
case DifferentialChangeType::FILE_SYMLINK:
$messages[] = pht('This is a symlink.');
break;
case DifferentialChangeType::FILE_SUBMODULE:
$messages[] = pht('This is a submodule.');
break;
}
break;
}
return $this->formatHeaderMessages($messages);
}
protected function renderUndershieldHeader() {
$messages = array();
$changeset = $this->getChangeset();
$file = $changeset->getFileType();
// If this is a text file with at least one hunk, we may have converted
// the text encoding. In this case, show a note.
$show_encoding = ($file == DifferentialChangeType::FILE_TEXT) &&
($changeset->getHunks());
if ($show_encoding) {
$encoding = $this->getOriginalCharacterEncoding();
if ($encoding != 'utf8') {
if ($encoding) {
$messages[] = pht(
'This file was converted from %s for display.',
phutil_tag('strong', array(), $encoding));
} else {
$messages[] = pht('This file uses an unknown character encoding.');
}
}
}
if ($this->getHighlightingDisabled()) {
$messages[] = pht(
'This file is larger than %s, so syntax highlighting is '.
'disabled by default.',
phutil_format_bytes(DifferentialChangesetParser::HIGHLIGHT_BYTE_LIMIT));
}
return $this->formatHeaderMessages($messages);
}
private function formatHeaderMessages(array $messages) {
if (!$messages) {
return null;
}
foreach ($messages as $key => $message) {
$messages[$key] = phutil_tag('li', array(), $message);
}
return phutil_tag(
'ul',
array(
'class' => 'differential-meta-notice',
),
$messages);
}
protected function renderPropertyChangeHeader() {
$changeset = $this->getChangeset();
list($old, $new) = $this->getChangesetProperties($changeset);
// If we don't have any property changes, don't render this table.
if ($old === $new) {
return null;
}
$keys = array_keys($old + $new);
sort($keys);
$key_map = array(
'unix:filemode' => pht('File Mode'),
'file:dimensions' => pht('Image Dimensions'),
'file:mimetype' => pht('MIME Type'),
'file:size' => pht('File Size'),
);
$rows = array();
foreach ($keys as $key) {
$oval = idx($old, $key);
$nval = idx($new, $key);
if ($oval !== $nval) {
if ($oval === null) {
$oval = phutil_tag('em', array(), 'null');
} else {
$oval = phutil_escape_html_newlines($oval);
}
if ($nval === null) {
$nval = phutil_tag('em', array(), 'null');
} else {
$nval = phutil_escape_html_newlines($nval);
}
$readable_key = idx($key_map, $key, $key);
$row = array(
$readable_key,
$oval,
$nval,
);
$rows[] = $row;
}
}
$classes = array('', 'oval', 'nval');
$headers = array(
pht('Property'),
pht('Old Value'),
pht('New Value'),
);
$table = id(new AphrontTableView($rows))
->setHeaders($headers)
->setColumnClasses($classes);
return phutil_tag(
'div',
array(
'class' => 'differential-property-table',
),
$table);
}
public function renderShield($message, $force = 'default') {
$end = count($this->getOldLines());
$reference = $this->getRenderingReference();
if ($force !== 'text' &&
$force !== 'none' &&
$force !== 'default') {
throw new Exception(
pht(
"Invalid '%s' parameter '%s'!",
'force',
$force));
}
$range = "0-{$end}";
if ($force == 'text') {
// If we're forcing text, force the whole file to be rendered.
$range = "{$range}/0-{$end}";
}
$meta = array(
'ref' => $reference,
'range' => $range,
);
$content = array();
$content[] = $message;
if ($force !== 'none') {
$content[] = ' ';
$content[] = javelin_tag(
'a',
array(
'mustcapture' => true,
'sigil' => 'show-more',
'class' => 'complete',
'href' => '#',
'meta' => $meta,
),
pht('Show File Contents'));
}
return $this->wrapChangeInTable(
javelin_tag(
'tr',
array(
'sigil' => 'context-target',
),
phutil_tag(
'td',
array(
'class' => 'differential-shield',
'colspan' => 6,
),
$content)));
}
abstract protected function renderColgroup();
protected function wrapChangeInTable($content) {
if (!$content) {
return null;
}
$classes = array();
$classes[] = 'differential-diff';
$classes[] = 'remarkup-code';
$classes[] = 'PhabricatorMonospaced';
$classes[] = $this->getRendererTableClass();
+ $sigils = array();
+ $sigils[] = 'differential-diff';
+ foreach ($this->getTableSigils() as $sigil) {
+ $sigils[] = $sigil;
+ }
+
return javelin_tag(
'table',
array(
'class' => implode(' ', $classes),
- 'sigil' => 'differential-diff intercept-copy',
+ 'sigil' => implode(' ', $sigils),
),
array(
$this->renderColgroup(),
$content,
));
}
+ protected function getTableSigils() {
+ return array();
+ }
+
protected function buildInlineComment(
PhabricatorInlineCommentInterface $comment,
$on_right = false) {
$user = $this->getUser();
$edit = $user &&
($comment->getAuthorPHID() == $user->getPHID()) &&
($comment->isDraft())
&& $this->getShowEditAndReplyLinks();
$allow_reply = (bool)$user && $this->getShowEditAndReplyLinks();
$allow_done = !$comment->isDraft() && $this->getCanMarkDone();
return id(new PHUIDiffInlineCommentDetailView())
->setUser($user)
->setInlineComment($comment)
->setIsOnRight($on_right)
->setHandles($this->getHandles())
->setMarkupEngine($this->getMarkupEngine())
->setEditable($edit)
->setAllowReply($allow_reply)
->setCanMarkDone($allow_done)
->setObjectOwnerPHID($this->getObjectOwnerPHID());
}
/**
* Build links which users can click to show more context in a changeset.
*
* @param int Beginning of the line range to build links for.
* @param int Length of the line range to build links for.
* @param int Total number of lines in the changeset.
* @return markup Rendered links.
*/
protected function renderShowContextLinks($top, $len, $changeset_length) {
$block_size = 20;
$end = ($top + $len) - $block_size;
// If this is a large block, such that the "top" and "bottom" ranges are
// non-overlapping, we'll provide options to show the top, bottom or entire
// block. For smaller blocks, we only provide an option to show the entire
// block, since it would be silly to show the bottom 20 lines of a 25-line
// block.
$is_large_block = ($len > ($block_size * 2));
$links = array();
if ($is_large_block) {
$is_first_block = ($top == 0);
if ($is_first_block) {
$text = pht('Show First %d Line(s)', $block_size);
} else {
$text = pht("\xE2\x96\xB2 Show %d Line(s)", $block_size);
}
$links[] = $this->renderShowContextLink(
false,
"{$top}-{$len}/{$top}-20",
$text);
}
$links[] = $this->renderShowContextLink(
true,
"{$top}-{$len}/{$top}-{$len}",
pht('Show All %d Line(s)', $len));
if ($is_large_block) {
$is_last_block = (($top + $len) >= $changeset_length);
if ($is_last_block) {
$text = pht('Show Last %d Line(s)', $block_size);
} else {
$text = "\xE2\x96\xBC ".pht('Show %d Line(s)', $block_size);
}
$links[] = $this->renderShowContextLink(
false,
"{$top}-{$len}/{$end}-20",
$text);
}
return phutil_implode_html(" \xE2\x80\xA2 ", $links);
}
/**
* Build a link that shows more context in a changeset.
*
* See @{method:renderShowContextLinks}.
*
* @param bool Does this link show all context when clicked?
* @param string Range specification for lines to show.
* @param string Text of the link.
* @return markup Rendered link.
*/
private function renderShowContextLink($is_all, $range, $text) {
$reference = $this->getRenderingReference();
return javelin_tag(
'a',
array(
'href' => '#',
'mustcapture' => true,
'sigil' => 'show-more',
'meta' => array(
'type' => ($is_all ? 'all' : null),
'range' => $range,
),
),
$text);
}
/**
* Build the prefixes for line IDs used to track inline comments.
*
* @return pair<wild, wild> Left and right prefixes.
*/
protected function getLineIDPrefixes() {
// These look like "C123NL45", which means the line is line 45 on the
// "new" side of the file in changeset 123.
// The "C" stands for "changeset", and is followed by a changeset ID.
// "N" stands for "new" and means the comment should attach to the new file
// when stored. "O" stands for "old" and means the comment should attach to
// the old file. These are important because either the old or new part
// of a file may appear on the left or right side of the diff in the
// diff-of-diffs view.
// The "L" stands for "line" and is followed by the line number.
if ($this->getOldChangesetID()) {
$left_prefix = array();
$left_prefix[] = 'C';
$left_prefix[] = $this->getOldChangesetID();
$left_prefix[] = $this->getOldAttachesToNewFile() ? 'N' : 'O';
$left_prefix[] = 'L';
$left_prefix = implode('', $left_prefix);
} else {
$left_prefix = null;
}
if ($this->getNewChangesetID()) {
$right_prefix = array();
$right_prefix[] = 'C';
$right_prefix[] = $this->getNewChangesetID();
$right_prefix[] = $this->getNewAttachesToNewFile() ? 'N' : 'O';
$right_prefix[] = 'L';
$right_prefix = implode('', $right_prefix);
} else {
$right_prefix = null;
}
return array($left_prefix, $right_prefix);
}
protected function renderImageStage(PhabricatorFile $file) {
return phutil_tag(
'div',
array(
'class' => 'differential-image-stage',
),
phutil_tag(
'img',
array(
'src' => $file->getBestURI(),
)));
}
}
diff --git a/src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php b/src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php
index 290272db49..7c728c1ff7 100644
--- a/src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php
+++ b/src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php
@@ -1,451 +1,461 @@
<?php
final class DifferentialChangesetTwoUpRenderer
extends DifferentialChangesetHTMLRenderer {
private $newOffsetMap;
public function isOneUpRenderer() {
return false;
}
protected function getRendererTableClass() {
return 'diff-2up';
}
public function getRendererKey() {
return '2up';
}
protected function renderColgroup() {
return phutil_tag('colgroup', array(), array(
phutil_tag('col', array('class' => 'num')),
phutil_tag('col', array('class' => 'left')),
phutil_tag('col', array('class' => 'num')),
phutil_tag('col', array('class' => 'copy')),
phutil_tag('col', array('class' => 'right')),
phutil_tag('col', array('class' => 'cov')),
));
}
public function renderTextChange(
$range_start,
$range_len,
$rows) {
$hunk_starts = $this->getHunkStartLines();
$context_not_available = null;
if ($hunk_starts) {
$context_not_available = javelin_tag(
'tr',
array(
'sigil' => 'context-target',
),
phutil_tag(
'td',
array(
'colspan' => 6,
'class' => 'show-more',
),
pht('Context not available.')));
}
$html = array();
$old_lines = $this->getOldLines();
$new_lines = $this->getNewLines();
$gaps = $this->getGaps();
$reference = $this->getRenderingReference();
list($left_prefix, $right_prefix) = $this->getLineIDPrefixes();
$changeset = $this->getChangeset();
$copy_lines = idx($changeset->getMetadata(), 'copy:lines', array());
$highlight_old = $this->getHighlightOld();
$highlight_new = $this->getHighlightNew();
$old_render = $this->getOldRender();
$new_render = $this->getNewRender();
$original_left = $this->getOriginalOld();
$original_right = $this->getOriginalNew();
$mask = $this->getMask();
$scope_engine = $this->getScopeEngine();
$offset_map = null;
$depth_only = $this->getDepthOnlyLines();
for ($ii = $range_start; $ii < $range_start + $range_len; $ii++) {
if (empty($mask[$ii])) {
// If we aren't going to show this line, we've just entered a gap.
// Pop information about the next gap off the $gaps stack and render
// an appropriate "Show more context" element. This branch eventually
// increments $ii by the entire size of the gap and then continues
// the loop.
$gap = array_pop($gaps);
$top = $gap[0];
$len = $gap[1];
$contents = $this->renderShowContextLinks($top, $len, $rows);
$is_last_block = false;
if ($ii + $len >= $rows) {
$is_last_block = true;
}
$context_text = null;
$context_line = null;
if (!$is_last_block) {
$target_line = $new_lines[$ii + $len]['line'];
$context_line = $scope_engine->getScopeStart($target_line);
if ($context_line !== null) {
// The scope engine returns a line number in the file. We need
// to map that back to a display offset in the diff.
if (!$offset_map) {
$offset_map = $this->getNewLineToOffsetMap();
}
$offset = $offset_map[$context_line];
$context_text = $new_render[$offset];
}
}
$container = javelin_tag(
'tr',
array(
'sigil' => 'context-target',
),
array(
phutil_tag(
'td',
array(
- 'colspan' => 2,
+ 'class' => 'show-context-line n left-context',
+ )),
+ phutil_tag(
+ 'td',
+ array(
'class' => 'show-more',
),
$contents),
phutil_tag(
- 'th',
+ 'td',
array(
- 'class' => 'show-context-line',
- ),
- $context_line ? (int)$context_line : null),
+ 'class' => 'show-context-line n',
+ 'data-n' => $context_line,
+ )),
phutil_tag(
'td',
array(
'colspan' => 3,
'class' => 'show-context',
),
// TODO: [HTML] Escaping model here isn't ideal.
phutil_safe_html($context_text)),
));
$html[] = $container;
$ii += ($len - 1);
continue;
}
$o_num = null;
$o_classes = '';
$o_text = null;
if (isset($old_lines[$ii])) {
$o_num = $old_lines[$ii]['line'];
$o_text = isset($old_render[$ii]) ? $old_render[$ii] : null;
if ($old_lines[$ii]['type']) {
if ($old_lines[$ii]['type'] == '\\') {
$o_text = $old_lines[$ii]['text'];
$o_class = 'comment';
} else if ($original_left && !isset($highlight_old[$o_num])) {
$o_class = 'old-rebase';
} else if (empty($new_lines[$ii])) {
$o_class = 'old old-full';
} else {
$o_class = 'old';
}
$o_classes = $o_class;
}
}
$n_copy = hsprintf('<td class="copy" />');
$n_cov = null;
$n_colspan = 2;
$n_classes = '';
$n_num = null;
$n_text = null;
if (isset($new_lines[$ii])) {
$n_num = $new_lines[$ii]['line'];
$n_text = isset($new_render[$ii]) ? $new_render[$ii] : null;
$coverage = $this->getCodeCoverage();
if ($coverage !== null) {
if (empty($coverage[$n_num - 1])) {
$cov_class = 'N';
} else {
$cov_class = $coverage[$n_num - 1];
}
$cov_class = 'cov-'.$cov_class;
$n_cov = phutil_tag('td', array('class' => "cov {$cov_class}"));
$n_colspan--;
}
if ($new_lines[$ii]['type']) {
if ($new_lines[$ii]['type'] == '\\') {
$n_text = $new_lines[$ii]['text'];
$n_class = 'comment';
} else if ($original_right && !isset($highlight_new[$n_num])) {
$n_class = 'new-rebase';
} else if (empty($old_lines[$ii])) {
$n_class = 'new new-full';
} else {
// NOTE: At least for the moment, I'm intentionally clearing the
// line highlighting only on the right side of the diff when a
// line has only depth changes. When a block depth is decreased,
// this gives us a large color block on the left (to make it easy
// to see the depth change) but a clean diff on the right (to make
// it easy to pick out actual code changes).
if (isset($depth_only[$ii])) {
$n_class = '';
} else {
$n_class = 'new';
}
}
$n_classes = $n_class;
$not_copied =
// If this line only changed depth, copy markers are pointless.
(!isset($copy_lines[$n_num])) ||
(isset($depth_only[$ii])) ||
($new_lines[$ii]['type'] == '\\');
if ($not_copied) {
$n_copy = phutil_tag('td', array('class' => "copy {$n_class}"));
} else {
list($orig_file, $orig_line, $orig_type) = $copy_lines[$n_num];
$title = ($orig_type == '-' ? 'Moved' : 'Copied').' from ';
if ($orig_file == '') {
$title .= "line {$orig_line}";
} else {
$title .=
basename($orig_file).
":{$orig_line} in dir ".
dirname('/'.$orig_file);
}
$class = ($orig_type == '-' ? 'new-move' : 'new-copy');
$n_copy = javelin_tag(
'td',
array(
'meta' => array(
'msg' => $title,
),
'class' => 'copy '.$class,
),
'');
}
}
}
if (isset($hunk_starts[$o_num])) {
$html[] = $context_not_available;
}
if ($o_num && $left_prefix) {
$o_id = $left_prefix.$o_num;
} else {
$o_id = null;
}
if ($n_num && $right_prefix) {
$n_id = $right_prefix.$n_num;
} else {
$n_id = null;
}
$old_comments = $this->getOldComments();
$new_comments = $this->getNewComments();
$scaffolds = array();
if ($o_num && isset($old_comments[$o_num])) {
foreach ($old_comments[$o_num] as $comment) {
$inline = $this->buildInlineComment(
$comment,
$on_right = false);
$scaffold = $this->getRowScaffoldForInline($inline);
if ($n_num && isset($new_comments[$n_num])) {
foreach ($new_comments[$n_num] as $key => $new_comment) {
if ($comment->isCompatible($new_comment)) {
$companion = $this->buildInlineComment(
$new_comment,
$on_right = true);
$scaffold->addInlineView($companion);
unset($new_comments[$n_num][$key]);
break;
}
}
}
$scaffolds[] = $scaffold;
}
}
if ($n_num && isset($new_comments[$n_num])) {
foreach ($new_comments[$n_num] as $comment) {
$inline = $this->buildInlineComment(
$comment,
$on_right = true);
$scaffolds[] = $this->getRowScaffoldForInline($inline);
}
}
$old_number = phutil_tag(
'td',
array(
'id' => $o_id,
'class' => $o_classes.' n',
'data-n' => $o_num,
));
$new_number = phutil_tag(
'td',
array(
'id' => $n_id,
'class' => $n_classes.' n',
'data-n' => $n_num,
));
$html[] = phutil_tag('tr', array(), array(
$old_number,
phutil_tag(
'td',
array(
'class' => $o_classes,
'data-copy-mode' => 'copy-l',
),
$o_text),
$new_number,
$n_copy,
phutil_tag(
'td',
array(
'class' => $n_classes,
'colspan' => $n_colspan,
'data-copy-mode' => 'copy-r',
),
$n_text),
$n_cov,
));
if ($context_not_available && ($ii == $rows - 1)) {
$html[] = $context_not_available;
}
foreach ($scaffolds as $scaffold) {
$html[] = $scaffold;
}
}
return $this->wrapChangeInTable(phutil_implode_html('', $html));
}
public function renderFileChange(
$old_file = null,
$new_file = null,
$id = 0,
$vs = 0) {
$old = null;
if ($old_file) {
$old = $this->renderImageStage($old_file);
}
$new = null;
if ($new_file) {
$new = $this->renderImageStage($new_file);
}
// If we don't have an explicit "vs" changeset, it's the left side of the
// "id" changeset.
if (!$vs) {
$vs = $id;
}
$html_old = array();
$html_new = array();
foreach ($this->getOldComments() as $on_line => $comment_group) {
foreach ($comment_group as $comment) {
$inline = $this->buildInlineComment(
$comment,
$on_right = false);
$html_old[] = $this->getRowScaffoldForInline($inline);
}
}
foreach ($this->getNewComments() as $lin_line => $comment_group) {
foreach ($comment_group as $comment) {
$inline = $this->buildInlineComment(
$comment,
$on_right = true);
$html_new[] = $this->getRowScaffoldForInline($inline);
}
}
if (!$old) {
$th_old = phutil_tag('th', array());
} else {
$th_old = phutil_tag('th', array('id' => "C{$vs}OL1"), 1);
}
if (!$new) {
$th_new = phutil_tag('th', array());
} else {
$th_new = phutil_tag('th', array('id' => "C{$id}NL1"), 1);
}
$output = hsprintf(
'<tr class="differential-image-diff">'.
'%s'.
'<td class="differential-old-image">%s</td>'.
'%s'.
'<td class="differential-new-image" colspan="3">%s</td>'.
'</tr>'.
'%s'.
'%s',
$th_old,
$old,
$th_new,
$new,
phutil_implode_html('', $html_old),
phutil_implode_html('', $html_new));
$output = $this->wrapChangeInTable($output);
return $this->renderChangesetTable($output);
}
public function getRowScaffoldForInline(PHUIDiffInlineCommentView $view) {
return id(new PHUIDiffTwoUpInlineCommentRowScaffold())
->addInlineView($view);
}
private function getNewLineToOffsetMap() {
if ($this->newOffsetMap === null) {
$new = $this->getNewLines();
$map = array();
foreach ($new as $offset => $new_line) {
if ($new_line['line'] === null) {
continue;
}
$map[$new_line['line']] = $offset;
}
$this->newOffsetMap = $map;
}
return $this->newOffsetMap;
}
+ protected function getTableSigils() {
+ return array(
+ 'intercept-copy',
+ );
+ }
+
}
diff --git a/webroot/rsrc/css/application/differential/changeset-view.css b/webroot/rsrc/css/application/differential/changeset-view.css
index bc1ca8b6ea..7d578ccc03 100644
--- a/webroot/rsrc/css/application/differential/changeset-view.css
+++ b/webroot/rsrc/css/application/differential/changeset-view.css
@@ -1,456 +1,475 @@
/**
* @provides differential-changeset-view-css
* @requires phui-inline-comment-view-css
*/
.differential-changeset {
position: relative;
margin: 0;
padding-top: 16px;
overflow-x: auto;
/* Fixes what seems to be a layout bug in Firefox which causes scrollbars,
to appear unpredictably, see discussion in T7690. */
overflow-y: hidden;
}
.device-phone .differential-changeset {
overflow-x: scroll;
-webkit-overflow-scrolling: touch;
}
.differential-diff {
background: {$diff.background};
width: 100%;
border-top: 1px solid {$lightblueborder};
border-bottom: 1px solid {$lightblueborder};
table-layout: fixed;
}
.differential-diff.diff-2up {
min-width: 780px;
}
.differential-diff col.num {
width: 45px;
}
.device .differential-diff.diff-1up col.num {
width: 32px;
}
.differential-diff.diff-2up col.left,
.differential-diff.diff-2up col.right {
width: 49.25%;
}
.differential-diff.diff-1up col.unified {
width: 99.5%;
}
.differential-diff col.copy {
width: 0.5%;
}
.differential-diff col.cov {
width: 1%;
}
.differential-diff td {
vertical-align: top;
white-space: pre-wrap;
word-wrap: break-word;
padding: 1px 8px;
}
.device .differential-diff td {
padding: 1px 4px;
}
.prose-diff {
padding: 12px 0;
white-space: pre-wrap;
color: {$greytext};
}
.prose-diff-frame {
padding: 12px;
}
.prose-diff span.old,
.prose-diff span.new {
padding: 0 2px;
}
.prose-diff span.old,
.prose-diff span.new {
color: {$darkgreytext};
}
-.differential-changeset-immutable .differential-diff th {
+.differential-changeset-immutable .differential-diff td {
cursor: auto;
}
.differential-diff td.old {
background: {$old-background};
}
.differential-diff td.new {
background: {$new-background};
}
.differential-diff td.old-rebase {
background: #ffeeee;
}
.differential-diff td.new-rebase {
background: #eeffee;
}
.differential-diff td.old span.bright,
.differential-diff td.old-full,
.prose-diff span.old {
background: {$old-bright};
}
.differential-diff td.new span.bright,
.differential-diff td.new-full,
.prose-diff span.new {
background: {$new-bright};
}
.differential-diff td span.depth-out,
.differential-diff td span.depth-in {
padding: 2px 0;
background-size: 12px 12px;
background-repeat: no-repeat;
background-position: left center;
}
.differential-diff td span.depth-out {
background-image: url(/rsrc/image/chevron-out.png);
background-color: {$old-background-strong};
}
.differential-diff td span.depth-in {
background-position: 2px center;
background-image: url(/rsrc/image/chevron-in.png);
background-color: {$new-background-strong};
}
.differential-diff td.copy {
min-width: 0.5%;
width: 0.5%;
padding: 0;
}
.differential-diff td.new-copy,
.differential-diff td.new-copy span.bright {
background: {$copy-background};
}
.differential-diff td.new-move,
.differential-diff td.new-move span.bright {
background: {$move-background};
}
.differential-diff td.comment {
background: #dddddd;
}
.differential-diff .inline > td {
padding: 0;
}
/* Specify line number behaviors after other behaviors because line numbers
should always have a boring grey background. */
.differential-diff td.n {
text-align: right;
padding: 1px 6px 1px 0;
vertical-align: top;
background: {$lightbluebackground};
color: {$bluetext};
cursor: pointer;
border-right: 1px solid {$thinblueborder};
overflow: hidden;
}
.differential-diff td.n::before {
content: attr(data-n);
}
+.differential-diff td.show-context-line.n {
+ cursor: auto;
+}
+
.differential-diff td.cov {
padding: 0;
}
td.cov-U {
background: #dd8866;
}
td.cov-C {
background: #66bbff;
}
td.cov-N {
background: #ddeeff;
}
td.cov-X {
background: #aa00aa;
}
td.cov-I {
background: {$lightgreybackground};
}
.differential-diff td.source-cov-C,
.differential-diff td.source-cov-C span.bright {
background: #cceeff;
}
.differential-diff td.source-cov-U,
.differential-diff td.source-cov-U span.bright {
background: #ffbb99;
}
.differential-diff td.source-cov-N,
.differential-diff td.source-cov-N span.bright {
background: #f3f6ff;
}
.differential-diff td.show-more,
-.differential-diff th.show-context-line,
+.differential-diff td.show-context-line,
.differential-diff td.show-context,
.differential-diff td.differential-shield {
background: {$lightbluebackground};
padding: 12px 0;
border-top: 1px solid {$thinblueborder};
border-bottom: 1px solid {$thinblueborder};
}
.device .differential-diff td.show-more,
-.device .differential-diff th.show-context-line,
+.device .differential-diff td.show-context-line,
.device .differential-diff td.show-context,
.device .differential-diff td.differential-shield {
padding: 6px 0;
}
.differential-diff td.show-more,
.differential-diff td.differential-shield {
font: {$basefont};
font-size: {$smallerfontsize};
white-space: normal;
}
.differential-diff td.show-more {
text-align: center;
color: {$bluetext};
}
-.differential-diff th.show-context-line {
+.differential-diff td.show-context-line {
padding-right: 6px;
}
+.differential-diff td.show-context-line.left-context {
+ border-right: none;
+}
+
.differential-diff td.show-context {
padding-left: 14px;
}
.differential-diff td.differential-shield {
text-align: center;
}
.differential-diff td.differential-shield a {
font-weight: bold;
}
.differential-diff .differential-image-diff {
background-image: url(/rsrc/image/checker_light.png);
}
.differential-diff .differential-image-diff:hover {
background-image: url(/rsrc/image/checker_dark.png);
}
.differential-diff .differential-image-diff td {
padding: 8px;
}
.differential-image-stage {
overflow: auto;
}
.differential-meta-notice {
border-top: 1px solid {$gentle.highlight.border};
border-bottom: 1px solid {$gentle.highlight.border};
background-color: {$gentle.highlight};
padding: 12px;
}
.differential-meta-notice + .differential-diff {
border-top: none;
}
.differential-changeset h1 {
font-size: {$biggestfontsize};
padding: 2px 0 20px 12px;
line-height: 20px;
color: {$blacktext};
}
.device-phone .differential-changeset h1 {
word-break: break-word;
margin-right: 8px;
}
.differential-reticle {
background-color: {$sh-yellowbackground};
border: 1px solid {$sh-yellowborder};
position: absolute;
opacity: 0.5;
top: 0;
left: 0;
box-sizing: border-box;
pointer-events: none;
}
.differential-loading {
border-top: 1px solid {$gentle.highlight.border};
border-bottom: 1px solid {$gentle.highlight.border};
background-color: {$gentle.highlight};
padding: 12px;
text-align: center;
}
.differential-collapse-undo {
color: {$darkbluetext};
padding: 12px;
border: 1px solid {$blue};
text-align: center;
background-color: {$lightblue};
margin: 8px;
}
.differential-collapse-undo a {
font-weight: bold;
}
.differential-file-icon-header .phui-icon-view {
display: inline-block;
margin: 0 6px 2px 0;
vertical-align: middle;
font-size: 14px;
}
.device-phone .differential-file-icon-header .phui-icon-view {
display: none;
}
.differential-changeset-buttons {
float: right;
margin-right: 12px;
}
.device-phone .differential-changeset-buttons .button .phui-button-text {
visibility: hidden;
width: 0;
margin-left: 8px;
}
.differential-property-table {
margin: 12px;
background: {$lightgreybackground};
border: 1px solid {$lightblueborder};
border-bottom: 1px solid {$blueborder};
}
.differential-property-table td em {
color: {$lightgreytext};
}
.differential-property-table td.oval {
background: #ffd0d0;
width: 50%;
}
.differential-property-table td.nval {
background: #d0ffd0;
width: 50%;
}
tr.differential-inline-hidden {
display: none;
}
tr.differential-inline-loading {
opacity: 0.5;
}
.differential-review-stage {
position: relative;
}
.diff-banner {
position: fixed;
top: 0;
left: 0;
right: 0;
background: {$page.content};
box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1);
border-bottom: 1px solid {$lightgreyborder};
padding: 8px 18px;
vertical-align: middle;
font-weight: bold;
font-size: {$biggerfontsize};
line-height: 28px;
}
.diff-banner-path {
color: {$greytext};
}
.diff-banner-buttons .button {
margin-left: 8px;
}
.diff-banner-has-unsaved,
.diff-banner-has-unsubmitted,
.diff-banner-has-draft-done {
background: {$gentle.highlight};
}
.diff-banner-buttons {
float: right;
}
/* In Firefox, making the table unselectable and then making cells selectable
does not work: the cells remain unselectable. Narrowly mark the cells as
unselectable. */
.differential-diff.copy-l > tbody > tr > td,
.differential-diff.copy-r > tbody > tr > td {
- -moz-user-select: -moz-none;
- -khtml-user-select: none;
+ -moz-user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
user-select: none;
}
.differential-diff.copy-l > tbody > tr > td,
.differential-diff.copy-r > tbody > tr > td {
opacity: 0.5;
}
.differential-diff.copy-l > tbody > tr > td:nth-child(2) {
+ -moz-user-select: auto;
+ -ms-user-select: auto;
-webkit-user-select: auto;
user-select: auto;
opacity: 1;
}
+.differential-diff.copy-l > tbody > tr > td.show-more:nth-child(2) {
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+ opacity: 0.25;
+}
+
.differential-diff.copy-r > tbody > tr > td:nth-child(5) {
+ -moz-user-select: auto;
+ -ms-user-select: auto;
-webkit-user-select: auto;
user-select: auto;
opacity: 1;
}
diff --git a/webroot/rsrc/js/application/diff/DiffChangesetList.js b/webroot/rsrc/js/application/diff/DiffChangesetList.js
index 8a1306967a..572faad987 100644
--- a/webroot/rsrc/js/application/diff/DiffChangesetList.js
+++ b/webroot/rsrc/js/application/diff/DiffChangesetList.js
@@ -1,1884 +1,1900 @@
/**
* @provides phabricator-diff-changeset-list
* @requires javelin-install
* phuix-button-view
* @javelin
*/
JX.install('DiffChangesetList', {
construct: function() {
this._changesets = [];
var onload = JX.bind(this, this._ifawake, this._onload);
JX.Stratcom.listen('click', 'differential-load', onload);
var onmore = JX.bind(this, this._ifawake, this._onmore);
JX.Stratcom.listen('click', 'show-more', onmore);
var onmenu = JX.bind(this, this._ifawake, this._onmenu);
JX.Stratcom.listen('click', 'differential-view-options', onmenu);
var oncollapse = JX.bind(this, this._ifawake, this._oncollapse, true);
JX.Stratcom.listen('click', 'hide-inline', oncollapse);
var onexpand = JX.bind(this, this._ifawake, this._oncollapse, false);
JX.Stratcom.listen('click', 'reveal-inline', onexpand);
var onedit = JX.bind(this, this._ifawake, this._onaction, 'edit');
JX.Stratcom.listen(
'click',
['differential-inline-comment', 'differential-inline-edit'],
onedit);
var ondone = JX.bind(this, this._ifawake, this._onaction, 'done');
JX.Stratcom.listen(
'click',
['differential-inline-comment', 'differential-inline-done'],
ondone);
var ondelete = JX.bind(this, this._ifawake, this._onaction, 'delete');
JX.Stratcom.listen(
'click',
['differential-inline-comment', 'differential-inline-delete'],
ondelete);
var onreply = JX.bind(this, this._ifawake, this._onaction, 'reply');
JX.Stratcom.listen(
'click',
['differential-inline-comment', 'differential-inline-reply'],
onreply);
var onresize = JX.bind(this, this._ifawake, this._onresize);
JX.Stratcom.listen('resize', null, onresize);
var onscroll = JX.bind(this, this._ifawake, this._onscroll);
JX.Stratcom.listen('scroll', null, onscroll);
var onselect = JX.bind(this, this._ifawake, this._onselect);
JX.Stratcom.listen(
'mousedown',
['differential-inline-comment', 'differential-inline-header'],
onselect);
var onhover = JX.bind(this, this._ifawake, this._onhover);
JX.Stratcom.listen(
['mouseover', 'mouseout'],
'differential-inline-comment',
onhover);
var onrangedown = JX.bind(this, this._ifawake, this._onrangedown);
JX.Stratcom.listen(
'mousedown',
['differential-changeset', 'tag:td'],
onrangedown);
var onrangemove = JX.bind(this, this._ifawake, this._onrangemove);
JX.Stratcom.listen(
['mouseover', 'mouseout'],
['differential-changeset', 'tag:td'],
onrangemove);
var onrangeup = JX.bind(this, this._ifawake, this._onrangeup);
JX.Stratcom.listen(
'mouseup',
null,
onrangeup);
},
properties: {
translations: null,
inlineURI: null,
inlineListURI: null,
isStandalone: false
},
members: {
_initialized: false,
_asleep: true,
_changesets: null,
_cursorItem: null,
_focusNode: null,
_focusStart: null,
_focusEnd: null,
_hoverNode: null,
_hoverInline: null,
_hoverOrigin: null,
_hoverTarget: null,
_rangeActive: false,
_rangeOrigin: null,
_rangeTarget: null,
_bannerNode: null,
_unsavedButton: null,
_unsubmittedButton: null,
_doneButton: null,
_doneMode: null,
_dropdownMenu: null,
_menuButton: null,
_menuItems: null,
sleep: function() {
this._asleep = true;
this._redrawFocus();
this._redrawSelection();
this.resetHover();
this._bannerChangeset = null;
this._redrawBanner();
},
wake: function() {
this._asleep = false;
this._redrawFocus();
this._redrawSelection();
this._bannerChangeset = null;
this._redrawBanner();
if (this._initialized) {
return;
}
this._initialized = true;
var pht = this.getTranslations();
// We may be viewing the normal "/D123" view (with all the changesets)
// or the standalone view (with just one changeset). In the standalone
// view, some options (like jumping to next or previous file) do not
// make sense and do not function.
var standalone = this.getIsStandalone();
var label;
label = pht('Jump to next change.');
this._installJumpKey('j', label, 1);
label = pht('Jump to previous change.');
this._installJumpKey('k', label, -1);
if (!standalone) {
label = pht('Jump to next file.');
this._installJumpKey('J', label, 1, 'file');
label = pht('Jump to previous file.');
this._installJumpKey('K', label, -1, 'file');
}
label = pht('Jump to next inline comment.');
this._installJumpKey('n', label, 1, 'comment');
label = pht('Jump to previous inline comment.');
this._installJumpKey('p', label, -1, 'comment');
label = pht('Jump to next inline comment, including collapsed comments.');
this._installJumpKey('N', label, 1, 'comment', true);
label = pht(
'Jump to previous inline comment, including collapsed comments.');
this._installJumpKey('P', label, -1, 'comment', true);
if (!standalone) {
label = pht('Hide or show the current file.');
this._installKey('h', label, this._onkeytogglefile);
label = pht('Jump to the table of contents.');
this._installKey('t', label, this._ontoc);
}
label = pht('Reply to selected inline comment or change.');
this._installKey('r', label, JX.bind(this, this._onkeyreply, false));
label = pht('Reply and quote selected inline comment.');
this._installKey('R', label, JX.bind(this, this._onkeyreply, true));
label = pht('Edit selected inline comment.');
this._installKey('e', label, this._onkeyedit);
label = pht('Mark or unmark selected inline comment as done.');
this._installKey('w', label, this._onkeydone);
label = pht('Collapse or expand inline comment.');
this._installKey('q', label, this._onkeycollapse);
label = pht('Hide or show all inline comments.');
this._installKey('A', label, this._onkeyhideall);
},
isAsleep: function() {
return this._asleep;
},
newChangesetForNode: function(node) {
var changeset = JX.DiffChangeset.getForNode(node);
this._changesets.push(changeset);
changeset.setChangesetList(this);
return changeset;
},
getChangesetForNode: function(node) {
return JX.DiffChangeset.getForNode(node);
},
getInlineByID: function(id) {
var inline = null;
for (var ii = 0; ii < this._changesets.length; ii++) {
inline = this._changesets[ii].getInlineByID(id);
if (inline) {
break;
}
}
return inline;
},
_ifawake: function(f) {
// This function takes another function and only calls it if the
// changeset list is awake, so we basically just ignore events when we
// are asleep. This may move up the stack at some point as we do more
// with Quicksand/Sheets.
if (this.isAsleep()) {
return;
}
return f.apply(this, [].slice.call(arguments, 1));
},
_onload: function(e) {
var data = e.getNodeData('differential-load');
// NOTE: We can trigger a load from either an explicit "Load" link on
// the changeset, or by clicking a link in the table of contents. If
// the event was a table of contents link, we let the anchor behavior
// run normally.
if (data.kill) {
e.kill();
}
var node = JX.$(data.id);
var changeset = this.getChangesetForNode(node);
changeset.load();
// TODO: Move this into Changeset.
var routable = changeset.getRoutable();
if (routable) {
routable.setPriority(2000);
}
},
_installKey: function(key, label, handler) {
handler = JX.bind(this, this._ifawake, handler);
return new JX.KeyboardShortcut(key, label)
.setHandler(handler)
.register();
},
_installJumpKey: function(key, label, delta, filter, show_collapsed) {
filter = filter || null;
var options = {
filter: filter,
collapsed: show_collapsed
};
var handler = JX.bind(this, this._onjumpkey, delta, options);
return this._installKey(key, label, handler);
},
_ontoc: function(manager) {
var toc = JX.$('toc');
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;
if (cursor) {
if (cursor.type == 'comment') {
var inline = cursor.target;
if (inline.canReply()) {
this.setFocus(null);
var text;
if (is_quote) {
text = inline.getRawText();
text = '> ' + text.replace(/\n/g, '\n> ') + '\n\n';
} else {
text = '';
}
inline.reply(text);
return;
}
}
// If the keyboard cursor is selecting a range of lines, we may have
// a mixture of old and new changes on the selected rows. It is not
// entirely unambiguous what the user means when they say they want
// to reply to this, but we use this logic: reply on the new file if
// there are any new lines. Otherwise (if there are only removed
// lines) reply on the old file.
if (cursor.type == 'change') {
var origin = cursor.nodes.begin;
var target = cursor.nodes.end;
// The "origin" and "target" are entire rows, but we need to find
// a range of "<th />" nodes to actually create an inline, so go
// fishing.
var old_list = [];
var new_list = [];
var row = origin;
while (row) {
var header = row.firstChild;
while (header) {
if (this.getLineNumberFromHeader(header)) {
if (header.className.indexOf('old') !== -1) {
old_list.push(header);
} else if (header.className.indexOf('new') !== -1) {
new_list.push(header);
}
}
header = header.nextSibling;
}
if (row == target) {
break;
}
row = row.nextSibling;
}
var use_list;
if (new_list.length) {
use_list = new_list;
} else {
use_list = old_list;
}
var src = use_list[0];
var dst = use_list[use_list.length - 1];
cursor.changeset.newInlineForRange(src, dst);
this.setFocus(null);
return;
}
}
var pht = this.getTranslations();
this._warnUser(pht('You must select a comment or change to reply to.'));
},
_onkeyedit: function() {
var cursor = this._cursorItem;
if (cursor) {
if (cursor.type == 'comment') {
var inline = cursor.target;
if (inline.canEdit()) {
this.setFocus(null);
inline.edit();
return;
}
}
}
var pht = this.getTranslations();
this._warnUser(pht('You must select a comment to edit.'));
},
_onkeydone: function() {
var cursor = this._cursorItem;
if (cursor) {
if (cursor.type == 'comment') {
var inline = cursor.target;
if (inline.canDone()) {
this.setFocus(null);
inline.toggleDone();
return;
}
}
}
var pht = this.getTranslations();
this._warnUser(pht('You must select a comment to mark done.'));
},
_onkeytogglefile: function() {
var cursor = this._cursorItem;
if (cursor) {
if (cursor.type == 'file') {
cursor.changeset.toggleVisibility();
return;
}
}
var pht = this.getTranslations();
this._warnUser(pht('You must select a file to hide or show.'));
},
_onkeycollapse: function() {
var cursor = this._cursorItem;
if (cursor) {
if (cursor.type == 'comment') {
var inline = cursor.target;
if (inline.canCollapse()) {
this.setFocus(null);
inline.setCollapsed(!inline.isCollapsed());
return;
}
}
}
var pht = this.getTranslations();
this._warnUser(pht('You must select a comment to hide.'));
},
_onkeyhideall: function() {
var inlines = this._getInlinesByType();
if (inlines.visible.length) {
this._toggleInlines('all');
} else {
this._toggleInlines('show');
}
},
_warnUser: function(message) {
new JX.Notification()
.setContent(message)
.alterClassName('jx-notification-alert', true)
.setDuration(3000)
.show();
},
_onjumpkey: function(delta, options) {
var state = this._getSelectionState();
var filter = options.filter || null;
var collapsed = options.collapsed || false;
var wrap = options.wrap || false;
var attribute = options.attribute || null;
var show = options.show || false;
var cursor = state.cursor;
var items = state.items;
// If there's currently no selection and the user tries to go back,
// don't do anything.
if ((cursor === null) && (delta < 0)) {
return;
}
var did_wrap = false;
while (true) {
if (cursor === null) {
cursor = 0;
} else {
cursor = cursor + delta;
}
// If we've gone backward past the first change, bail out.
if (cursor < 0) {
return;
}
// If we've gone forward off the end of the list, figure out where we
// should end up.
if (cursor >= items.length) {
if (!wrap) {
// If we aren't wrapping around, we're done.
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)
// and the next item isn't of that type, move past it.
if (filter !== null) {
if (items[cursor].type !== filter) {
continue;
}
}
// If the item is collapsed, don't select it when iterating with jump
// keys. It can still potentially be selected in other ways.
if (!collapsed) {
if (items[cursor].collapsed) {
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;
}
}
// 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.
break;
}
this._setSelectionState(items[cursor], true);
},
_getSelectionState: function() {
var items = this._getSelectableItems();
var cursor = null;
if (this._cursorItem !== null) {
for (var ii = 0; ii < items.length; ii++) {
var item = items[ii];
if (this._cursorItem.target === item.target) {
cursor = ii;
break;
}
}
}
return {
cursor: cursor,
items: items
};
},
_setSelectionState: function(item, scroll) {
this._cursorItem = item;
this._redrawSelection(scroll);
return this;
},
_redrawSelection: function(scroll) {
var cursor = this._cursorItem;
if (!cursor) {
this.setFocus(null);
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);
if (scroll) {
var pos = JX.$V(cursor.nodes.begin);
JX.DOM.scrollToPosition(0, pos.y - 60);
}
return this;
},
redrawCursor: function() {
// NOTE: This is setting the cursor to the current cursor. Usually, this
// would have no effect.
// However, if the old cursor pointed at an inline and the inline has
// been edited so the rows have changed, this updates the cursor to point
// at the new inline with the proper rows for the current state, and
// redraws the reticle correctly.
var state = this._getSelectionState();
if (state.cursor !== null) {
this._setSelectionState(state.items[state.cursor], false);
}
},
_getSelectableItems: function() {
var result = [];
for (var ii = 0; ii < this._changesets.length; ii++) {
var items = this._changesets[ii].getSelectableItems();
for (var jj = 0; jj < items.length; jj++) {
result.push(items[jj]);
}
}
return result;
},
_onhover: function(e) {
if (e.getIsTouchEvent()) {
return;
}
var inline;
if (e.getType() == 'mouseout') {
inline = null;
} else {
inline = this._getInlineForEvent(e);
}
this._setHoverInline(inline);
},
_onmore: function(e) {
e.kill();
var node = e.getNode('differential-changeset');
var changeset = this.getChangesetForNode(node);
var data = e.getNodeData('show-more');
var target = e.getNode('context-target');
changeset.loadContext(data.range, target);
},
_onmenu: function(e) {
var button = e.getNode('differential-view-options');
var data = JX.Stratcom.getData(button);
if (data.menu) {
// We've already built this menu, so we can let the menu itself handle
// the event.
return;
}
e.prevent();
var pht = this.getTranslations();
var node = JX.DOM.findAbove(
button,
'div',
'differential-changeset');
var changeset_list = this;
var changeset = this.getChangesetForNode(node);
var menu = new JX.PHUIXDropdownMenu(button);
var list = new JX.PHUIXActionListView();
var add_link = function(icon, name, href, local) {
if (!href) {
return;
}
var link = new JX.PHUIXActionView()
.setIcon(icon)
.setName(name)
.setHref(href)
.setHandler(function(e) {
if (local) {
window.location.assign(href);
} else {
window.open(href);
}
menu.close();
e.prevent();
});
list.addItem(link);
return link;
};
var reveal_item = new JX.PHUIXActionView()
.setIcon('fa-eye');
list.addItem(reveal_item);
var visible_item = new JX.PHUIXActionView()
.setHandler(function(e) {
e.prevent();
menu.close();
changeset.toggleVisibility();
});
list.addItem(visible_item);
add_link('fa-file-text', pht('Browse in Diffusion'), data.diffusionURI);
add_link('fa-file-o', pht('View Standalone'), data.standaloneURI);
var up_item = new JX.PHUIXActionView()
.setHandler(function(e) {
if (changeset.isLoaded()) {
// Don't let the user swap display modes if a comment is being
// edited, since they might lose their work. See PHI180.
var inlines = changeset.getInlines();
for (var ii = 0; ii < inlines.length; ii++) {
if (inlines[ii].isEditing()) {
changeset_list._warnUser(
pht(
'Finish editing inline comments before changing display ' +
'modes.'));
e.prevent();
menu.close();
return;
}
}
var renderer = changeset.getRenderer();
if (renderer == '1up') {
renderer = '2up';
} else {
renderer = '1up';
}
changeset.setRenderer(renderer);
}
changeset.reload();
e.prevent();
menu.close();
});
list.addItem(up_item);
var encoding_item = new JX.PHUIXActionView()
.setIcon('fa-font')
.setName(pht('Change Text Encoding...'))
.setHandler(function(e) {
var params = {
encoding: changeset.getEncoding()
};
new JX.Workflow('/services/encoding/', params)
.setHandler(function(r) {
changeset.setEncoding(r.encoding);
changeset.reload();
})
.start();
e.prevent();
menu.close();
});
list.addItem(encoding_item);
var highlight_item = new JX.PHUIXActionView()
.setIcon('fa-sun-o')
.setName(pht('Highlight As...'))
.setHandler(function(e) {
var params = {
highlight: changeset.getHighlight()
};
new JX.Workflow('/services/highlight/', params)
.setHandler(function(r) {
changeset.setHighlight(r.highlight);
changeset.reload();
})
.start();
e.prevent();
menu.close();
});
list.addItem(highlight_item);
add_link('fa-arrow-left', pht('Show Raw File (Left)'), data.leftURI);
add_link('fa-arrow-right', pht('Show Raw File (Right)'), data.rightURI);
add_link('fa-pencil', pht('Open in Editor'), data.editor, true);
add_link('fa-wrench', pht('Configure Editor'), data.editorConfigure);
menu.setContent(list.getNode());
menu.listen('open', function() {
// When the user opens the menu, check if there are any "Show More"
// links in the changeset body. If there aren't, disable the "Show
// Entire File" menu item since it won't change anything.
var nodes = JX.DOM.scry(JX.$(data.containerID), 'a', 'show-more');
if (nodes.length) {
reveal_item
.setDisabled(false)
.setName(pht('Show All Context'))
.setIcon('fa-file-o')
.setHandler(function(e) {
changeset.loadAllContext();
e.prevent();
menu.close();
});
} else {
reveal_item
.setDisabled(true)
.setIcon('fa-file')
.setName(pht('All Context Shown'))
.setHandler(function(e) { e.prevent(); });
}
encoding_item.setDisabled(!changeset.isLoaded());
highlight_item.setDisabled(!changeset.isLoaded());
if (changeset.isLoaded()) {
if (changeset.getRenderer() == '2up') {
up_item
.setIcon('fa-list-alt')
.setName(pht('View Unified'));
} else {
up_item
.setIcon('fa-files-o')
.setName(pht('View Side-by-Side'));
}
} else {
up_item
.setIcon('fa-refresh')
.setName(pht('Load Changes'));
}
visible_item
.setDisabled(true)
.setIcon('fa-expand')
.setName(pht('Can\'t Toggle Unloaded File'));
var diffs = JX.DOM.scry(
JX.$(data.containerID),
'table',
'differential-diff');
if (diffs.length > 1) {
JX.$E(
'More than one node with sigil "differential-diff" was found in "'+
data.containerID+'."');
} else if (diffs.length == 1) {
var diff = diffs[0];
visible_item.setDisabled(false);
if (!changeset.isVisible()) {
visible_item
.setName(pht('Expand File'))
.setIcon('fa-expand');
} else {
visible_item
.setName(pht('Collapse File'))
.setIcon('fa-compress');
}
} else {
// Do nothing when there is no diff shown in the table. For example,
// the file is binary.
}
});
data.menu = menu;
menu.open();
},
_oncollapse: function(is_collapse, e) {
e.kill();
var inline = this._getInlineForEvent(e);
inline.setCollapsed(is_collapse);
},
_onresize: function() {
this._redrawFocus();
this._redrawSelection();
this._redrawHover();
// Force a banner redraw after a resize event. Particularly, this makes
// sure the inline state updates immediately after an inline edit
// operation, even if the changeset itself has not changed.
this._bannerChangeset = null;
this._redrawBanner();
var changesets = this._changesets;
for (var ii = 0; ii < changesets.length; ii++) {
changesets[ii].redrawFileTree();
}
},
_onscroll: function() {
this._redrawBanner();
},
_onselect: function(e) {
// If the user clicked some element inside the header, like an action
// icon, ignore the event. They have to click the header element itself.
if (e.getTarget() !== e.getNode('differential-inline-header')) {
return;
}
var inline = this._getInlineForEvent(e);
if (!inline) {
return;
}
// The user definitely clicked an inline, so we're going to handle the
// event.
e.kill();
this.selectInline(inline);
},
selectInline: function(inline) {
var selection = this._getSelectionState();
var item;
// If the comment the user clicked is currently selected, deselect it.
// This makes it easy to undo things if you clicked by mistake.
if (selection.cursor !== null) {
item = selection.items[selection.cursor];
if (item.target === inline) {
this._setSelectionState(null, false);
return;
}
}
// Otherwise, select the item that the user clicked. This makes it
// easier to resume keyboard operations after using the mouse to do
// something else.
var items = selection.items;
for (var ii = 0; ii < items.length; ii++) {
item = items[ii];
if (item.target === inline) {
this._setSelectionState(item, false);
}
}
},
_onaction: function(action, e) {
e.kill();
var inline = this._getInlineForEvent(e);
var is_ref = false;
// If we don't have a natural inline object, the user may have clicked
// an action (like "Delete") inside a preview element at the bottom of
// the page.
// If they did, try to find an associated normal inline to act on, and
// pretend they clicked that instead. This makes the overall state of
// the page more consistent.
// However, there may be no normal inline (for example, because it is
// on a version of the diff which is not visible). In this case, we
// act by reference.
if (inline === null) {
var data = e.getNodeData('differential-inline-comment');
inline = this.getInlineByID(data.id);
if (inline) {
is_ref = true;
} else {
switch (action) {
case 'delete':
this._deleteInlineByID(data.id);
return;
}
}
}
// TODO: For normal operations, highlight the inline range here.
switch (action) {
case 'edit':
inline.edit();
break;
case 'done':
inline.toggleDone();
break;
case 'delete':
inline.delete(is_ref);
break;
case 'reply':
inline.reply();
break;
}
},
redrawPreview: function() {
// TODO: This isn't the cleanest way to find the preview form, but
// rendering no longer has direct access to it.
var forms = JX.DOM.scry(document.body, 'form', 'transaction-append');
if (forms.length) {
JX.DOM.invoke(forms[0], 'shouldRefresh');
}
// Clear the mouse hover reticle after a substantive edit: we don't get
// a "mouseout" event if the row vanished because of row being removed
// after an edit.
this.resetHover();
},
setFocus: function(node, extended_node) {
this._focusStart = node;
this._focusEnd = extended_node;
this._redrawFocus();
},
_redrawFocus: function() {
var node = this._focusStart;
var extended_node = this._focusEnd || node;
var reticle = this._getFocusNode();
if (!node || this.isAsleep()) {
JX.DOM.remove(reticle);
return;
}
// Outset the reticle some pixels away from the element, so there's some
// space between the focused element and the outline.
var p = JX.Vector.getPos(node);
var s = JX.Vector.getAggregateScrollForNode(node);
p.add(s).add(-4, -4).setPos(reticle);
// Compute the size we need to extend to the full extent of the focused
// nodes.
JX.Vector.getPos(extended_node)
.add(-p.x, -p.y)
.add(JX.Vector.getDim(extended_node))
.add(8, 8)
.setDim(reticle);
JX.DOM.getContentFrame().appendChild(reticle);
},
_getFocusNode: function() {
if (!this._focusNode) {
var node = JX.$N('div', {className : 'keyboard-focus-focus-reticle'});
this._focusNode = node;
}
return this._focusNode;
},
_setHoverInline: function(inline) {
this._hoverInline = inline;
if (inline) {
var changeset = inline.getChangeset();
var changeset_id;
var side = inline.getDisplaySide();
if (side == 'right') {
changeset_id = changeset.getRightChangesetID();
} else {
changeset_id = changeset.getLeftChangesetID();
}
var new_part;
if (inline.isNewFile()) {
new_part = 'N';
} else {
new_part = 'O';
}
var prefix = 'C' + changeset_id + new_part + 'L';
var number = inline.getLineNumber();
var length = inline.getLineLength();
try {
var origin = JX.$(prefix + number);
var target = JX.$(prefix + (number + length));
this._hoverOrigin = origin;
this._hoverTarget = target;
} catch (error) {
// There may not be any nodes present in the document. A case where
// this occurs is when you reply to a ghost inline which was made
// on lines near the bottom of "long.txt" in an earlier diff, and
// the file was later shortened so those lines no longer exist. For
// more details, see T11662.
this._hoverOrigin = null;
this._hoverTarget = null;
}
} else {
this._hoverOrigin = null;
this._hoverTarget = null;
}
this._redrawHover();
},
_setHoverRange: function(origin, target) {
this._hoverOrigin = origin;
this._hoverTarget = target;
this._redrawHover();
},
resetHover: function() {
this._setHoverInline(null);
this._hoverOrigin = null;
this._hoverTarget = null;
},
_redrawHover: function() {
var reticle = this._getHoverNode();
if (!this._hoverOrigin || this.isAsleep()) {
JX.DOM.remove(reticle);
return;
}
JX.DOM.getContentFrame().appendChild(reticle);
var top = this._hoverOrigin;
var bot = this._hoverTarget;
if (JX.$V(top).y > JX.$V(bot).y) {
var tmp = top;
top = bot;
bot = tmp;
}
// Find the leftmost cell that we're going to highlight: this is the next
// <td /> in the row. In 2up views, it should be directly adjacent. In
// 1up views, we may have to skip over the other line number column.
var l = top;
while (JX.DOM.isType(l, 'th')) {
l = l.nextSibling;
}
// Find the rightmost cell that we're going to highlight: this is the
// farthest consecutive, adjacent <td /> in the row. Sometimes the left
// and right nodes are the same (left side of 2up view); sometimes we're
// going to highlight several nodes (copy + code + coverage).
var r = l;
while (r.nextSibling && JX.DOM.isType(r.nextSibling, 'td')) {
r = r.nextSibling;
}
var pos = JX.$V(l)
.add(JX.Vector.getAggregateScrollForNode(l));
var dim = JX.$V(r)
.add(JX.Vector.getAggregateScrollForNode(r))
.add(-pos.x, -pos.y)
.add(JX.Vector.getDim(r));
var bpos = JX.$V(bot)
.add(JX.Vector.getAggregateScrollForNode(bot));
dim.y = (bpos.y - pos.y) + JX.Vector.getDim(bot).y;
pos.setPos(reticle);
dim.setDim(reticle);
JX.DOM.show(reticle);
},
_getHoverNode: function() {
if (!this._hoverNode) {
var attributes = {
className: 'differential-reticle'
};
this._hoverNode = JX.$N('div', attributes);
}
return this._hoverNode;
},
_deleteInlineByID: function(id) {
var uri = this.getInlineURI();
var data = {
op: 'refdelete',
id: id
};
var handler = JX.bind(this, this.redrawPreview);
new JX.Workflow(uri, data)
.setHandler(handler)
.start();
},
_getInlineForEvent: function(e) {
var node = e.getNode('differential-changeset');
if (!node) {
return null;
}
var changeset = this.getChangesetForNode(node);
var inline_row = e.getNode('inline-row');
return changeset.getInlineForRow(inline_row);
},
- getLineNumberFromHeader: function(th) {
- return parseInt(th.getAttribute('data-n'));
+ getLineNumberFromHeader: function(node) {
+ var n = parseInt(node.getAttribute('data-n'));
+
+ if (!n) {
+ return null;
+ }
+
+ // If this is a line number that's part of a row showing more context,
+ // we don't want to let users leave inlines here.
+
+ try {
+ JX.DOM.findAbove(node, 'tr', 'context-target');
+ return null;
+ } catch (ex) {
+ // Ignore.
+ }
+
+ return n;
},
getDisplaySideFromHeader: function(th) {
return (th.parentNode.firstChild != th) ? 'right' : 'left';
},
_onrangedown: function(e) {
// NOTE: We're allowing "mousedown" from a touch event through so users
// can leave inlines on a single line.
// See PHI985. We want to exclude both right-mouse and middle-mouse
// clicks from continuing.
if (!e.isLeftButton()) {
return;
}
if (this._rangeActive) {
return;
}
var target = e.getTarget();
var number = this.getLineNumberFromHeader(target);
if (!number) {
return;
}
e.kill();
this._rangeActive = true;
this._rangeOrigin = target;
this._rangeTarget = target;
this._setHoverRange(this._rangeOrigin, this._rangeTarget);
},
_onrangemove: function(e) {
if (e.getIsTouchEvent()) {
return;
}
var is_out = (e.getType() == 'mouseout');
var target = e.getTarget();
this._updateRange(target, is_out);
},
_updateRange: function(target, is_out) {
- // Don't update the range if this "<th />" doesn't correspond to a line
+ // Don't update the range if this target doesn't correspond to a line
// number. For instance, this may be a dead line number, like the empty
// line numbers on the left hand side of a newly added file.
var number = this.getLineNumberFromHeader(target);
if (!number) {
return;
}
if (this._rangeActive) {
var origin = this._hoverOrigin;
// Don't update the reticle if we're selecting a line range and the
// "<th />" under the cursor is on the wrong side of the file. You can
// only leave inline comments on the left or right side of a file, not
// across lines on both sides.
var origin_side = this.getDisplaySideFromHeader(origin);
var target_side = this.getDisplaySideFromHeader(target);
if (origin_side != target_side) {
return;
}
// Don't update the reticle if we're selecting a line range and the
// "<th />" under the cursor corresponds to a different file. You can
// only leave inline comments on lines in a single file, not across
// multiple files.
var origin_table = JX.DOM.findAbove(origin, 'table');
var target_table = JX.DOM.findAbove(target, 'table');
if (origin_table != target_table) {
return;
}
}
if (is_out) {
if (this._rangeActive) {
// If we're dragging a range, just leave the state as it is. This
// allows you to drag over something invalid while selecting a
// range without the range flickering or getting lost.
} else {
// Otherwise, clear the current range.
this.resetHover();
}
return;
}
if (this._rangeActive) {
this._rangeTarget = target;
} else {
this._rangeOrigin = target;
this._rangeTarget = target;
}
this._setHoverRange(this._rangeOrigin, this._rangeTarget);
},
_onrangeup: function(e) {
if (!this._rangeActive) {
return;
}
e.kill();
var origin = this._rangeOrigin;
var target = this._rangeTarget;
// If the user dragged a range from the bottom to the top, swap the node
// order around.
if (JX.$V(origin).y > JX.$V(target).y) {
var tmp = target;
target = origin;
origin = tmp;
}
var node = JX.DOM.findAbove(origin, null, 'differential-changeset');
var changeset = this.getChangesetForNode(node);
changeset.newInlineForRange(origin, target);
this._rangeActive = false;
this._rangeOrigin = null;
this._rangeTarget = null;
this.resetHover();
},
_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 changeset = this._getVisibleChangeset();
if (!changeset) {
this._bannerChangeset = null;
JX.DOM.remove(node);
return;
}
// Don't do anything if nothing has changed. This seems to avoid some
// flickering issues in Safari, at least.
if (this._bannerChangeset === changeset) {
return;
}
this._bannerChangeset = changeset;
var inlines = this._getInlinesByType();
var unsaved = inlines.unsaved;
var unsubmitted = inlines.unsubmitted;
var undone = inlines.undone;
var done = inlines.done;
var draft_done = inlines.draftDone;
JX.DOM.alterClass(
node,
'diff-banner-has-unsaved',
!!unsaved.length);
JX.DOM.alterClass(
node,
'diff-banner-has-unsubmitted',
!!unsubmitted.length);
JX.DOM.alterClass(
node,
'diff-banner-has-draft-done',
!!draft_done.length);
var pht = this.getTranslations();
var unsaved_button = this._getUnsavedButton();
var unsubmitted_button = this._getUnsubmittedButton();
var done_button = this._getDoneButton();
var menu_button = this._getMenuButton();
if (unsaved.length) {
unsaved_button.setText(unsaved.length + ' ' + pht('Unsaved'));
JX.DOM.show(unsaved_button.getNode());
} else {
JX.DOM.hide(unsaved_button.getNode());
}
if (unsubmitted.length || draft_done.length) {
var any_draft_count = unsubmitted.length + draft_done.length;
unsubmitted_button.setText(any_draft_count + ' ' + pht('Unsubmitted'));
JX.DOM.show(unsubmitted_button.getNode());
} else {
JX.DOM.hide(unsubmitted_button.getNode());
}
if (done.length || undone.length) {
// If you haven't marked any comments as "Done", we just show text
// like "3 Comments". If you've marked at least one done, we show
// "1 / 3 Comments".
var done_text;
if (done.length) {
done_text = [
done.length,
' / ',
(done.length + undone.length),
' ',
pht('Comments')
];
} else {
done_text = [
undone.length,
' ',
pht('Comments')
];
}
done_button.setText(done_text);
JX.DOM.show(done_button.getNode());
// If any comments are not marked "Done", this cycles through the
// missing comments. Otherwise, it cycles through all the saved
// comments.
if (undone.length) {
this._doneMode = 'undone';
} else {
this._doneMode = 'done';
}
} else {
JX.DOM.hide(done_button.getNode());
}
var path_view = [icon, ' ', changeset.getDisplayPath()];
var buttons_attrs = {
className: 'diff-banner-buttons'
};
var buttons_list = [
unsaved_button.getNode(),
unsubmitted_button.getNode(),
done_button.getNode(),
menu_button.getNode()
];
var buttons_view = JX.$N('div', buttons_attrs, buttons_list);
var icon = new JX.PHUIXIconView()
.setIcon(changeset.getIcon())
.getNode();
JX.DOM.setContent(node, [buttons_view, path_view]);
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() {
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;
},
_getUnsubmittedButton: function() {
if (!this._unsubmittedButton) {
var button = new JX.PHUIXButtonView()
.setIcon('fa-comment-o')
.setButtonType(JX.PHUIXButtonView.BUTTONTYPE_SIMPLE);
var node = button.getNode();
var onunsubmitted = JX.bind(this, this._onunsubmittedclick);
JX.DOM.listen(node, 'click', null, onunsubmitted);
this._unsubmittedButton = button;
}
return this._unsubmittedButton;
},
_getDoneButton: function() {
if (!this._doneButton) {
var button = new JX.PHUIXButtonView()
.setIcon('fa-comment')
.setButtonType(JX.PHUIXButtonView.BUTTONTYPE_SIMPLE);
var node = button.getNode();
var ondone = JX.bind(this, this._ondoneclick);
JX.DOM.listen(node, 'click', null, ondone);
this._doneButton = button;
}
return this._doneButton;
},
_getMenuButton: function() {
if (!this._menuButton) {
var pht = this.getTranslations();
var button = new JX.PHUIXButtonView()
.setIcon('fa-bars')
.setButtonType(JX.PHUIXButtonView.BUTTONTYPE_SIMPLE)
.setAuralLabel(pht('Display Options'));
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));
if (this.getInlineListURI()) {
list.addItem(
new JX.PHUIXActionView()
.setDivider(true));
list.addItem(
new JX.PHUIXActionView()
.setIcon('fa-external-link')
.setName(pht('List Inline Comments'))
.setHref(this.getInlineListURI()));
}
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();
this._toggleInlines(type);
},
_toggleInlines: function(type) {
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) {
e.kill();
var options = {
filter: 'comment',
wrap: true,
show: true,
attribute: 'unsaved'
};
this._onjumpkey(1, options);
},
_onunsubmittedclick: function(e) {
e.kill();
var options = {
filter: 'comment',
wrap: true,
show: true,
attribute: 'anyDraft'
};
this._onjumpkey(1, options);
},
_ondoneclick: function(e) {
e.kill();
var options = {
filter: 'comment',
wrap: true,
show: true,
attribute: this._doneMode
};
this._onjumpkey(1, options);
},
_getBannerNode: function() {
if (!this._bannerNode) {
var attributes = {
className: 'diff-banner',
id: 'diff-banner'
};
this._bannerNode = JX.$N('div', attributes);
}
return this._bannerNode;
},
_getVisibleChangeset: function() {
if (this.isAsleep()) {
return null;
}
if (JX.Device.getDevice() != 'desktop') {
return null;
}
// Never show the banner if we're very near the top of the page.
var margin = 480;
var s = JX.Vector.getScroll();
if (s.y < margin) {
return null;
}
// We're going to find the changeset which spans an invisible line a
// little underneath the bottom of the banner. This makes the header
// tick over from "A.txt" to "B.txt" just as "A.txt" scrolls completely
// offscreen.
var detect_height = 64;
for (var ii = 0; ii < this._changesets.length; ii++) {
var changeset = this._changesets[ii];
var c = changeset.getVectors();
// If the changeset starts above the line...
if (c.pos.y <= (s.y + detect_height)) {
// ...and ends below the line, this is the current visible changeset.
if ((c.pos.y + c.dim.y) >= (s.y + detect_height)) {
return changeset;
}
}
}
return null;
}
}
});
diff --git a/webroot/rsrc/js/core/behavior-oncopy.js b/webroot/rsrc/js/core/behavior-oncopy.js
index a37474abf9..e7309a1e18 100644
--- a/webroot/rsrc/js/core/behavior-oncopy.js
+++ b/webroot/rsrc/js/core/behavior-oncopy.js
@@ -1,272 +1,315 @@
/**
* @provides javelin-behavior-phabricator-oncopy
* @requires javelin-behavior
* javelin-dom
*/
JX.behavior('phabricator-oncopy', function() {
var copy_root;
var copy_mode;
function onstartselect(e) {
var target = e.getTarget();
var container;
try {
// NOTE: For now, all elements with custom oncopy behavior are tables,
// so this tag selection will hit everything we need it to.
container = JX.DOM.findAbove(target, 'table', 'intercept-copy');
} catch (ex) {
container = null;
}
var old_mode = copy_mode;
clear_selection_mode();
if (!container) {
return;
}
// If the potential selection is starting inside an inline comment,
// don't do anything special.
try {
if (JX.DOM.findAbove(target, 'div', 'differential-inline-comment')) {
return;
}
} catch (ex) {
// Continue.
}
// Find the row and cell we're copying from. If we don't find anything,
// don't do anything special.
var row;
var cell;
try {
// The target may be the cell we're after, particularly if you click
// in the white area to the right of the text, towards the end of a line.
if (JX.DOM.isType(target, 'td')) {
cell = target;
} else {
cell = JX.DOM.findAbove(target, 'td');
}
row = JX.DOM.findAbove(target, 'tr');
} catch (ex) {
return;
}
// If the row doesn't have enough nodes, bail out. Note that it's okay
// to begin a selection in the whitespace on the opposite side of an inline
// comment. For example, if there's an inline comment on the right side of
// a diff, it's okay to start selecting the left side of the diff by
// clicking the corresponding empty space on the left side.
if (row.childNodes.length < 4) {
return;
}
// If the selection's cell is in the "old" diff or the "new" diff, we'll
// activate an appropriate copy mode.
var mode;
if (cell === row.childNodes[1]) {
mode = 'copy-l';
} else if ((row.childNodes.length >= 4) && (cell === row.childNodes[4])) {
mode = 'copy-r';
} else {
return;
}
// We found a copy mode, so set it as the current active mode.
copy_root = container;
copy_mode = mode;
// If the user makes a selection, then clicks again inside the same
// selection, browsers retain the selection. This is because the user may
// want to drag-and-drop the text to another window.
// Handle special cases when the click is inside an existing selection.
var ranges = get_selected_ranges();
if (ranges.length) {
// We'll have an existing selection if the user selects text on the right
// side of a diff, then clicks the selection on the left side of the
// diff, even if the second click is clicking part of the selection
// range where the selection highlight is currently invisible because
// of CSS rules.
// This behavior looks and feels glitchy: an invisible selection range
// suddenly pops into existence and there's a bunch of flicker. If we're
// switching selection modes, clear the old selection to avoid this:
// assume the user is not trying to drag-and-drop text which is not
// visually selected.
if (old_mode !== copy_mode) {
window.getSelection().removeAllRanges();
}
// In the more mundane case, if the user selects some text on one side
// of a diff and then clicks that same selection in a normal way (in
// the visible part of the highlighted text), we may either be altering
// the selection range or may be initiating a text drag depending on how
// long they hold the button for. Regardless of what we're doing, we're
// still in a selection mode, so keep the visual hints active.
JX.DOM.alterClass(copy_root, copy_mode, true);
}
// We've chosen a mode and saved it now, but we don't actually update to
// apply any visual changes until the user actually starts making some
// kind of selection.
}
// When the selection range changes, apply CSS classes if the selection is
// nonempty. We don't want to make visual changes to the document immediately
// when the user press the mouse button, since we aren't yet sure that
// they are starting a selection: instead, wait for them to actually select
// something.
function onchangeselect() {
if (!copy_mode) {
return;
}
var ranges = get_selected_ranges();
JX.DOM.alterClass(copy_root, copy_mode, !!ranges.length);
}
// When the user releases the mouse, get rid of the selection mode if we
// don't have a selection.
function onendselect(e) {
if (!copy_mode) {
return;
}
var ranges = get_selected_ranges();
if (!ranges.length) {
clear_selection_mode();
}
}
function get_selected_ranges() {
var ranges = [];
if (!window.getSelection) {
return ranges;
}
var selection = window.getSelection();
for (var ii = 0; ii < selection.rangeCount; ii++) {
var range = selection.getRangeAt(ii);
if (range.collapsed) {
continue;
}
ranges.push(range);
}
return ranges;
}
function clear_selection_mode() {
if (!copy_root) {
return;
}
JX.DOM.alterClass(copy_root, copy_mode, false);
copy_root = null;
copy_mode = null;
}
function oncopy(e) {
// If we aren't in a special copy mode, just fall back to default
// behavior.
if (!copy_mode) {
return;
}
var ranges = get_selected_ranges();
if (!ranges.length) {
return;
}
- var text_nodes = [];
+ var text = [];
for (var ii = 0; ii < ranges.length; ii++) {
var range = ranges[ii];
var fragment = range.cloneContents();
- if (!fragment.children.length) {
+ if (!fragment.childNodes.length) {
continue;
}
// In Chrome and Firefox, because we've already applied "user-select"
// CSS to everything we don't intend to copy, the text in the selection
// range is correct, and the range will include only the correct text
// nodes.
// However, in Safari, "user-select" does not apply to clipboard
// operations, so we get everything in the document between the beginning
// and end of the selection, even if it isn't visibly selected.
// Even in Chrome and Firefox, we can get partial empty nodes: for
// example, where a "<tr>" is selectable but no content in the node is
// selectable. (We have to leave the "<tr>" itself selectable because
// of how Firefox applies "user-select" rules.)
// The nodes we get here can also start and end more or less anywhere.
// One saving grace is that we use "content: attr(data-n);" to render
// the line numbers and no browsers copy this content, so we don't have
// to worry about figuring out when text is line numbers.
for (var jj = 0; jj < fragment.childNodes.length; jj++) {
var node = fragment.childNodes[jj];
- if (JX.DOM.isType(node, 'tr')) {
- // This is an inline comment row, so we never want to copy any
- // content inside of it.
- if (JX.Stratcom.hasSigil(node, 'inline-row')) {
- continue;
- }
-
- // Assume anything else is a source code row. Keep only "<td>" cells
- // with the correct mode.
- for (var kk = 0; kk < node.childNodes.length; kk++) {
- var child = node.childNodes[kk];
-
- var node_mode = child.getAttribute('data-copy-mode');
- if (node_mode === copy_mode) {
- text_nodes.push(child);
- }
- }
- } else {
- // For anything else, assume this is a text fragment or part of
- // a table cell or something and should be included in the selection
- // range.
- text_nodes.push(node);
- }
+ text.push(extract_text(node));
+ }
+ }
+
+ text = flatten_list(text);
+ text = text.join('');
+
+ var rawEvent = e.getRawEvent();
+ var data;
+ if ('clipboardData' in rawEvent) {
+ data = rawEvent.clipboardData;
+ } else {
+ data = window.clipboardData;
+ }
+ data.setData('Text', text);
+
+ e.prevent();
+ }
+
+ function extract_text(node) {
+ var ii;
+ var text = [];
+
+ if (JX.DOM.isType(node, 'tr')) {
+ // This is an inline comment row, so we never want to copy any
+ // content inside of it.
+ if (JX.Stratcom.hasSigil(node, 'inline-row')) {
+ return null;
}
- var text = [];
- for (ii = 0; ii < text_nodes.length; ii++) {
- text.push(text_nodes[ii].textContent);
+ // This is a "Show More Context" row, so we never want to copy any
+ // of the content inside.
+ if (JX.Stratcom.hasSigil(node, 'context-target')) {
+ return null;
}
- text = text.join('');
- var rawEvent = e.getRawEvent();
- var data;
- if ('clipboardData' in rawEvent) {
- data = rawEvent.clipboardData;
- } else {
- data = window.clipboardData;
+ // Assume anything else is a source code row. Keep only "<td>" cells
+ // with the correct mode.
+ for (ii = 0; ii < node.childNodes.length; ii++) {
+ text.push(extract_text(node.childNodes[ii]));
}
- data.setData('Text', text);
- e.prevent();
+ return text;
}
+
+ if (JX.DOM.isType(node, 'td')) {
+ var node_mode = node.getAttribute('data-copy-mode');
+ if (node_mode !== copy_mode) {
+ return;
+ }
+
+ // Otherwise, fall through and extract this node's text normally.
+ }
+
+ if (!node.childNodes || !node.childNodes.length) {
+ return node.textContent;
+ }
+
+ for (ii = 0; ii < node.childNodes.length; ii++) {
+ var child = node.childNodes[ii];
+ text.push(extract_text(child));
+ }
+
+ return text;
+ }
+
+ function flatten_list(list) {
+ var stack = [list];
+ var result = [];
+ while (stack.length) {
+ var next = stack.pop();
+ if (JX.isArray(next)) {
+ for (var ii = 0; ii < next.length; ii++) {
+ stack.push(next[ii]);
+ }
+ } else if (next === null) {
+ continue;
+ } else if (next === undefined) {
+ continue;
+ } else {
+ result.push(next);
+ }
+ }
+
+ return result.reverse();
}
JX.enableDispatch(document.body, 'copy');
JX.enableDispatch(window, 'selectionchange');
JX.Stratcom.listen('mousedown', null, onstartselect);
JX.Stratcom.listen('selectionchange', null, onchangeselect);
JX.Stratcom.listen('mouseup', null, onendselect);
JX.Stratcom.listen('copy', null, oncopy);
});

File Metadata

Mime Type
text/x-diff
Expires
Thu, Aug 14, 7:15 AM (1 d, 23 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
196411
Default Alt Text
(194 KB)

Event Timeline