Path: blob/master/webroot/rsrc/externals/javelin/ext/view/ViewPlaceholder.js
12242 views
/**1* Initialize a client-side view from the server. The main idea here is to2* give server-side views a way to integrate with client-side views.3*4* The idea is that a client-side view will have an accompanying5* thin server-side component. The server-side component renders a placeholder6* element in the document, and then it will invoke this behavior to initialize7* the view into the placeholder.8*9* Because server-side views may be nested, we need to add complexity to10* handle nesting properly.11*12* Assuming a server-side view design that looks like hierarchical views,13* we have to handle structures like14*15* <server:component>16* <client:component id="1">17* <server:component>18* <client:component id="2">19* </client:component>20* </server:component>21* </client:component>22* </server:component>23*24* This leads to a problem: Client component 1 needs to initialize the behavior25* with its children, which includes client component 2. So client component26* 2 must be rendered first. When client component 2 is rendered, it will also27* initialize a copy of this behavior. If behaviors are run in the order they28* are initialized, the child component will run before the parent, and its29* placeholder won't exist yet.30*31* To solve this problem, placeholder behaviors are initialized with the token32* of a containing view that must be rendered first (if any) and a token33* representing it for its own children to depend on. This means the server code34* is free to call initBehavior in any order.35*36* In Phabricator, AphrontJavelinView demonstrates how to handle this correctly.37*38* config: {39* id: Node id to replace.40* view: class of view, without the 'JX.' prefix.41* params: view parameters42* children: messy and loud, cute when drunk43* trigger_id: id of containing view that must be rendered first44* }45*46* @provides javelin-behavior-view-placeholder47* @requires javelin-behavior48* javelin-dom49* javelin-view-renderer50* javelin-install51*/52535455JX.behavior('view-placeholder', function(config) {56JX.ViewPlaceholder.register(config.trigger_id, config.id, function() {57var replace = JX.$(config.id);5859var children = config.children;60if (typeof children === 'string') {61children = JX.$H(children);62}6364var view = new JX[config.view](config.params, children);65var rendered = JX.ViewRenderer.render(view);6667JX.DOM.replace(replace, rendered);68});69});7071JX.install('ViewPlaceholder', {72statics: {73register: function(wait_on_token, token, cb) {74var ready_q = [];75var waiting;7677if (!wait_on_token || wait_on_token in JX.ViewPlaceholder.ready) {78ready_q.push({token: token, cb: cb});79} else {80waiting = JX.ViewPlaceholder.waiting;81waiting[wait_on_token] = waiting[wait_on_token] || [];82waiting[wait_on_token].push({token: token, cb: cb});83}8485while(ready_q.length) {86var ready = ready_q.shift();8788waiting = JX.ViewPlaceholder.waiting[ready.token];89if (waiting) {90for (var ii = 0; ii < waiting.length; ii++) {91ready_q.push(waiting[ii]);92}93delete JX.ViewPlaceholder.waiting[ready.token];94}95ready.cb();9697JX.ViewPlaceholder.ready[token] = true;98}99100},101ready: {},102waiting: {}103}104});105106107