Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15525568
D8979.id21301.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
24 KB
Referenced Files
None
Subscribers
None
D8979.id21301.diff
View Options
diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -7,14 +7,14 @@
return array(
'names' =>
array(
- 'core.pkg.css' => 'afe6e16d',
- 'core.pkg.js' => 'c415c382',
+ 'core.pkg.css' => '45bad36d',
+ 'core.pkg.js' => 'aea7224e',
'darkconsole.pkg.js' => 'ca8671ce',
'differential.pkg.css' => '4b8686e3',
- 'differential.pkg.js' => '11a5b750',
+ 'differential.pkg.js' => '965d0202',
'diffusion.pkg.css' => '3783278d',
'diffusion.pkg.js' => '5b4010f4',
- 'javelin.pkg.js' => '9f6d38c7',
+ 'javelin.pkg.js' => '3bdbcf36',
'maniphest.pkg.css' => 'f1887d71',
'maniphest.pkg.js' => '2fe8af22',
'rsrc/css/aphront/aphront-bars.css' => '231ac33c',
@@ -105,7 +105,7 @@
'rsrc/css/application/subscriptions/subscribers-list.css' => '5bb30c78',
'rsrc/css/application/tokens/tokens.css' => '5f7bca25',
'rsrc/css/application/uiexample/example.css' => '528b19de',
- 'rsrc/css/core/core.css' => '7dff07c3',
+ 'rsrc/css/core/core.css' => 'd0e1db64',
'rsrc/css/core/remarkup.css' => '0ec9ea61',
'rsrc/css/core/syntax.css' => '3c18c1cb',
'rsrc/css/core/z-index.css' => '7e4989ed',
@@ -204,11 +204,13 @@
'rsrc/externals/javelin/lib/History.js' => 'c60f4327',
'rsrc/externals/javelin/lib/JSON.js' => '08e56a4e',
'rsrc/externals/javelin/lib/Mask.js' => 'b9f26029',
- 'rsrc/externals/javelin/lib/Request.js' => '23f9bb8d',
+ 'rsrc/externals/javelin/lib/Request.js' => '7bad574b',
'rsrc/externals/javelin/lib/Resource.js' => '356de121',
+ 'rsrc/externals/javelin/lib/Routable.js' => 'b3e7d692',
+ 'rsrc/externals/javelin/lib/Router.js' => '29274e2b',
'rsrc/externals/javelin/lib/URI.js' => 'd9a9b862',
'rsrc/externals/javelin/lib/Vector.js' => '039fb90d',
- 'rsrc/externals/javelin/lib/Workflow.js' => 'ff8091f7',
+ 'rsrc/externals/javelin/lib/Workflow.js' => '09b15cf1',
'rsrc/externals/javelin/lib/__tests__/Cookie.js' => '5ed109e8',
'rsrc/externals/javelin/lib/__tests__/DOM.js' => 'c984504b',
'rsrc/externals/javelin/lib/__tests__/JSON.js' => '2295d074',
@@ -365,7 +367,7 @@
'rsrc/js/application/differential/behavior-dropdown-menus.js' => '5f004630',
'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '00861799',
'rsrc/js/application/differential/behavior-keyboard-nav.js' => '173ce7e7',
- 'rsrc/js/application/differential/behavior-populate.js' => 'ce0c217a',
+ 'rsrc/js/application/differential/behavior-populate.js' => 'dfdf9f34',
'rsrc/js/application/differential/behavior-show-all-comments.js' => '7c273581',
'rsrc/js/application/differential/behavior-show-field-details.js' => '441f2137',
'rsrc/js/application/differential/behavior-show-more.js' => 'dd7e8ef5',
@@ -440,7 +442,7 @@
'rsrc/js/core/MultirowRowManager.js' => '50395a1b',
'rsrc/js/core/Notification.js' => '0c6946e7',
'rsrc/js/core/Prefab.js' => '0326e5d0',
- 'rsrc/js/core/ShapedRequest.js' => 'dfa181a4',
+ 'rsrc/js/core/ShapedRequest.js' => '837eb14d',
'rsrc/js/core/TextAreaUtils.js' => 'b3ec3cfc',
'rsrc/js/core/ToolTip.js' => '3915d490',
'rsrc/js/core/behavior-active-nav.js' => 'c81bc98f',
@@ -469,7 +471,7 @@
'rsrc/js/core/behavior-oncopy.js' => 'c3e218fe',
'rsrc/js/core/behavior-phabricator-nav.js' => 'b5842a5e',
'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => 'c021950a',
- 'rsrc/js/core/behavior-refresh-csrf.js' => 'c4b31646',
+ 'rsrc/js/core/behavior-refresh-csrf.js' => '7814b593',
'rsrc/js/core/behavior-remarkup-preview.js' => 'f7379f45',
'rsrc/js/core/behavior-reveal-content.js' => '8f24abfc',
'rsrc/js/core/behavior-search-typeahead.js' => 'd8469741',
@@ -478,7 +480,7 @@
'rsrc/js/core/behavior-tokenizer.js' => 'b3a4b884',
'rsrc/js/core/behavior-tooltip.js' => '48db4145',
'rsrc/js/core/behavior-watch-anchor.js' => '06e05112',
- 'rsrc/js/core/behavior-workflow.js' => 'fee00761',
+ 'rsrc/js/core/behavior-workflow.js' => '0a3f3021',
'rsrc/js/core/phtize.js' => 'd254d646',
'rsrc/js/phui/behavior-phui-object-box-tabs.js' => 'a3e2244e',
'rsrc/swf/aphlict.swf' => 'abac967d',
@@ -556,7 +558,7 @@
'javelin-behavior-differential-edit-inline-comments' => '00861799',
'javelin-behavior-differential-feedback-preview' => '127f2018',
'javelin-behavior-differential-keyboard-navigation' => '173ce7e7',
- 'javelin-behavior-differential-populate' => 'ce0c217a',
+ 'javelin-behavior-differential-populate' => 'dfdf9f34',
'javelin-behavior-differential-show-field-details' => '441f2137',
'javelin-behavior-differential-show-more' => 'dd7e8ef5',
'javelin-behavior-differential-toggle-files' => 'ca3f91eb',
@@ -620,7 +622,7 @@
'javelin-behavior-ponder-votebox' => '327dbe61',
'javelin-behavior-project-boards' => 'd8e135db',
'javelin-behavior-project-create' => '065227cc',
- 'javelin-behavior-refresh-csrf' => 'c4b31646',
+ 'javelin-behavior-refresh-csrf' => '7814b593',
'javelin-behavior-releeph-preview-branch' => '9eb2cedb',
'javelin-behavior-releeph-request-state-change' => 'd259e7c9',
'javelin-behavior-releeph-request-typeahead' => 'cd9e7094',
@@ -633,7 +635,7 @@
'javelin-behavior-test-payment-form' => 'b3e5ee60',
'javelin-behavior-toggle-class' => 'a82a7769',
'javelin-behavior-view-placeholder' => '2fa810fc',
- 'javelin-behavior-workflow' => 'fee00761',
+ 'javelin-behavior-workflow' => '0a3f3021',
'javelin-color' => '7e41274a',
'javelin-cookie' => '6b3dcf44',
'javelin-dom' => '32a4d380',
@@ -649,8 +651,10 @@
'javelin-reactor-dom' => 'b6d401d6',
'javelin-reactor-node-calmer' => '76f4ebed',
'javelin-reactornode' => 'b4c30592',
- 'javelin-request' => '23f9bb8d',
+ 'javelin-request' => '7bad574b',
'javelin-resource' => '356de121',
+ 'javelin-routable' => 'b3e7d692',
+ 'javelin-router' => '29274e2b',
'javelin-stratcom' => 'c293f7b9',
'javelin-tokenizer' => 'e7c21fb3',
'javelin-typeahead' => 'c54eeefb',
@@ -668,7 +672,7 @@
'javelin-view-interpreter' => '0c33c1a0',
'javelin-view-renderer' => '6c2b09a2',
'javelin-view-visitor' => 'efe49472',
- 'javelin-workflow' => 'ff8091f7',
+ 'javelin-workflow' => '09b15cf1',
'lightbox-attachment-css' => '7acac05d',
'maniphest-batch-editor' => '8f380ebc',
'maniphest-report-css' => '6fc16517',
@@ -686,7 +690,7 @@
'phabricator-busy' => '6453c869',
'phabricator-chatlog-css' => '852140ff',
'phabricator-content-source-view-css' => '4b8b05d4',
- 'phabricator-core-css' => '7dff07c3',
+ 'phabricator-core-css' => 'd0e1db64',
'phabricator-countdown-css' => '86b7b0a0',
'phabricator-crumbs-view-css' => '0222cbe0',
'phabricator-drag-and-drop-file-upload' => 'ae6abfba',
@@ -716,7 +720,7 @@
'phabricator-remarkup-css' => '0ec9ea61',
'phabricator-search-results-css' => 'f240504c',
'phabricator-settings-css' => 'ea8f5915',
- 'phabricator-shaped-request' => 'dfa181a4',
+ 'phabricator-shaped-request' => '837eb14d',
'phabricator-side-menu-view-css' => '503699d0',
'phabricator-slowvote-css' => '266df6a1',
'phabricator-source-code-view-css' => '62a99814',
@@ -867,6 +871,26 @@
array(
0 => 'javelin-install',
),
+ '09b15cf1' =>
+ array(
+ 0 => 'javelin-stratcom',
+ 1 => 'javelin-request',
+ 2 => 'javelin-dom',
+ 3 => 'javelin-vector',
+ 4 => 'javelin-install',
+ 5 => 'javelin-util',
+ 6 => 'javelin-mask',
+ 7 => 'javelin-uri',
+ 8 => 'javelin-routable',
+ ),
+ '0a3f3021' =>
+ array(
+ 0 => 'javelin-behavior',
+ 1 => 'javelin-stratcom',
+ 2 => 'javelin-workflow',
+ 3 => 'javelin-dom',
+ 4 => 'javelin-router',
+ ),
'0c33c1a0' =>
array(
0 => 'javelin-view',
@@ -977,16 +1001,6 @@
5 => 'javelin-json',
6 => 'phabricator-prefab',
),
- '23f9bb8d' =>
- array(
- 0 => 'javelin-install',
- 1 => 'javelin-stratcom',
- 2 => 'javelin-util',
- 3 => 'javelin-behavior',
- 4 => 'javelin-json',
- 5 => 'javelin-dom',
- 6 => 'javelin-resource',
- ),
'263aeb8c' =>
array(
0 => 'javelin-behavior',
@@ -999,6 +1013,11 @@
7 => 'javelin-typeahead-preloaded-source',
8 => 'javelin-json',
),
+ '29274e2b' =>
+ array(
+ 0 => 'javelin-install',
+ 1 => 'javelin-util',
+ ),
'2a2dba85' =>
array(
0 => 'javelin-behavior',
@@ -1266,6 +1285,15 @@
0 => 'javelin-install',
1 => 'javelin-util',
),
+ '7814b593' =>
+ array(
+ 0 => 'javelin-request',
+ 1 => 'javelin-behavior',
+ 2 => 'javelin-dom',
+ 3 => 'javelin-router',
+ 4 => 'javelin-util',
+ 5 => 'phabricator-busy',
+ ),
'79473b62' =>
array(
0 => 'javelin-install',
@@ -1281,6 +1309,17 @@
1 => 'javelin-dom',
2 => 'javelin-reactor-dom',
),
+ '7bad574b' =>
+ array(
+ 0 => 'javelin-install',
+ 1 => 'javelin-stratcom',
+ 2 => 'javelin-util',
+ 3 => 'javelin-behavior',
+ 4 => 'javelin-json',
+ 5 => 'javelin-dom',
+ 6 => 'javelin-resource',
+ 7 => 'javelin-routable',
+ ),
'7c273581' =>
array(
0 => 'javelin-behavior',
@@ -1319,6 +1358,13 @@
1 => 'javelin-dom',
2 => 'javelin-reactor-dom',
),
+ '837eb14d' =>
+ array(
+ 0 => 'javelin-install',
+ 1 => 'javelin-util',
+ 2 => 'javelin-request',
+ 3 => 'javelin-router',
+ ),
'845731b8' =>
array(
0 => 'javelin-behavior',
@@ -1538,6 +1584,10 @@
1 => 'javelin-dom',
2 => 'phortune-credit-card-form',
),
+ 'b3e7d692' =>
+ array(
+ 0 => 'javelin-install',
+ ),
'b3ec3cfc' =>
array(
0 => 'javelin-install',
@@ -1652,13 +1702,6 @@
0 => 'javelin-behavior',
1 => 'javelin-dom',
),
- 'c4b31646' =>
- array(
- 0 => 'javelin-request',
- 1 => 'javelin-behavior',
- 2 => 'javelin-dom',
- 3 => 'phabricator-busy',
- ),
'c51a6616' =>
array(
0 => 'phabricator-notification',
@@ -1713,17 +1756,6 @@
0 => 'javelin-install',
1 => 'javelin-typeahead-source',
),
- 'ce0c217a' =>
- array(
- 0 => 'javelin-behavior',
- 1 => 'javelin-workflow',
- 2 => 'javelin-util',
- 3 => 'javelin-dom',
- 4 => 'javelin-stratcom',
- 5 => 'javelin-behavior-device',
- 6 => 'javelin-vector',
- 7 => 'phabricator-tooltip',
- ),
'cf76cfd5' =>
array(
0 => 'javelin-behavior',
@@ -1819,11 +1851,17 @@
1 => 'javelin-dom',
2 => 'phabricator-prefab',
),
- 'dfa181a4' =>
+ 'dfdf9f34' =>
array(
- 0 => 'javelin-install',
- 1 => 'javelin-util',
- 2 => 'javelin-request',
+ 0 => 'javelin-behavior',
+ 1 => 'javelin-workflow',
+ 2 => 'javelin-util',
+ 3 => 'javelin-dom',
+ 4 => 'javelin-stratcom',
+ 5 => 'javelin-behavior-device',
+ 6 => 'javelin-vector',
+ 7 => 'javelin-router',
+ 8 => 'phabricator-tooltip',
),
'e1ff79b1' =>
array(
@@ -1967,24 +2005,6 @@
4 => 'multirow-row-manager',
5 => 'javelin-json',
),
- 'fee00761' =>
- array(
- 0 => 'javelin-behavior',
- 1 => 'javelin-stratcom',
- 2 => 'javelin-workflow',
- 3 => 'javelin-dom',
- ),
- 'ff8091f7' =>
- array(
- 0 => 'javelin-stratcom',
- 1 => 'javelin-request',
- 2 => 'javelin-dom',
- 3 => 'javelin-vector',
- 4 => 'javelin-install',
- 5 => 'javelin-util',
- 6 => 'javelin-mask',
- 7 => 'javelin-uri',
- ),
28497740 =>
array(
0 => 'javelin-behavior',
diff --git a/webroot/rsrc/css/core/core.css b/webroot/rsrc/css/core/core.css
--- a/webroot/rsrc/css/core/core.css
+++ b/webroot/rsrc/css/core/core.css
@@ -149,3 +149,21 @@
background: #990066;
opacity: 0.25;
}
+
+.routing-bar {
+ position: fixed;
+ top: 0;
+ width: 100%;
+ height: 2px;
+ background: {$darkbluetext};
+ z-index: 80;
+ box-shadow: 0 2px 1px rgba(0, 128, 255, 0.25);
+}
+
+.routing-progress {
+ position: fixed;
+ top: 0;
+ left: 0;
+ height: 2px;
+ background: {$sky};
+}
diff --git a/webroot/rsrc/externals/javelin/lib/Request.js b/webroot/rsrc/externals/javelin/lib/Request.js
--- a/webroot/rsrc/externals/javelin/lib/Request.js
+++ b/webroot/rsrc/externals/javelin/lib/Request.js
@@ -6,6 +6,7 @@
* javelin-json
* javelin-dom
* javelin-resource
+ * javelin-routable
* @provides javelin-request
* @javelin
*/
@@ -69,6 +70,18 @@
return this._transport;
},
+ getRoutable: function() {
+ var routable = new JX.Routable();
+ routable.listen('start', JX.bind(this, function() {
+ // Pass the event to allow other listeners to "start" to configure this
+ // request before it fires.
+ JX.Stratcom.pass(JX.Stratcom.context());
+ this.send();
+ }));
+ this.listen('finally', JX.bind(routable, routable.done));
+ return routable;
+ },
+
send : function() {
if (this._sent || this._finished) {
if (__DEV__) {
diff --git a/webroot/rsrc/externals/javelin/lib/Routable.js b/webroot/rsrc/externals/javelin/lib/Routable.js
new file mode 100644
--- /dev/null
+++ b/webroot/rsrc/externals/javelin/lib/Routable.js
@@ -0,0 +1,41 @@
+/**
+ * @provides javelin-routable
+ * @requires javelin-install
+ * @javelin
+ */
+
+JX.install('Routable', {
+
+ construct : function() {
+ this._id = (JX.Routable._nextID++);
+ },
+
+ properties: {
+ key: null,
+ priority: 1000,
+ type: 'default'
+ },
+
+ events: ['start', 'done'],
+
+ members: {
+ _id: null,
+
+ getID: function() {
+ return this._id;
+ },
+
+ start: function() {
+ this.invoke('start');
+ },
+
+ done: function() {
+ this.invoke('done');
+ }
+ },
+
+ statics: {
+ _nextID: 0
+ }
+
+});
diff --git a/webroot/rsrc/externals/javelin/lib/Router.js b/webroot/rsrc/externals/javelin/lib/Router.js
new file mode 100644
--- /dev/null
+++ b/webroot/rsrc/externals/javelin/lib/Router.js
@@ -0,0 +1,115 @@
+/**
+ * @provides javelin-router
+ * @requires javelin-install
+ * javelin-util
+ * @javelin
+ */
+
+/**
+ * Route requests. Primarily, this class provides a quality-of-service
+ * priority queue so large numbers of background loading tasks don't block
+ * interactive requests.
+ */
+JX.install('Router', {
+
+ construct: function() {
+ this._queue = [];
+ },
+
+ events: ['queue', 'start', 'done'],
+
+ members: {
+ _queue: null,
+ _active: 0,
+ _limit: 5,
+
+ queue: function(routable) {
+ this._queue.push(routable);
+
+ this.invoke('queue', routable);
+ this._update();
+ },
+
+ getRoutableByKey: function(key) {
+ for (var ii = 0; ii < this._queue.length; ii++) {
+ if (this._queue[ii].getKey() == key) {
+ return this._queue[ii];
+ }
+ }
+ return null;
+ },
+
+ /**
+ * Start new requests if we have slots free for them.
+ */
+ _update: function() {
+ var active = this._active;
+ var limit = this._limit;
+
+ if (active >= limit) {
+ // If we're already at the request limit, we can't add any more
+ // requests.
+ return;
+ }
+
+ // If we only have one free slot, we reserve it for a request with
+ // at least priority 1000.
+ var minimum;
+ if ((active + 1) == limit) {
+ minimum = 1000;
+ } else {
+ minimum = 0;
+ }
+
+ var idx = this._getNextRoutable(minimum);
+ if (idx === null) {
+ return;
+ }
+
+ var routable = this._queue[idx];
+ this._queue.splice(idx, 1);
+
+
+ routable.listen('done', JX.bind(this, this._done, routable));
+
+ this._active++;
+ routable.start();
+ this.invoke('start', routable);
+
+ this._update();
+ },
+
+ _done: function(routable) {
+ this._active--;
+ this.invoke('done', routable);
+
+ this._update();
+ },
+
+ _getNextRoutable: function(minimum) {
+ var best = (minimum - 1);
+
+ var routable = null;
+ for (var ii = 0; ii < this._queue.length; ii++) {
+ var priority = this._queue[ii].getPriority();
+ if (priority > best) {
+ best = priority;
+ routable = ii;
+ }
+ }
+
+ return routable;
+ }
+
+ },
+
+ statics: {
+ _instance: null,
+ getInstance: function() {
+ if (!JX.Router._instance) {
+ JX.Router._instance = new JX.Router();
+ }
+ return JX.Router._instance;
+ }
+ }
+});
diff --git a/webroot/rsrc/externals/javelin/lib/Workflow.js b/webroot/rsrc/externals/javelin/lib/Workflow.js
--- a/webroot/rsrc/externals/javelin/lib/Workflow.js
+++ b/webroot/rsrc/externals/javelin/lib/Workflow.js
@@ -7,6 +7,7 @@
* javelin-util
* javelin-mask
* javelin-uri
+ * javelin-routable
* @provides javelin-workflow
* @javelin
*/
@@ -259,6 +260,18 @@
r.send();
},
+ getRoutable: function() {
+ var routable = new JX.Routable();
+ routable.listen('start', JX.bind(this, function() {
+ // Pass the event to allow other listeners to "start" to configure this
+ // workflow before it fires.
+ JX.Stratcom.pass(JX.Stratcom.context());
+ this.start();
+ }));
+ this.listen('finally', JX.bind(routable, routable.done));
+ return routable;
+ },
+
setData : function(dictionary) {
this._data = [];
for (var k in dictionary) {
diff --git a/webroot/rsrc/js/application/differential/behavior-populate.js b/webroot/rsrc/js/application/differential/behavior-populate.js
--- a/webroot/rsrc/js/application/differential/behavior-populate.js
+++ b/webroot/rsrc/js/application/differential/behavior-populate.js
@@ -7,6 +7,7 @@
* javelin-stratcom
* javelin-behavior-device
* javelin-vector
+ * javelin-router
* phabricator-tooltip
*/
@@ -74,6 +75,25 @@
// TODO: Once 1up works better, figure out when to show it.
renderer = '2up';
+ var get_key = function(id) {
+ return 'differential-populate.' + id;
+ };
+
+ var load = function(id, data) {
+ var routable = new JX.Workflow(config.uri, data)
+ .setHandler(JX.bind(null, onresponse, id))
+ .getRoutable();
+
+ routable
+ .setPriority(500)
+ .setType('content')
+ .setKey(get_key(id));
+
+ JX.Router.getInstance().queue(routable);
+
+ return routable;
+ };
+
for (var k in config.registry) {
var data = {
ref : config.registry[k],
@@ -81,9 +101,7 @@
renderer: renderer
};
- new JX.Workflow(config.uri, data)
- .setHandler(JX.bind(null, onresponse, k))
- .start();
+ load(k, data);
}
var highlighted = null;
@@ -104,13 +122,24 @@
JX.DOM.setContent(
diff,
JX.$H('<div class="differential-loading">Loading...</div>'));
- var data = {
- ref : meta.ref,
- whitespace : config.whitespace
- };
- new JX.Workflow(config.uri, data)
- .setHandler(JX.bind(null, onresponse, meta.id))
- .start();
+
+ // When a user explicitly clicks "Load" (or clicks a link in the table
+ // of contents) prioritize this request if it already exists. If it
+ // doesn't, make a new high-priority request.
+
+ var key = get_key(meta.id);
+ var routable = JX.Router.getInstance().getRoutableByKey(key);
+
+ if (!routable) {
+ var data = {
+ ref : meta.ref,
+ whitespace : config.whitespace
+ };
+
+ routable = load(meta.id, data);
+ }
+
+ routable.setPriority(2000);
}
if (meta.kill) {
e.kill();
diff --git a/webroot/rsrc/js/core/ShapedRequest.js b/webroot/rsrc/js/core/ShapedRequest.js
--- a/webroot/rsrc/js/core/ShapedRequest.js
+++ b/webroot/rsrc/js/core/ShapedRequest.js
@@ -2,6 +2,7 @@
* @requires javelin-install
* javelin-util
* javelin-request
+ * javelin-router
* @provides phabricator-shaped-request
* @javelin
*/
@@ -62,8 +63,18 @@
this._request = null;
}));
this._request.setData(data);
- this._request.setTimeout(this.getRequestTimeout());
- this._request.send();
+
+ var routable = this._request.getRoutable();
+
+ routable
+ .setType('draft')
+ .setPriority(750);
+
+ routable.listen('start', JX.bind(this, function() {
+ this._request.setTimeout(this.getRequestTimeout());
+ }));
+
+ JX.Router.getInstance().queue(routable);
} else {
this._defer = setTimeout(
JX.bind(this, this.trigger),
diff --git a/webroot/rsrc/js/core/behavior-refresh-csrf.js b/webroot/rsrc/js/core/behavior-refresh-csrf.js
--- a/webroot/rsrc/js/core/behavior-refresh-csrf.js
+++ b/webroot/rsrc/js/core/behavior-refresh-csrf.js
@@ -3,6 +3,8 @@
* @requires javelin-request
* javelin-behavior
* javelin-dom
+ * javelin-router
+ * javelin-util
* phabricator-busy
*/
@@ -49,11 +51,97 @@
// Additionally, add the CSRF token as an HTTP header to every AJAX request.
JX.Request.listen('open', function(r) {
r.getTransport().setRequestHeader(config.header, current_token);
- JX.Busy.start();
});
- JX.Request.listen('finally', function(r) {
- JX.Busy.done();
+ // Does this type of routable show the "Busy" spinner?
+ var is_busy_type = function(type) {
+ switch (type) {
+ case 'workflow':
+ return true;
+ }
+
+ return false;
+ };
+
+ // Does this type of routable show the "Loading" bar?
+ var is_bar_type = function(type) {
+ switch (type) {
+ case 'content':
+ return true;
+ }
+
+ return false;
+ };
+
+
+ var queue = {};
+ var count = 0;
+ var node;
+
+ // Redraw the loading bar.
+ var redraw_bar = function() {
+ // If all requests have completed, hide the bar after a moment.
+ if (!count) {
+ if (node) {
+ node.firstChild.style.width = '100%';
+ setTimeout(JX.bind(null, JX.DOM.remove, node), 500);
+ }
+ node = null;
+ queue = {};
+ return;
+ }
+
+ // If we don't have the bar yet, draw it.
+ if (!node) {
+ node = JX.$N('div', {className: 'routing-bar'});
+ document.body.appendChild(node);
+ node.appendChild(JX.$N('div', {className: 'routing-progress'}));
+ }
+
+ // Update the bar progress.
+ var done = 0;
+ var total = 0;
+ for (var k in queue) {
+ total++;
+ if (queue[k]) {
+ done++;
+ }
+ }
+
+ node.firstChild.style.width = (100 * (done / total)) + '%';
+ };
+
+
+ // Listen for queued requests.
+ JX.Router.listen('queue', function(r) {
+ var type = r.getType();
+
+ if (is_bar_type(type)) {
+ queue[r.getID()] = false;
+ count++;
+ redraw_bar();
+ }
+
+ if (is_busy_type(r.getType())) {
+ JX.Busy.start();
+ }
+ });
+
+
+ // Listen for completed requests.
+ JX.Router.listen('done', function(r) {
+ var type = r.getType();
+
+ if (is_bar_type(type)) {
+ queue[r.getID()] = true;
+ count--;
+ redraw_bar();
+ }
+
+ if (is_busy_type(r.getType())) {
+ JX.Busy.done();
+ }
});
+
});
diff --git a/webroot/rsrc/js/core/behavior-workflow.js b/webroot/rsrc/js/core/behavior-workflow.js
--- a/webroot/rsrc/js/core/behavior-workflow.js
+++ b/webroot/rsrc/js/core/behavior-workflow.js
@@ -4,10 +4,21 @@
* javelin-stratcom
* javelin-workflow
* javelin-dom
+ * javelin-router
*/
JX.behavior('workflow', function() {
+ // Queue a workflow at elevated priority. The user just clicked or submitted
+ // something, so service this before loading background content.
+ var queue = function(workflow) {
+ var routable = workflow.getRoutable()
+ .setPriority(2000)
+ .setType('workflow');
+
+ JX.Router.getInstance().queue(routable);
+ };
+
// If a user clicks an alternate submit button, make sure it gets marshalled
// into the workflow.
JX.Stratcom.listen(
@@ -40,7 +51,8 @@
// not just the <form /> itself.
e.prevent();
- JX.Workflow.newFromForm(e.getNode('tag:form'), extra).start();
+
+ queue(JX.Workflow.newFromForm(e.getNode('tag:form'), extra));
});
JX.Stratcom.listen(
@@ -70,7 +82,7 @@
}
e.prevent();
- JX.Workflow.newFromLink(e.getNode('tag:a')).start();
+ queue(JX.Workflow.newFromLink(e.getNode('tag:a')));
});
});
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Apr 22, 3:22 PM (1 d, 4 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7707716
Default Alt Text
D8979.id21301.diff (24 KB)
Attached To
Mode
D8979: Provide a global router for Ajax requests
Attached
Detach File
Event Timeline
Log In to Comment