-
Notifications
You must be signed in to change notification settings - Fork 0
/
reflux-preload.js
143 lines (141 loc) · 4.22 KB
/
reflux-preload.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
var PromiseCollector = require('promise-collector');
// Easy flag to distinquish state.
var isServer = typeof window === 'undefined';
var Preload = new PromiseCollector();
// Name for stashing to the client.
Preload.payloadName = 'refluxPreload';
/**
* Pipes a component's load action to Preload.
*
* Component should define the following hooks:
* <Promise> preload
* When called, returns a promise representing
* <boolean> isLoaded
* If it returns TRUE, then the View already has it's required data,
* and skip calling preload when changing to this View.
*
* @param {string} name
* Identifier for this loading action.
* @param {Reflux.Action} action
* The async Action to trigger with loaded or rejected data.
* @param {object} hooks
* Optional, key-values to override hooks.
* Values can be functions, or names of methods on the component.
* @return {Mixin}
* Mixin for a React Component.
*/
Preload.connect = function(name, action, hooks) {
var preload = 'preload';
var isLoaded = 'isLoaded';
if (hooks) {
preload = hooks.preload;
isLoaded = hooks.isLoaded;
}
Preload.receiveAction(name, action);
return {
componentWillMount: function () {
var _preload = typeof preload === "string" ? this[preload] : preload;
var _isLoaded = typeof isLoaded === "string" ? this[isLoaded] : isLoaded;
// If we have a preload method. Listen and start it.
if (_preload) {
// Server: Listen to preload's data, and kick off loading.
if (isServer) {
Preload.promise(name, _preload);
}
// Client: If Component would like, kick off loading on Component's behalf.
else if (!_isLoaded || !_isLoaded()) {
_preload();
}
}
}
};
};
/**
* Give PromiseCollector a function to call, when it has data to deliver.
*
* @param {string} name
* Identifier for this loading action.
* @param {Reflux.Action} action
* The async Action to trigger with loaded or rejected data.
*/
Preload.receiveAction = function(name, action) {
if (action.completed) {
Preload.receive(name,
action.completed.trigger.bind(action.completed),
action.failed.trigger.bind(action.failed));
}
else {
Preload.receive(name, action.trigger.bind(action));
}
};
/**
* Trigger an get a Sync Action's promise.
*
* @param {Reflux.Action} syncAction
* The Sync Action to trigger.
* @return {promise}
* Resolves with the emitted value.
*/
Preload.triggerPromise = function (syncAction) {
var promise = new Promise(function(resolve) {
var clear = syncAction.listen(function () {
var actionArgs = Array.prototype.slice.call(arguments, 0);
resolve.apply(this, actionArgs);
clear();
});
});
syncAction.apply(syncAction, Array.prototype.slice.call(arguments, 1));
return promise;
};
/**
* Wrap a render function in preload detection and delivery.
*
* @param {function} render
* A callback to render markup.
* @param {...}
* Params to be passed to render callback
* @return {Promise}
* Yields string of html from rendering function w/ paylaod attached.
* Rejects with object:
* -{object} errors: Errors from rejection
* -{string} html: String of html from rendering function w/ paylaod attached.
*/
Preload.render = function (render) {
var args = Array.prototype.slice.call(arguments, 1);
function postRender (preloadPackage) {
Preload.deliver(preloadPackage);
return render.apply(this, args) +
Preload.toPayload(preloadPackage);
}
return Preload.collect(function() {
render.apply(this, args);
})
.then(postRender, function (preloadPackage) {
throw {
html: postRender(preloadPackage),
errors: preloadPackage.rejected
};
});
};
/**
* Convert object to pass on to client.
*
* @param {object} collection
* Generic Object to store on client
* @return {string}
* Encoded payload to deliver to client (with inline script).
*/
Preload.toPayload = function (collection) {
return '<script>' + this.payloadName + '=' + JSON.stringify(collection) + '</script>';
};
/**
* Get payload on client side.
*
* @return {object}
* The payload.
*/
Preload.getPayload = function () {
/*global window*/
return window[this.payloadName];
};
module.exports = Preload;