Page MenuHomePhabricator

D8979.id21301.diff
No OneTemporary

D8979.id21301.diff

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

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)

Event Timeline