Created
December 11, 2012 05:02
-
-
Save kmnk/4256030 to your computer and use it in GitHub Desktop.
namespace.coffee
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
Namespace = ( -> | |
merge = (target, source) -> | |
for key, val of source | |
if source.hasOwnProperty key then target[key] = val | |
target | |
_assertValidFQN = (fqn) -> | |
unless ///^[a-z0-9_.]+///.test fqn then throw 'invalid namespace' | |
class Procedure | |
constructor : -> | |
@state = {} | |
@steps = [] | |
@_status = 'init' | |
next : (state) -> | |
if state then @enqueue state | |
@ | |
isRunning : -> @_status is 'running' | |
enqueue : (state) -> @steps.push state | |
dequeue : -> @steps.shift() | |
call : (initialState, callback) -> | |
if @isRunning() then throw 'do not run twice' | |
@state = initialState or {} | |
@enqueue ($c) -> | |
$c() | |
if callback then callback @ | |
@_status = 'running' | |
@_invoke() | |
_invoke : -> | |
_self = @ | |
step = _self.dequeue() | |
unless step | |
_self._status = 'finished' | |
return | |
if step.call then return step.call _self.state, (state) -> | |
if state then _self.state = state | |
_self._invoke() | |
finishedProcess = 0 | |
if step.length is 0 then _self._invoke() | |
l = step.length | |
for one in step | |
one.call _self.state, -> | |
finishedProcess++ | |
if finishedProcess is l then _self._invoke() | |
return | |
createProcedure = (state) -> new Procedure().next(state) | |
class NamespaceObject | |
constructor : (fqn) -> | |
@stash = CURRENT_NAMESPACE : fqn | |
@fqn = fqn | |
@proc = createProcedure() | |
enqueue : (context) -> @proc.next context | |
call : (state, callback) -> @proc.call state, callback | |
valueOf : () -> "#NamespaceObject<#{@fqn}>" | |
merge : (obj) -> | |
merge @stash, obj | |
@ | |
getStash : () -> @stash | |
getExport : (importName) -> | |
if importName is '*' then return @stash | |
importNames = importName.split ',' | |
retStash = {} | |
for importName in importNames | |
names = importName.split '=>' | |
if 1 < names.length | |
retStash[ names[1] ] = @stash[ names[0] ] | |
else | |
retStash[ importName ] = @stash[ importName ] | |
retStash | |
NamespaceObjectFactory = ( -> | |
cache = {} | |
create : (fqn) -> | |
_assertValidFQN fqn | |
(cache[fqn] or (cache[fqn] = new NamespaceObject fqn)) | |
)() | |
class NamespaceDefinition | |
constructor : (nsObj) -> | |
@namespaceObject = nsObj | |
@requires = [] | |
@useList = [] | |
@stash = {} | |
@defineCallback = undefined | |
_self = @ | |
nsObj.enqueue ($c) -> _self.apply $c | |
use : (syntax) -> | |
@useList.push syntax | |
splitted = syntax.split ///\s+/// | |
fqn = splitted[0] | |
splitted[0] = '' | |
importName = splitted.join '' | |
_assertValidFQN fqn | |
@requires.push ($c) -> | |
context = @ | |
require = NamespaceObjectFactory.create fqn | |
require.call @, (state) -> | |
context.loadImport require, importName | |
$c() | |
@ | |
_mergeStashWithNS : (nsObj) -> | |
nsList = nsObj.fqn.split ///\./// | |
current = @getStash() | |
for ns in nsList[0..nsList.length-1] | |
unless current[ns] then current[ns] = {} | |
current = current[ns] | |
lastLeaf = nsList[nsList.length-1] | |
current[lastLeaf] = merge current[lastLeaf] or {}, nsObj.getStash() | |
loadImport : (nsObj, importName) -> | |
if importName | |
merge @stash, nsObj.getExport importName | |
else | |
@_mergeStashWithNS nsObj | |
define : (callback) -> | |
nsDef = @ | |
nsObj = @namespaceObject | |
@defineCallback = ($c) -> | |
ns = provide : (obj) -> | |
nsObj.merge obj | |
$c() | |
merge ns, nsDef.getStash() | |
merge ns, nsObj.getStash() | |
callback ns | |
getStash : -> @stash | |
valueOf : -> "#NamespaceDefinition<#{@namespaceObject}> uses : #{@useList.join ','}" | |
apply : (callback) -> | |
nsDef = @ | |
createProcedure(nsDef.requires) | |
.next(nsDef.defineCallback) | |
.call(nsDef, -> callback nsDef.getStash()) | |
createNamespace = (fqn) -> | |
new NamespaceDefinition NamespaceObjectFactory.create fqn or 'main' | |
merge createNamespace, | |
'Object' : NamespaceObjectFactory | |
Definition : NamespaceDefinition | |
Proc : createProcedure | |
createNamespace | |
)() | |
Namespace.use = (useSyntax) -> Namespace().use useSyntax | |
Namespace.fromInternal = Namespace.GET = ( -> | |
get = ( -> | |
createRequester = -> | |
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' | |
xhr | |
isSuccessStatus = (status) -> | |
( status >= 200 and status < 300 ) or | |
status is 304 or | |
status is 1223 or | |
( !status and (location.protocol is 'file:' or location.protocol is 'chrome:') ) | |
(url, callback) -> | |
xhr = createRequester() | |
xhr.open 'GET', url, true | |
xhr.onreadystatechange = -> | |
if xhr.readyState is 4 | |
if isSuccessStatus xhr.status or 0 | |
callback true, xhr.responseText | |
else | |
callback false | |
xhr.send '' | |
)() | |
(url, isManualProvide) -> | |
(ns) -> | |
get url, (isSuccess, responseText) -> | |
if isSuccess | |
if isManualProvide | |
eval responseText | |
else | |
ns.provide eval responseText | |
else | |
pub = {} | |
pub[url] = 'loading error' | |
ns.provide pub | |
)() | |
Namespace.fromExternal = ( -> | |
callbacks = {} | |
createScriptElement = (url, callback) -> | |
scriptElement = document.createElement 'script' | |
scriptElement.loaded = false | |
scriptElement.onload = -> | |
@loaded = true | |
callback() | |
scriptElement.onreadystatechange = -> | |
return unless ///^(loaded|complete)$///.test @readyState | |
return if @loaded | |
scriptElement.loaded = true | |
callback() | |
scriptElement.src = url | |
document.body.appendChild scriptElement | |
scriptElement.src | |
domSrc = (url) -> | |
(ns) -> | |
src = createScriptElement url, -> | |
name = ns.CURRENT_NAMESPACE | |
cb = callbacks[name] | |
delete callbacks[name] | |
cb ns | |
domSrc.registerCallback = (namespace, callback) -> | |
callbacks[namespace] = callback | |
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