Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/webroot/rsrc/externals/javelin/ext/view/ViewPlaceholder.js
12242 views
1
/**
2
* Initialize a client-side view from the server. The main idea here is to
3
* give server-side views a way to integrate with client-side views.
4
*
5
* The idea is that a client-side view will have an accompanying
6
* thin server-side component. The server-side component renders a placeholder
7
* element in the document, and then it will invoke this behavior to initialize
8
* the view into the placeholder.
9
*
10
* Because server-side views may be nested, we need to add complexity to
11
* handle nesting properly.
12
*
13
* Assuming a server-side view design that looks like hierarchical views,
14
* we have to handle structures like
15
*
16
* <server:component>
17
* <client:component id="1">
18
* <server:component>
19
* <client:component id="2">
20
* </client:component>
21
* </server:component>
22
* </client:component>
23
* </server:component>
24
*
25
* This leads to a problem: Client component 1 needs to initialize the behavior
26
* with its children, which includes client component 2. So client component
27
* 2 must be rendered first. When client component 2 is rendered, it will also
28
* initialize a copy of this behavior. If behaviors are run in the order they
29
* are initialized, the child component will run before the parent, and its
30
* placeholder won't exist yet.
31
*
32
* To solve this problem, placeholder behaviors are initialized with the token
33
* of a containing view that must be rendered first (if any) and a token
34
* representing it for its own children to depend on. This means the server code
35
* is free to call initBehavior in any order.
36
*
37
* In Phabricator, AphrontJavelinView demonstrates how to handle this correctly.
38
*
39
* config: {
40
* id: Node id to replace.
41
* view: class of view, without the 'JX.' prefix.
42
* params: view parameters
43
* children: messy and loud, cute when drunk
44
* trigger_id: id of containing view that must be rendered first
45
* }
46
*
47
* @provides javelin-behavior-view-placeholder
48
* @requires javelin-behavior
49
* javelin-dom
50
* javelin-view-renderer
51
* javelin-install
52
*/
53
54
55
56
JX.behavior('view-placeholder', function(config) {
57
JX.ViewPlaceholder.register(config.trigger_id, config.id, function() {
58
var replace = JX.$(config.id);
59
60
var children = config.children;
61
if (typeof children === 'string') {
62
children = JX.$H(children);
63
}
64
65
var view = new JX[config.view](config.params, children);
66
var rendered = JX.ViewRenderer.render(view);
67
68
JX.DOM.replace(replace, rendered);
69
});
70
});
71
72
JX.install('ViewPlaceholder', {
73
statics: {
74
register: function(wait_on_token, token, cb) {
75
var ready_q = [];
76
var waiting;
77
78
if (!wait_on_token || wait_on_token in JX.ViewPlaceholder.ready) {
79
ready_q.push({token: token, cb: cb});
80
} else {
81
waiting = JX.ViewPlaceholder.waiting;
82
waiting[wait_on_token] = waiting[wait_on_token] || [];
83
waiting[wait_on_token].push({token: token, cb: cb});
84
}
85
86
while(ready_q.length) {
87
var ready = ready_q.shift();
88
89
waiting = JX.ViewPlaceholder.waiting[ready.token];
90
if (waiting) {
91
for (var ii = 0; ii < waiting.length; ii++) {
92
ready_q.push(waiting[ii]);
93
}
94
delete JX.ViewPlaceholder.waiting[ready.token];
95
}
96
ready.cb();
97
98
JX.ViewPlaceholder.ready[token] = true;
99
}
100
101
},
102
ready: {},
103
waiting: {}
104
}
105
});
106
107