Page MenuHomestyx hydra

No OneTemporary

diff --git a/resources/celerity/map.php b/resources/celerity/map.php
index 04036e70e4..98d44804d3 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' => 'e368deda',
+ 'core.pkg.css' => 'e3c1a8f2',
+ 'core.pkg.js' => '2cda17a4',
'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' => '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/syntax.css' => '4234f572',
'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' => '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' => 'de59bf15',
+ 'rsrc/js/core/behavior-oncopy.js' => 'ff7b3f22',
'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' => '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' => 'de59bf15',
+ 'javelin-behavior-phabricator-oncopy' => 'ff7b3f22',
'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' => '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',
+ 'syntax-highlighting-css' => '4234f572',
'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',
),
'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',
),
+ '4234f572' => array(
+ 'syntax-default-css',
+ ),
'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',
),
'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',
),
'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',
),
+ 'ff7b3f22' => array(
+ 'javelin-behavior',
+ 'javelin-dom',
+ ),
),
'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/parser/DifferentialChangesetParser.php b/src/applications/differential/parser/DifferentialChangesetParser.php
index f0f952328a..8ed6d80eed 100644
--- a/src/applications/differential/parser/DifferentialChangesetParser.php
+++ b/src/applications/differential/parser/DifferentialChangesetParser.php
@@ -1,1419 +1,1603 @@
<?php
final class DifferentialChangesetParser extends Phobject {
const HIGHLIGHT_BYTE_LIMIT = 262144;
protected $visible = array();
protected $new = array();
protected $old = array();
protected $intra = array();
protected $depthOnlyLines = array();
protected $newRender = null;
protected $oldRender = null;
protected $filename = null;
protected $hunkStartLines = array();
protected $comments = array();
protected $specialAttributes = array();
protected $changeset;
protected $renderCacheKey = null;
private $handles = array();
private $user;
private $leftSideChangesetID;
private $leftSideAttachesToNewFile;
private $rightSideChangesetID;
private $rightSideAttachesToNewFile;
private $originalLeft;
private $originalRight;
private $renderingReference;
private $isSubparser;
private $isTopLevel;
private $coverage;
private $markupEngine;
private $highlightErrors;
private $disableCache;
private $renderer;
private $characterEncoding;
private $highlightAs;
private $highlightingDisabled;
private $showEditAndReplyLinks = true;
private $canMarkDone;
private $objectOwnerPHID;
private $offsetMode;
private $rangeStart;
private $rangeEnd;
private $mask;
private $linesOfContext = 8;
private $highlightEngine;
public function setRange($start, $end) {
$this->rangeStart = $start;
$this->rangeEnd = $end;
return $this;
}
public function setMask(array $mask) {
$this->mask = $mask;
return $this;
}
public function renderChangeset() {
return $this->render($this->rangeStart, $this->rangeEnd, $this->mask);
}
public function setShowEditAndReplyLinks($bool) {
$this->showEditAndReplyLinks = $bool;
return $this;
}
public function getShowEditAndReplyLinks() {
return $this->showEditAndReplyLinks;
}
public function setHighlightAs($highlight_as) {
$this->highlightAs = $highlight_as;
return $this;
}
public function getHighlightAs() {
return $this->highlightAs;
}
public function setCharacterEncoding($character_encoding) {
$this->characterEncoding = $character_encoding;
return $this;
}
public function getCharacterEncoding() {
return $this->characterEncoding;
}
public function setRenderer(DifferentialChangesetRenderer $renderer) {
$this->renderer = $renderer;
return $this;
}
public function getRenderer() {
if (!$this->renderer) {
return new DifferentialChangesetTwoUpRenderer();
}
return $this->renderer;
}
public function setDisableCache($disable_cache) {
$this->disableCache = $disable_cache;
return $this;
}
public function getDisableCache() {
return $this->disableCache;
}
public function setCanMarkDone($can_mark_done) {
$this->canMarkDone = $can_mark_done;
return $this;
}
public function getCanMarkDone() {
return $this->canMarkDone;
}
public function setObjectOwnerPHID($phid) {
$this->objectOwnerPHID = $phid;
return $this;
}
public function getObjectOwnerPHID() {
return $this->objectOwnerPHID;
}
public function setOffsetMode($offset_mode) {
$this->offsetMode = $offset_mode;
return $this;
}
public function getOffsetMode() {
return $this->offsetMode;
}
public static function getDefaultRendererForViewer(PhabricatorUser $viewer) {
$is_unified = $viewer->compareUserSetting(
PhabricatorUnifiedDiffsSetting::SETTINGKEY,
PhabricatorUnifiedDiffsSetting::VALUE_ALWAYS_UNIFIED);
if ($is_unified) {
return '1up';
}
return null;
}
public function readParametersFromRequest(AphrontRequest $request) {
$this->setCharacterEncoding($request->getStr('encoding'));
$this->setHighlightAs($request->getStr('highlight'));
$renderer = null;
// If the viewer prefers unified diffs, always set the renderer to unified.
// Otherwise, we leave it unspecified and the client will choose a
// renderer based on the screen size.
if ($request->getStr('renderer')) {
$renderer = $request->getStr('renderer');
} else {
$renderer = self::getDefaultRendererForViewer($request->getViewer());
}
switch ($renderer) {
case '1up':
$this->setRenderer(new DifferentialChangesetOneUpRenderer());
break;
default:
$this->setRenderer(new DifferentialChangesetTwoUpRenderer());
break;
}
return $this;
}
- const CACHE_VERSION = 13;
+ const CACHE_VERSION = 14;
const CACHE_MAX_SIZE = 8e6;
const ATTR_GENERATED = 'attr:generated';
const ATTR_DELETED = 'attr:deleted';
const ATTR_UNCHANGED = 'attr:unchanged';
const ATTR_MOVEAWAY = 'attr:moveaway';
public function setOldLines(array $lines) {
$this->old = $lines;
return $this;
}
public function setNewLines(array $lines) {
$this->new = $lines;
return $this;
}
public function setSpecialAttributes(array $attributes) {
$this->specialAttributes = $attributes;
return $this;
}
public function setIntraLineDiffs(array $diffs) {
$this->intra = $diffs;
return $this;
}
public function setDepthOnlyLines(array $lines) {
$this->depthOnlyLines = $lines;
return $this;
}
public function getDepthOnlyLines() {
return $this->depthOnlyLines;
}
public function setVisibileLinesMask(array $mask) {
$this->visible = $mask;
return $this;
}
public function setLinesOfContext($lines_of_context) {
$this->linesOfContext = $lines_of_context;
return $this;
}
public function getLinesOfContext() {
return $this->linesOfContext;
}
/**
* Configure which Changeset comments added to the right side of the visible
* diff will be attached to. The ID must be the ID of a real Differential
* Changeset.
*
* The complexity here is that we may show an arbitrary side of an arbitrary
* changeset as either the left or right part of a diff. This method allows
* the left and right halves of the displayed diff to be correctly mapped to
* storage changesets.
*
* @param id The Differential Changeset ID that comments added to the right
* side of the visible diff should be attached to.
* @param bool If true, attach new comments to the right side of the storage
* changeset. Note that this may be false, if the left side of
* some storage changeset is being shown as the right side of
* a display diff.
* @return this
*/
public function setRightSideCommentMapping($id, $is_new) {
$this->rightSideChangesetID = $id;
$this->rightSideAttachesToNewFile = $is_new;
return $this;
}
/**
* See setRightSideCommentMapping(), but this sets information for the left
* side of the display diff.
*/
public function setLeftSideCommentMapping($id, $is_new) {
$this->leftSideChangesetID = $id;
$this->leftSideAttachesToNewFile = $is_new;
return $this;
}
public function setOriginals(
DifferentialChangeset $left,
DifferentialChangeset $right) {
$this->originalLeft = $left;
$this->originalRight = $right;
return $this;
}
public function diffOriginals() {
$engine = new PhabricatorDifferenceEngine();
$changeset = $engine->generateChangesetFromFileContent(
implode('', mpull($this->originalLeft->getHunks(), 'getChanges')),
implode('', mpull($this->originalRight->getHunks(), 'getChanges')));
$parser = new DifferentialHunkParser();
return $parser->parseHunksForHighlightMasks(
$changeset->getHunks(),
$this->originalLeft->getHunks(),
$this->originalRight->getHunks());
}
/**
* Set a key for identifying this changeset in the render cache. If set, the
* parser will attempt to use the changeset render cache, which can improve
* performance for frequently-viewed changesets.
*
* By default, there is no render cache key and parsers do not use the cache.
* This is appropriate for rarely-viewed changesets.
*
* NOTE: Currently, this key must be a valid Differential Changeset ID.
*
* @param string Key for identifying this changeset in the render cache.
* @return this
*/
public function setRenderCacheKey($key) {
$this->renderCacheKey = $key;
return $this;
}
private function getRenderCacheKey() {
return $this->renderCacheKey;
}
public function setChangeset(DifferentialChangeset $changeset) {
$this->changeset = $changeset;
$this->setFilename($changeset->getFilename());
return $this;
}
public function setRenderingReference($ref) {
$this->renderingReference = $ref;
return $this;
}
private function getRenderingReference() {
return $this->renderingReference;
}
public function getChangeset() {
return $this->changeset;
}
public function setFilename($filename) {
$this->filename = $filename;
return $this;
}
public function setHandles(array $handles) {
assert_instances_of($handles, 'PhabricatorObjectHandle');
$this->handles = $handles;
return $this;
}
public function setMarkupEngine(PhabricatorMarkupEngine $engine) {
$this->markupEngine = $engine;
return $this;
}
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
public function getUser() {
return $this->user;
}
public function setCoverage($coverage) {
$this->coverage = $coverage;
return $this;
}
private function getCoverage() {
return $this->coverage;
}
public function parseInlineComment(
PhabricatorInlineCommentInterface $comment) {
// Parse only comments which are actually visible.
if ($this->isCommentVisibleOnRenderedDiff($comment)) {
$this->comments[] = $comment;
}
return $this;
}
private function loadCache() {
$render_cache_key = $this->getRenderCacheKey();
if (!$render_cache_key) {
return false;
}
$data = null;
$changeset = new DifferentialChangeset();
$conn_r = $changeset->establishConnection('r');
$data = queryfx_one(
$conn_r,
'SELECT * FROM %T WHERE id = %d',
$changeset->getTableName().'_parse_cache',
$render_cache_key);
if (!$data) {
return false;
}
if ($data['cache'][0] == '{') {
// This is likely an old-style JSON cache which we will not be able to
// deserialize.
return false;
}
$data = unserialize($data['cache']);
if (!is_array($data) || !$data) {
return false;
}
foreach (self::getCacheableProperties() as $cache_key) {
if (!array_key_exists($cache_key, $data)) {
// If we're missing a cache key, assume we're looking at an old cache
// and ignore it.
return false;
}
}
if ($data['cacheVersion'] !== self::CACHE_VERSION) {
return false;
}
// Someone displays contents of a partially cached shielded file.
if (!isset($data['newRender']) && (!$this->isTopLevel || $this->comments)) {
return false;
}
unset($data['cacheVersion'], $data['cacheHost']);
$cache_prop = array_select_keys($data, self::getCacheableProperties());
foreach ($cache_prop as $cache_key => $v) {
$this->$cache_key = $v;
}
return true;
}
protected static function getCacheableProperties() {
return array(
'visible',
'new',
'old',
'intra',
'depthOnlyLines',
'newRender',
'oldRender',
'specialAttributes',
'hunkStartLines',
'cacheVersion',
'cacheHost',
'highlightingDisabled',
);
}
public function saveCache() {
if (PhabricatorEnv::isReadOnly()) {
return false;
}
if ($this->highlightErrors) {
return false;
}
$render_cache_key = $this->getRenderCacheKey();
if (!$render_cache_key) {
return false;
}
$cache = array();
foreach (self::getCacheableProperties() as $cache_key) {
switch ($cache_key) {
case 'cacheVersion':
$cache[$cache_key] = self::CACHE_VERSION;
break;
case 'cacheHost':
$cache[$cache_key] = php_uname('n');
break;
default:
$cache[$cache_key] = $this->$cache_key;
break;
}
}
$cache = serialize($cache);
// We don't want to waste too much space by a single changeset.
if (strlen($cache) > self::CACHE_MAX_SIZE) {
return;
}
$changeset = new DifferentialChangeset();
$conn_w = $changeset->establishConnection('w');
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
try {
queryfx(
$conn_w,
'INSERT INTO %T (id, cache, dateCreated) VALUES (%d, %B, %d)
ON DUPLICATE KEY UPDATE cache = VALUES(cache)',
DifferentialChangeset::TABLE_CACHE,
$render_cache_key,
$cache,
time());
} catch (AphrontQueryException $ex) {
// Ignore these exceptions. A common cause is that the cache is
// larger than 'max_allowed_packet', in which case we're better off
// not writing it.
// TODO: It would be nice to tailor this more narrowly.
}
unset($unguarded);
}
private function markGenerated($new_corpus_block = '') {
$generated_guess = (strpos($new_corpus_block, '@'.'generated') !== false);
if (!$generated_guess) {
$generated_path_regexps = PhabricatorEnv::getEnvConfig(
'differential.generated-paths');
foreach ($generated_path_regexps as $regexp) {
if (preg_match($regexp, $this->changeset->getFilename())) {
$generated_guess = true;
break;
}
}
}
$event = new PhabricatorEvent(
PhabricatorEventType::TYPE_DIFFERENTIAL_WILLMARKGENERATED,
array(
'corpus' => $new_corpus_block,
'is_generated' => $generated_guess,
)
);
PhutilEventEngine::dispatchEvent($event);
$generated = $event->getValue('is_generated');
$attribute = $this->changeset->isGeneratedChangeset();
if ($attribute) {
$generated = true;
}
$this->specialAttributes[self::ATTR_GENERATED] = $generated;
}
public function isGenerated() {
return idx($this->specialAttributes, self::ATTR_GENERATED, false);
}
public function isDeleted() {
return idx($this->specialAttributes, self::ATTR_DELETED, false);
}
public function isUnchanged() {
return idx($this->specialAttributes, self::ATTR_UNCHANGED, false);
}
public function isMoveAway() {
return idx($this->specialAttributes, self::ATTR_MOVEAWAY, false);
}
private function applyIntraline(&$render, $intra, $corpus) {
foreach ($render as $key => $text) {
+ $result = $text;
+
if (isset($intra[$key])) {
- $render[$key] = ArcanistDiffUtils::applyIntralineDiff(
- $text,
+ $result = ArcanistDiffUtils::applyIntralineDiff(
+ $result,
$intra[$key]);
}
+
+ $result = $this->adjustRenderedLineForDisplay($result);
+
+ $render[$key] = $result;
}
}
private function getHighlightFuture($corpus) {
$language = $this->highlightAs;
if (!$language) {
$language = $this->highlightEngine->getLanguageFromFilename(
$this->filename);
if (($language != 'txt') &&
(strlen($corpus) > self::HIGHLIGHT_BYTE_LIMIT)) {
$this->highlightingDisabled = true;
$language = 'txt';
}
}
return $this->highlightEngine->getHighlightFuture(
$language,
$corpus);
}
protected function processHighlightedSource($data, $result) {
$result_lines = phutil_split_lines($result);
foreach ($data as $key => $info) {
if (!$info) {
unset($result_lines[$key]);
}
}
return $result_lines;
}
private function tryCacheStuff() {
$skip_cache = false;
if ($this->disableCache) {
$skip_cache = true;
}
if ($this->characterEncoding) {
$skip_cache = true;
}
if ($this->highlightAs) {
$skip_cache = true;
}
$changeset = $this->changeset;
if ($changeset->getFileType() != DifferentialChangeType::FILE_TEXT &&
$changeset->getFileType() != DifferentialChangeType::FILE_SYMLINK) {
$this->markGenerated();
} else {
if ($skip_cache || !$this->loadCache()) {
$this->process();
if (!$skip_cache) {
$this->saveCache();
}
}
}
}
private function process() {
$changeset = $this->changeset;
$hunk_parser = new DifferentialHunkParser();
$hunk_parser->parseHunksForLineData($changeset->getHunks());
$this->realignDiff($changeset, $hunk_parser);
$hunk_parser->reparseHunksForSpecialAttributes();
$unchanged = false;
if (!$hunk_parser->getHasAnyChanges()) {
$filetype = $this->changeset->getFileType();
if ($filetype == DifferentialChangeType::FILE_TEXT ||
$filetype == DifferentialChangeType::FILE_SYMLINK) {
$unchanged = true;
}
}
$moveaway = false;
$changetype = $this->changeset->getChangeType();
if ($changetype == DifferentialChangeType::TYPE_MOVE_AWAY) {
$moveaway = true;
}
$this->setSpecialAttributes(array(
self::ATTR_UNCHANGED => $unchanged,
self::ATTR_DELETED => $hunk_parser->getIsDeleted(),
self::ATTR_MOVEAWAY => $moveaway,
));
$lines_context = $this->getLinesOfContext();
$hunk_parser->generateIntraLineDiffs();
$hunk_parser->generateVisibileLinesMask($lines_context);
$this->setOldLines($hunk_parser->getOldLines());
$this->setNewLines($hunk_parser->getNewLines());
$this->setIntraLineDiffs($hunk_parser->getIntraLineDiffs());
$this->setDepthOnlyLines($hunk_parser->getDepthOnlyLines());
$this->setVisibileLinesMask($hunk_parser->getVisibleLinesMask());
$this->hunkStartLines = $hunk_parser->getHunkStartLines(
$changeset->getHunks());
$new_corpus = $hunk_parser->getNewCorpus();
$new_corpus_block = implode('', $new_corpus);
$this->markGenerated($new_corpus_block);
if ($this->isTopLevel &&
!$this->comments &&
($this->isGenerated() ||
$this->isUnchanged() ||
$this->isDeleted())) {
return;
}
$old_corpus = $hunk_parser->getOldCorpus();
$old_corpus_block = implode('', $old_corpus);
$old_future = $this->getHighlightFuture($old_corpus_block);
$new_future = $this->getHighlightFuture($new_corpus_block);
$futures = array(
'old' => $old_future,
'new' => $new_future,
);
$corpus_blocks = array(
'old' => $old_corpus_block,
'new' => $new_corpus_block,
);
$this->highlightErrors = false;
foreach (new FutureIterator($futures) as $key => $future) {
try {
try {
$highlighted = $future->resolve();
} catch (PhutilSyntaxHighlighterException $ex) {
$this->highlightErrors = true;
$highlighted = id(new PhutilDefaultSyntaxHighlighter())
->getHighlightFuture($corpus_blocks[$key])
->resolve();
}
switch ($key) {
case 'old':
$this->oldRender = $this->processHighlightedSource(
$this->old,
$highlighted);
break;
case 'new':
$this->newRender = $this->processHighlightedSource(
$this->new,
$highlighted);
break;
}
} catch (Exception $ex) {
phlog($ex);
throw $ex;
}
}
$this->applyIntraline(
$this->oldRender,
ipull($this->intra, 0),
$old_corpus);
$this->applyIntraline(
$this->newRender,
ipull($this->intra, 1),
$new_corpus);
}
private function shouldRenderPropertyChangeHeader($changeset) {
if (!$this->isTopLevel) {
// We render properties only at top level; otherwise we get multiple
// copies of them when a user clicks "Show More".
return false;
}
return true;
}
public function render(
$range_start = null,
$range_len = null,
$mask_force = array()) {
// "Top level" renders are initial requests for the whole file, versus
// requests for a specific range generated by clicking "show more". We
// generate property changes and "shield" UI elements only for toplevel
// requests.
$this->isTopLevel = (($range_start === null) && ($range_len === null));
$this->highlightEngine = PhabricatorSyntaxHighlighter::newEngine();
$encoding = null;
if ($this->characterEncoding) {
// We are forcing this changeset to be interpreted with a specific
// character encoding, so force all the hunks into that encoding and
// propagate it to the renderer.
$encoding = $this->characterEncoding;
foreach ($this->changeset->getHunks() as $hunk) {
$hunk->forceEncoding($this->characterEncoding);
}
} else {
// We're just using the default, so tell the renderer what that is
// (by reading the encoding from the first hunk).
foreach ($this->changeset->getHunks() as $hunk) {
$encoding = $hunk->getDataEncoding();
break;
}
}
$this->tryCacheStuff();
// If we're rendering in an offset mode, treat the range numbers as line
// numbers instead of rendering offsets.
$offset_mode = $this->getOffsetMode();
if ($offset_mode) {
if ($offset_mode == 'new') {
$offset_map = $this->new;
} else {
$offset_map = $this->old;
}
// NOTE: Inline comments use zero-based lengths. For example, a comment
// that starts and ends on line 123 has length 0. Rendering considers
// this range to have length 1. Probably both should agree, but that
// ship likely sailed long ago. Tweak things here to get the two systems
// to agree. See PHI985, where this affected mail rendering of inline
// comments left on the final line of a file.
$range_end = $this->getOffset($offset_map, $range_start + $range_len);
$range_start = $this->getOffset($offset_map, $range_start);
$range_len = ($range_end - $range_start) + 1;
}
$render_pch = $this->shouldRenderPropertyChangeHeader($this->changeset);
$rows = max(
count($this->old),
count($this->new));
$renderer = $this->getRenderer()
->setUser($this->getUser())
->setChangeset($this->changeset)
->setRenderPropertyChangeHeader($render_pch)
->setIsTopLevel($this->isTopLevel)
->setOldRender($this->oldRender)
->setNewRender($this->newRender)
->setHunkStartLines($this->hunkStartLines)
->setOldChangesetID($this->leftSideChangesetID)
->setNewChangesetID($this->rightSideChangesetID)
->setOldAttachesToNewFile($this->leftSideAttachesToNewFile)
->setNewAttachesToNewFile($this->rightSideAttachesToNewFile)
->setCodeCoverage($this->getCoverage())
->setRenderingReference($this->getRenderingReference())
->setMarkupEngine($this->markupEngine)
->setHandles($this->handles)
->setOldLines($this->old)
->setNewLines($this->new)
->setOriginalCharacterEncoding($encoding)
->setShowEditAndReplyLinks($this->getShowEditAndReplyLinks())
->setCanMarkDone($this->getCanMarkDone())
->setObjectOwnerPHID($this->getObjectOwnerPHID())
->setHighlightingDisabled($this->highlightingDisabled)
->setDepthOnlyLines($this->getDepthOnlyLines());
$shield = null;
if ($this->isTopLevel && !$this->comments) {
if ($this->isGenerated()) {
$shield = $renderer->renderShield(
pht(
'This file contains generated code, which does not normally '.
'need to be reviewed.'));
} else if ($this->isMoveAway()) {
// We put an empty shield on these files. Normally, they do not have
// any diff content anyway. However, if they come through `arc`, they
// may have content. We don't want to show it (it's not useful) and
// we bailed out of fully processing it earlier anyway.
// We could show a message like "this file was moved", but we show
// that as a change header anyway, so it would be redundant. Instead,
// just render an empty shield to skip rendering the diff body.
$shield = '';
} else if ($this->isUnchanged()) {
$type = 'text';
if (!$rows) {
// NOTE: Normally, diffs which don't change files do not include
// file content (for example, if you "chmod +x" a file and then
// run "git show", the file content is not available). Similarly,
// if you move a file from A to B without changing it, diffs normally
// do not show the file content. In some cases `arc` is able to
// synthetically generate content for these diffs, but for raw diffs
// we'll never have it so we need to be prepared to not render a link.
$type = 'none';
}
$type_add = DifferentialChangeType::TYPE_ADD;
if ($this->changeset->getChangeType() == $type_add) {
// Although the generic message is sort of accurate in a technical
// sense, this more-tailored message is less confusing.
$shield = $renderer->renderShield(
pht('This is an empty file.'),
$type);
} else {
$shield = $renderer->renderShield(
pht('The contents of this file were not changed.'),
$type);
}
} else if ($this->isDeleted()) {
$shield = $renderer->renderShield(
pht('This file was completely deleted.'));
} else if ($this->changeset->getAffectedLineCount() > 2500) {
$shield = $renderer->renderShield(
pht(
'This file has a very large number of changes (%s lines).',
new PhutilNumber($this->changeset->getAffectedLineCount())));
}
}
if ($shield !== null) {
return $renderer->renderChangesetTable($shield);
}
// This request should render the "undershield" headers if it's a top-level
// request which made it this far (indicating the changeset has no shield)
// or it's a request with no mask information (indicating it's the request
// that removes the rendering shield). Possibly, this second class of
// request might need to be made more explicit.
$is_undershield = (empty($mask_force) || $this->isTopLevel);
$renderer->setIsUndershield($is_undershield);
$old_comments = array();
$new_comments = array();
$old_mask = array();
$new_mask = array();
$feedback_mask = array();
$lines_context = $this->getLinesOfContext();
if ($this->comments) {
// If there are any comments which appear in sections of the file which
// we don't have, we're going to move them backwards to the closest
// earlier line. Two cases where this may happen are:
//
// - Porting ghost comments forward into a file which was mostly
// deleted.
// - Porting ghost comments forward from a full-context diff to a
// partial-context diff.
list($old_backmap, $new_backmap) = $this->buildLineBackmaps();
foreach ($this->comments as $comment) {
$new_side = $this->isCommentOnRightSideWhenDisplayed($comment);
$line = $comment->getLineNumber();
if ($new_side) {
$back_line = $new_backmap[$line];
} else {
$back_line = $old_backmap[$line];
}
if ($back_line != $line) {
// TODO: This should probably be cleaner, but just be simple and
// obvious for now.
$ghost = $comment->getIsGhost();
if ($ghost) {
$moved = pht(
'This comment originally appeared on line %s, but that line '.
'does not exist in this version of the diff. It has been '.
'moved backward to the nearest line.',
new PhutilNumber($line));
$ghost['reason'] = $ghost['reason']."\n\n".$moved;
$comment->setIsGhost($ghost);
}
$comment->setLineNumber($back_line);
$comment->setLineLength(0);
}
$start = max($comment->getLineNumber() - $lines_context, 0);
$end = $comment->getLineNumber() +
$comment->getLineLength() +
$lines_context;
for ($ii = $start; $ii <= $end; $ii++) {
if ($new_side) {
$new_mask[$ii] = true;
} else {
$old_mask[$ii] = true;
}
}
}
foreach ($this->old as $ii => $old) {
if (isset($old['line']) && isset($old_mask[$old['line']])) {
$feedback_mask[$ii] = true;
}
}
foreach ($this->new as $ii => $new) {
if (isset($new['line']) && isset($new_mask[$new['line']])) {
$feedback_mask[$ii] = true;
}
}
$this->comments = id(new PHUIDiffInlineThreader())
->reorderAndThreadCommments($this->comments);
foreach ($this->comments as $comment) {
$final = $comment->getLineNumber() +
$comment->getLineLength();
$final = max(1, $final);
if ($this->isCommentOnRightSideWhenDisplayed($comment)) {
$new_comments[$final][] = $comment;
} else {
$old_comments[$final][] = $comment;
}
}
}
$renderer
->setOldComments($old_comments)
->setNewComments($new_comments);
switch ($this->changeset->getFileType()) {
case DifferentialChangeType::FILE_IMAGE:
$old = null;
$new = null;
// TODO: Improve the architectural issue as discussed in D955
// https://secure.phabricator.com/D955
$reference = $this->getRenderingReference();
$parts = explode('/', $reference);
if (count($parts) == 2) {
list($id, $vs) = $parts;
} else {
$id = $parts[0];
$vs = 0;
}
$id = (int)$id;
$vs = (int)$vs;
if (!$vs) {
$metadata = $this->changeset->getMetadata();
$data = idx($metadata, 'attachment-data');
$old_phid = idx($metadata, 'old:binary-phid');
$new_phid = idx($metadata, 'new:binary-phid');
} else {
$vs_changeset = id(new DifferentialChangeset())->load($vs);
$old_phid = null;
$new_phid = null;
// TODO: This is spooky, see D6851
if ($vs_changeset) {
$vs_metadata = $vs_changeset->getMetadata();
$old_phid = idx($vs_metadata, 'new:binary-phid');
}
$changeset = id(new DifferentialChangeset())->load($id);
if ($changeset) {
$metadata = $changeset->getMetadata();
$new_phid = idx($metadata, 'new:binary-phid');
}
}
if ($old_phid || $new_phid) {
// grab the files, (micro) optimization for 1 query not 2
$file_phids = array();
if ($old_phid) {
$file_phids[] = $old_phid;
}
if ($new_phid) {
$file_phids[] = $new_phid;
}
$files = id(new PhabricatorFileQuery())
->setViewer($this->getUser())
->withPHIDs($file_phids)
->execute();
foreach ($files as $file) {
if (empty($file)) {
continue;
}
if ($file->getPHID() == $old_phid) {
$old = $file;
} else if ($file->getPHID() == $new_phid) {
$new = $file;
}
}
}
$renderer->attachOldFile($old);
$renderer->attachNewFile($new);
return $renderer->renderFileChange($old, $new, $id, $vs);
case DifferentialChangeType::FILE_DIRECTORY:
case DifferentialChangeType::FILE_BINARY:
$output = $renderer->renderChangesetTable(null);
return $output;
}
if ($this->originalLeft && $this->originalRight) {
list($highlight_old, $highlight_new) = $this->diffOriginals();
$highlight_old = array_flip($highlight_old);
$highlight_new = array_flip($highlight_new);
$renderer
->setHighlightOld($highlight_old)
->setHighlightNew($highlight_new);
}
$renderer
->setOriginalOld($this->originalLeft)
->setOriginalNew($this->originalRight);
if ($range_start === null) {
$range_start = 0;
}
if ($range_len === null) {
$range_len = $rows;
}
$range_len = min($range_len, $rows - $range_start);
list($gaps, $mask) = $this->calculateGapsAndMask(
$mask_force,
$feedback_mask,
$range_start,
$range_len);
$renderer
->setGaps($gaps)
->setMask($mask);
$html = $renderer->renderTextChange(
$range_start,
$range_len,
$rows);
return $renderer->renderChangesetTable($html);
}
/**
* This function calculates a lot of stuff we need to know to display
* the diff:
*
* Gaps - compute gaps in the visible display diff, where we will render
* "Show more context" spacers. If a gap is smaller than the context size,
* we just display it. Otherwise, we record it into $gaps and will render a
* "show more context" element instead of diff text below. A given $gap
* is a tuple of $gap_line_number_start and $gap_length.
*
* Mask - compute the actual lines that need to be shown (because they
* are near changes lines, near inline comments, or the request has
* explicitly asked for them, i.e. resulting from the user clicking
* "show more"). The $mask returned is a sparsely populated dictionary
* of $visible_line_number => true.
*
* @return array($gaps, $mask)
*/
private function calculateGapsAndMask(
$mask_force,
$feedback_mask,
$range_start,
$range_len) {
$lines_context = $this->getLinesOfContext();
$gaps = array();
$gap_start = 0;
$in_gap = false;
$base_mask = $this->visible + $mask_force + $feedback_mask;
$base_mask[$range_start + $range_len] = true;
for ($ii = $range_start; $ii <= $range_start + $range_len; $ii++) {
if (isset($base_mask[$ii])) {
if ($in_gap) {
$gap_length = $ii - $gap_start;
if ($gap_length <= $lines_context) {
for ($jj = $gap_start; $jj <= $gap_start + $gap_length; $jj++) {
$base_mask[$jj] = true;
}
} else {
$gaps[] = array($gap_start, $gap_length);
}
$in_gap = false;
}
} else {
if (!$in_gap) {
$gap_start = $ii;
$in_gap = true;
}
}
}
$gaps = array_reverse($gaps);
$mask = $base_mask;
return array($gaps, $mask);
}
/**
* Determine if an inline comment will appear on the rendered diff,
* taking into consideration which halves of which changesets will actually
* be shown.
*
* @param PhabricatorInlineCommentInterface Comment to test for visibility.
* @return bool True if the comment is visible on the rendered diff.
*/
private function isCommentVisibleOnRenderedDiff(
PhabricatorInlineCommentInterface $comment) {
$changeset_id = $comment->getChangesetID();
$is_new = $comment->getIsNewFile();
if ($changeset_id == $this->rightSideChangesetID &&
$is_new == $this->rightSideAttachesToNewFile) {
return true;
}
if ($changeset_id == $this->leftSideChangesetID &&
$is_new == $this->leftSideAttachesToNewFile) {
return true;
}
return false;
}
/**
* Determine if a comment will appear on the right side of the display diff.
* Note that the comment must appear somewhere on the rendered changeset, as
* per isCommentVisibleOnRenderedDiff().
*
* @param PhabricatorInlineCommentInterface Comment to test for display
* location.
* @return bool True for right, false for left.
*/
private function isCommentOnRightSideWhenDisplayed(
PhabricatorInlineCommentInterface $comment) {
if (!$this->isCommentVisibleOnRenderedDiff($comment)) {
throw new Exception(pht('Comment is not visible on changeset!'));
}
$changeset_id = $comment->getChangesetID();
$is_new = $comment->getIsNewFile();
if ($changeset_id == $this->rightSideChangesetID &&
$is_new == $this->rightSideAttachesToNewFile) {
return true;
}
return false;
}
/**
* Parse the 'range' specification that this class and the client-side JS
* emit to indicate that a user clicked "Show more..." on a diff. Generally,
* use is something like this:
*
* $spec = $request->getStr('range');
* $parsed = DifferentialChangesetParser::parseRangeSpecification($spec);
* list($start, $end, $mask) = $parsed;
* $parser->render($start, $end, $mask);
*
* @param string Range specification, indicating the range of the diff that
* should be rendered.
* @return tuple List of <start, end, mask> suitable for passing to
* @{method:render}.
*/
public static function parseRangeSpecification($spec) {
$range_s = null;
$range_e = null;
$mask = array();
if ($spec) {
$match = null;
if (preg_match('@^(\d+)-(\d+)(?:/(\d+)-(\d+))?$@', $spec, $match)) {
$range_s = (int)$match[1];
$range_e = (int)$match[2];
if (count($match) > 3) {
$start = (int)$match[3];
$len = (int)$match[4];
for ($ii = $start; $ii < $start + $len; $ii++) {
$mask[$ii] = true;
}
}
}
}
return array($range_s, $range_e, $mask);
}
/**
* Render "modified coverage" information; test coverage on modified lines.
* This synthesizes diff information with unit test information into a useful
* indicator of how well tested a change is.
*/
public function renderModifiedCoverage() {
$na = phutil_tag('em', array(), '-');
$coverage = $this->getCoverage();
if (!$coverage) {
return $na;
}
$covered = 0;
$not_covered = 0;
foreach ($this->new as $k => $new) {
if (!$new['line']) {
continue;
}
if (!$new['type']) {
continue;
}
if (empty($coverage[$new['line'] - 1])) {
continue;
}
switch ($coverage[$new['line'] - 1]) {
case 'C':
$covered++;
break;
case 'U':
$not_covered++;
break;
}
}
if (!$covered && !$not_covered) {
return $na;
}
return sprintf('%d%%', 100 * ($covered / ($covered + $not_covered)));
}
/**
* Build maps from lines comments appear on to actual lines.
*/
private function buildLineBackmaps() {
$old_back = array();
$new_back = array();
foreach ($this->old as $ii => $old) {
$old_back[$old['line']] = $old['line'];
}
foreach ($this->new as $ii => $new) {
$new_back[$new['line']] = $new['line'];
}
$max_old_line = 0;
$max_new_line = 0;
foreach ($this->comments as $comment) {
if ($this->isCommentOnRightSideWhenDisplayed($comment)) {
$max_new_line = max($max_new_line, $comment->getLineNumber());
} else {
$max_old_line = max($max_old_line, $comment->getLineNumber());
}
}
$cursor = 1;
for ($ii = 1; $ii <= $max_old_line; $ii++) {
if (empty($old_back[$ii])) {
$old_back[$ii] = $cursor;
} else {
$cursor = $old_back[$ii];
}
}
$cursor = 1;
for ($ii = 1; $ii <= $max_new_line; $ii++) {
if (empty($new_back[$ii])) {
$new_back[$ii] = $cursor;
} else {
$cursor = $new_back[$ii];
}
}
return array($old_back, $new_back);
}
private function getOffset(array $map, $line) {
if (!$map) {
return null;
}
$line = (int)$line;
foreach ($map as $key => $spec) {
if ($spec && isset($spec['line'])) {
if ((int)$spec['line'] >= $line) {
return $key;
}
}
}
return $key;
}
private function realignDiff(
DifferentialChangeset $changeset,
DifferentialHunkParser $hunk_parser) {
// Normalizing and realigning the diff depends on rediffing the files, and
// we currently need complete representations of both files to do anything
// reasonable. If we only have parts of the files, skip realignment.
// We have more than one hunk, so we're definitely missing part of the file.
$hunks = $changeset->getHunks();
if (count($hunks) !== 1) {
return null;
}
// The first hunk doesn't start at the beginning of the file, so we're
// missing some context.
$first_hunk = head($hunks);
if ($first_hunk->getOldOffset() != 1 || $first_hunk->getNewOffset() != 1) {
return null;
}
$old_file = $changeset->makeOldFile();
$new_file = $changeset->makeNewFile();
if ($old_file === $new_file) {
// If the old and new files are exactly identical, the synthetic
// diff below will give us nonsense and whitespace modes are
// irrelevant anyway. This occurs when you, e.g., copy a file onto
// itself in Subversion (see T271).
return null;
}
$engine = id(new PhabricatorDifferenceEngine())
->setNormalize(true);
$normalized_changeset = $engine->generateChangesetFromFileContent(
$old_file,
$new_file);
$type_parser = new DifferentialHunkParser();
$type_parser->parseHunksForLineData($normalized_changeset->getHunks());
$hunk_parser->setNormalized(true);
$hunk_parser->setOldLineTypeMap($type_parser->getOldLineTypeMap());
$hunk_parser->setNewLineTypeMap($type_parser->getNewLineTypeMap());
}
+ private function adjustRenderedLineForDisplay($line) {
+ // IMPORTANT: We're using "str_replace()" against raw HTML here, which can
+ // easily become unsafe. The input HTML has already had syntax highlighting
+ // and intraline diff highlighting applied, so it's full of "<span />" tags.
+
+ static $search;
+ static $replace;
+ if ($search === null) {
+ $rules = $this->newSuspiciousCharacterRules();
+
+ $map = array();
+ foreach ($rules as $key => $spec) {
+ $tag = phutil_tag(
+ 'span',
+ array(
+ 'data-copy-text' => $key,
+ 'class' => $spec['class'],
+ 'title' => $spec['title'],
+ ),
+ $spec['replacement']);
+ $map[$key] = phutil_string_cast($tag);
+ }
+
+ $search = array_keys($map);
+ $replace = array_values($map);
+ }
+
+ $is_html = false;
+ if ($line instanceof PhutilSafeHTML) {
+ $is_html = true;
+ $line = hsprintf('%s', $line);
+ }
+
+ $line = phutil_string_cast($line);
+
+ if (strpos($line, "\t") !== false) {
+ $line = $this->replaceTabsWithSpaces($line);
+ }
+ $line = str_replace($search, $replace, $line);
+
+ if ($is_html) {
+ $line = phutil_safe_html($line);
+ }
+
+ return $line;
+ }
+
+ private function newSuspiciousCharacterRules() {
+ // The "title" attributes are cached in the database, so they're
+ // intentionally not wrapped in "pht(...)".
+
+ $rules = array(
+ "\xE2\x80\x8B" => array(
+ 'title' => 'ZWS',
+ 'class' => 'suspicious-character',
+ 'replacement' => '!',
+ ),
+ "\xC2\xA0" => array(
+ 'title' => 'NBSP',
+ 'class' => 'suspicious-character',
+ 'replacement' => '!',
+ ),
+ "\x7F" => array(
+ 'title' => 'DEL (0x7F)',
+ 'class' => 'suspicious-character',
+ 'replacement' => "\xE2\x90\xA1",
+ ),
+ );
+
+ // Unicode defines special pictures for the control characters in the
+ // range between "0x00" and "0x1F".
+
+ $control = array(
+ 'NULL',
+ 'SOH',
+ 'STX',
+ 'ETX',
+ 'EOT',
+ 'ENQ',
+ 'ACK',
+ 'BEL',
+ 'BS',
+ null, // "\t" Tab
+ null, // "\n" New Line
+ 'VT',
+ 'FF',
+ null, // "\r" Carriage Return,
+ 'SO',
+ 'SI',
+ 'DLE',
+ 'DC1',
+ 'DC2',
+ 'DC3',
+ 'DC4',
+ 'NAK',
+ 'SYN',
+ 'ETB',
+ 'CAN',
+ 'EM',
+ 'SUB',
+ 'ESC',
+ 'FS',
+ 'GS',
+ 'RS',
+ 'US',
+ );
+
+ foreach ($control as $idx => $label) {
+ if ($label === null) {
+ continue;
+ }
+
+ $rules[chr($idx)] = array(
+ 'title' => sprintf('%s (0x%02X)', $label, $idx),
+ 'class' => 'suspicious-character',
+ 'replacement' => "\xE2\x90".chr(0x80 + $idx),
+ );
+ }
+
+ return $rules;
+ }
+
+ private function replaceTabsWithSpaces($line) {
+ // TODO: This should be flexible, eventually.
+ $tab_width = 2;
+
+ static $tags;
+ if ($tags === null) {
+ $tags = array();
+ for ($ii = 1; $ii <= $tab_width; $ii++) {
+ $tag = phutil_tag(
+ 'span',
+ array(
+ 'data-copy-text' => "\t",
+ ),
+ str_repeat(' ', $ii));
+ $tag = phutil_string_cast($tag);
+ $tags[$ii] = $tag;
+ }
+ }
+
+ // If the line is particularly long, don't try to vectorize it. Use a
+ // faster approximation of the correct tabstop expansion instead. This
+ // usually still arrives at the right result.
+ if (strlen($line) > 256) {
+ return str_replace("\t", $tags[$tab_width], $line);
+ }
+
+ $line = phutil_utf8v_combined($line);
+ $in_tag = false;
+ $pos = 0;
+ foreach ($line as $key => $char) {
+ if ($char === '<') {
+ $in_tag = true;
+ continue;
+ }
+
+ if ($char === '>') {
+ $in_tag = false;
+ continue;
+ }
+
+ if ($in_tag) {
+ continue;
+ }
+
+ if ($char === "\t") {
+ $count = $tab_width - ($pos % $tab_width);
+ $pos += $count;
+ $line[$key] = $tags[$count];
+ continue;
+ }
+
+ $pos++;
+ }
+
+ return implode('', $line);
+ }
}
diff --git a/src/applications/differential/render/DifferentialChangesetRenderer.php b/src/applications/differential/render/DifferentialChangesetRenderer.php
index 866ae15ac9..b7f78b5b68 100644
--- a/src/applications/differential/render/DifferentialChangesetRenderer.php
+++ b/src/applications/differential/render/DifferentialChangesetRenderer.php
@@ -1,708 +1,708 @@
<?php
abstract class DifferentialChangesetRenderer extends Phobject {
private $user;
private $changeset;
private $renderingReference;
private $renderPropertyChangeHeader;
private $isTopLevel;
private $isUndershield;
private $hunkStartLines;
private $oldLines;
private $newLines;
private $oldComments;
private $newComments;
private $oldChangesetID;
private $newChangesetID;
private $oldAttachesToNewFile;
private $newAttachesToNewFile;
private $highlightOld = array();
private $highlightNew = array();
private $codeCoverage;
private $handles;
private $markupEngine;
private $oldRender;
private $newRender;
private $originalOld;
private $originalNew;
private $gaps;
private $mask;
private $originalCharacterEncoding;
private $showEditAndReplyLinks;
private $canMarkDone;
private $objectOwnerPHID;
private $highlightingDisabled;
private $scopeEngine;
private $depthOnlyLines;
private $oldFile = false;
private $newFile = false;
abstract public function getRendererKey();
public function setShowEditAndReplyLinks($bool) {
$this->showEditAndReplyLinks = $bool;
return $this;
}
public function getShowEditAndReplyLinks() {
return $this->showEditAndReplyLinks;
}
public function setHighlightingDisabled($highlighting_disabled) {
$this->highlightingDisabled = $highlighting_disabled;
return $this;
}
public function getHighlightingDisabled() {
return $this->highlightingDisabled;
}
public function setOriginalCharacterEncoding($original_character_encoding) {
$this->originalCharacterEncoding = $original_character_encoding;
return $this;
}
public function getOriginalCharacterEncoding() {
return $this->originalCharacterEncoding;
}
public function setIsUndershield($is_undershield) {
$this->isUndershield = $is_undershield;
return $this;
}
public function getIsUndershield() {
return $this->isUndershield;
}
public function setMask($mask) {
$this->mask = $mask;
return $this;
}
protected function getMask() {
return $this->mask;
}
public function setGaps($gaps) {
$this->gaps = $gaps;
return $this;
}
protected function getGaps() {
return $this->gaps;
}
public function setDepthOnlyLines(array $lines) {
$this->depthOnlyLines = $lines;
return $this;
}
public function getDepthOnlyLines() {
return $this->depthOnlyLines;
}
public function attachOldFile(PhabricatorFile $old = null) {
$this->oldFile = $old;
return $this;
}
public function getOldFile() {
if ($this->oldFile === false) {
throw new PhabricatorDataNotAttachedException($this);
}
return $this->oldFile;
}
public function hasOldFile() {
return (bool)$this->oldFile;
}
public function attachNewFile(PhabricatorFile $new = null) {
$this->newFile = $new;
return $this;
}
public function getNewFile() {
if ($this->newFile === false) {
throw new PhabricatorDataNotAttachedException($this);
}
return $this->newFile;
}
public function hasNewFile() {
return (bool)$this->newFile;
}
public function setOriginalNew($original_new) {
$this->originalNew = $original_new;
return $this;
}
protected function getOriginalNew() {
return $this->originalNew;
}
public function setOriginalOld($original_old) {
$this->originalOld = $original_old;
return $this;
}
protected function getOriginalOld() {
return $this->originalOld;
}
public function setNewRender($new_render) {
$this->newRender = $new_render;
return $this;
}
protected function getNewRender() {
return $this->newRender;
}
public function setOldRender($old_render) {
$this->oldRender = $old_render;
return $this;
}
protected function getOldRender() {
return $this->oldRender;
}
public function setMarkupEngine(PhabricatorMarkupEngine $markup_engine) {
$this->markupEngine = $markup_engine;
return $this;
}
public function getMarkupEngine() {
return $this->markupEngine;
}
public function setHandles(array $handles) {
assert_instances_of($handles, 'PhabricatorObjectHandle');
$this->handles = $handles;
return $this;
}
protected function getHandles() {
return $this->handles;
}
public function setCodeCoverage($code_coverage) {
$this->codeCoverage = $code_coverage;
return $this;
}
protected function getCodeCoverage() {
return $this->codeCoverage;
}
public function setHighlightNew($highlight_new) {
$this->highlightNew = $highlight_new;
return $this;
}
protected function getHighlightNew() {
return $this->highlightNew;
}
public function setHighlightOld($highlight_old) {
$this->highlightOld = $highlight_old;
return $this;
}
protected function getHighlightOld() {
return $this->highlightOld;
}
public function setNewAttachesToNewFile($attaches) {
$this->newAttachesToNewFile = $attaches;
return $this;
}
protected function getNewAttachesToNewFile() {
return $this->newAttachesToNewFile;
}
public function setOldAttachesToNewFile($attaches) {
$this->oldAttachesToNewFile = $attaches;
return $this;
}
protected function getOldAttachesToNewFile() {
return $this->oldAttachesToNewFile;
}
public function setNewChangesetID($new_changeset_id) {
$this->newChangesetID = $new_changeset_id;
return $this;
}
protected function getNewChangesetID() {
return $this->newChangesetID;
}
public function setOldChangesetID($old_changeset_id) {
$this->oldChangesetID = $old_changeset_id;
return $this;
}
protected function getOldChangesetID() {
return $this->oldChangesetID;
}
public function setNewComments(array $new_comments) {
foreach ($new_comments as $line_number => $comments) {
assert_instances_of($comments, 'PhabricatorInlineCommentInterface');
}
$this->newComments = $new_comments;
return $this;
}
protected function getNewComments() {
return $this->newComments;
}
public function setOldComments(array $old_comments) {
foreach ($old_comments as $line_number => $comments) {
assert_instances_of($comments, 'PhabricatorInlineCommentInterface');
}
$this->oldComments = $old_comments;
return $this;
}
protected function getOldComments() {
return $this->oldComments;
}
public function setNewLines(array $new_lines) {
$this->newLines = $new_lines;
return $this;
}
protected function getNewLines() {
return $this->newLines;
}
public function setOldLines(array $old_lines) {
$this->oldLines = $old_lines;
return $this;
}
protected function getOldLines() {
return $this->oldLines;
}
public function setHunkStartLines(array $hunk_start_lines) {
$this->hunkStartLines = $hunk_start_lines;
return $this;
}
protected function getHunkStartLines() {
return $this->hunkStartLines;
}
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
protected function getUser() {
return $this->user;
}
public function setChangeset(DifferentialChangeset $changeset) {
$this->changeset = $changeset;
return $this;
}
protected function getChangeset() {
return $this->changeset;
}
public function setRenderingReference($rendering_reference) {
$this->renderingReference = $rendering_reference;
return $this;
}
protected function getRenderingReference() {
return $this->renderingReference;
}
public function setRenderPropertyChangeHeader($should_render) {
$this->renderPropertyChangeHeader = $should_render;
return $this;
}
private function shouldRenderPropertyChangeHeader() {
return $this->renderPropertyChangeHeader;
}
public function setIsTopLevel($is) {
$this->isTopLevel = $is;
return $this;
}
private function getIsTopLevel() {
return $this->isTopLevel;
}
public function setCanMarkDone($can_mark_done) {
$this->canMarkDone = $can_mark_done;
return $this;
}
public function getCanMarkDone() {
return $this->canMarkDone;
}
public function setObjectOwnerPHID($phid) {
$this->objectOwnerPHID = $phid;
return $this;
}
public function getObjectOwnerPHID() {
return $this->objectOwnerPHID;
}
final public function renderChangesetTable($content) {
$props = null;
if ($this->shouldRenderPropertyChangeHeader()) {
$props = $this->renderPropertyChangeHeader();
}
$notice = null;
if ($this->getIsTopLevel()) {
$force = (!$content && !$props);
$notice = $this->renderChangeTypeHeader($force);
}
$undershield = null;
if ($this->getIsUndershield()) {
$undershield = $this->renderUndershieldHeader();
}
- $result = $notice.$props.$undershield.$content;
-
- // TODO: Let the user customize their tab width / display style.
- // TODO: We should possibly post-process "\r" as well.
- // TODO: Both these steps should happen earlier.
- $result = str_replace("\t", ' ', $result);
+ $result = array(
+ $notice,
+ $props,
+ $undershield,
+ $content,
+ );
- return phutil_safe_html($result);
+ return hsprintf('%s', $result);
}
abstract public function isOneUpRenderer();
abstract public function renderTextChange(
$range_start,
$range_len,
$rows);
abstract public function renderFileChange(
$old = null,
$new = null,
$id = 0,
$vs = 0);
abstract protected function renderChangeTypeHeader($force);
abstract protected function renderUndershieldHeader();
protected function didRenderChangesetTableContents($contents) {
return $contents;
}
/**
* Render a "shield" over the diff, with a message like "This file is
* generated and does not need to be reviewed." or "This file was completely
* deleted." This UI element hides unimportant text so the reviewer doesn't
* need to scroll past it.
*
* The shield includes a link to view the underlying content. This link
* may force certain rendering modes when the link is clicked:
*
* - `"default"`: Render the diff normally, as though it was not
* shielded. This is the default and appropriate if the underlying
* diff is a normal change, but was hidden for reasons of not being
* important (e.g., generated code).
* - `"text"`: Force the text to be shown. This is probably only relevant
* when a file is not changed.
* - `"none"`: Don't show the link (e.g., text not available).
*
* @param string Message explaining why the diff is hidden.
* @param string|null Force mode, see above.
* @return string Shield markup.
*/
abstract public function renderShield($message, $force = 'default');
abstract protected function renderPropertyChangeHeader();
protected function buildPrimitives($range_start, $range_len) {
$primitives = array();
$hunk_starts = $this->getHunkStartLines();
$mask = $this->getMask();
$gaps = $this->getGaps();
$old = $this->getOldLines();
$new = $this->getNewLines();
$old_render = $this->getOldRender();
$new_render = $this->getNewRender();
$old_comments = $this->getOldComments();
$new_comments = $this->getNewComments();
$size = count($old);
for ($ii = $range_start; $ii < $range_start + $range_len; $ii++) {
if (empty($mask[$ii])) {
list($top, $len) = array_pop($gaps);
$primitives[] = array(
'type' => 'context',
'top' => $top,
'len' => $len,
);
$ii += ($len - 1);
continue;
}
$ospec = array(
'type' => 'old',
'htype' => null,
'cursor' => $ii,
'line' => null,
'oline' => null,
'render' => null,
);
$nspec = array(
'type' => 'new',
'htype' => null,
'cursor' => $ii,
'line' => null,
'oline' => null,
'render' => null,
'copy' => null,
'coverage' => null,
);
if (isset($old[$ii])) {
$ospec['line'] = (int)$old[$ii]['line'];
$nspec['oline'] = (int)$old[$ii]['line'];
$ospec['htype'] = $old[$ii]['type'];
if (isset($old_render[$ii])) {
$ospec['render'] = $old_render[$ii];
}
}
if (isset($new[$ii])) {
$nspec['line'] = (int)$new[$ii]['line'];
$ospec['oline'] = (int)$new[$ii]['line'];
$nspec['htype'] = $new[$ii]['type'];
if (isset($new_render[$ii])) {
$nspec['render'] = $new_render[$ii];
}
}
if (isset($hunk_starts[$ospec['line']])) {
$primitives[] = array(
'type' => 'no-context',
);
}
$primitives[] = $ospec;
$primitives[] = $nspec;
if ($ospec['line'] !== null && isset($old_comments[$ospec['line']])) {
foreach ($old_comments[$ospec['line']] as $comment) {
$primitives[] = array(
'type' => 'inline',
'comment' => $comment,
'right' => false,
);
}
}
if ($nspec['line'] !== null && isset($new_comments[$nspec['line']])) {
foreach ($new_comments[$nspec['line']] as $comment) {
$primitives[] = array(
'type' => 'inline',
'comment' => $comment,
'right' => true,
);
}
}
if ($hunk_starts && ($ii == $size - 1)) {
$primitives[] = array(
'type' => 'no-context',
);
}
}
if ($this->isOneUpRenderer()) {
$primitives = $this->processPrimitivesForOneUp($primitives);
}
return $primitives;
}
private function processPrimitivesForOneUp(array $primitives) {
// Primitives come out of buildPrimitives() in two-up format, because it
// is the most general, flexible format. To put them into one-up format,
// we need to filter and reorder them. In particular:
//
// - We discard unchanged lines in the old file; in one-up format, we
// render them only once.
// - We group contiguous blocks of old-modified and new-modified lines, so
// they render in "block of old, block of new" order instead of
// alternating old and new lines.
$out = array();
$old_buf = array();
$new_buf = array();
foreach ($primitives as $primitive) {
$type = $primitive['type'];
if ($type == 'old') {
if (!$primitive['htype']) {
// This is a line which appears in both the old file and the new
// file, or the spacer corresponding to a line added in the new file.
// Ignore it when rendering a one-up diff.
continue;
}
$old_buf[] = $primitive;
} else if ($type == 'new') {
if ($primitive['line'] === null) {
// This is an empty spacer corresponding to a line removed from the
// old file. Ignore it when rendering a one-up diff.
continue;
}
if (!$primitive['htype']) {
// If this line is the same in both versions of the file, put it in
// the old line buffer. This makes sure inlines on old, unchanged
// lines end up in the right place.
// First, we need to flush the line buffers if they're not empty.
if ($old_buf) {
$out[] = $old_buf;
$old_buf = array();
}
if ($new_buf) {
$out[] = $new_buf;
$new_buf = array();
}
$old_buf[] = $primitive;
} else {
$new_buf[] = $primitive;
}
} else if ($type == 'context' || $type == 'no-context') {
$out[] = $old_buf;
$out[] = $new_buf;
$old_buf = array();
$new_buf = array();
$out[] = array($primitive);
} else if ($type == 'inline') {
// If this inline is on the left side, put it after the old lines.
if (!$primitive['right']) {
$out[] = $old_buf;
$out[] = array($primitive);
$old_buf = array();
} else {
$out[] = $old_buf;
$out[] = $new_buf;
$out[] = array($primitive);
$old_buf = array();
$new_buf = array();
}
} else {
throw new Exception(pht("Unknown primitive type '%s'!", $primitive));
}
}
$out[] = $old_buf;
$out[] = $new_buf;
$out = array_mergev($out);
return $out;
}
protected function getChangesetProperties($changeset) {
$old = $changeset->getOldProperties();
$new = $changeset->getNewProperties();
// When adding files, don't show the uninteresting 644 filemode change.
if ($changeset->getChangeType() == DifferentialChangeType::TYPE_ADD &&
$new == array('unix:filemode' => '100644')) {
unset($new['unix:filemode']);
}
// Likewise when removing files.
if ($changeset->getChangeType() == DifferentialChangeType::TYPE_DELETE &&
$old == array('unix:filemode' => '100644')) {
unset($old['unix:filemode']);
}
$metadata = $changeset->getMetadata();
if ($this->hasOldFile()) {
$file = $this->getOldFile();
if ($file->getImageWidth()) {
$dimensions = $file->getImageWidth().'x'.$file->getImageHeight();
$old['file:dimensions'] = $dimensions;
}
$old['file:mimetype'] = $file->getMimeType();
$old['file:size'] = phutil_format_bytes($file->getByteSize());
} else {
$old['file:mimetype'] = idx($metadata, 'old:file:mime-type');
$size = idx($metadata, 'old:file:size');
if ($size !== null) {
$old['file:size'] = phutil_format_bytes($size);
}
}
if ($this->hasNewFile()) {
$file = $this->getNewFile();
if ($file->getImageWidth()) {
$dimensions = $file->getImageWidth().'x'.$file->getImageHeight();
$new['file:dimensions'] = $dimensions;
}
$new['file:mimetype'] = $file->getMimeType();
$new['file:size'] = phutil_format_bytes($file->getByteSize());
} else {
$new['file:mimetype'] = idx($metadata, 'new:file:mime-type');
$size = idx($metadata, 'new:file:size');
if ($size !== null) {
$new['file:size'] = phutil_format_bytes($size);
}
}
return array($old, $new);
}
public function renderUndoTemplates() {
$views = array(
'l' => id(new PHUIDiffInlineCommentUndoView())->setIsOnRight(false),
'r' => id(new PHUIDiffInlineCommentUndoView())->setIsOnRight(true),
);
foreach ($views as $key => $view) {
$scaffold = $this->getRowScaffoldForInline($view);
$views[$key] = id(new PHUIDiffInlineCommentTableScaffold())
->addRowScaffold($scaffold);
}
return $views;
}
final protected function getScopeEngine() {
if (!$this->scopeEngine) {
$line_map = $this->getNewLineTextMap();
$scope_engine = id(new PhabricatorDiffScopeEngine())
->setLineTextMap($line_map);
$this->scopeEngine = $scope_engine;
}
return $this->scopeEngine;
}
private function getNewLineTextMap() {
$new = $this->getNewLines();
$text_map = array();
foreach ($new as $new_line) {
if (!isset($new_line['line'])) {
continue;
}
$text_map[$new_line['line']] = $new_line['text'];
}
return $text_map;
}
}
diff --git a/src/applications/differential/render/DifferentialChangesetTestRenderer.php b/src/applications/differential/render/DifferentialChangesetTestRenderer.php
index c7b35d1fb4..e2bd3f53ed 100644
--- a/src/applications/differential/render/DifferentialChangesetTestRenderer.php
+++ b/src/applications/differential/render/DifferentialChangesetTestRenderer.php
@@ -1,147 +1,147 @@
<?php
abstract class DifferentialChangesetTestRenderer
extends DifferentialChangesetRenderer {
protected function renderChangeTypeHeader($force) {
$changeset = $this->getChangeset();
$old = nonempty($changeset->getOldFile(), '-');
$current = nonempty($changeset->getFilename(), '-');
$away = nonempty(implode(', ', $changeset->getAwayPaths()), '-');
$ctype = $changeset->getChangeType();
$ftype = $changeset->getFileType();
$force = ($force ? '(forced)' : '(unforced)');
return "CTYPE {$ctype} {$ftype} {$force}\n".
"{$old}\n".
"{$current}\n".
"{$away}\n";
}
protected function renderUndershieldHeader() {
return null;
}
public function renderShield($message, $force = 'default') {
return "SHIELD ({$force}) {$message}\n";
}
protected function renderPropertyChangeHeader() {
$changeset = $this->getChangeset();
list($old, $new) = $this->getChangesetProperties($changeset);
foreach (array_keys($old) as $key) {
if ($old[$key] === idx($new, $key)) {
unset($old[$key]);
unset($new[$key]);
}
}
if (!$old && !$new) {
return null;
}
$props = '';
foreach ($old as $key => $value) {
$props .= "P - {$key} {$value}~\n";
}
foreach ($new as $key => $value) {
$props .= "P + {$key} {$value}~\n";
}
return "PROPERTIES\n".$props;
}
public function renderTextChange(
$range_start,
$range_len,
$rows) {
$out = array();
$any_old = false;
$any_new = false;
$primitives = $this->buildPrimitives($range_start, $range_len);
foreach ($primitives as $p) {
$type = $p['type'];
switch ($type) {
case 'old':
case 'new':
if ($type == 'old') {
$any_old = true;
}
if ($type == 'new') {
$any_new = true;
}
$num = nonempty($p['line'], '-');
$render = $p['render'];
$htype = nonempty($p['htype'], '.');
// TODO: This should probably happen earlier, whenever we deal with
// \r and \t normalization?
$render = str_replace(
array(
"\r",
"\n",
),
array(
'\\r',
'\\n',
),
$render);
$render = str_replace(
array(
'<span class="bright">',
'</span>',
'<span class="depth-out">',
'<span class="depth-in">',
),
array(
'{(',
')}',
'{<',
'{>',
),
$render);
$render = html_entity_decode($render, ENT_QUOTES);
$t = ($type == 'old') ? 'O' : 'N';
$out[] = "{$t} {$num} {$htype} {$render}~";
break;
case 'no-context':
$out[] = 'X <MISSING-CONTEXT>';
break;
default:
$out[] = $type;
break;
}
}
if (!$any_old) {
$out[] = 'O X <EMPTY>';
}
if (!$any_new) {
$out[] = 'N X <EMPTY>';
}
$out = implode("\n", $out)."\n";
- return $out;
+ return phutil_safe_html($out);
}
public function renderFileChange(
$old_file = null,
$new_file = null,
$id = 0,
$vs = 0) {
throw new PhutilMethodNotImplementedException();
}
}
diff --git a/webroot/rsrc/css/core/syntax.css b/webroot/rsrc/css/core/syntax.css
index cfc82da09b..90f2981ba6 100644
--- a/webroot/rsrc/css/core/syntax.css
+++ b/webroot/rsrc/css/core/syntax.css
@@ -1,31 +1,37 @@
/**
* @provides syntax-highlighting-css
* @requires syntax-default-css
*/
.remarkup-code .uu { /* Forbidden Unicode */
color: #aa0066;
}
.remarkup-code td > span {
display: inline;
word-break: break-all;
}
.remarkup-code .rbw_r { color: red; }
.remarkup-code .rbw_o { color: orange; }
.remarkup-code .rbw_y { color: yellow; }
.remarkup-code .rbw_g { color: green; }
.remarkup-code .rbw_b { color: blue; }
.remarkup-code .rbw_i { color: indigo; }
.remarkup-code .rbw_v { color: violet; }
span.crossreference-item {
background: {$lightyellow};
border-bottom: 1px solid {$yellow};
cursor: help;
}
.remarkup-code .invisible {
color: #222222;
background: #dddddd;
}
+
+.suspicious-character {
+ background: #ff7700;
+ color: #ffffff;
+ cursor: default;
+}
diff --git a/webroot/rsrc/js/core/behavior-oncopy.js b/webroot/rsrc/js/core/behavior-oncopy.js
index e7309a1e18..b56e83ab32 100644
--- a/webroot/rsrc/js/core/behavior-oncopy.js
+++ b/webroot/rsrc/js/core/behavior-oncopy.js
@@ -1,315 +1,322 @@
/**
* @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 = [];
for (var ii = 0; ii < ranges.length; ii++) {
var range = ranges[ii];
var fragment = range.cloneContents();
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];
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;
}
// 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;
}
// 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]));
}
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.getAttribute) {
+ var copy_text = node.getAttribute('data-copy-text');
+ if (copy_text) {
+ return copy_text;
+ }
+ }
+
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
Wed, Jul 23, 10:46 PM (21 h, 34 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
182378
Default Alt Text
(167 KB)

Event Timeline