Skip to content

Instantly share code, notes, and snippets.

@defunkt
Forked from kmnk/namespace.coffee
Created December 11, 2012 05:05
Show Gist options
  • Save defunkt/4256043 to your computer and use it in GitHub Desktop.
Save defunkt/4256043 to your computer and use it in GitHub Desktop.
Namespace = ( ->
merge = (target, source) ->
for key, val of source
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