todomvc
Version:
> Helping you select an MV\* framework
2,071 lines (1,648 loc) • 42.1 kB
JavaScript
// source ../src/head.js
(function (root, factory) {
'use strict';
var _global, _exports;
if (typeof exports !== 'undefined' && (root === exports || root == null)){
// raw nodejs module
_global = _exports = global;
}
if (_global == null) {
_global = typeof window === 'undefined' ? global : window;
}
if (_exports == null) {
_exports = root || _global;
}
if (typeof include !== 'undefined' && typeof include.js === 'function') {
// allow only one `include` per application
_exports.include = include;
_exports.includeLib = include.Lib || _global.includeLib;
return;
}
factory(_global, _exports, _global.document);
}(this, function (global, exports, document) {
'use strict';
// end:source ../src/head.js
// source ../src/1.scope-vars.js
/**
* .cfg
* : path := root path. @default current working path, im browser window.location;
* : eval := in node.js this conf. is forced
* : lockedToFolder := makes current url as root path
* Example "/script/main.js" within this window.location "{domain}/apps/1.html"
* will become "{domain}/apps/script/main.js" instead of "{domain}/script/main.js"
*/
var bin = {
js: {},
css: {},
load: {}
},
isWeb = !! (global.location && global.location.protocol && /^https?:/.test(global.location.protocol)),
reg_subFolder = /([^\/]+\/)?\.\.\//,
reg_hasProtocol = /^(file|https?):/i,
cfg = {
path: null,
loader: null,
version: null,
lockedToFolder: null,
sync: null,
eval: document == null
},
handler = {},
hasOwnProp = {}.hasOwnProperty,
emptyResponse = {
load: {}
},
__array_slice = Array.prototype.slice,
XMLHttpRequest = global.XMLHttpRequest;
// end:source ../src/1.scope-vars.js
// source ../src/2.Helper.js
var Helper = { /** TODO: improve url handling*/
reportError: function(e) {
console.error('IncludeJS Error:', e, e.message, e.url);
typeof handler.onerror === 'function' && handler.onerror(e);
}
},
XHR = function(resource, callback) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
xhr.readyState === 4 && callback && callback(resource, xhr.responseText);
};
xhr.open('GET', typeof resource === 'object' ? resource.url : resource, true);
xhr.send();
};
// end:source ../src/2.Helper.js
// source ../src/utils/fn.js
function fn_proxy(fn, ctx) {
return function(){
fn.apply(ctx, arguments);
};
}
function fn_doNothing(fn) {
typeof fn === 'function' && fn();
}
// end:source ../src/utils/fn.js
// source ../src/utils/object.js
var obj_inherit,
obj_getProperty,
obj_setProperty
;
(function(){
obj_inherit = function(target /* source, ..*/ ) {
if (typeof target === 'function') {
target = target.prototype;
}
var i = 1,
imax = arguments.length,
source, key;
for (; i < imax; i++) {
source = typeof arguments[i] === 'function'
? arguments[i].prototype
: arguments[i];
for (key in source) {
target[key] = source[key];
}
}
return target;
};
obj_getProperty = function(obj, property) {
var chain = property.split('.'),
length = chain.length,
i = 0;
for (; i < length; i++) {
if (obj == null)
return null;
obj = obj[chain[i]];
}
return obj;
};
obj_setProperty = function(obj, property, value) {
var chain = property.split('.'),
imax = chain.length - 1,
i = -1,
key;
while ( ++i < imax ) {
key = chain[i];
if (obj[key] == null)
obj[key] = {};
obj = obj[key];
}
obj[chain[i]] = value;
};
}());
// end:source ../src/utils/object.js
// source ../src/utils/array.js
function arr_invoke(arr, args, ctx) {
if (arr == null || arr instanceof Array === false) {
return;
}
for (var i = 0, length = arr.length; i < length; i++) {
if (typeof arr[i] !== 'function') {
continue;
}
if (args == null) {
arr[i].call(ctx);
}else{
arr[i].apply(ctx, args);
}
}
}
function arr_ensure(obj, xpath) {
if (!xpath) {
return obj;
}
var arr = xpath.split('.'),
imax = arr.length - 1,
i = 0,
key;
for (; i < imax; i++) {
key = arr[i];
obj = obj[key] || (obj[key] = {});
}
key = arr[imax];
return obj[key] || (obj[key] = []);
}
// end:source ../src/utils/array.js
// source ../src/utils/path.js
var path_getDir,
path_getFile,
path_getExtension,
path_resolveCurrent,
path_normalize,
path_win32Normalize,
path_resolveUrl,
path_combine,
path_isRelative
;
(function(){
path_getDir = function(path) {
return path.substring(0, path.lastIndexOf('/') + 1);
};
path_getFile = function(path) {
path = path
.replace('file://', '')
.replace(/\\/g, '/')
.replace(/\?[^\n]+$/, '');
if (/^\/\w+:\/[^\/]/i.test(path)){
// win32 drive
return path.substring(1);
}
return path;
};
path_getExtension = function(path) {
var query = path.indexOf('?');
if (query === -1) {
return path.substring(path.lastIndexOf('.') + 1);
}
return path.substring(path.lastIndexOf('.', query) + 1, query);
};
path_resolveCurrent = function() {
if (document == null) {
return typeof module === 'undefined'
? ''
: path_win32Normalize(module.parent.filename);
}
var scripts = document.getElementsByTagName('script'),
last = scripts[scripts.length - 1],
url = last && last.getAttribute('src') || '';
if (url[0] === '/') {
return url;
}
var location = window
.location
.pathname
.replace(/\/[^\/]+\.\w+$/, '');
if (location[location.length - 1] !== '/') {
location += '/';
}
return location + url;
};
path_normalize = function(path) {
return path
.replace(/\\/g, '/')
// remove double slashes, but not near protocol
.replace(/([^:\/])\/{2,}/g, '$1/')
;
};
path_win32Normalize = function(path){
path = path_normalize(path);
if (path.substring(0, 5) === 'file:')
return path;
return 'file:///' + path;
};
path_resolveUrl = function(url, parent) {
if (reg_hasProtocol.test(url))
return path_collapse(url);
if (url.substring(0, 2) === './')
url = url.substring(2);
if (url[0] === '/' && parent != null && parent.base != null) {
url = path_combine(parent.base, url);
if (reg_hasProtocol.test(url))
return path_collapse(url);
}
if (url[0] === '/' && cfg.path) {
url = cfg.path + url.substring(1);
if (reg_hasProtocol.test(url))
return path_collapse(url);
}
if (url[0] === '/') {
if (isWeb === false || cfg.lockedToFolder === true) {
url = url.substring(1);
}
} else if (parent != null && parent.location != null) {
url = parent.location + url;
}
return path_collapse(url);
};
path_isRelative = function(path) {
var c = path.charCodeAt(0);
switch (c) {
case 47:
// /
return false;
case 102:
// f
case 104:
// h
return reg_hasProtocol.test(path) === false;
}
return true;
};
path_combine = function() {
var out = '',
imax = arguments.length,
i = -1,
x
;
while ( ++i < imax ){
x = arguments[i];
if (!x)
continue;
x = path_normalize(x);
if (out === '') {
out = x;
continue;
}
if (out[out.length - 1] !== '/')
out += '/'
if (x[0] === '/')
x = x.substring(1);
out += x;
}
return out;
};
function path_collapse(url) {
while (url.indexOf('../') !== -1) {
url = url.replace(reg_subFolder, '');
}
return url.replace(/\/\.\//g, '/');
}
}());
// end:source ../src/utils/path.js
// source ../src/utils/tree.js
var tree_resolveUsage;
(function(){
tree_resolveUsage = function(resource, usage, next){
var use = [],
imax = usage.length,
i = -1,
obj, path, name, index, parent
;
while( ++i < imax ) {
name = path = usage[i];
index = path.indexOf('.');
if ( index !== -1) {
name = path.substring(0, index);
path = path.substring(index + 1);
}
parent = use_resolveParent(name, resource.parent, resource);
if (parent == null)
return null;
if (parent.state !== 4){
resource.state = 3;
parent.on(4, next, parent, 'push');
return null;
}
obj = parent.exports;
if (name !== path)
obj = obj_getProperty(obj, path);
// if DEBUG
(typeof obj === 'object' && obj == null)
&& console.warn('<include:use> Used resource has no exports', name, resource.url);
// endif
use[i] = obj;
}
return use;
};
function use_resolveParent(name, resource, initiator){
if (resource == null) {
// if DEBUG
console.warn('<include> Usage Not Found:', name);
console.warn('- Ensure to have it included before with the correct alias')
console.warn('- Initiator Stacktrace:');
var arr = [], res = initiator;
while(res != null){
arr.push(res.url);
res = res.parent;
}
console.warn(arr.join('\n'));
// endif
return null;
}
var includes = resource.includes,
i = -1,
imax = includes.length,
include, exports, alias
;
while( ++i < imax ) {
include = includes[i];
alias = include.route.alias || Routes.parseAlias(include.route);
if (alias === name)
return include.resource;
}
return use_resolveParent(name, resource.parent, initiator);
}
}());
// end:source ../src/utils/tree.js
// source ../src/2.Routing.js
var RoutesLib = function() {
var routes = {},
regexpAlias = /([^\\\/]+)\.\w+$/;
return {
/**
* @param route {String} = Example: '.reference/libjs/{0}/{1}.js'
*/
register: function(namespace, route, currentInclude) {
if (typeof route === 'string' && path_isRelative(route)) {
var res = currentInclude || include,
location = res.location || path_getDir(res.url || path_resolveCurrent());
if (path_isRelative(location)) {
location = '/' + location;
}
route = location + route;
}
routes[namespace] = route instanceof Array ? route : route.split(/[\{\}]/g);
},
/**
* @param {String} template = Example: 'scroller/scroller.min?ui=black'
*/
resolve: function(namespace, template) {
var questionMark = template.indexOf('?'),
aliasIndex = template.indexOf('::'),
alias,
path,
params,
route,
i,
x,
length,
arr;
if (aliasIndex !== -1){
alias = template.substring(aliasIndex + 2);
template = template.substring(0, aliasIndex);
}
if (questionMark !== -1) {
arr = template.substring(questionMark + 1).split('&');
params = {};
for (i = 0, length = arr.length; i < length; i++) {
x = arr[i].split('=');
params[x[0]] = x[1];
}
template = template.substring(0, questionMark);
}
template = template.split('/');
route = routes[namespace];
if (route == null){
return {
path: template.join('/'),
params: params,
alias: alias
};
}
path = route[0];
for (i = 1; i < route.length; i++) {
if (i % 2 === 0) {
path += route[i];
} else {
/** if template provides less "breadcrumbs" than needed -
* take always the last one for failed peaces */
var index = route[i] << 0;
if (index > template.length - 1) {
index = template.length - 1;
}
path += template[index];
if (i === route.length - 2){
for(index++; index < template.length; index++){
path += '/' + template[index];
}
}
}
}
return {
path: path,
params: params,
alias: alias
};
},
/**
* @arg includeData :
* 1. string - URL to resource
* 2. array - URLs to resources
* 3. object - {route: x} - route defines the route template to resource,
* it must be set before in include.cfg.
* example:
* include.cfg('net','scripts/net/{name}.js')
* include.js({net: 'downloader'}) // -> will load scipts/net/downloader.js
* @arg namespace - route in case of resource url template, or namespace in case of LazyModule
*
* @arg fn - callback function, which receives namespace|route, url to resource and ?id in case of not relative url
* @arg xpath - xpath string of a lazy object 'object.sub.and.othersub';
*/
each: function(type, includeData, fn, namespace, xpath) {
var key;
if (includeData == null) {
return;
}
if (type === 'lazy' && xpath == null) {
for (key in includeData) {
this.each(type, includeData[key], fn, null, key);
}
return;
}
if (includeData instanceof Array) {
for (var i = 0; i < includeData.length; i++) {
this.each(type, includeData[i], fn, namespace, xpath);
}
return;
}
if (typeof includeData === 'object') {
for (key in includeData) {
if (hasOwnProp.call(includeData, key)) {
this.each(type, includeData[key], fn, key, xpath);
}
}
return;
}
if (typeof includeData === 'string') {
var x = this.resolve(namespace, includeData);
if (namespace){
namespace += '.' + includeData;
}
fn(namespace, x, xpath);
return;
}
console.error('Include Package is invalid', arguments);
},
getRoutes: function(){
return routes;
},
parseAlias: function(route){
var path = route.path,
result = regexpAlias.exec(path);
return result && result[1];
}
};
};
var Routes = RoutesLib();
/*{test}
console.log(JSON.stringify(Routes.resolve(null,'scroller.js::Scroller')));
Routes.register('lib', '.reference/libjs/{0}/lib/{1}.js');
console.log(JSON.stringify(Routes.resolve('lib','scroller::Scroller')));
console.log(JSON.stringify(Routes.resolve('lib','scroller/scroller.mobile?ui=black')));
Routes.register('framework', '.reference/libjs/framework/{0}.js');
console.log(JSON.stringify(Routes.resolve('framework','dom/jquery')));
*/
// end:source ../src/2.Routing.js
// source ../src/3.Events.js
var Events = (function(document) {
if (document == null) {
return {
ready: fn_doNothing,
load: fn_doNothing
};
}
var readycollection = [];
function onReady() {
Events.ready = fn_doNothing;
if (readycollection == null) {
return;
}
arr_invoke(readycollection);
readycollection = null;
}
/** TODO: clean this */
if ('onreadystatechange' in document) {
document.onreadystatechange = function() {
if (/complete|interactive/g.test(document.readyState) === false) {
return;
}
onReady();
};
} else if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', onReady);
}else {
window.onload = onReady;
}
return {
ready: function(callback) {
readycollection.unshift(callback);
}
};
})(document);
// end:source ../src/3.Events.js
// source ../src/6.ScriptStack.js
/** @TODO Refactor loadBy* {combine logic} */
var ScriptStack = (function() {
var head,
currentResource,
stack = [],
_cb_complete = [],
_paused;
function loadScript(url, callback) {
//console.log('load script', url);
var tag = document.createElement('script');
tag.type = 'text/javascript';
tag.src = url;
if ('onreadystatechange' in tag) {
tag.onreadystatechange = function() {
(this.readyState === 'complete' || this.readyState === 'loaded') && callback();
};
} else {
tag.onload = tag.onerror = callback;
}
;(head || (head = document.getElementsByTagName('head')[0])).appendChild(tag);
}
function loadByEmbedding() {
if (_paused) {
return;
}
if (stack.length === 0){
trigger_complete();
return;
}
if (currentResource != null) {
return;
}
var resource = (currentResource = stack[0]);
if (resource.state === 1) {
return;
}
resource.state = 1;
global.include = resource;
global.iparams = resource.route.params;
function resourceLoaded(e) {
if (e && e.type === 'error') {
console.log('Script Loaded Error', resource.url);
}
var i = 0,
length = stack.length;
for (; i < length; i++) {
if (stack[i] === resource) {
stack.splice(i, 1);
break;
}
}
if (i === length) {
console.error('Loaded Resource not found in stack', resource);
return;
}
if (resource.state !== 2.5)
resource.readystatechanged(3);
currentResource = null;
loadByEmbedding();
}
if (resource.source) {
__eval(resource.source, resource);
resourceLoaded();
return;
}
loadScript(resource.url, resourceLoaded);
}
function processByEval() {
if (_paused) {
return;
}
if (stack.length === 0){
trigger_complete();
return;
}
if (currentResource != null) {
return;
}
var resource = stack[0];
if (resource.state < 2) {
return;
}
currentResource = resource;
resource.state = 1;
global.include = resource;
//console.log('evaling', resource.url, stack.length);
__eval(resource.source, resource);
for (var i = 0, x, length = stack.length; i < length; i++) {
x = stack[i];
if (x === resource) {
stack.splice(i, 1);
break;
}
}
if (resource.state !== 2.5)
resource.readystatechanged(3);
currentResource = null;
processByEval();
}
function trigger_complete() {
var i = -1,
imax = _cb_complete.length;
while (++i < imax) {
_cb_complete[i]();
}
_cb_complete.length = 0;
}
return {
load: function(resource, parent, forceEmbed) {
this.add(resource, parent);
if (!cfg.eval || forceEmbed) {
loadByEmbedding();
return;
}
// was already loaded, with custom loader for example
if (resource.source) {
resource.state = 2;
processByEval();
return;
}
XHR(resource, function(resource, response) {
if (!response) {
console.error('Not Loaded:', resource.url);
console.error('- Initiator:', resource.parent && resource.parent.url || '<root resource>');
}
resource.source = response;
resource.state = 2;
processByEval();
});
},
add: function(resource, parent){
if (resource.priority === 1)
return stack.unshift(resource);
if (parent == null)
return stack.push(resource);
var imax = stack.length,
i = -1
;
// move close to parent
while( ++i < imax){
if (stack[i] === parent)
return stack.splice(i, 0, resource);
}
// was still not added
stack.push(resource);
},
/* Move resource in stack close to parent */
moveToParent: function(resource, parent) {
var length = stack.length,
parentIndex = -1,
resourceIndex = -1,
i;
for (i = 0; i < length; i++) {
if (stack[i] === resource) {
resourceIndex = i;
break;
}
}
if (resourceIndex === -1) {
return;
}
for (i= 0; i < length; i++) {
if (stack[i] === parent) {
parentIndex = i;
break;
}
}
if (parentIndex === -1) {
return;
}
if (resourceIndex < parentIndex) {
return;
}
stack.splice(resourceIndex, 1);
stack.splice(parentIndex, 0, resource);
},
pause: function(){
_paused = true;
},
resume: function(){
_paused = false;
if (currentResource != null)
return;
this.touch();
},
touch: function(){
var fn = cfg.eval
? processByEval
: loadByEmbedding
;
fn();
},
complete: function(callback){
if (_paused !== true && stack.length === 0) {
callback();
return;
}
_cb_complete.push(callback);
}
};
})();
// end:source ../src/6.ScriptStack.js
// source ../src/4.IncludeDeferred.js
/**
* STATES:
* 0: Resource Created
* 1: Loading
* 2: Loaded - Evaluating
* 2.5: Paused - Evaluating paused
* 3: Evaluated - Childs Loading
* 4: Childs Loaded - Completed
*/
var IncludeDeferred = function() {
this.callbacks = [];
this.state = -1;
};
IncludeDeferred.prototype = { /** state observer */
on: function(state, callback, sender, mutator) {
if (this === sender && this.state === -1) {
callback(this);
return this;
}
// this === sender in case when script loads additional
// resources and there are already parents listeners
if (mutator == null) {
mutator = (this.state < 3 || this === sender)
? 'unshift'
: 'push'
;
}
state <= this.state ? callback(this) : this.callbacks[mutator]({
state: state,
callback: callback
});
return this;
},
readystatechanged: function(state) {
var i, length, x, currentInclude;
if (state > this.state) {
this.state = state;
}
if (this.state === 3) {
var includes = this.includes;
if (includes != null && includes.length) {
for (i = 0; i < includes.length; i++) {
if (includes[i].resource.state !== 4) {
return;
}
}
}
this.state = 4;
}
i = 0;
length = this.callbacks.length;
if (length === 0){
return;
}
//do not set asset resource to global
if (this.type === 'js' && this.state === 4) {
currentInclude = global.include;
global.include = this;
}
for (; i < length; i++) {
x = this.callbacks[i];
if (x == null || x.state > this.state) {
continue;
}
this.callbacks.splice(i,1);
length--;
i--;
/* if (!DEBUG)
try {
*/
x.callback(this);
/* if (!DEBUG)
} catch(error){
console.error(error.toString(), 'file:', this.url);
}
*/
if (this.state < 4){
break;
}
}
if (currentInclude != null){
global.include = currentInclude;
}
},
/** assets loaded and DomContentLoaded */
ready: function(callback) {
var that = this;
return this.on(4, function() {
Events.ready(function(){
that.resolve(callback);
});
}, this);
},
/** assets loaded */
done: function(callback) {
var that = this;
return this.on(4, function(){
that.resolve(callback);
}, this);
},
resolve: function(callback) {
var includes = this.includes,
length = includes == null
? 0
: includes.length
;
if (length > 0 && this.response == null){
this.response = {};
var resource,
route;
for(var i = 0, x; i < length; i++){
x = includes[i];
resource = x.resource;
route = x.route;
if (typeof resource.exports === 'undefined')
continue;
var type = resource.type;
switch (type) {
case 'js':
case 'load':
case 'ajax':
var alias = route.alias || Routes.parseAlias(route),
obj = type === 'js'
? (this.response)
: (this.response[type] || (this.response[type] = {}))
;
if (alias != null) {
obj_setProperty(obj, alias, resource.exports);
break;
}
console.warn('<includejs> Alias is undefined', resource);
break;
}
}
}
var response = this.response || emptyResponse;
var that = this;
if (this._use == null && this._usage != null){
this._use = tree_resolveUsage(this, this._usage, function(){
that.state = 4;
that.resolve(callback);
that.readystatechanged(4);
});
if (this.state < 4)
return;
}
if (this._use) {
callback.apply(null, [response].concat(this._use));
return;
}
callback(response);
}
};
// end:source ../src/4.IncludeDeferred.js
// source ../src/5.Include.js
var Include,
IncludeLib = {};
(function(IncludeDeferred) {
Include = function() {
IncludeDeferred.call(this);
};
stub_release(Include.prototype);
obj_inherit(Include, IncludeDeferred, {
// Array: exports
_use: null,
// Array: names
_usage: null,
isBrowser: true,
isNode: false,
setCurrent: function(data) {
var url = data.url,
resource = this.getResourceById(url, 'js');
if (resource == null) {
if (url[0] === '/' && this.base)
url = this.base + url.substring(1);
var resource = new Resource(
'js'
, { path: url }
, data.namespace
, null
, null
, url);
}
if (resource.state < 3) {
console.error("<include> Resource should be loaded", data);
}
/**@TODO - probably state shoulb be changed to 2 at this place */
resource.state = 3;
global.include = resource;
},
cfg: function(arg) {
switch (typeof arg) {
case 'object':
var key, value;
for (key in arg) {
value = arg[key];
switch(key){
case 'loader':
for(var x in value){
CustomLoader.register(x, value[x]);
}
break;
case 'modules':
if (value === true){
enableModules();
}
break;
default:
cfg[key] = value;
break;
}
}
break;
case 'string':
if (arguments.length === 1) {
return cfg[arg];
}
if (arguments.length === 2) {
cfg[arg] = arguments[1];
}
break;
case 'undefined':
return cfg;
}
return this;
},
routes: function(mix) {
if (mix == null) {
return Routes.getRoutes();
}
if (arguments.length === 2) {
Routes.register(mix, arguments[1], this);
return this;
}
for (var key in mix) {
Routes.register(key, mix[key], this);
}
return this;
},
promise: function(namespace) {
var arr = namespace.split('.'),
obj = global;
while (arr.length) {
var key = arr.shift();
obj = obj[key] || (obj[key] = {});
}
return obj;
},
/** @TODO - `id` property seems to be unsed and always equal to `url` */
register: function(_bin) {
var base = this.base,
key,
info,
infos,
imax,
i;
function transform(info){
if (base == null)
return info;
if (info.url[0] === '/')
info.url = base + info.url.substring(1);
if (info.parent[0] === '/')
info.parent = base + info.parent.substring(1);
info.id = info.url;
return info;
}
for (key in _bin) {
infos = _bin[key];
imax = infos.length;
i = -1;
while ( ++i < imax ) {
info = transform(infos[i]);
var id = info.id,
url = info.url,
namespace = info.namespace,
parent = info.parent && incl_getResource(info.parent, 'js'),
resource = new Resource(),
state = info.state
;
if (! (id || url))
continue;
if (url) {
if (url[0] === '/') {
url = url.substring(1);
}
resource.location = path_getDir(url);
}
resource.state = state == null
? (key === 'js' ? 3 : 4)
: state
;
resource.namespace = namespace;
resource.type = key;
resource.url = url || id;
resource.parent = parent;
resource.base = parent && parent.base || base;
switch (key) {
case 'load':
case 'lazy':
var container = document.querySelector('#includejs-' + id.replace(/\W/g, ''));
if (container == null) {
console.error('"%s" Data was not embedded into html', id);
break;
}
resource.exports = container.innerHTML;
if (CustomLoader.exists(resource)){
resource.state = 3;
CustomLoader.load(resource, CustomLoader_onComplete);
}
break;
}
//
(bin[key] || (bin[key] = {}))[id] = resource;
}
}
function CustomLoader_onComplete(resource, response) {
resource.exports = response;
resource.readystatechanged(4);
}
},
/**
* Create new Resource Instance,
* as sometimes it is necessary to call include. on new empty context
*/
instance: function(url, parent) {
var resource;
if (url == null) {
resource = new Include();
resource.state = 4;
return resource;
}
resource = new Resource();
resource.state = 4;
resource.location = path_getDir(path_normalize(url));
resource.parent = parent;
return resource;
},
getResource: function(url, type){
if (this.base && url[0] === '/')
url = this.base + url.substring(1);
return incl_getResource(url, type)
},
getResourceById: function(url, type){
var _bin = bin[type],
_res = _bin[url];
if (_res != null)
return _res;
if (this.base && url[0] === '/') {
_res = _bin[path_combine(this.base, url)];
if (_res != null)
return _res;
}
if (this.base && this.location) {
_res = _bin[path_combine(this.base, this.location, url)];
if (_res != null)
return _res;
}
if (this.location) {
_res = _bin[path_combine(this.location, url)];
if (_res != null)
return _res;
}
return null;
},
getResources: function(){
return bin;
},
plugin: function(pckg, callback) {
var urls = [],
length = 0,
j = 0,
i = 0,
onload = function(url, response) {
j++;
embedPlugin(response);
if (j === length - 1 && callback) {
callback();
callback = null;
}
};
Routes.each('', pckg, function(namespace, route) {
urls.push(route.path[0] === '/' ? route.path.substring(1) : route.path);
});
length = urls.length;
for (; i < length; i++) {
XHR(urls[i], onload);
}
return this;
},
client: function(){
if (cfg.server === true)
stub_freeze(this);
return this;
},
server: function(){
if (cfg.server !== true)
stub_freeze(this);
return this;
},
use: function(){
if (this.parent == null) {
console.error('<include.use> Parent resource is undefined');
return this;
}
this._usage = arguments;
return this;
},
pauseStack: fn_proxy(ScriptStack.pause, ScriptStack),
resumeStack: fn_proxy(ScriptStack.resume, ScriptStack),
allDone: function(callback){
ScriptStack.complete(function(){
var pending = include.getPending('js'),
await = pending.length;
if (await === 0) {
callback();
return;
}
var i = -1,
imax = await;
while( ++i < imax ){
pending[i].on(4, check);
}
function check() {
if (--await < 1)
callback();
}
});
},
getPending: function(type){
var resources = [],
res, key, id;
for(key in bin){
if (type != null && type !== key)
continue;
for (id in bin[key]){
res = bin[key][id];
if (res.state < 4)
resources.push(res);
}
}
return resources;
},
Lib: IncludeLib
});
// >> FUNCTIONS
function incl_getResource(url, type) {
var id = url;
if (path_isRelative(url) === true)
id = '/' + id;
if (type != null){
return bin[type][id];
}
for (var key in bin) {
if (bin[key].hasOwnProperty(id)) {
return bin[key][id];
}
}
return null;
}
function embedPlugin(source) {
eval(source);
}
function enableModules() {
if (typeof Object.defineProperty === 'undefined'){
console.warn('Browser do not support Object.defineProperty');
return;
}
Object.defineProperty(global, 'module', {
get: function() {
return global.include;
}
});
Object.defineProperty(global, 'exports', {
get: function() {
var current = global.include;
return (current.exports || (current.exports = {}));
},
set: function(exports) {
global.include.exports = exports;
}
});
}
function includePackage(resource, type, mix){
var pckg = mix.length === 1 ? mix[0] : __array_slice.call(mix);
if (resource instanceof Resource) {
return resource.include(type, pckg);
}
return new Resource('js').include(type, pckg);
}
function createIncluder(type) {
return function(){
return includePackage(this, type, arguments);
};
}
function doNothing() {
return this;
}
function stub_freeze(include) {
include.js =
include.css =
include.load =
include.ajax =
include.embed =
include.lazy =
include.inject =
doNothing;
}
function stub_release(proto) {
var fns = ['js', 'css', 'load', 'ajax', 'embed', 'lazy'],
i = fns.length;
while (--i !== -1){
proto[fns[i]] = createIncluder(fns[i]);
}
proto['inject'] = proto.js;
}
}(IncludeDeferred));
// end:source ../src/5.Include.js
// source ../src/7.CustomLoader.js
var CustomLoader = (function() {
// source loader/json.js
var JSONParser = {
process: function(source, res){
try {
return JSON.parse(source);
} catch(error) {
console.error(error, source);
return null;
}
}
};
// end:source loader/json.js
cfg.loader = {
json : JSONParser
};
function loader_isInstance(x) {
if (typeof x === 'string')
return false;
return typeof x.ready === 'function' || typeof x.process === 'function';
}
function createLoader(url) {
var extension = path_getExtension(url),
loader = cfg.loader[extension];
if (loader_isInstance(loader)) {
return loader;
}
var path = loader,
namespace;
if (typeof path === 'object') {
// is route {namespace: path}
for (var key in path) {
namespace = key;
path = path[key];
break;
}
}
return (cfg.loader[extension] = new Resource(
'js',
Routes.resolve(namespace, path),
namespace,
null,
null,
null,
1
));
}
function loader_completeDelegate(callback, resource) {
return function(response){
callback(resource, response);
};
}
function loader_process(source, resource, loader, callback) {
if (loader.process == null) {
callback(resource, source);
return;
}
var delegate = loader_completeDelegate(callback, resource),
syncResponse = loader.process(source, resource, delegate);
// match also null
if (typeof syncResponse !== 'undefined') {
callback(resource, syncResponse);
}
}
function tryLoad(resource, loader, callback) {
if (typeof resource.exports === 'string') {
loader_process(resource.exports, resource, loader, callback);
return;
}
function onLoad(resource, response){
loader_process(response, resource, loader, callback);
}
if (loader.load)
return loader.load(resource, onLoad);
XHR(resource, onLoad);
}
return {
load: function(resource, callback) {
var loader = createLoader(resource.url);
if (loader.process) {
tryLoad(resource, loader, callback);
return;
}
loader.on(4, function() {
tryLoad(resource, loader.exports, callback);
}, null, 'push');
},
exists: function(resource) {
if (!resource.url) {
return false;
}
var ext = path_getExtension(resource.url);
return cfg.loader.hasOwnProperty(ext);
},
/**
* IHandler:
* { process: function(content) { return _handler(content); }; }
*
* Url:
* path to IHandler
*/
register: function(extension, handler){
if (typeof handler === 'string'){
var resource = include;
if (resource.location == null) {
resource = {
location: path_getDir(path_resolveCurrent())
};
}
handler = path_resolveUrl(handler, resource);
}
cfg.loader[extension] = handler;
}
};
}());
// end:source ../src/7.CustomLoader.js
// source ../src/8.LazyModule.js
var LazyModule = {
create: function(xpath, code) {
var arr = xpath.split('.'),
obj = global,
module = arr[arr.length - 1];
while (arr.length > 1) {
var prop = arr.shift();
obj = obj[prop] || (obj[prop] = {});
}
arr = null;
Object.defineProperty(obj, module, {
get: function() {
delete obj[module];
try {
var r = __eval(code, global.include);
if (!(r == null || r instanceof Resource)){
obj[module] = r;
}
} catch (error) {
error.xpath = xpath;
Helper.reportError(error);
} finally {
code = null;
xpath = null;
return obj[module];
}
}
});
}
};
// end:source ../src/8.LazyModule.js
// source ../src/9.Resource.js
var Resource;
(function(Include, Routes, ScriptStack, CustomLoader) {
Resource = function(type, route, namespace, xpath, parent, id, priority) {
Include.call(this);
this.childLoaded = fn_proxy(this.childLoaded, this);
var url = route && route.path;
if (url != null)
this.url = url = path_resolveUrl(url, parent);
this.type = type;
this.xpath = xpath;
this.route = route;
this.parent = parent;
this.priority = priority;
this.namespace = namespace;
this.base = parent && parent.base;
if (id == null && url)
id = (path_isRelative(url) ? '/' : '') + url;
var resource = bin[type] && bin[type][id];
if (resource) {
if (resource.state < 4 && type === 'js')
ScriptStack.moveToParent(resource, parent);
return resource;
}
if (url == null) {
this.state = 3;
this.location = path_getDir(path_resolveCurrent());
return this;
}
this.state = 0;
this.location = path_getDir(url);
(bin[type] || (bin[type] = {}))[id] = this;
if (cfg.version)
this.url += (this.url.indexOf('?') === -1 ? '?' : '&') + 'v=' + cfg.version;
return process(this);
};
Resource.prototype = obj_inherit(Resource, Include, {
state: null,
location: null,
includes: null,
response: null,
url: null,
base: null,
type: null,
xpath: null,
route: null,
parent: null,
priority: null,
namespace: null,
setBase: function(baseUrl){
this.base = baseUrl;
return this;
},
childLoaded: function(child) {
var resource = this,
includes = resource.includes;
if (includes && includes.length) {
if (resource.state < 3) {
// resource still loading/include is in process, but one of sub resources are already done
return;
}
for (var i = 0; i < includes.length; i++) {
if (includes[i].resource.state !== 4) {
return;
}
}
}
resource.readystatechanged(4);
},
create: function(type, route, namespace, xpath, id) {
var resource;
this.state = this.state >= 3
? 3
: 2;
this.response = null;
if (this.includes == null)
this.includes = [];
resource = new Resource(type, route, namespace, xpath, this, id);
this.includes.push({
resource: resource,
route: route
});
return resource;
},
include: function(type, pckg) {
var that = this,
children = [],
child;
Routes.each(type, pckg, function(namespace, route, xpath) {
if (that.route != null && that.route.path === route.path) {
// loading itself
return;
}
child = that.create(type, route, namespace, xpath);
children.push(child);
});
var i = -1,
imax = children.length;
while ( ++i < imax ){
children[i].on(4, this.childLoaded);
}
return this;
},
pause: function(){
this.state = 2.5;
var that = this;
return function(exports){
if (arguments.length === 1)
that.exports = exports;
that.readystatechanged(3);
};
},
getNestedOfType: function(type){
return resource_getChildren(this.includes, type);
}
});
// private
function process(resource) {
var type = resource.type,
parent = resource.parent,
url = resource.url;
if (document == null && type === 'css') {
resource.state = 4;
return resource;
}
if (CustomLoader.exists(resource) === false) {
switch (type) {
case 'js':
case 'embed':
ScriptStack.load(resource, parent, type === 'embed');
break;
case 'ajax':
case 'load':
case 'lazy':
XHR(resource, onXHRCompleted);
break;
case 'css':
resource.state = 4;
var tag = document.createElement('link');
tag.href = url;
tag.rel = "stylesheet";
tag.type = "text/css";
document.getElementsByTagName('head')[0].appendChild(tag);
break;
}
} else {
if ('js' === type || 'embed' === type) {
ScriptStack.add(resource, resource.parent);
}
CustomLoader.load(resource, onXHRCompleted);
}
return resource;
}
function onXHRCompleted(resource, response) {
if (!response) {
console.warn('Resource cannt be loaded', resource.url);
//- resource.readystatechanged(4);
//- return;
}
switch (resource.type) {
case 'js':
case 'embed':
resource.source = response;
resource.state = 2;
ScriptStack.touch();
return;
case 'load':
case 'ajax':
resource.exports = response;
break;
case 'lazy':
LazyModule.create(resource.xpath, response);
break;
case 'css':
var tag = document.createElement('style');
tag.type = "text/css";
tag.innerHTML = response;
document.getElementsByTagName('head')[0].appendChild(tag);
break;
}
resource.readystatechanged(4);
}
function resource_getChildren(includes, type, out) {
if (includes == null)
return null;
if (out == null)
out = [];
var imax = includes.length,
i = -1,
x;
while ( ++i < imax ){
x = includes[i].resource;
if (type === x.type)
out.push(x);
if (x.includes != null)
resource_getChildren(x.includes, type, out);
}
return out;
}
}(Include, Routes, ScriptStack, CustomLoader));
// end:source ../src/9.Resource.js
// source ../src/10.export.js
IncludeLib.Routes = RoutesLib;
IncludeLib.Resource = Resource;
IncludeLib.ScriptStack = ScriptStack;
IncludeLib.registerLoader = CustomLoader.register;
exports.include = new Include();
exports.includeLib = IncludeLib;
// end:source ../src/10.export.js
}));
// source ../src/global-vars.js
function __eval(source, include) {
"use strict";
var iparams = include && include.route.params;
/* if !DEBUG
try {
*/
return eval.call(window, source);
/* if !DEBUG
} catch (error) {
error.url = include && include.url;
//Helper.reportError(error);
console.error(error);
}
*/
}
// end:source ../src/global-vars.js