Created
December 11, 2012 05:03
-
-
Save kmnk/4256034 to your computer and use it in GitHub Desktop.
namespace.js by CoffeeScript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var Namespace; | |
Namespace = (function() { | |
var NamespaceDefinition, NamespaceObject, NamespaceObjectFactory, Procedure, createNamespace, createProcedure, merge, _assertValidFQN; | |
merge = function(target, source) { | |
var key, val; | |
for (key in source) { | |
val = source[key]; | |
target[key] = val; | |
} | |
return target; | |
}; | |
_assertValidFQN = function(fqn) { | |
if (!/^[a-z0-9_.]+/.test(fqn)) throw 'invalid namespace'; | |
}; | |
Procedure = (function() { | |
function Procedure() { | |
this.state = {}; | |
this.steps = []; | |
this._status = 'init'; | |
} | |
Procedure.prototype.next = function(state) { | |
if (state) this.enqueue(state); | |
return this; | |
}; | |
Procedure.prototype.isRunning = function() { | |
return this._status === 'running'; | |
}; | |
Procedure.prototype.enqueue = function(state) { | |
return this.steps.push(state); | |
}; | |
Procedure.prototype.dequeue = function() { | |
return this.steps.shift(); | |
}; | |
Procedure.prototype.call = function(initialState, callback) { | |
if (this.isRunning()) throw 'do not run twice'; | |
this.state = initialState || {}; | |
this.enqueue(function($c) { | |
$c(); | |
if (callback) return callback(this); | |
}); | |
this._status = 'running'; | |
return this._invoke(); | |
}; | |
Procedure.prototype._invoke = function() { | |
var finishedProcess, l, one, step, _i, _len, _self; | |
_self = this; | |
step = _self.dequeue(); | |
if (!step) { | |
_self._status = 'finished'; | |
return; | |
} | |
if (step.call) { | |
return step.call(_self.state, function(state) { | |
if (state) _self.state = state; | |
return _self._invoke(); | |
}); | |
} | |
finishedProcess = 0; | |
if (step.length === 0) _self._invoke(); | |
l = step.length; | |
for (_i = 0, _len = step.length; _i < _len; _i++) { | |
one = step[_i]; | |
one.call(_self.state, function() { | |
finishedProcess++; | |
if (finishedProcess === l) return _self._invoke(); | |
}); | |
} | |
}; | |
return Procedure; | |
})(); | |
createProcedure = function(state) { | |
return new Procedure().next(state); | |
}; | |
NamespaceObject = (function() { | |
function NamespaceObject(fqn) { | |
this.stash = { | |
CURRENT_NAMESPACE: fqn | |
}; | |
this.fqn = fqn; | |
this.proc = createProcedure(); | |
} | |
NamespaceObject.prototype.enqueue = function(context) { | |
return this.proc.next(context); | |
}; | |
NamespaceObject.prototype.call = function(state, callback) { | |
return this.proc.call(state, callback); | |
}; | |
NamespaceObject.prototype.valueOf = function() { | |
return "#NamespaceObject<" + this.fqn + ">"; | |
}; | |
NamespaceObject.prototype.merge = function(obj) { | |
merge(this.stash, obj); | |
return this; | |
}; | |
NamespaceObject.prototype.getStash = function() { | |
return this.stash; | |
}; | |
NamespaceObject.prototype.getExport = function(importName) { | |
var importNames, names, retStash, _i, _len; | |
if (importName === '*') return this.stash; | |
importNames = importName.split(','); | |
retStash = {}; | |
for (_i = 0, _len = importNames.length; _i < _len; _i++) { | |
importName = importNames[_i]; | |
names = importName.split('=>'); | |
if (1 < names.length) { | |
retStash[names[1]] = this.stash[names[0]]; | |
} else { | |
retStash[importName] = this.stash[importName]; | |
} | |
} | |
return retStash; | |
}; | |
return NamespaceObject; | |
})(); | |
NamespaceObjectFactory = (function() { | |
var cache; | |
cache = {}; | |
return { | |
create: function(fqn) { | |
_assertValidFQN(fqn); | |
return cache[fqn] || (cache[fqn] = new NamespaceObject(fqn)); | |
} | |
}; | |
})(); | |
NamespaceDefinition = (function() { | |
function NamespaceDefinition(nsObj) { | |
var _self; | |
this.namespaceObject = nsObj; | |
this.requires = []; | |
this.useList = []; | |
this.stash = {}; | |
this.defineCallback = void 0; | |
_self = this; | |
nsObj.enqueue(function($c) { | |
return _self.apply($c); | |
}); | |
} | |
NamespaceDefinition.prototype.use = function(syntax) { | |
var fqn, importName, splitted; | |
this.useList.push(syntax); | |
splitted = syntax.split(/\s+/); | |
fqn = splitted[0]; | |
splitted[0] = ''; | |
importName = splitted.join(''); | |
_assertValidFQN(fqn); | |
this.requires.push(function($c) { | |
var context, require; | |
context = this; | |
require = NamespaceObjectFactory.create(fqn); | |
return require.call(this, function(state) { | |
context.loadImport(require, importName); | |
return $c(); | |
}); | |
}); | |
return this; | |
}; | |
NamespaceDefinition.prototype._mergeStashWithNS = function(nsObj) { | |
var current, lastLeaf, ns, nsList, _i, _len, _ref; | |
nsList = nsObj.fqn.split(/\./); | |
current = this.getStash(); | |
_ref = nsList.slice(0, (nsList.length - 1) + 1 || 9e9); | |
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | |
ns = _ref[_i]; | |
if (!current[ns]) current[ns] = {}; | |
current = current[ns]; | |
} | |
lastLeaf = nsList[nsList.length - 1]; | |
return current[lastLeaf] = merge(current[lastLeaf] || {}, nsObj.getStash()); | |
}; | |
NamespaceDefinition.prototype.loadImport = function(nsObj, importName) { | |
if (importName) { | |
return merge(this.stash, nsObj.getExport(importName)); | |
} else { | |
return this._mergeStashWithNS(nsObj); | |
} | |
}; | |
NamespaceDefinition.prototype.define = function(callback) { | |
var nsDef, nsObj; | |
nsDef = this; | |
nsObj = this.namespaceObject; | |
return this.defineCallback = function($c) { | |
var ns; | |
ns = { | |
provide: function(obj) { | |
nsObj.merge(obj); | |
return $c(); | |
} | |
}; | |
merge(ns, nsDef.getStash()); | |
merge(ns, nsObj.getStash()); | |
return callback(ns); | |
}; | |
}; | |
NamespaceDefinition.prototype.getStash = function() { | |
return this.stash; | |
}; | |
NamespaceDefinition.prototype.valueOf = function() { | |
return "#NamespaceDefinition<" + this.namespaceObject + "> uses : " + (this.useList.join(',')); | |
}; | |
NamespaceDefinition.prototype.apply = function(callback) { | |
var nsDef; | |
nsDef = this; | |
return createProcedure(nsDef.requires).next(nsDef.defineCallback).call(nsDef, function() { | |
return callback(nsDef.getStash()); | |
}); | |
}; | |
return NamespaceDefinition; | |
})(); | |
createNamespace = function(fqn) { | |
return new NamespaceDefinition(NamespaceObjectFactory.create(fqn || 'main')); | |
}; | |
merge(createNamespace, { | |
'Object': NamespaceObjectFactory, | |
Definition: NamespaceDefinition, | |
Proc: createProcedure | |
}); | |
return createNamespace; | |
})(); | |
Namespace.use = function(useSyntax) { | |
return Namespace().use(useSyntax); | |
}; | |
Namespace.fromInternal = Namespace.GET = (function() { | |
var get; | |
get = (function() { | |
var createRequester, isSuccessStatus; | |
createRequester = function() { | |
var xhr; | |
try { | |
xhr = new XMLHttpRequest(); | |
} catch (e) { | |
try { | |
xhr = new ActiveXObject('Msxml2.XMLHTTP.6.0'); | |
} catch (e) { | |
try { | |
xhr = new ActiveXObject('Mmsxml2.XMLHTTP.3.0'); | |
} catch (e) { | |
try { | |
xhr = new ActiveXObject('Msxml2.XMLHTTP'); | |
} catch (e) { | |
try { | |
xhr = new ActiveXObject('Microsoft.XMLHTTP'); | |
} catch (e) { | |
throw new Error('This browser does not support XMLHttpRequest'); | |
} | |
} | |
} | |
} | |
} | |
return xhr; | |
}; | |
isSuccessStatus = function(status) { | |
return (status >= 200 && status < 300) || status === 304 || status === 1223 || (!status && (location.protocol === 'file:' || location.protocol === 'chrome:')); | |
}; | |
return function(url, callback) { | |
var xhr; | |
xhr = createRequester(); | |
xhr.open('GET', url, true); | |
xhr.onreadystatechange = function() { | |
if (xhr.readyState === 4) { | |
if (isSuccessStatus(xhr.status || 0)) { | |
return callback(true, xhr.responseText); | |
} else { | |
return callback(false); | |
} | |
} | |
}; | |
return xhr.send(''); | |
}; | |
})(); | |
return function(url, isManualProvide) { | |
return function(ns) { | |
return get(url, function(isSuccess, responseText) { | |
var pub; | |
if (isSuccess) { | |
if (isManualProvide) { | |
return eval(responseText); | |
} else { | |
return ns.provide(eval(responseText)); | |
} | |
} else { | |
pub = {}; | |
pub[url] = 'loading error'; | |
return ns.provide(pub); | |
} | |
}); | |
}; | |
}; | |
})(); | |
Namespace.fromExternal = (function() { | |
var callbacks, createScriptElement; | |
callbacks = {}; | |
return createScriptElement = function(url, callback) { | |
var domSrc, scriptElement; | |
scriptElement = document.createElement('script'); | |
scriptElement.loaded = false; | |
scriptElement.onload = function() { | |
this.loaded = true; | |
return callback(); | |
}; | |
scriptElement.onreadystatechange = function() { | |
if (!/^(loaded|complete)$/.test(this.readyState)) return; | |
if (this.loaded) return; | |
scriptElement.loaded = true; | |
return callback(); | |
}; | |
scriptElement.src = url; | |
document.body.appendChild(scriptElement); | |
scriptElement.src; | |
domSrc = function(url) { | |
return function(ns) { | |
var src; | |
return src = createScriptElement(url, function() { | |
var cb, name; | |
name = ns.CURRENT_NAMESPACE; | |
cb = callbacks[name]; | |
delete callbacks[name]; | |
return cb(ns); | |
}); | |
}; | |
}; | |
domSrc.registerCallback = function(namespace, callback) { | |
return callbacks[namespace] = callback; | |
}; | |
return domSrc; | |
}; | |
})(); | |
try { | |
module.exports = Namespace; | |
} catch (e) { | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment