Skip to content

Instantly share code, notes, and snippets.

@kmnk
Created December 11, 2012 05:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kmnk/4256034 to your computer and use it in GitHub Desktop.
Save kmnk/4256034 to your computer and use it in GitHub Desktop.
namespace.js by CoffeeScript
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