yui-pathogen-encoder
Version:
Enables pathogen encoding in YUI Loader
179 lines (152 loc) • 5.45 kB
JavaScript
/*
* Copyright (c) 2013, Yahoo! Inc. All rights reserved.
* Copyrights licensed under the New BSD License.
* See the accompanying LICENSE file for terms.
*/
/*jshint eqeqeq: false*/
/**
The `express-yui.client` extension that provides a set of features
to control a YUI instance on the client side. This module will be
serialized and sent to the client side thru `res.expose()` and available
in the client side thru `window.app.yui`.
@module yui
@submodule client
**/
var utils = require('./utils');
/**
This method is meant to be serialized and sent to the client side to boot
the application in the browser. Please, do not modify if you don't know
what you're doing it.
**/
function bootstrap() {
var self = this,
d = document,
head = d.getElementsByTagName('head')[0],
// Some basic browser sniffing...
ie = /MSIE/.test(navigator.userAgent),
// number of seed parts that are still pending
pending = 0,
// it is just a queue of pending "use" statements until YUI gets ready
callback = [],
args = arguments,
config = typeof YUI_config != "undefined" ? YUI_config : {};
function flush() {
var l = callback.length,
i;
if (!self.YUI && typeof YUI == "undefined") {
throw new Error("YUI was not injected correctly!");
}
self.YUI = self.YUI || YUI;
// callback on every pending use statement
for (i = 0; i < l; i++) {
callback.shift()();
}
}
function decrementRequestPending() {
pending--;
if (pending <= 0) {
setTimeout(flush, 0);
} else {
// in case there is any remaining script to be loaded
load();
}
}
function createScriptNode(src) {
var node = d.createElement('script');
// use async=false for ordered async?
// parallel-load-serial-execute http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
if (node.async) {
node.async = false;
}
if (ie) {
node.onreadystatechange = function () {
if (/loaded|complete/.test(this.readyState)) {
this.onreadystatechange = null;
}
decrementRequestPending();
};
} else {
node.onload = node.onerror = decrementRequestPending;
}
node.setAttribute('src', src);
return node;
}
function load() {
if (!config.seed) {
throw new Error('YUI_config.seed array is required.');
}
var seed = config.seed,
l = seed.length,
i, node;
pending = seed.length;
// flagging the injection process to avoid duplicated entries
self._injected = true;
// appending every file from seed collection into the header
for (i = 0; i < l; i++) {
node = createScriptNode(seed.shift());
head.appendChild(node);
// TODO: if original node.async is undefined, it means the order has
// to be preserved manually, in which case we should insert
// them one by one to guarantee serial execute
if (node.async !== false) {
break;
}
}
}
callback.push(function () {
if (!self._Y) {
// extend core (YUI_config.extendedCore)
self.YUI.Env.core.push.apply(self.YUI.Env.core, config.extendedCore || []);
// create unique instance which is accesible thru app.yui.use()
self._Y = self.YUI();
self.use = self._Y.use;
}
// call use for arguments
self._Y.use.apply(self._Y, args);
});
// just in case YUI was injected manually in the page
self.YUI = self.YUI || (typeof YUI != "undefined" ? YUI : null);
if (!self.YUI && !self._injected) {
// attach seed and wait for it to finish to flush the callback
load();
} else if (pending <= 0) {
// everything is ready, just flush
flush();
} // else do nothing, the current pending job will take care of flushing callback
return this; // chaining
}
module.exports = {
/**
Attaches the seed into the head, then creates a YUI Instance and attaches
`modules` into it. This is equivalent to `YUI().use()` after getting the seed
ready. This method is a bootstrap implementation for the library, and the way
you use this in your templates, is by doing this:
<script>{{{state}}}</script>
<script>
app.yui.use('foo', 'bar', function (Y) {
// do something!
});
</script>
Where `state` defines the state of the express app, and `app.yui` defines
the helpers that `express-yui` brings into the client side.
@method use
@public
**/
use: utils.minifyFunction(bootstrap), // optimizing before exposing it to the client side
/**
Boots the application, rehydrate the app state and calls back to notify the
`ready` state of the app.
<script>{{{state}}}</script>
<script>
app.yui.ready(function () {
// do something!
});
</script>
@method ready
@param {Function} callback when the app is ready
@public
**/
ready: utils.minifyFunction(function (callback) {
this.use(callback);
})
};