diff --git a/scripts/celerity_mapper.php b/scripts/celerity_mapper.php
index 1172c93a98..ab37b3482e 100755
--- a/scripts/celerity_mapper.php
+++ b/scripts/celerity_mapper.php
@@ -56,6 +56,7 @@ $package_spec = array(
'javelin-behavior-phabricator-remarkup-assist',
'phabricator-textareautils',
'phabricator-file-upload',
+ 'javelin-behavior-global-drag-and-drop',
),
'core.pkg.css' => array(
'phabricator-core-css',
@@ -100,6 +101,7 @@ $package_spec = array(
'phabricator-side-menu-view-css',
'phabricator-crumbs-view-css',
'phabricator-object-item-list-view-css',
+ 'global-drag-and-drop-css',
),
'differential.pkg.css' => array(
diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php
index bcaae2417b..4392213523 100644
--- a/src/__celerity_resource_map__.php
+++ b/src/__celerity_resource_map__.php
@@ -478,15 +478,15 @@ celerity_register_resource_map(array(
),
'/rsrc/image/sprite-apps-large-X2.png' =>
array(
- 'hash' => 'dffe647d8d8ddfab9b2f5f870b4e40b4',
- 'uri' => '/res/dffe647d/rsrc/image/sprite-apps-large-X2.png',
+ 'hash' => 'f1218e52784088e7aabdb2744bda2cc3',
+ 'uri' => '/res/f1218e52/rsrc/image/sprite-apps-large-X2.png',
'disk' => '/rsrc/image/sprite-apps-large-X2.png',
'type' => 'png',
),
'/rsrc/image/sprite-apps-large.png' =>
array(
- 'hash' => 'cc0d81cafa185c541350a9db6f3befb7',
- 'uri' => '/res/cc0d81ca/rsrc/image/sprite-apps-large.png',
+ 'hash' => 'f19222adadaddd0dd7e12bcd1b1fdba9',
+ 'uri' => '/res/f19222ad/rsrc/image/sprite-apps-large.png',
'disk' => '/rsrc/image/sprite-apps-large.png',
'type' => 'png',
),
@@ -513,15 +513,15 @@ celerity_register_resource_map(array(
),
'/rsrc/image/sprite-icon-X2.png' =>
array(
- 'hash' => 'd4c36b33f4961bdcf63a0cc0bb4ecb1e',
- 'uri' => '/res/d4c36b33/rsrc/image/sprite-icon-X2.png',
+ 'hash' => 'c9fae25bc6221922ce26517e654a18e4',
+ 'uri' => '/res/c9fae25b/rsrc/image/sprite-icon-X2.png',
'disk' => '/rsrc/image/sprite-icon-X2.png',
'type' => 'png',
),
'/rsrc/image/sprite-icon.png' =>
array(
- 'hash' => 'b29f5d1d2781b6589946bd73734100f1',
- 'uri' => '/res/b29f5d1d/rsrc/image/sprite-icon.png',
+ 'hash' => 'b690ea69bf5f2abe84d0a6e9ef64b03d',
+ 'uri' => '/res/b690ea69/rsrc/image/sprite-icon.png',
'disk' => '/rsrc/image/sprite-icon.png',
'type' => 'png',
),
@@ -690,7 +690,7 @@ celerity_register_resource_map(array(
),
'aphront-table-view-css' =>
array(
- 'uri' => '/res/732d5e1f/rsrc/css/aphront/table-view.css',
+ 'uri' => '/res/d2cd4818/rsrc/css/aphront/table-view.css',
'type' => 'css',
'requires' =>
array(
@@ -760,7 +760,7 @@ celerity_register_resource_map(array(
),
'differential-local-commits-view-css' =>
array(
- 'uri' => '/res/86432ba7/rsrc/css/application/differential/local-commits-view.css',
+ 'uri' => '/res/224f3703/rsrc/css/application/differential/local-commits-view.css',
'type' => 'css',
'requires' =>
array(
@@ -805,7 +805,7 @@ celerity_register_resource_map(array(
),
'differential-revision-history-css' =>
array(
- 'uri' => '/res/71cffe43/rsrc/css/application/differential/revision-history.css',
+ 'uri' => '/res/d41bc64c/rsrc/css/application/differential/revision-history.css',
'type' => 'css',
'requires' =>
array(
@@ -857,14 +857,14 @@ celerity_register_resource_map(array(
),
'disk' => '/rsrc/css/application/diffusion/diffusion-source.css',
),
- 'files-css' =>
+ 'global-drag-and-drop-css' =>
array(
- 'uri' => '/res/a265a77d/rsrc/css/application/files/files.css',
+ 'uri' => '/res/f3fb4a79/rsrc/css/application/files/global-drag-and-drop.css',
'type' => 'css',
'requires' =>
array(
),
- 'disk' => '/rsrc/css/application/files/files.css',
+ 'disk' => '/rsrc/css/application/files/global-drag-and-drop.css',
),
'herald-css' =>
array(
@@ -978,20 +978,19 @@ celerity_register_resource_map(array(
),
'javelin-behavior-aphront-drag-and-drop' =>
array(
- 'uri' => '/res/0910fc0a/rsrc/js/application/core/behavior-drag-and-drop.js',
+ 'uri' => '/res/84a67d72/rsrc/js/application/core/behavior-drag-and-drop.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-dom',
- 2 => 'javelin-util',
- 3 => 'phabricator-drag-and-drop-file-upload',
+ 2 => 'phabricator-drag-and-drop-file-upload',
),
'disk' => '/rsrc/js/application/core/behavior-drag-and-drop.js',
),
'javelin-behavior-aphront-drag-and-drop-textarea' =>
array(
- 'uri' => '/res/ad737ce4/rsrc/js/application/core/behavior-drag-and-drop-textarea.js',
+ 'uri' => '/res/853e33b9/rsrc/js/application/core/behavior-drag-and-drop-textarea.js',
'type' => 'js',
'requires' =>
array(
@@ -1363,18 +1362,19 @@ celerity_register_resource_map(array(
),
'disk' => '/rsrc/js/application/core/behavior-fancy-datepicker.js',
),
- 'javelin-behavior-files-drag-and-drop' =>
+ 'javelin-behavior-global-drag-and-drop' =>
array(
- 'uri' => '/res/4eb2f339/rsrc/js/application/core/behavior-files-drag-and-drop.js',
+ 'uri' => '/res/73ae3fd1/rsrc/js/application/core/behavior-global-drag-and-drop.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-dom',
2 => 'javelin-uri',
- 3 => 'phabricator-drag-and-drop-file-upload',
+ 3 => 'javelin-mask',
+ 4 => 'phabricator-drag-and-drop-file-upload',
),
- 'disk' => '/rsrc/js/application/core/behavior-files-drag-and-drop.js',
+ 'disk' => '/rsrc/js/application/core/behavior-global-drag-and-drop.js',
),
'javelin-behavior-herald-rule-editor' =>
array(
@@ -2473,7 +2473,7 @@ celerity_register_resource_map(array(
),
'phabricator-drag-and-drop-file-upload' =>
array(
- 'uri' => '/res/496110e1/rsrc/js/application/core/DragAndDropFileUpload.js',
+ 'uri' => '/res/ce71f19a/rsrc/js/application/core/DragAndDropFileUpload.js',
'type' => 'js',
'requires' =>
array(
@@ -2753,7 +2753,7 @@ celerity_register_resource_map(array(
),
'phabricator-property-list-view-css' =>
array(
- 'uri' => '/res/9f4d6640/rsrc/css/layout/phabricator-property-list-view.css',
+ 'uri' => '/res/cd84ee5a/rsrc/css/layout/phabricator-property-list-view.css',
'type' => 'css',
'requires' =>
array(
@@ -2810,7 +2810,7 @@ celerity_register_resource_map(array(
),
'phabricator-source-code-view-css' =>
array(
- 'uri' => '/res/cf0c566c/rsrc/css/layout/phabricator-source-code-view.css',
+ 'uri' => '/res/aa04c202/rsrc/css/layout/phabricator-source-code-view.css',
'type' => 'css',
'requires' =>
array(
@@ -3128,7 +3128,7 @@ celerity_register_resource_map(array(
),
'sprite-apps-large-css' =>
array(
- 'uri' => '/res/3629ff92/rsrc/css/sprite-apps-large.css',
+ 'uri' => '/res/250ebd13/rsrc/css/sprite-apps-large.css',
'type' => 'css',
'requires' =>
array(
@@ -3155,7 +3155,7 @@ celerity_register_resource_map(array(
),
'sprite-icon-css' =>
array(
- 'uri' => '/res/2e174787/rsrc/css/sprite-icon.css',
+ 'uri' => '/res/ff841245/rsrc/css/sprite-icon.css',
'type' => 'css',
'requires' =>
array(
@@ -3201,7 +3201,7 @@ celerity_register_resource_map(array(
), array(
'packages' =>
array(
- 'e6ad9cda' =>
+ 'a5058778' =>
array(
'name' => 'core.pkg.css',
'symbols' =>
@@ -3244,11 +3244,12 @@ celerity_register_resource_map(array(
35 => 'phabricator-side-menu-view-css',
36 => 'phabricator-crumbs-view-css',
37 => 'phabricator-object-item-list-view-css',
+ 38 => 'global-drag-and-drop-css',
),
- 'uri' => '/res/pkg/e6ad9cda/core.pkg.css',
+ 'uri' => '/res/pkg/a5058778/core.pkg.css',
'type' => 'css',
),
- 'ba3c323b' =>
+ '70c8162b' =>
array(
'name' => 'core.pkg.js',
'symbols' =>
@@ -3284,8 +3285,9 @@ celerity_register_resource_map(array(
28 => 'javelin-behavior-phabricator-remarkup-assist',
29 => 'phabricator-textareautils',
30 => 'phabricator-file-upload',
+ 31 => 'javelin-behavior-global-drag-and-drop',
),
- 'uri' => '/res/pkg/ba3c323b/core.pkg.js',
+ 'uri' => '/res/pkg/70c8162b/core.pkg.js',
'type' => 'js',
),
'8edbada5' =>
@@ -3300,7 +3302,7 @@ celerity_register_resource_map(array(
'uri' => '/res/pkg/8edbada5/darkconsole.pkg.js',
'type' => 'js',
),
- '94cb8965' =>
+ '380df740' =>
array(
'name' => 'differential.pkg.css',
'symbols' =>
@@ -3320,10 +3322,10 @@ celerity_register_resource_map(array(
12 => 'differential-local-commits-view-css',
13 => 'inline-comment-summary-css',
),
- 'uri' => '/res/pkg/94cb8965/differential.pkg.css',
+ 'uri' => '/res/pkg/380df740/differential.pkg.css',
'type' => 'css',
),
- '7ecd31fa' =>
+ 'a8e8f2b7' =>
array(
'name' => 'differential.pkg.js',
'symbols' =>
@@ -3348,7 +3350,7 @@ celerity_register_resource_map(array(
17 => 'javelin-behavior-differential-toggle-files',
18 => 'javelin-behavior-differential-user-select',
),
- 'uri' => '/res/pkg/7ecd31fa/differential.pkg.js',
+ 'uri' => '/res/pkg/a8e8f2b7/differential.pkg.js',
'type' => 'js',
),
'c8ce2d88' =>
@@ -3433,82 +3435,84 @@ celerity_register_resource_map(array(
'reverse' =>
array(
'aphront-attached-file-view-css' => '7839ae2d',
- 'aphront-crumbs-view-css' => 'e6ad9cda',
- 'aphront-dialog-view-css' => 'e6ad9cda',
- 'aphront-error-view-css' => 'e6ad9cda',
- 'aphront-form-view-css' => 'e6ad9cda',
- 'aphront-headsup-action-list-view-css' => '94cb8965',
- 'aphront-headsup-view-css' => 'e6ad9cda',
- 'aphront-list-filter-view-css' => 'e6ad9cda',
- 'aphront-pager-view-css' => 'e6ad9cda',
- 'aphront-panel-view-css' => 'e6ad9cda',
- 'aphront-side-nav-view-css' => 'e6ad9cda',
- 'aphront-table-view-css' => 'e6ad9cda',
- 'aphront-tokenizer-control-css' => 'e6ad9cda',
- 'aphront-tooltip-css' => 'e6ad9cda',
- 'aphront-typeahead-control-css' => 'e6ad9cda',
- 'differential-changeset-view-css' => '94cb8965',
- 'differential-core-view-css' => '94cb8965',
- 'differential-inline-comment-editor' => '7ecd31fa',
- 'differential-local-commits-view-css' => '94cb8965',
- 'differential-results-table-css' => '94cb8965',
- 'differential-revision-add-comment-css' => '94cb8965',
- 'differential-revision-comment-css' => '94cb8965',
- 'differential-revision-comment-list-css' => '94cb8965',
- 'differential-revision-history-css' => '94cb8965',
- 'differential-revision-list-css' => '94cb8965',
- 'differential-table-of-contents-css' => '94cb8965',
+ 'aphront-crumbs-view-css' => 'a5058778',
+ 'aphront-dialog-view-css' => 'a5058778',
+ 'aphront-error-view-css' => 'a5058778',
+ 'aphront-form-view-css' => 'a5058778',
+ 'aphront-headsup-action-list-view-css' => '380df740',
+ 'aphront-headsup-view-css' => 'a5058778',
+ 'aphront-list-filter-view-css' => 'a5058778',
+ 'aphront-pager-view-css' => 'a5058778',
+ 'aphront-panel-view-css' => 'a5058778',
+ 'aphront-side-nav-view-css' => 'a5058778',
+ 'aphront-table-view-css' => 'a5058778',
+ 'aphront-tokenizer-control-css' => 'a5058778',
+ 'aphront-tooltip-css' => 'a5058778',
+ 'aphront-typeahead-control-css' => 'a5058778',
+ 'differential-changeset-view-css' => '380df740',
+ 'differential-core-view-css' => '380df740',
+ 'differential-inline-comment-editor' => 'a8e8f2b7',
+ 'differential-local-commits-view-css' => '380df740',
+ 'differential-results-table-css' => '380df740',
+ 'differential-revision-add-comment-css' => '380df740',
+ 'differential-revision-comment-css' => '380df740',
+ 'differential-revision-comment-list-css' => '380df740',
+ 'differential-revision-history-css' => '380df740',
+ 'differential-revision-list-css' => '380df740',
+ 'differential-table-of-contents-css' => '380df740',
'diffusion-commit-view-css' => 'c8ce2d88',
'diffusion-icons-css' => 'c8ce2d88',
- 'inline-comment-summary-css' => '94cb8965',
- 'javelin-aphlict' => 'ba3c323b',
+ 'global-drag-and-drop-css' => 'a5058778',
+ 'inline-comment-summary-css' => '380df740',
+ 'javelin-aphlict' => '70c8162b',
'javelin-behavior' => 'db6d724d',
- 'javelin-behavior-aphlict-dropdown' => 'ba3c323b',
- 'javelin-behavior-aphlict-listen' => 'ba3c323b',
- 'javelin-behavior-aphront-basic-tokenizer' => 'ba3c323b',
- 'javelin-behavior-aphront-drag-and-drop' => '7ecd31fa',
- 'javelin-behavior-aphront-drag-and-drop-textarea' => '7ecd31fa',
- 'javelin-behavior-aphront-form-disable-on-submit' => 'ba3c323b',
+ 'javelin-behavior-aphlict-dropdown' => '70c8162b',
+ 'javelin-behavior-aphlict-listen' => '70c8162b',
+ 'javelin-behavior-aphront-basic-tokenizer' => '70c8162b',
+ 'javelin-behavior-aphront-drag-and-drop' => 'a8e8f2b7',
+ 'javelin-behavior-aphront-drag-and-drop-textarea' => 'a8e8f2b7',
+ 'javelin-behavior-aphront-form-disable-on-submit' => '70c8162b',
'javelin-behavior-audit-preview' => '5e68be89',
'javelin-behavior-dark-console' => '8edbada5',
'javelin-behavior-dark-console-ajax' => '8edbada5',
- 'javelin-behavior-device' => 'ba3c323b',
- 'javelin-behavior-differential-accept-with-errors' => '7ecd31fa',
- 'javelin-behavior-differential-add-reviewers-and-ccs' => '7ecd31fa',
- 'javelin-behavior-differential-comment-jump' => '7ecd31fa',
- 'javelin-behavior-differential-diff-radios' => '7ecd31fa',
- 'javelin-behavior-differential-dropdown-menus' => '7ecd31fa',
- 'javelin-behavior-differential-edit-inline-comments' => '7ecd31fa',
- 'javelin-behavior-differential-feedback-preview' => '7ecd31fa',
- 'javelin-behavior-differential-keyboard-navigation' => '7ecd31fa',
- 'javelin-behavior-differential-populate' => '7ecd31fa',
- 'javelin-behavior-differential-show-more' => '7ecd31fa',
- 'javelin-behavior-differential-toggle-files' => '7ecd31fa',
- 'javelin-behavior-differential-user-select' => '7ecd31fa',
+ 'javelin-behavior-device' => '70c8162b',
+ 'javelin-behavior-differential-accept-with-errors' => 'a8e8f2b7',
+ 'javelin-behavior-differential-add-reviewers-and-ccs' => 'a8e8f2b7',
+ 'javelin-behavior-differential-comment-jump' => 'a8e8f2b7',
+ 'javelin-behavior-differential-diff-radios' => 'a8e8f2b7',
+ 'javelin-behavior-differential-dropdown-menus' => 'a8e8f2b7',
+ 'javelin-behavior-differential-edit-inline-comments' => 'a8e8f2b7',
+ 'javelin-behavior-differential-feedback-preview' => 'a8e8f2b7',
+ 'javelin-behavior-differential-keyboard-navigation' => 'a8e8f2b7',
+ 'javelin-behavior-differential-populate' => 'a8e8f2b7',
+ 'javelin-behavior-differential-show-more' => 'a8e8f2b7',
+ 'javelin-behavior-differential-toggle-files' => 'a8e8f2b7',
+ 'javelin-behavior-differential-user-select' => 'a8e8f2b7',
'javelin-behavior-diffusion-commit-graph' => '5e68be89',
'javelin-behavior-diffusion-pull-lastmodified' => '5e68be89',
'javelin-behavior-error-log' => '8edbada5',
- 'javelin-behavior-konami' => 'ba3c323b',
- 'javelin-behavior-lightbox-attachments' => 'ba3c323b',
+ 'javelin-behavior-global-drag-and-drop' => '70c8162b',
+ 'javelin-behavior-konami' => '70c8162b',
+ 'javelin-behavior-lightbox-attachments' => '70c8162b',
'javelin-behavior-maniphest-batch-selector' => '7707de41',
'javelin-behavior-maniphest-subpriority-editor' => '7707de41',
'javelin-behavior-maniphest-transaction-controls' => '7707de41',
'javelin-behavior-maniphest-transaction-expand' => '7707de41',
'javelin-behavior-maniphest-transaction-preview' => '7707de41',
- 'javelin-behavior-phabricator-active-nav' => 'ba3c323b',
- 'javelin-behavior-phabricator-autofocus' => 'ba3c323b',
- 'javelin-behavior-phabricator-keyboard-shortcuts' => 'ba3c323b',
- 'javelin-behavior-phabricator-nav' => 'ba3c323b',
- 'javelin-behavior-phabricator-object-selector' => '7ecd31fa',
- 'javelin-behavior-phabricator-oncopy' => 'ba3c323b',
- 'javelin-behavior-phabricator-remarkup-assist' => 'ba3c323b',
- 'javelin-behavior-phabricator-search-typeahead' => 'ba3c323b',
- 'javelin-behavior-phabricator-tooltips' => 'ba3c323b',
- 'javelin-behavior-phabricator-watch-anchor' => 'ba3c323b',
- 'javelin-behavior-refresh-csrf' => 'ba3c323b',
- 'javelin-behavior-repository-crossreference' => '7ecd31fa',
- 'javelin-behavior-toggle-class' => 'ba3c323b',
- 'javelin-behavior-workflow' => 'ba3c323b',
+ 'javelin-behavior-phabricator-active-nav' => '70c8162b',
+ 'javelin-behavior-phabricator-autofocus' => '70c8162b',
+ 'javelin-behavior-phabricator-keyboard-shortcuts' => '70c8162b',
+ 'javelin-behavior-phabricator-nav' => '70c8162b',
+ 'javelin-behavior-phabricator-object-selector' => 'a8e8f2b7',
+ 'javelin-behavior-phabricator-oncopy' => '70c8162b',
+ 'javelin-behavior-phabricator-remarkup-assist' => '70c8162b',
+ 'javelin-behavior-phabricator-search-typeahead' => '70c8162b',
+ 'javelin-behavior-phabricator-tooltips' => '70c8162b',
+ 'javelin-behavior-phabricator-watch-anchor' => '70c8162b',
+ 'javelin-behavior-refresh-csrf' => '70c8162b',
+ 'javelin-behavior-repository-crossreference' => 'a8e8f2b7',
+ 'javelin-behavior-toggle-class' => '70c8162b',
+ 'javelin-behavior-workflow' => '70c8162b',
'javelin-dom' => 'db6d724d',
'javelin-event' => 'db6d724d',
'javelin-install' => 'db6d724d',
@@ -3527,48 +3531,48 @@ celerity_register_resource_map(array(
'javelin-util' => 'db6d724d',
'javelin-vector' => 'db6d724d',
'javelin-workflow' => 'db6d724d',
- 'lightbox-attachment-css' => 'e6ad9cda',
+ 'lightbox-attachment-css' => 'a5058778',
'maniphest-task-summary-css' => '7839ae2d',
'maniphest-transaction-detail-css' => '7839ae2d',
- 'phabricator-app-buttons-css' => 'e6ad9cda',
- 'phabricator-busy' => 'ba3c323b',
- 'phabricator-content-source-view-css' => '94cb8965',
- 'phabricator-core-buttons-css' => 'e6ad9cda',
- 'phabricator-core-css' => 'e6ad9cda',
- 'phabricator-crumbs-view-css' => 'e6ad9cda',
- 'phabricator-directory-css' => 'e6ad9cda',
- 'phabricator-drag-and-drop-file-upload' => '7ecd31fa',
- 'phabricator-dropdown-menu' => 'ba3c323b',
- 'phabricator-file-upload' => 'ba3c323b',
- 'phabricator-filetree-view-css' => 'e6ad9cda',
- 'phabricator-flag-css' => 'e6ad9cda',
- 'phabricator-form-view-css' => 'e6ad9cda',
- 'phabricator-header-view-css' => 'e6ad9cda',
- 'phabricator-jump-nav' => 'e6ad9cda',
- 'phabricator-keyboard-shortcut' => 'ba3c323b',
- 'phabricator-keyboard-shortcut-manager' => 'ba3c323b',
- 'phabricator-main-menu-view' => 'e6ad9cda',
- 'phabricator-menu-item' => 'ba3c323b',
- 'phabricator-nav-view-css' => 'e6ad9cda',
- 'phabricator-notification' => 'ba3c323b',
- 'phabricator-notification-css' => 'e6ad9cda',
- 'phabricator-notification-menu-css' => 'e6ad9cda',
- 'phabricator-object-item-list-view-css' => 'e6ad9cda',
- 'phabricator-object-selector-css' => '94cb8965',
- 'phabricator-paste-file-upload' => 'ba3c323b',
- 'phabricator-prefab' => 'ba3c323b',
+ 'phabricator-app-buttons-css' => 'a5058778',
+ 'phabricator-busy' => '70c8162b',
+ 'phabricator-content-source-view-css' => '380df740',
+ 'phabricator-core-buttons-css' => 'a5058778',
+ 'phabricator-core-css' => 'a5058778',
+ 'phabricator-crumbs-view-css' => 'a5058778',
+ 'phabricator-directory-css' => 'a5058778',
+ 'phabricator-drag-and-drop-file-upload' => 'a8e8f2b7',
+ 'phabricator-dropdown-menu' => '70c8162b',
+ 'phabricator-file-upload' => '70c8162b',
+ 'phabricator-filetree-view-css' => 'a5058778',
+ 'phabricator-flag-css' => 'a5058778',
+ 'phabricator-form-view-css' => 'a5058778',
+ 'phabricator-header-view-css' => 'a5058778',
+ 'phabricator-jump-nav' => 'a5058778',
+ 'phabricator-keyboard-shortcut' => '70c8162b',
+ 'phabricator-keyboard-shortcut-manager' => '70c8162b',
+ 'phabricator-main-menu-view' => 'a5058778',
+ 'phabricator-menu-item' => '70c8162b',
+ 'phabricator-nav-view-css' => 'a5058778',
+ 'phabricator-notification' => '70c8162b',
+ 'phabricator-notification-css' => 'a5058778',
+ 'phabricator-notification-menu-css' => 'a5058778',
+ 'phabricator-object-item-list-view-css' => 'a5058778',
+ 'phabricator-object-selector-css' => '380df740',
+ 'phabricator-paste-file-upload' => '70c8162b',
+ 'phabricator-prefab' => '70c8162b',
'phabricator-project-tag-css' => '7839ae2d',
- 'phabricator-remarkup-css' => 'e6ad9cda',
- 'phabricator-shaped-request' => '7ecd31fa',
- 'phabricator-side-menu-view-css' => 'e6ad9cda',
- 'phabricator-standard-page-view' => 'e6ad9cda',
- 'phabricator-textareautils' => 'ba3c323b',
- 'phabricator-tooltip' => 'ba3c323b',
- 'phabricator-transaction-view-css' => 'e6ad9cda',
- 'sprite-apps-large-css' => 'e6ad9cda',
- 'sprite-gradient-css' => 'e6ad9cda',
- 'sprite-icon-css' => 'e6ad9cda',
- 'sprite-menu-css' => 'e6ad9cda',
- 'syntax-highlighting-css' => 'e6ad9cda',
+ 'phabricator-remarkup-css' => 'a5058778',
+ 'phabricator-shaped-request' => 'a8e8f2b7',
+ 'phabricator-side-menu-view-css' => 'a5058778',
+ 'phabricator-standard-page-view' => 'a5058778',
+ 'phabricator-textareautils' => '70c8162b',
+ 'phabricator-tooltip' => '70c8162b',
+ 'phabricator-transaction-view-css' => 'a5058778',
+ 'sprite-apps-large-css' => 'a5058778',
+ 'sprite-gradient-css' => 'a5058778',
+ 'sprite-icon-css' => 'a5058778',
+ 'sprite-menu-css' => 'a5058778',
+ 'syntax-highlighting-css' => 'a5058778',
),
));
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
index a2c5a03c02..1b8324a2e1 100644
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -788,7 +788,6 @@ phutil_register_library_map(array(
'PhabricatorFileTransformController' => 'applications/files/controller/PhabricatorFileTransformController.php',
'PhabricatorFileUploadController' => 'applications/files/controller/PhabricatorFileUploadController.php',
'PhabricatorFileUploadException' => 'applications/files/exception/PhabricatorFileUploadException.php',
- 'PhabricatorFileUploadView' => 'applications/files/view/PhabricatorFileUploadView.php',
'PhabricatorFilesManagementEnginesWorkflow' => 'applications/files/management/PhabricatorFilesManagementEnginesWorkflow.php',
'PhabricatorFilesManagementMigrateWorkflow' => 'applications/files/management/PhabricatorFilesManagementMigrateWorkflow.php',
'PhabricatorFilesManagementWorkflow' => 'applications/files/management/PhabricatorFilesManagementWorkflow.php',
@@ -807,6 +806,7 @@ phutil_register_library_map(array(
'PhabricatorGarbageCollectorDaemon' => 'infrastructure/daemon/PhabricatorGarbageCollectorDaemon.php',
'PhabricatorGitGraphStream' => 'applications/repository/daemon/PhabricatorGitGraphStream.php',
'PhabricatorGlobalLock' => 'infrastructure/util/PhabricatorGlobalLock.php',
+ 'PhabricatorGlobalUploadTargetView' => 'applications/files/view/PhabricatorGlobalUploadTargetView.php',
'PhabricatorHandleObjectSelectorDataView' => 'applications/phid/handle/view/PhabricatorHandleObjectSelectorDataView.php',
'PhabricatorHash' => 'infrastructure/util/PhabricatorHash.php',
'PhabricatorHeaderView' => 'view/layout/PhabricatorHeaderView.php',
@@ -2070,7 +2070,6 @@ phutil_register_library_map(array(
'PhabricatorFileTransformController' => 'PhabricatorFileController',
'PhabricatorFileUploadController' => 'PhabricatorFileController',
'PhabricatorFileUploadException' => 'Exception',
- 'PhabricatorFileUploadView' => 'AphrontView',
'PhabricatorFilesManagementEnginesWorkflow' => 'PhabricatorFilesManagementWorkflow',
'PhabricatorFilesManagementMigrateWorkflow' => 'PhabricatorFilesManagementWorkflow',
'PhabricatorFilesManagementWorkflow' => 'PhutilArgumentWorkflow',
@@ -2086,6 +2085,7 @@ phutil_register_library_map(array(
'PhabricatorFormExample' => 'PhabricatorUIExample',
'PhabricatorGarbageCollectorDaemon' => 'PhabricatorDaemon',
'PhabricatorGlobalLock' => 'PhutilLock',
+ 'PhabricatorGlobalUploadTargetView' => 'AphrontView',
'PhabricatorHeaderView' => 'AphrontView',
'PhabricatorHelpController' => 'PhabricatorController',
'PhabricatorHelpKeyboardShortcutController' => 'PhabricatorHelpController',
diff --git a/src/applications/directory/controller/PhabricatorDirectoryMainController.php b/src/applications/directory/controller/PhabricatorDirectoryMainController.php
index 1aa2a86819..b5a5d53b51 100644
--- a/src/applications/directory/controller/PhabricatorDirectoryMainController.php
+++ b/src/applications/directory/controller/PhabricatorDirectoryMainController.php
@@ -76,6 +76,7 @@ final class PhabricatorDirectoryMainController
);
$nav->appendChild($content);
+ $nav->appendChild(new PhabricatorGlobalUploadTargetView());
return $this->buildStandardPageResponse(
$nav,
@@ -491,7 +492,7 @@ final class PhabricatorDirectoryMainController
$nav_buttons[] = array(
'Upload File',
- '/file/',
+ '/file/upload/',
'upload-file',
'Share Files');
$nav_buttons[] = array(
diff --git a/src/applications/files/controller/PhabricatorFileController.php b/src/applications/files/controller/PhabricatorFileController.php
index a850f8c847..9c6a1a68b1 100644
--- a/src/applications/files/controller/PhabricatorFileController.php
+++ b/src/applications/files/controller/PhabricatorFileController.php
@@ -2,17 +2,43 @@
abstract class PhabricatorFileController extends PhabricatorController {
- public function buildStandardPageResponse($view, array $data) {
- $page = $this->buildStandardPageView();
+ public function buildApplicationCrumbs() {
+ $crumbs = parent::buildApplicationCrumbs();
+ $crumbs->addAction(
+ id(new PhabricatorMenuItemView())
+ ->setName(pht('Upload File'))
+ ->setIcon('create') // TODO: Get @chad to build an "upload" icon.
+ ->setHref($this->getApplicationURI('/upload/')));
- $page->setApplicationName('Files');
- $page->setBaseURI('/file/');
- $page->setTitle(idx($data, 'title'));
- $page->setGlyph("\xE2\x87\xAA");
- $page->appendChild($view);
-
- $response = new AphrontWebpageResponse();
- return $response->setContent($page->render());
+ return $crumbs;
}
+ protected function buildSideNavView() {
+ $menu = $this->buildMenu($for_devices = false);
+ return AphrontSideNavFilterView::newFromMenu($menu);
+ }
+
+ protected function buildApplicationMenu() {
+ return $this->buildMenu($for_devices = true);
+ }
+
+ private function buildMenu($for_devices) {
+ $menu = new PhabricatorMenuView();
+
+ if ($for_devices) {
+ $menu->newLink(pht('Upload File'), $this->getApplicationURI('/upload/'));
+ }
+
+ $menu->newLabel(pht('Filters'));
+
+ $menu->newLink(pht('My Files'), $this->getApplicationURI('filter/my/'))
+ ->setKey('my');
+
+ $menu->newLink(pht('All Files'), $this->getApplicationURI('filter/all/'))
+ ->setKey('all');
+
+ return $menu;
+ }
+
+
}
diff --git a/src/applications/files/controller/PhabricatorFileListController.php b/src/applications/files/controller/PhabricatorFileListController.php
index c88c9a06aa..ecdaba4d6b 100644
--- a/src/applications/files/controller/PhabricatorFileListController.php
+++ b/src/applications/files/controller/PhabricatorFileListController.php
@@ -1,35 +1,20 @@
filter = $filter;
return $this;
}
+
private function getFilter() {
return $this->filter;
}
- private function useBasicUploader() {
- return $this->getUseBasicUploader();
- }
- private function getUseBasicUploader() {
- return $this->useBasicUploader;
- }
- private function setUseBasicUploader($use_basic_uploader) {
- $this->useBasicUploader = $use_basic_uploader;
- return $this;
- }
-
public function willProcessRequest(array $data) {
- $this->setFilter(idx($data, 'filter', 'upload'));
+ $this->setFilter(idx($data, 'filter', 'my'));
}
public function processRequest() {
@@ -42,26 +27,13 @@ final class PhabricatorFileListController extends PhabricatorFileController {
$query = id(new PhabricatorFileQuery())
->setViewer($user);
- $show_pager = true;
- $show_upload = false;
-
switch ($this->getFilter()) {
- case 'upload':
- default:
- $this->setUseBasicUploader($request->getExists('basic_uploader'));
-
- $query->withAuthorPHIDs(array($user->getPHID()));
- $pager->setPageSize(10);
-
- $header = pht('Recently Uploaded Files');
- $show_pager = false;
- $show_upload = true;
- break;
case 'my':
$query->withAuthorPHIDs(array($user->getPHID()));
$header = pht('Files You Uploaded');
break;
case 'all':
+ default:
$header = pht('All Files');
break;
}
@@ -74,9 +46,6 @@ final class PhabricatorFileListController extends PhabricatorFileController {
$side_nav = $this->buildSideNavView();
$side_nav->selectFilter($this->getFilter());
- if ($show_upload) {
- $side_nav->appendChild($this->renderUploadPanel());
- }
$header_view = id(new PhabricatorHeaderView())
->setHeader($header);
@@ -85,13 +54,23 @@ final class PhabricatorFileListController extends PhabricatorFileController {
array(
$header_view,
$file_list,
- $show_pager ? $pager : null,
+ $pager,
+ new PhabricatorGlobalUploadTargetView(),
));
+ $side_nav->setCrumbs(
+ $this
+ ->buildApplicationCrumbs()
+ ->addCrumb(
+ id(new PhabricatorCrumbView())
+ ->setName($header)
+ ->setHref($request->getRequestURI())));
+
return $this->buildApplicationPage(
$side_nav,
array(
'title' => 'Files',
+ 'device' => true,
));
}
@@ -141,66 +120,4 @@ final class PhabricatorFileListController extends PhabricatorFileController {
return $list_view;
}
- private function buildSideNavView() {
- $view = new AphrontSideNavFilterView();
- $view->setBaseURI(new PhutilURI($this->getApplicationURI('/filter/')));
-
- $view->addLabel('Files');
- $view->addFilter('upload', 'Upload File');
- $view->addFilter('my', 'My Files');
- $view->addFilter('all', 'All Files');
-
- return $view;
- }
-
- private function renderUploadPanel() {
- $request = $this->getRequest();
- $user = $request->getUser();
-
- $limit_text = PhabricatorFileUploadView::renderUploadLimit();
-
- if ($this->useBasicUploader()) {
-
- $upload_panel = new PhabricatorFileUploadView();
- $upload_panel->setUser($user);
-
- } else {
-
- require_celerity_resource('files-css');
- $upload_id = celerity_generate_unique_node_id();
- $panel_id = celerity_generate_unique_node_id();
-
- $upload_panel = new AphrontPanelView();
- $upload_panel->setHeader('Upload Files');
- $upload_panel->setCaption($limit_text);
- $upload_panel->setCreateButton('Basic Uploader',
- $request->getRequestURI()->setQueryParam('basic_uploader', true)
- );
-
- $upload_panel->setWidth(AphrontPanelView::WIDTH_FULL);
- $upload_panel->setID($panel_id);
-
- $upload_panel->appendChild(
- phutil_render_tag(
- 'div',
- array(
- 'id' => $upload_id,
- 'style' => 'display: none;',
- 'class' => 'files-drag-and-drop',
- ),
- ''));
-
- Javelin::initBehavior(
- 'files-drag-and-drop',
- array(
- 'uri' => '/file/dropupload/',
- 'browseURI' => '/file/filter/my/',
- 'control' => $upload_id,
- 'target' => $panel_id,
- 'activatedClass' => 'aphront-panel-view-drag-and-drop',
- ));
- }
-
- return $upload_panel;
- }
}
diff --git a/src/applications/files/controller/PhabricatorFileUploadController.php b/src/applications/files/controller/PhabricatorFileUploadController.php
index 8055211119..f94e587a19 100644
--- a/src/applications/files/controller/PhabricatorFileUploadController.php
+++ b/src/applications/files/controller/PhabricatorFileUploadController.php
@@ -3,28 +3,114 @@
final class PhabricatorFileUploadController extends PhabricatorFileController {
public function processRequest() {
-
$request = $this->getRequest();
$user = $request->getUser();
+ $e_file = true;
+ $errors = array();
if ($request->isFormPost()) {
- $file = PhabricatorFile::newFromPHPUpload(
- idx($_FILES, 'file'),
- array(
- 'name' => $request->getStr('name'),
- 'authorPHID' => $user->getPHID(),
- ));
+ if (!$request->getFileExists('file')) {
+ $e_file = pht('Required');
+ $errors[] = pht('You must select a file to upload.');
+ } else {
+ $file = PhabricatorFile::newFromPHPUpload(
+ idx($_FILES, 'file'),
+ array(
+ 'name' => $request->getStr('name'),
+ 'authorPHID' => $user->getPHID(),
+ ));
+ }
- return id(new AphrontRedirectResponse())->setURI($file->getBestURI());
+ if (!$errors) {
+ return id(new AphrontRedirectResponse())->setURI($file->getViewURI());
+ }
}
- $panel = new PhabricatorFileUploadView();
- $panel->setUser($user);
+ $support_id = celerity_generate_unique_node_id();
+ $instructions = id(new AphrontFormMarkupControl())
+ ->setControlID($support_id)
+ ->setControlStyle('display: none')
+ ->setValue(
+ '
'.
+ pht(
+ 'Drag and Drop: You can also upload files by '.
+ 'dragging and dropping them from your desktop onto this page or '.
+ 'the Phabricator home page.').
+ '
');
- return $this->buildStandardPageResponse(
- array($panel),
+ $form = id(new AphrontFormView())
+ ->setFlexible(true)
+ ->setUser($user)
+ ->setEncType('multipart/form-data')
+ ->appendChild(
+ id(new AphrontFormFileControl())
+ ->setLabel(pht('File'))
+ ->setName('file')
+ ->setError($e_file)
+ ->setCaption($this->renderUploadLimit()))
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setLabel(pht('Name'))
+ ->setName('name')
+ ->setValue($request->getStr('name'))
+ ->setCaption('Optional file display name.'))
+ ->appendChild(
+ id(new AphrontFormSubmitControl())
+ ->setValue(pht('Upload'))
+ ->addCancelButton('/file/'))
+ ->appendChild($instructions);
+
+ $crumbs = $this->buildApplicationCrumbs();
+ $crumbs->addCrumb(
+ id(new PhabricatorCrumbView())
+ ->setName(pht('Upload'))
+ ->setHref($request->getRequestURI()));
+
+ $header = id(new PhabricatorHeaderView())
+ ->setHeader(pht('Upload File'));
+
+ if ($errors) {
+ $errors = id(new AphrontErrorView())
+ ->setTitle('Form Errors')
+ ->setErrors($errors);
+ }
+
+ $global_upload = id(new PhabricatorGlobalUploadTargetView())
+ ->setShowIfSupportedID($support_id);
+
+ return $this->buildApplicationPage(
+ array(
+ $crumbs,
+ $header,
+ $errors,
+ $form,
+ $global_upload,
+ ),
array(
'title' => 'Upload File',
+ 'device' => true,
));
}
+
+ private function renderUploadLimit() {
+ $limit = PhabricatorEnv::getEnvConfig('storage.upload-size-limit');
+ $limit = phabricator_parse_bytes($limit);
+ if ($limit) {
+ $formatted = phabricator_format_bytes($limit);
+ return 'Maximum file size: '.phutil_escape_html($formatted);
+ }
+
+ $doc_href = PhabricatorEnv::getDocLink(
+ 'article/Configuring_File_Upload_Limits.html');
+ $doc_link = phutil_render_tag(
+ 'a',
+ array(
+ 'href' => $doc_href,
+ 'target' => '_blank',
+ ),
+ 'Configuring File Upload Limits');
+
+ return 'Upload limit is not configured, see '.$doc_link.'.';
+ }
+
}
diff --git a/src/applications/files/view/PhabricatorFileUploadView.php b/src/applications/files/view/PhabricatorFileUploadView.php
deleted file mode 100644
index b9160bf278..0000000000
--- a/src/applications/files/view/PhabricatorFileUploadView.php
+++ /dev/null
@@ -1,73 +0,0 @@
-user;
- }
- public function setUser(PhabricatorUser $user) {
- $this->user = $user;
- return $this;
- }
-
- public function render() {
- $user = $this->getUser();
- if (!$user) {
- throw new Exception("Call setUser() before render()!");
- }
-
- $form = new AphrontFormView();
- $form->setAction('/file/upload/');
- $form->setUser($user);
-
- $form
- ->setEncType('multipart/form-data')
- ->appendChild(
- id(new AphrontFormFileControl())
- ->setLabel('File')
- ->setName('file')
- ->setError(true)
- ->setCaption(self::renderUploadLimit()))
- ->appendChild(
- id(new AphrontFormTextControl())
- ->setLabel('Name')
- ->setName('name')
- ->setCaption('Optional file display name.'))
- ->appendChild(
- id(new AphrontFormSubmitControl())
- ->setValue('Upload')
- ->addCancelButton('/file/'));
-
- $panel = new AphrontPanelView();
- $panel->setHeader('Upload File');
-
- $panel->appendChild($form);
- $panel->setWidth(AphrontPanelView::WIDTH_FULL);
-
- return $panel->render();
- }
-
- public static function renderUploadLimit() {
- $limit = PhabricatorEnv::getEnvConfig('storage.upload-size-limit');
- $limit = phabricator_parse_bytes($limit);
- if ($limit) {
- $formatted = phabricator_format_bytes($limit);
- return 'Maximum file size: '.phutil_escape_html($formatted);
- }
-
- $doc_href = PhabricatorEnv::getDocLink(
- 'article/Configuring_File_Upload_Limits.html');
- $doc_link = phutil_render_tag(
- 'a',
- array(
- 'href' => $doc_href,
- 'target' => '_blank',
- ),
- 'Configuring File Upload Limits');
-
- return 'Upload limit is not configured, see '.$doc_link.'.';
- }
-}
-
diff --git a/src/applications/files/view/PhabricatorGlobalUploadTargetView.php b/src/applications/files/view/PhabricatorGlobalUploadTargetView.php
new file mode 100644
index 0000000000..6b8a0f566a
--- /dev/null
+++ b/src/applications/files/view/PhabricatorGlobalUploadTargetView.php
@@ -0,0 +1,37 @@
+showIfSupportedID = $show_if_supported_id;
+ return $this;
+ }
+
+ public function getShowIfSupportedID() {
+ return $this->showIfSupportedID;
+ }
+
+ public function render() {
+ $instructions_id = celerity_generate_unique_node_id();
+
+ require_celerity_resource('global-drag-and-drop-css');
+
+ Javelin::initBehavior('global-drag-and-drop', array(
+ 'ifSupported' => $this->showIfSupportedID,
+ 'instructions' => $instructions_id,
+ 'uploadURI' => '/file/dropupload/',
+ 'browseURI' => '/file/filter/my/',
+ ));
+
+ return phutil_render_tag(
+ 'div',
+ array(
+ 'id' => $instructions_id,
+ 'class' => 'phabricator-global-upload-instructions',
+ 'style' => 'display: none;',
+ ),
+ pht("\xE2\x87\xAA Drop Files to Upload"));
+ }
+}
diff --git a/src/view/form/control/AphrontFormTextAreaControl.php b/src/view/form/control/AphrontFormTextAreaControl.php
index 378fa5e8bf..55327e40cd 100644
--- a/src/view/form/control/AphrontFormTextAreaControl.php
+++ b/src/view/form/control/AphrontFormTextAreaControl.php
@@ -11,7 +11,6 @@ class AphrontFormTextAreaControl extends AphrontFormControl {
private $height;
private $readOnly;
- private $enableDragAndDropFileUploads;
private $customClass;
public function setHeight($height) {
diff --git a/src/view/layout/AphrontSideNavFilterView.php b/src/view/layout/AphrontSideNavFilterView.php
index 3d7629a200..574f9f31ef 100644
--- a/src/view/layout/AphrontSideNavFilterView.php
+++ b/src/view/layout/AphrontSideNavFilterView.php
@@ -32,6 +32,13 @@ final class AphrontSideNavFilterView extends AphrontView {
$this->menu = new PhabricatorMenuView();
}
+ public static function newFromMenu(PhabricatorMenuView $menu) {
+ $object = new AphrontSideNavFilterView();
+ $object->setBaseURI(new PhutilURI('/'));
+ $object->menu = $menu;
+ return $object;
+ }
+
public function setCrumbs(PhabricatorCrumbsView $crumbs) {
$this->crumbs = $crumbs;
return $this;
@@ -128,7 +135,7 @@ final class AphrontSideNavFilterView extends AphrontView {
public function selectFilter($key, $default = null) {
$this->selectedFilter = $default;
- if ($this->menu->getItem($key)) {
+ if ($this->menu->getItem($key) && strlen($key)) {
$this->selectedFilter = $key;
}
return $this->selectedFilter;
diff --git a/src/view/layout/PhabricatorMenuView.php b/src/view/layout/PhabricatorMenuView.php
index eaaa04520c..5bd799db0a 100644
--- a/src/view/layout/PhabricatorMenuView.php
+++ b/src/view/layout/PhabricatorMenuView.php
@@ -3,7 +3,6 @@
final class PhabricatorMenuView extends AphrontView {
private $items = array();
- private $map = array();
private $classes = array();
public function addClass($class) {
@@ -34,14 +33,6 @@ final class PhabricatorMenuView extends AphrontView {
public function addMenuItem(PhabricatorMenuItemView $item) {
$key = $item->getKey();
- if ($key !== null) {
- if (isset($this->map[$key])) {
- throw new Exception(
- "Menu contains duplicate items with key '{$key}'!");
- }
- $this->map[$key] = $item;
- }
-
$this->items[] = $item;
$this->appendChild($item);
@@ -49,7 +40,19 @@ final class PhabricatorMenuView extends AphrontView {
}
public function getItem($key) {
- return idx($this->map, (string)$key);
+ $key = (string)$key;
+
+ // NOTE: We could optimize this, but need to update any map when items have
+ // their keys change. Since that's moderately complex, wait for a profile
+ // or use case.
+
+ foreach ($this->items as $item) {
+ if ($item->getKey() == $key) {
+ return $item;
+ }
+ }
+
+ return null;
}
public function getItems() {
@@ -57,6 +60,18 @@ final class PhabricatorMenuView extends AphrontView {
}
public function render() {
+ $key_map = array();
+ foreach ($this->items as $item) {
+ $key = $item->getKey();
+ if ($key !== null) {
+ if (isset($key_map[$key])) {
+ throw new Exception(
+ "Menu contains duplicate items with key '{$key}'!");
+ }
+ $key_map[$key] = $item;
+ }
+ }
+
$classes = $this->classes;
$classes[] = 'phabricator-menu-view';
diff --git a/webroot/rsrc/css/application/files/files.css b/webroot/rsrc/css/application/files/files.css
deleted file mode 100644
index beaff56c38..0000000000
--- a/webroot/rsrc/css/application/files/files.css
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * @provides files-css
- */
-
-.files-drag-and-drop {
- text-align: center;
- padding: 0em 1em .5em;
- font-size: 15px;
- color: #666666;
-}
diff --git a/webroot/rsrc/css/application/files/global-drag-and-drop.css b/webroot/rsrc/css/application/files/global-drag-and-drop.css
new file mode 100644
index 0000000000..d4c5af095f
--- /dev/null
+++ b/webroot/rsrc/css/application/files/global-drag-and-drop.css
@@ -0,0 +1,22 @@
+/**
+ * @provides global-drag-and-drop-css
+ */
+
+.phabricator-global-upload-instructions {
+ text-align: center;
+ position: fixed;
+ font-size: 36px;
+ font-weight: bold;
+
+ margin: 0 10%;
+ width: 80%;
+ left: 0;
+ top: 30%;
+ padding: 18px 0;
+
+ color: #ffffff;
+ background: rgba(0, 0, 0, 0.8);
+ z-index: 8;
+ border-radius: 18px;
+
+}
diff --git a/webroot/rsrc/js/application/core/DragAndDropFileUpload.js b/webroot/rsrc/js/application/core/DragAndDropFileUpload.js
index d77e7c2986..e3e3b69706 100644
--- a/webroot/rsrc/js/application/core/DragAndDropFileUpload.js
+++ b/webroot/rsrc/js/application/core/DragAndDropFileUpload.js
@@ -15,12 +15,19 @@ JX.install('PhabricatorDragAndDropFileUpload', {
this._node = node;
},
- events : ['willUpload', 'progress', 'didUpload', 'didError'],
+ events : [
+ 'didBeginDrag',
+ 'didEndDrag',
+ 'willUpload',
+ 'progress',
+ 'didUpload',
+ 'didError'],
statics : {
isSupported : function() {
// TODO: Is there a better capability test for this? This seems okay in
// Safari, Firefox and Chrome.
+
return !!window.FileList;
}
},
@@ -29,11 +36,17 @@ JX.install('PhabricatorDragAndDropFileUpload', {
_node : null,
_depth : 0,
_updateDepth : function(delta) {
+ if (this._depth == 0 && delta > 0) {
+ JX.log('begin: ' + this._depth + ' @ ' + delta);
+ this.invoke('didBeginDrag');
+ }
+
this._depth += delta;
- JX.DOM.alterClass(
- this._node,
- this.getActivatedClass(),
- (this._depth > 0));
+
+ if (this._depth == 0 && delta < 0) {
+ JX.log('end: ' + this._depth + ' @ ' + delta);
+ this.invoke('didEndDrag');
+ }
},
start : function() {
diff --git a/webroot/rsrc/js/application/core/behavior-drag-and-drop-textarea.js b/webroot/rsrc/js/application/core/behavior-drag-and-drop-textarea.js
index 9ad1cd11fb..0deff3dda7 100644
--- a/webroot/rsrc/js/application/core/behavior-drag-and-drop-textarea.js
+++ b/webroot/rsrc/js/application/core/behavior-drag-and-drop-textarea.js
@@ -12,13 +12,29 @@ JX.behavior('aphront-drag-and-drop-textarea', function(config) {
var target = JX.$(config.target);
function onupload(f) {
- JX.TextAreaUtils.setSelectionText(target, '{F' + f.getID() + '}');
+ var text = JX.TextAreaUtils.getSelectionText(target);
+ var ref = '{F' + f.getID() + '}';
+
+ // If the user has dragged and dropped multiple files, we'll get an event
+ // each time an upload completes. Rather than overwriting the first
+ // reference, append the new reference if the selected text looks like an
+ // existing file reference.
+ if (text.match(/^\{F/)) {
+ ref = text + "\n\n" + ref;
+ }
+
+ JX.TextAreaUtils.setSelectionText(target, ref);
}
if (JX.PhabricatorDragAndDropFileUpload.isSupported()) {
var drop = new JX.PhabricatorDragAndDropFileUpload(target)
- .setActivatedClass(config.activatedClass)
.setURI(config.uri);
+ drop.listen('didBeginDrag', function(e) {
+ JX.DOM.alterClass(target, config.activatedClass, true);
+ });
+ drop.listen('didEndDrag', function(e) {
+ JX.DOM.alterClass(target, config.activatedClass, false);
+ });
drop.listen('didUpload', onupload);
drop.start();
}
diff --git a/webroot/rsrc/js/application/core/behavior-drag-and-drop.js b/webroot/rsrc/js/application/core/behavior-drag-and-drop.js
index 6788f38d0e..4e115e1826 100644
--- a/webroot/rsrc/js/application/core/behavior-drag-and-drop.js
+++ b/webroot/rsrc/js/application/core/behavior-drag-and-drop.js
@@ -2,7 +2,6 @@
* @provides javelin-behavior-aphront-drag-and-drop
* @requires javelin-behavior
* javelin-dom
- * javelin-util
* phabricator-drag-and-drop-file-upload
*/
@@ -23,9 +22,16 @@ JX.behavior('aphront-drag-and-drop', function(config) {
var list = JX.$(config.list);
var drop = new JX.PhabricatorDragAndDropFileUpload(JX.$(config.list))
- .setActivatedClass(config.activatedClass)
.setURI(config.uri);
+ drop.listen('didBeginDrag', function(e) {
+ JX.DOM.alterClass(list, config.activatedClass, true);
+ });
+
+ drop.listen('didEndDrag', function(e) {
+ JX.DOM.alterClass(list, config.activatedClass, false);
+ });
+
drop.listen('willUpload', function(f) {
pending++;
redraw();
diff --git a/webroot/rsrc/js/application/core/behavior-files-drag-and-drop.js b/webroot/rsrc/js/application/core/behavior-files-drag-and-drop.js
deleted file mode 100644
index b2da9efa01..0000000000
--- a/webroot/rsrc/js/application/core/behavior-files-drag-and-drop.js
+++ /dev/null
@@ -1,84 +0,0 @@
-/**
- * @provides javelin-behavior-files-drag-and-drop
- * @requires javelin-behavior
- * javelin-dom
- * javelin-uri
- * phabricator-drag-and-drop-file-upload
- */
-
-JX.behavior('files-drag-and-drop', function(config) {
-
- // The control renders hidden by default; if we don't have support for
- // drag-and-drop just leave it hidden.
- if (!JX.PhabricatorDragAndDropFileUpload.isSupported()) {
- return;
- }
-
- var pending = 0;
- var files = [];
- var errors = false;
-
- var control = JX.$(config.control);
- // Show the control, since we have browser support.
- control.style.display = '';
-
- var drop = new JX.PhabricatorDragAndDropFileUpload(JX.$(config.target))
- .setActivatedClass(config.activatedClass)
- .setURI(config.uri);
-
- drop.listen('willUpload', function(f) {
- pending++;
- redraw();
- });
-
- drop.listen('didUpload', function(f) {
- files.push(f);
-
- pending--;
- if (pending == 0 && !errors) {
- // If whatever the user dropped in has finished uploading, send them to
- // their uploads.
- var uri;
- uri = JX.$U(config.browseURI);
- var ids = [];
- for (var ii = 0; ii < files.length; ii++) {
- ids.push(files[ii].getID());
- }
- uri.setQueryParam('h', ids.join(','));
-
- // Reset so if you hit 'back' into the bfcache the page is still in a
- // sensible state.
- redraw();
- files = [];
-
- uri.go();
- } else {
- redraw();
- }
- });
-
- drop.listen('didError', function(f) {
- pending--;
- errors = true;
- redraw();
- });
-
- drop.start();
- redraw();
-
- function redraw() {
-
- var status;
- if (pending) {
- status = 'Uploading ' + pending + ' files...';
- } else {
- var arrow = String.fromCharCode(0x21EA);
- status = JX.$H(
- arrow + ' Drag and Drop files here to upload them.');
- }
-
- JX.DOM.setContent(control, status);
- }
-
-});
-
diff --git a/webroot/rsrc/js/application/core/behavior-global-drag-and-drop.js b/webroot/rsrc/js/application/core/behavior-global-drag-and-drop.js
new file mode 100644
index 0000000000..8d4b7a443b
--- /dev/null
+++ b/webroot/rsrc/js/application/core/behavior-global-drag-and-drop.js
@@ -0,0 +1,68 @@
+/**
+ * @provides javelin-behavior-global-drag-and-drop
+ * @requires javelin-behavior
+ * javelin-dom
+ * javelin-uri
+ * javelin-mask
+ * phabricator-drag-and-drop-file-upload
+ */
+
+JX.behavior('global-drag-and-drop', function(config) {
+ if (!JX.PhabricatorDragAndDropFileUpload.isSupported()) {
+ return;
+ }
+
+ var pending = 0;
+ var files = [];
+ var errors = false;
+
+ if (config.ifSupported) {
+ JX.$(config.ifSupported).style.display = '';
+ }
+
+ var drop = new JX.PhabricatorDragAndDropFileUpload(document.documentElement)
+ .setURI(config.uploadURI);
+
+ drop.listen('didBeginDrag', function(f) {
+ JX.Mask.show();
+ JX.DOM.show(JX.$(config.instructions));
+ });
+
+ drop.listen('didEndDrag', function(f) {
+ JX.Mask.hide();
+ JX.DOM.hide(JX.$(config.instructions));
+ });
+
+ drop.listen('willUpload', function(f) {
+ pending++;
+ });
+
+ drop.listen('didUpload', function(f) {
+ files.push(f);
+
+ pending--;
+ if (pending == 0 && !errors) {
+ // If whatever the user dropped in has finished uploading, send them to
+ // their uploads.
+ var uri;
+ uri = JX.$U(config.browseURI);
+ var ids = [];
+ for (var ii = 0; ii < files.length; ii++) {
+ ids.push(files[ii].getID());
+ }
+ uri.setQueryParam('h', ids.join(','));
+
+ files = [];
+
+ uri.go();
+ }
+ });
+
+ drop.listen('didError', function(f) {
+ pending--;
+ errors = true;
+ });
+
+ drop.start();
+});
+