chocolate
Version:
A full stack Node.js web framework built using Coffeescript
1,338 lines (1,115 loc) • 64.4 kB
text/coffeescript
_module = window ? module
_module[if _module.exports? then "exports" else "Chocodash"] = _ = {}
# `_.type` returns the type of an object
#
# _type({}) === '[object Object]'
_.type = (o) -> Object.prototype.toString.apply o
# `_.Type` provides a Type enumeration
#
# _type({}) === _.Type.Object
_.Type =
Object: '[object Object]'
Array: '[object Array]'
Boolean: '[object Boolean]'
Number: '[object Number]'
Date: '[object Date]'
Function: '[object Function]'
Math: '[object Math]'
String: '[object String]'
Undefined: '[object Undefined]'
Null: '[object Null]'
# `_.isObject` returns true if value is not a primitive
_.isObject = (o) ->
return no unless o?
switch typeof o
when 'object', 'function' then yes
else no
# `_.isBasicObject` returns true if value is a basic object
_.isBasicObject = (o) ->
return no unless o?
return o.constructor is {}.constructor
# `_.prototype` makes it easy to create a Javascript prototype
#
# following the classical class way:
#
# Service = _.prototype
# add: (a,b) -> a+b
# sub: (a,b) -> a-b
#
# or the mixin way:
#
# Service = _.prototype()
# Service.use ->
# @add = (a,b) -> a+b
# @sub = (a,b) -> a-b
#
# Then use your prototype to create javascript objects:
#
# sevr = new Service
# expect(serv instanceof Service).toBe true
# expect(serv.add 1,1).toBe 2
#
# You can define a prototype initializer by using the `constructor` keyword:
#
# Service = _.prototype
# constructor: (@name) ->
#
# serv = new Service "MyDoc"
# expect(serv.name).toBe "MyDoc"
#
# You can also create a prototype by adopting/copying
# another prototype's beahaviour and adding new functions:
#
# MoreMath = ->
# @multiply = (a,b) -> a * b
# @divide = (a,b) -> a / b
#
# CopiedService = _.prototype adopt:Service, use:MoreMath
# cop = new CopiedService
#
# expect(cop.add 2,2).toBe 4
# expect(cop.multiply 3,3).toBe 9
#
# You can finally create a prototype by inheriting
# another prototype's beahaviour and adding new functions
# that can access parent's overriden function:
#
# InheritedService = _.prototype
# inherit:Service
# use: -> @sub = (a,b) ->
# a + ' - ' + b + ' = ' + _.super @, a,b
#
# inh = new InheritedService
# expect(inh.add 2,2).toBe 4
# expect(inh.sub 2,2).toBe "2 - 2 = 0"
#
_.prototype = (options, more) ->
if more?
if options? then for k,v of more then options[k] = v
else options = more
more = null
prototype = do ->
flatten = ->
args = []
if arguments[0]? then Array::push.apply args, arguments
args
use: ->
for o in flatten arguments...
switch _.type o
when _.Type.Function then o.call @::
when _.Type.Array then for oo in o then @::[k] = v for own k,v of oo
else @::[k] = v for own k,v of o
@
adopt: ->
for o in flatten arguments...
@[k] = v for own k,v of o
@::[k] = v for own k,v of o::
@
inherit: (parent) ->
child = @
if parent?
if parent instanceof _.Prototyper then child.__prototyper__ = parent.prototyper ; parent = parent.prototyper()
child[k] = v for own k,v of parent
ctor = -> @constructor = child; return
ctor:: = parent::
child:: = new ctor()
child.__super__ = parent::
@
constructor = if options?.hasOwnProperty 'constructor' then options.constructor else null
ctor = switch
when constructor? and options?.inherit? then -> Array.prototype.unshift.call(arguments, @); _.super.apply _, arguments; Array.prototype.shift.call(arguments); constructor.apply @, arguments; return
when constructor? then -> constructor.apply @, arguments; return
when options?.inherit? then -> Array.prototype.unshift.call(arguments, @); _.super.apply _, arguments; return
else ->
if options?
for name, value of options
if name in ['adopt', 'inherit', 'use']
prototype[name].call ctor, value
else ctor::[name] = value unless name is 'constructor'
ctor[name] = fn for own name,fn of prototype when not ctor[name]?
ctor
# `_.super` calls the parent's function in an overriden function
_.super = () ->
super_func = null
[func, self] = arguments
if typeof func isnt 'function' then self = func; func = null
_func = if func? then func else arguments.callee.caller
_name = null
constructor = self.constructor
if constructor is _func.caller then super_func = constructor.__super__.constructor
else while constructor and not super_func?
for own k, v of constructor.prototype
if _func is v then _name = k; break
super_func = constructor.__super__[_name] if _name?
unless super_func?
constructor = if constructor.__super__? then constructor.__super__.constructor else null
if super_func
return super_func.apply self, Array.prototype.slice.call arguments, if func? then 2 else 1
# `_.Prototyper` allows to higher order function inheritance
_.Prototyper = _.prototype constructor:(@prototyper) ->
_.prototyper = (prototyper) -> new _.Prototyper prototyper
# `_.stringify` transforms a javascript object in a string that can be parsed back as an object
#
# You can stringify every property of an object, even a function or a Date:
#
# options:
# mode:
# 'json': reroute to JSON.stringify
# 'js' (default): returns a javascript string to recreate the given object
# 'full' : same as 'js' mode, but treats Array objects as full object and stringifies values by keys instead of by indexes
# prettify: multiline indented json presentation
# own: stringify only own object properties not inherited ones
# write: a function that will be called for each produced chunk of data
# write = function (chunk) {...}
# chunk: piece of string produced by the stringify function
# strict: a boolean to tell wether we accept a user typed object. If not we throw an error
# filter: do not stringify user defined types ojects unless type is present in filter list
# variable: when stringifying an object, it will declare a variable for every object's property
#
# o = {
# u: void 0,
# n: null,
# i: 1,
# f: 1.11,
# s: '2',
# b: true,
# add: function(a, b) {
# return a + b;
# },
# d: new Date("Sat Jan 01 2011 00:00:00 GMT+0100")
# };
#
# s = _.stringify o
# expect(s).toBe "{u:void 0,n:null,i:1,f:1.11,s:'2',b:true,add:function (a, b) {\n return a + b;\n },d:new Date(1293836400000)}"
_.stringify = ->
return undefined if arguments.length is 0
object = arguments[0]
options = arguments[1] ? {}
if options.prettify is yes
tab = ' '
newline = '\n'
else
tab = newline = ''
array_as_object = no
switch options.mode
when 'json' then return JSON.stringify object, null, tab
when 'full' then array_as_object = yes
when 'js' then # default mode
write = if typeof options.write is 'function' then options.write else (str) -> str
concat = (w1, w2, w3) ->
if w1? and w2? and w3? then w1 + w2 + w3
else null
doit = (o, level, index) ->
level ?= 0
indent = if tab is '' then '' else (tab for [0...level]).join ''
ni = newline + indent
nit = ni + tab
type = Object.prototype.toString.apply o
sep = if index > 0 then ',' + nit else ''
switch type
when '[object Object]'
if o.constructor isnt {}.constructor and o.stringify? and typeof o.stringify is 'function' then write sep + o.stringify()
else
if o.constructor isnt {}.constructor and options.strict then throw "_.stringify in strict mode can only accept pure objects"
else
i = 0 ; concat write(sep + "#{if options.variable and level is 0 then 'var ' else '{'}#{nit}"), "#{(chunk for k,v of o when ((not options.filter? or (v.constructor in options.filter))) and (options.own isnt true or {}.hasOwnProperty.call(o, k)) and (chunk = write((if i++ > 0 then ',' + nit else '') + (if options.variable and level is 0 then k else "'" + k.toString().replace(/\'/g, "\\'") + "'") + (if options.variable and level is 0 then '=' else ':')) + doit(v, level+1))).join('')}", write("#{ni}#{if options.variable and level is 0 then ';' else '}'}")
when '[object Array]'
if array_as_object then i = 0 ; concat write(sep + "function () {#{nit}var a = []; var o = {#{nit}"), "#{(chunk for own k,v of o when (chunk = write((if i++ > 0 then ',' + nit else '') + k + ':') + doit(v, level+1))).join('')}", write("};#{nit}for (var k in o) {a[k] = o[k];} return a; }()") # necessary to serialize both indexes and properties
else concat write(sep + "[#{nit}"), "#{(chunk for v, i in o when (chunk = doit(v, level+1, i))?).join ('')}", write("#{ni}]")
when '[object Boolean]' then write sep + o
when '[object Number]' then write sep + o
when '[object Date]' then write sep + "new Date(#{o.valueOf()})"
when '[object Function]' then write sep + o.toString()
when '[object Math]' then write sep + 'Math'
when '[object String]' then write sep + "'#{o.replace /\'/g, '\\\''}'"
when '[object Undefined]' then write sep + 'void 0'
when '[object Null]' then write sep + 'null'
when '[object Buffer]', '[object SlowBuffer]'
write sep + (if o.length is 16 then _.Uuid.unparse o else o.toString())
doit(object)
# `_.parse` transforms a stringified javascript object back to a javascript object
#
# a = _.parse "{u:void 0,n:null,i:1,f:1.11,s:'2',b:true,add:function (a, b) {\n return a + b;\n },d:new Date(1293836400000)}"
#
# expect(a.u).toBe undefined
# expect(a.n).toBe null
# expect(a.i).toBe 1
# expect(a.f).toBe 1.11
# expect(a.s).toBe '2'
# expect(a.b).toBe yes
# expect(a.add(1,1)).toBe 2
# expect(a.d.valueOf()).toBe new Date("Sat Jan 01 2011 00:00:00 GMT+0100").valueOf()
_.parse = (str) ->
(new Function "return " + str)()
# `_.param` transforms a javascript value to an url query parameter
#
# a = _.param {u:1, v:2}
# expect(a).toBe 'u=1&v=2'
_.param = (parameters) ->
return encodeURIComponent parameters if _.type(parameters) is _.Type.String
serialize = []
for own parameter of parameters
serialize.push "#{encodeURIComponent parameter}=#{encodeURIComponent parameters[parameter]}"
serialize.join '&'
# `_.serialize` helps manage asynchronous calls serialization.
# `_.async` and `_.run` are synonyms to `_.serialize`
#
# - use **self** to pass an object to which **this** will be binded when deferred functions will be called
# - use **local** to share variables between the deferred functions
# - **fn** is a function in which you declare the asynchronous functions you want to defer and serialize
# - use **option** with value `async:off` to ask **_.serialize** not to enforce async call on the last `run`
#
# Here is a simple example:
#
# _.serialize (run) ->
# run (end) ->
# my_first_async_func ->
# # ...
# end()
# run (end) ->
# my_second_async_func ->
# # ...
# end()
#
# Each run can return one of the following values that will tell the _.serialize service that
# one of its runs is async and that it is not forced to run its last run as async
#
#
# run (end) -> end
#
# or
#
# run (end) -> end.later
#
# Here is an example with async and sync runs mixed, using the _.async syntax:
#
# _.async (await) ->
# await (next) ->
# my_first_async_func ->
# # ...
# next()
# next.later
#
# await (next) ->
# sync_func()
# next()
#
# await (next) ->
# my_second_async_func ->
# # ...
# next()
# next.later
_.serialize = _.flow = _.async = (options, fn) ->
unless fn? then fn = options; options = {}
{self, local} = options
self ?= {}
local ?= {}
deferred = []
async = off
defer = (fn) ->
if arguments.length is 2 then fn = arguments[1] # first argument now unused
deferred.push fn
undefer = ->
undefered = deferred.shift()
if deferred.length is 0 and async is off and options?.async isnt off
(next) ->
setTimeout (-> undefered?.call self, next), 0
else
undefered
next = ->
if deferred.length
result = undefer().call self, next
switch result
when next then async = on
result
next.later = next
next.with = (result) -> if result isnt next.later then next() else next.later
self.next = next unless options.self?
fn_self = options.self
fn_self ?= fn
fn.call fn_self, defer, local
undefer()?.call self, next
_.parallelize = (self, fn) ->
if typeof self is 'function' then fn = self; self = fn
pushed = []
on_join = null ; join = (fn) -> on_join = fn
count = 0 ; end = -> count += -1 ; if count is 0 then on_join?.call self
push = (fn) -> pushed.push -> setTimeout (-> fn.call(self) ; end()), 0
fn.call self, push, join
count = pushed.length ; for dfn in pushed then dfn.call self
# `_.throttle` Returns a new function that, when invoked, invokes `func` at most once per `wait` milliseconds
#
# `options`:
# `wait`: number of milliseconds that must elapse between `func` invocations (defaults to 1000).
# `reset`: boolean to reset wait period every time the throttled function is called
# `accumulate`: boolean to accumulate arguments before calling throttled function.
# `func`: Function to wrap.
# return A new function that wraps the `func` function passed in.
#
# Thanks to: https://github.com/component/throttle
_.throttle = (options, func) ->
unless func? then func = options ; options = {}
{wait, reset, accumulate} = options
wait ?= 1000; reset ?= off; accumulate ?= off
ctx = undefined
args = undefined
rtn = undefined
timeoutID = undefined
last = 0
call = ->
timeoutID = undefined
last = (new Date).getTime()
rtn = func.apply(ctx, args)
ctx = undefined
args = undefined
return
->
ctx = this
unless accumulate then args = arguments
else unless args? then args = [arguments] else args.push(arguments)
delta = (new Date).getTime() - last
if reset
clearTimeout timeoutID if timeoutID?
timeoutID = setTimeout(call, wait)
else unless timeoutID?
if delta >= wait then call()
else timeoutID = setTimeout(call, wait - delta)
rtn
# `_.extend` copies values from an object to another object (no deep copy)
#
# Copy values unless `overwrite` is false:
#
# o = _.extend {first:1}, {second:2}
# expect(o.first).toBe(1)
# expect(o.second).toBe(2)
#
# Copy values on sub-object and overwrite values if overwrite is not false:
#
# o = _.extend {second:{sub1:'sub1'}}, {first:2, second:sub1:'sub2'}
# expect(o.first).toBe(2)
# expect(o.second.sub1).toBe('sub2')
#
# Copy values on sub-object if not set and preserve other values if overwrite is false:
#
# o = _.extend {second:{sub1:'sub1'}}, {first:2, second:sub1:'sub2'}, false
# expect(o.first).toBe(2)
# expect(o.second.sub1).toBe('sub1')
_.extend = (object, values, overwrite) ->
set = (o, val) ->
for own k,v of val
o ?= {}
if _.type(o[k]) is _.Type.Object and _.type(v) is _.Type.Object then set o[k], v
else o[k] = v unless overwrite is false and o[k]?
o
set object, values
# `_.clone` deeply copies values from an object to another object
#
# o = _.clone {first:1}, {second:2}
# expect(o.first).toBe(1)
# expect(o.second).toBe(2)
#
_.clone = ->
target = arguments[0] or {}
length = arguments.length
if length is 1 then i = 0; target = {}
else
i = 1
# Handle case when target is a string or something (possible in deep copy)
target = {} if typeof target isnt "object" and not _.type(target) is _.Type.Function
while i < length
# Only deal with non-null/undefined values
if (options = arguments[i])?
# Extend the base object
for name of options
src = target[name]
copy = options[name]
# Prevent never-ending loop
if target is copy then continue
# Recurse if we're merging plain objects or arrays
if copy and (_.isBasicObject(copy) or (copyIsArray = _.type(copy) is _.Type.Array))
if copyIsArray
copyIsArray = no
clone = if src and _.type(src) is _.Type.Array then src else []
else
clone = if src and _.isBasicObject(src) then src else {}
# Never move original objects, clone them
target[name] = _.clone clone, copy
# Don't bring in undefined values
else if copy isnt undefined
target[name] = copy
i += 1
# Return the modified object
target
# `_.defaults` ensure default values are set on an object
#
# Set default values if not set:
#
# o = _.defaults {first:1}, {second:2}
# expect(o.first).toBe(1)
# expect(o.second).toBe(2)
#
# Set default values on sub-object if not set and preserve other values:
#
# o = _.defaults {second:{sub1:'sub1'}}, {first:2, second:sub2:'sub2'}
# expect(o.first).toBe(2)
# expect(o.second.sub1).toBe('sub1')
# expect(o.second.sub2).toBe('sub2')
_.defaults = (object, defaults) ->
_.extend object, defaults, false
# Signals and Observers
# Adapted from Reactor written by fynyky (https://github.com/fynyky/reactor.js)
# Signals represent values which can be observed
# A signal's value can depend on one or more other signals
# Observers represent reactions to changes in signals
# An observer can be observing one or more signals
# Together, signals and observers form a directed acyclic graph
# Signals form the root and intermediate nodes of the graph
# Observers form the leaf nodes in the graph
# When a signal is updated, it propagates its changes through the graph
# Observers are updated last after all affected signals have been updated
# From the perspective of observers, all signals are updated atomically and instantly
# Observer and signal dependencies are set automatically and dynamically
# It detects when a signal is being used at run time, and automatically establishes the link
# This means there is no need to explicitly declare dependencies
# And dependencies can be changed across the execution of the program
# TODOs
# remove redundant triggering when same value is made
# add in ability to get old values
# events
# multi commit batching
# Signals are objects representing observed values
# They are read by executing the `value()` function with no arguments
# They are set by executing the `value()` function with a signal definition as the only argument
# If the definition is itself a function - it is executed to retrive the signal value
# Otherwise, the definition is read as the value directly
# If a signal definition reads other signals, it automatically gets set as a dependent
# If any signal dependencies get updated, the dependent signal is automatically updated as well
# Note that a signal's definition should NOT have any external effects
# It should only read other values and return its own value
# For external effects, use observers instead
# To "destroy" a signal, just set its definition to null
# -----------------------------------------------------------------------------
# Signals themselves have 3 main components
# A value - the cached value returned when the signal is read
# An evaluate function - the "guts" which sets the value and handles propagation
# The signal function - a wrapper providing the interface to read and write to the signal
_.cell = (def, options) ->
signal = new Signal def, options
(def) ->
if def?
if arguments.length > 1
method = Array::splice.call arguments, 0, 1
signal[method].apply signal, arguments
else
if def is _ then return signal
else signal.value.call(signal, def)
else
signal.value.call(signal)
_.observer = (def) ->
observer = new Observer def
(def) ->
if arguments.length > 1
method = Array::splice.call arguments, 0, 1
observer[method].apply observer, arguments
else
observer.observe def
_.Signal = _.Cell = Signal = _.prototype
adopt:
# Constants
type: 0x0100
idle: 0x0001
initialized: 0x0002
object: 0x0004
array: 0x0008
### Events on evalutate
value REPLACE - ALL
set REPLACE - ONE - at KEY
delete REMOVE - ONE - at KEY
pop REMOVE - ONE - at LAST
push INSERT - ONE or MANY - at END
reverse SORT - ALL
shift REMOVE - ONE - at FIRST
sort SORT - ALL
splice REMOVE - ONE or MANY - at INDEX and INSERT - ONE or MANY - at INDEX
unshift INSERT - ONE or MANY - at FIRST
###
Event:
Type: Insert:0, Replace:1, Remove:2, Sort:3
What: All:0, One:1, Many:2
Where: First:0, Last:1, End:2, Index:3
EventDef:
value: type:'Replace', what: 'All'
set: type:'Replace', what:'One', where:'Index'
delete: type:'Remove', what:'One', where:'Index'
pop: type:'Remove', what:'One', where:'Last'
push: type:'Insert', what:'One', where:'End'
reverse: type:'Sort', what:'All'
shift: type:'Remove', what:'One', where:'First'
sort: type:'Sort', what:'All'
splice: type:'Remove', what:'One', where:'Index'
unshift: type:'Insert', what:'One', where:'First'
# Global stack to automatically track signal dependencies
# Whenever a signal or observer is evaluated - it puts itself on the dependency stack
# When another signals is read - the dependency stack is checked to see who is asking
# The reader gets added as a dependent to the signal being read
# wWen a signal or observer's evaluation is done - it pops itself off the stack
dependencyStack: []
# Global dictionnary to trac active transactions
transactions: {}
# Defered signal management
Defer: _.prototype
constructor: (@signal, @observerList) -> @count = 0
value: (fn) ->
stack = Signal.dependencyStack
Signal.dependencyStack = [@signal]
@signal._value = fn()
@signal.flags |= Signal.idle | Signal.initialized
Signal.dependencyStack = stack
@count += -1
# Now ask dependant signals to evaluate themselves
@signal.propagate @observerList, @
if @signal.observers? then observer.notify on for observer in @signal.observers
one: -> @count += 1
idle: -> @count is 0
constructor: (@definition, @options) ->
# @helpers can contain 'set' and array functions helpers to be called in place of our version
for name, helper of @helpers then do (helper, name, self=@) -> self.helpers[name] = -> helper.apply self, arguments
# With id option, every value inside has to have an id associated
if @options?.id? then @globalize('uuid', -> _.Uuid())
# Cached value of this signal calculated by the evaluate function
# Recalculated when a definition has changed or when notified by a dependency
@_value = null
# Flags for: Type, Idle, Initialized, Object, Array
# Initialized will be true after first evaluation
# Idle is false when Signal is calculating its value
@flags = Signal.type | Signal.idle
# List of signals that this depends on
# Used to remove self from their dependents list when necessary
# Note that while the dependents is a list of evaluate objects
# dependencies is a list of the signals themselves
@dependencies = undefined
# Symmetrically - the list of other signals and observers that depend on this signal
# Used to know who to notify when this signal has been updatedls
@dependents = undefined
@observers = undefined
@dependentTargets = undefined
# run an initial evaluation on creation
# no need to notify observers/dependents because it shouldnt have any yet
@evaluate()
# `globalize` adds an attribute to every Signal's value's sub property
# and transform any sub-property to a Signal object
globalize: (property, builder) ->
globalized = {}
globalize = (current) ->
unless current[property]?
item = current.value
type = _.type item
current[property] = builder()
switch type
when _.Type.Object, _.Type.Array
switch type
when _.Type.Object
for name, value of item when name isnt '_' and globalized[value._?._?.uuid] isnt true
globalize {item:value, name:name, parent:item}
if _.isObject value then globalized[value._._.uuid] = true
when _.Type.Array
for name, value of item when name isnt '_' and globalized[value._?._?.uuid] isnt true
pos = null unless (pos = parseInt name).toString() is name
globalize {item:value, name: (if pos? then undefined else name), parent:{item}}
if _.isObject value then globalized[value._._.uuid] = true
return
globalize this
return
# evaluate is a function to calculates the signal value given the definition
# it recursively revaluates any signals that depend on it as well
# Simultaneously, in order to know what observers to notify later
# it recursively passes through a list to collect all the observers of the updated signals
# After the signal cascade has completed, the observers on the list are notified
evaluate: (observerList, defer) ->
# by default the value is the definition
@_value = @definition
@error = null
@flags |= Signal.idle
@flags &= ~Signal.object & ~Signal.array
# clear old dependencies both forward and back pointers
if @dependencies? then for dependency in @dependencies when dependency.dependents?
dependentIndex = dependency.dependents.indexOf @
dependency.dependents.splice dependentIndex, 1
@dependencies = undefined
switch type = _.type @definition
# if definition is a function then we need to evaluate it
# and set it up to be notified when its dependencies change
when _.Type.Function
defer ?= new Signal.Defer @, observerList
# evaluate the definition and set new dependencies
Signal.dependencyStack.push @
try
deferred = do (signal=@) ->
(fn) ->
prev = defer.signal
defer.signal = signal
defer.value.call defer, fn
defer.signal = prev
try
result = @definition deferred
catch e
result = undefined
@error = e
if @_catch?
@_catch e
@error = null
if result is deferred
@flags &= ~Signal.idle
defer.one()
if @observers? then observer.notify(off) for observer in @observers
else
@_value = result
@flags |= Signal.idle | Signal.initialized
finally
Signal.dependencyStack.pop()
when _.Type.Object, _.Type.Array
@flags |= if type is _.Type.Object then Signal.object else Signal.array
# Add this signals own observers to the observer list
if observerList? and @observers? then for observer in @observers
observerList.push observer if observer? and (observerList.indexOf observer) < 0
# Now propagate the evaluation to the dependant signals
@propagate observerList, defer unless deferred? and result is deferred
defer?.idle() ? yes
set: (key, value, trigger) ->
if arguments.length is 1 then @definition = key
else if @flags & (Signal.object | Signal.array) then @definition[key] = value
@value(@definition) unless trigger is off # Manually trigger the refresh
get: -> @value()
delete: (key, trigger)->
if @flags & (Signal.object | Signal.array) then delete @definition[key]
@value(@definition) unless trigger is off # Manually trigger the refresh
pop: -> return unless @flags & Signal.array ; o = @definition.pop.apply @definition, arguments ; @value(@definition) ; o
push: -> return unless @flags & Signal.array ; o = @definition.push.apply @definition, arguments ; @value(@definition) ; o
reverse: -> return unless @flags & Signal.array ; o = @definition.reverse.apply @definition, arguments ; @value(@definition) ; o
shift: -> return unless @flags & Signal.array ; o = @definition.shift.apply @definition, arguments ; @value(@definition) ; o
sort: -> return unless @flags & Signal.array ; o = @definition.sort.apply @definition, arguments ; @value(@definition) ; o
splice: -> return unless @flags & Signal.array ; o = @definition.splice.apply @definition, arguments ; @value(@definition) ; o
unshift: -> return unless @flags & Signal.array ; o = @definition.unshift.apply @definition, arguments ; @value(@definition) ; o
# catch register a function to call when an error occurs
catch: (report) ->
@_catch = report
if @error?
@_catch @error
@error = null
# Tells whether this Signal is currently calculating its value
idle: -> @flags & Signal.idle
# propagate the evaluation to the dependant signals
propagate: (observerList, defer) ->
return unless @dependents?
# A copy of the dependents to be evaluated
# This is used to avoid redundant evaluation where a descendent has
# already read from this value
# Check to see if a dependent is still on this list before evaluating it
# If a descendent reads from this signal at some point
# it will remove itself from this list
@dependentTargets = @dependents[...]
# Recursively evaluate any dependents
# Note that the dependents is a list of the dependents signals
# and give them the observer list to add to as well
# need to duplicate list since it will be modified by child evaluations
for dependent in @dependents[...]
if dependent? and @dependentTargets.indexOf(dependent) >= 0
dependent.evaluate observerList, defer
# The actual signal function that is returned to the caller
# Read by calling with no arguments
# Write by calling with a new argument
# Array methods if previously given an array as a definition
# "set" convenience method if given an object/array as a definition
value: (newDefinition)->
# Write path
# If a new definition is given, update the signal
# recursively update any dependent signals
# then finally trigger affected observers
# -------------------------------------------------------------------------
# Life of a write
# new definition is set
# delete/configure convenience methods
# execute new definition if necessary
# add observers to notify later
# make list of target dependents to be notified
# recursively evaluate dependents
# delete/configure convenience methods
# execute new definition
# get removed from target dependents list
# add observers to notify later
# notify observers
if newDefinition isnt undefined
@definition = newDefinition
observerList = []
if @evaluate observerList
observer.trigger() for observer in observerList[...] # Copy the list since the trigger might modify it
return @_value
# Read path
# If no definition is given, we treat it as a read call and return the cached value
# Simultaneously, check for calling signals/observers and register dependenceis accordingly
# -------------------------------------------------------------------------
# Life of a read
# check to see who is asking
# remove them from the list of targets to be notified
# register them as a dependent
# register self and their dependency
# return value
else
# check the global stack for the most recent dependent being evaluated
# assume this is the caller and set it as a dependent
dependent = Signal.dependencyStack[Signal.dependencyStack.length - 1]
# If its a signal dependency - register it as such
# symmetrically register self as a dependency for cleaning dependencies later
if dependent? and dependent.flags & Signal.type
@dependents ?= []
@dependents.push dependent if @dependents.indexOf(dependent) < 0
dependent.dependencies ?= []
dependent.dependencies.push @ if dependent.dependencies.indexOf(@) < 0
# remove the dependent from the targets list if necessary
# this is used to avoid duplicate redundant evaluation
@dependentTargets ?= []
targetDependentIndex = @dependentTargets.indexOf dependent
@dependentTargets[targetDependentIndex] = null if targetDependentIndex >= 0
# If it is a observer dependency - similarly register it as a observer
# symmetrically register self as a observee for cleaning dependencies later
else if dependent? and dependent.flags & Observer.type
@observers ?= []
@observers.push dependent if @observers.indexOf(dependent) < 0
dependent.observees.push @ if dependent.observees.indexOf(@) < 0
return @_value
# Observers report signal changes
# They are defined in a manner similar to Signals
# The primary differences of observers are
# - they have no value to read
# - they cannot be observed themselves
# - they are notified only after signals have all been updated
# to remove an observer - just set its value to null
_.Observer = Observer = _.prototype
adopt:
# Constants
type: 0x0200
constructor: (report) ->
@observees = []
@flags = Observer.type
@observe report
# returned in the form of a function
# as with signals - can be provided with a new definition
observe: (report)->
@report = report
@_ready = true
# clear old observees
for observee in @observees when observee.observers?
observerIndex = observee.observers.indexOf @
observee.observers.splice(dependentIndex, 1)
@observees = []
# do initial trigger and set up to listen for future updates
Signal.dependencyStack.push @
try @report() if @report?
finally Signal.dependencyStack.pop()
@_ready = true
for observee in @observees
unless observee.idle() then @_ready = false ; break
return null
# receive a report from a deferred signal and trigger when ready
notify: (status) ->
if status is on
@_ready = true
for observee in @observees
unless observee.idle() then @_ready = false ; break
if @_ready then @trigger()
else @_ready = false
# Activate the observer as well as reconfigure dependencies
trigger: ->
# do trigger and set up to listen for future updates
Signal.dependencyStack.push @
try @report() if @report?
finally Signal.dependencyStack.pop()
# Tells whether this Observer is currently calculating its value
ready: -> @_ready
# Publishers report basic signal changes to one-to-many reporters
_.Publisher = Publisher = _.prototype
constructor: ->
@subscribers = []
@unreported = []
notify: (value) ->
if @subscribers.length > 0 then for report in @subscribers then report value
else @unreported.push value
subscribe: (reporter) ->
@subscribers.push reporter
if @unreported.length > 0
self = @
notify = ->
for value in self.unreported then reporter value
self.unreported.length = 0
setTimeout notify, 0
# slugify function
_.slugify = (text) ->
# Use hash map for special characters
specialChars =
'à': 'a'
'ä': 'a'
'á': 'a'
'â': 'a'
'æ': 'a'
'å': 'a'
'ë': 'e'
'è': 'e'
'é': 'e'
'ê': 'e'
'î': 'i'
'ï': 'i'
'ì': 'i'
'í': 'i'
'ò': 'o'
'ó': 'o'
'ö': 'o'
'ô': 'o'
'ø': 'o'
'ù': 'o'
'ú': 'u'
'ü': 'u'
'û': 'u'
'ñ': 'n'
'ç': 'c'
'ß': 's'
'ÿ': 'y'
'œ': 'o'
'ŕ': 'r'
'ś': 's'
'ń': 'n'
'ṕ': 'p'
'ẃ': 'w'
'ǵ': 'g'
'ǹ': 'n'
'ḿ': 'm'
'ǘ': 'u'
'ẍ': 'x'
'ź': 'z'
'ḧ': 'h'
'·': '-'
'/': '-'
'_': '-'
',': '-'
':': '-'
';': '-'
text.toString().toLowerCase().replace(/\s+/g, '-').replace(/./g, (target, index, str) ->
specialChars[target] or target
).replace(/&/g, '-and-').replace(/[^\w\-]+/g, '').replace(/\-\-+/g, '-').replace(/^-+/, '').replace /-+$/, ''
# cuid.js
# Collision-resistant UID generator for browsers and node.
# Sequential for fast db lookups and recency sorting.
# Safe for element IDs and server-side lookups.
#
# Extracted from CLCTR
#
# Copyright (c) Eric Elliott 2012
# MIT License
_.Cuid = do ->
c = 0 ; blockSize = 4 ; base = 36 ; discreteValues = Math.pow base, blockSize
pad = (num, size) -> s = '000000000' + num ; s.substr s.length - size
randomBlock = -> pad (Math.random() * discreteValues << 0).toString(base), blockSize
safeCounter = -> (c = if c < discreteValues then c else 0) ; c++ ; c - 1
api = ->
letter = 'c'
timestamp = (new Date).getTime().toString(base)
counter = undefined
fingerprint = api.fingerprint()
random = randomBlock() + randomBlock()
counter = pad(safeCounter().toString(base), blockSize)
letter + timestamp + counter + fingerprint + random
api.slug = ->
date = (new Date).getTime().toString(36)
counter = undefined
print = api.fingerprint().slice(0, 1) + api.fingerprint().slice(-1)
random = randomBlock().slice(-2)
counter = safeCounter().toString(36).slice(-4)
date.slice(-2) + counter + print + random
api.globalCount = unless window? then undefined else ->
cache = (-> i = undefined ; count = 0 ; (for i of window then count++) ; count)()
api.globalCount = -> cache
cache
api.fingerprint = if window?
->
pad (navigator.mimeTypes.length + navigator.userAgent.length).toString(36) + api.globalCount().toString(36), 4
else
->
os = require('os') ; padding = 2
pid = pad(process.pid.toString(36), padding)
hostname = os.hostname() ; length = hostname.length
hostId = pad(hostname.split('').reduce(((prev, char) -> +prev + char.charCodeAt(0) ), +length + 36).toString(36), padding)
pid + hostId
api
# uuid.js
# Generate RFC4122(v4) UUIDs, and also non-RFC compact ids
#
# uuid = Uuid() // produces a string like "88a8814c-fd78-44cc-b4c1-dbff3cc63abd"
#
# expect(Uuid.parse("49A15746135C4DEDAB55B2C5F74BD5BB").toString()).toBe([73, 161, 87, 70, 19, 92, 77, 237, 171, 85, 178, 197, 247, 75, 213, 187].toString());
#
# Copyright (c) 2010-2012 Robert Kieffer
# MIT License - http://opensource.org/licenses/mit-license.php
_.Uuid = do (_module) ->
{require, crypto, define, module} = _module
# Unique ID creation requires a high quality random # generator. We feature
# detect to determine the best RNG source, normalizing to a function that
# returns 128-bits of randomness, since that's what's usually required
_rng = undefined
# Node.js crypto-based RNG - http://nodejs.org/docs/v0.6.2/api/crypto.html
#
# Moderately fast, high quality
if typeof (require) is "function"
try
_rb = require("crypto").randomBytes
_rng = _rb and ->
_rb 16
if not _rng and crypto and crypto.getRandomValues
# WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto
#
# Moderately fast, high quality
_rnds8 = new Uint8Array(16)
_rng = whatwgRNG = ->
crypto.getRandomValues _rnds8
_rnds8
unless _rng
# Math.random()-based (RNG)
#
# If all else fails, use Math.random(). It's fast, but is of unspecified
# quality.
_rnds = new Array(16)
_rng = ->
i = 0
r = undefined
while i < 16
r = Math.random() * 0x100000000 if (i & 0x03) is 0
_rnds[i] = r >>> ((i & 0x03) << 3) & 0xff
i++
_rnds
# Buffer class to use
BufferClass = (if typeof (Buffer) is "function" then Buffer else Array)
# Maps for number <-> hex string conversion
_byteToHex = []
_hexToByte = {}
i = 0
while i < 256
_byteToHex[i] = (i + 0x100).toString(16).substr(1)
_hexToByte[_byteToHex[i]] = i
i++
# **`parse()` - Parse a UUID into it's component bytes**
parse = (s, buf, offset) ->
i = (buf and offset) or 0
ii = 0
buf = buf or []
s.toLowerCase().replace /[0-9a-f]{2}/g, (oct) ->
buf[i + ii++] = _hexToByte[oct] if ii < 16 # Don't overflow!
# Zero out remaining bytes if string was short
buf[i + ii++] = 0 while ii < 16
buf
# **`unparse()` - Convert UUID byte array (ala parse()) into a string**
unparse = (buf, offset) ->
i = offset or 0
bth = _byteToHex
bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + "-" + bth[buf[i++]] + bth[buf[i++]] + "-" + bth[buf[i++]] + bth[buf[i++]] + "-" + bth[buf[i++]] + bth[buf[i++]] + "-" + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]]
# **`v1()` - Generate time-based UUID**
#
# Inspired by https://github.com/LiosK/UUID.js
# and http://docs.python.org/library/uuid.html
# random #'s we need to init node and clockseq
_seedBytes = _rng()
# Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)
_nodeId = [_seedBytes[0] | 0x01, _seedBytes[1], _seedBytes[2], _seedBytes[3], _seedBytes[4], _seedBytes[5]]
# Per 4.2.2, randomize (14 bit) clockseq
_clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff
# Previous uuid creation time
_lastMSecs = 0
_lastNSecs = 0
# See https://github.com/broofa/node-uuid for API details
v1 = (options, buf, offset) ->
i = buf and offset or 0
b = buf or []
options = options or {}
clockseq = (if options.clockseq? then options.clockseq else _clockseq)
# UUID timestamps are 100 nano-second units since the Gregorian epoch,
# (1582-10-15 00:00). JSNumbers aren't precise enough for this, so
# time is handled internally as 'msecs' (integer milliseconds) and 'nsecs'
# (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00.
msecs = (if options.msecs? then options.msecs else new Date().getTime())
# Per 4.2.1.2, use count of uuid's generated during the current clock
# cycle to simulate higher resolution clock
nsecs = (if options.nsecs? then options.nsecs else _lastNSecs + 1)
# Time since last uuid creation (in msecs)
dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs) / 10000
# Per 4.2.1.2, Bump clockseq on clock regression
clockseq = clockseq + 1 & 0x3fff if dt < 0 and not options.clockseq?
# Reset nsecs if clock regresses (new clockseq) or we've moved onto a new
# time interval
nsecs = 0 if (dt < 0 or msecs > _lastMSecs) and not options.nsecs?
# Per 4.2.1.2 Throw error if too many uuids are requested
throw new Error("uuid.v1(): Can't create more than 10M uuids/sec") if nsecs >= 10000
_lastMSecs = msecs
_lastNSecs = nsecs
_clockseq = clockseq
# Per 4.1.4 - Convert from unix epoch to Gregorian epoch
msecs += 12219292800000
# `time_low`
tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000
b[i++] = tl >>> 24 & 0xff
b[i++] = tl >>> 16 & 0xff
b[i++] = tl >>> 8 & 0xff
b[i++] = tl & 0xff
# `time_mid`
tmh = (msecs / 0x100000000 * 10000) & 0xfffffff
b[i++] = tmh >>> 8 & 0xff
b[i++] = tmh & 0xff
# `time_high_and_version`
b[i++] = tmh >>> 24 & 0xf | 0x10 # include version
b[i++] = tmh >>> 16 & 0xff
# `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)
b[i++] = clockseq >>> 8 | 0x80
# `clock_seq_low`
b[i++] = clockseq & 0xff
# `node`
node = options.node or _nodeId
n = 0
while n < 6
b[i + n] = node[n]
n++
(if buf then buf else unparse(b))
# **`v4()` - Generate random UUID**
# See https://github.com/broofa/node-uuid for API details
v4 = (options, buf, offset) ->
# Deprecated - 'format' argument, as supported in v1.2
i = buf and offset or 0
if typeof (options) is "string"
buf = (if options is "binary" then new BufferClass(16) else null)
options = null
options = options or {}
rnds = options.random or (options.rng or _rng)()
# Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
rnds[6] = (rnds[6] & 0x0f) | 0x40
rnds[8] = (rnds[8] & 0x3f) | 0x80
# Copy bytes to buffer, if provided
if buf
ii = 0
while ii < 16
buf[i + ii] = rnds[ii]
ii++
buf or unparse(rnds)
# Export public API
uuid = v4
uuid.v1 = v1
uuid.v4 = v4
uuid.parse = parse
uuid.unparse = unparse
uuid.BufferClass = BufferClass
if typeof define is "function" and define.amd
# Publish as AMD module
define ->
uuid
else if typeof module isnt "undefined" and module.exports
# Publish as node.js module
module.exports = uuid
else
# Publish as global (in browsers)
_previousRoot = _module.Uuid
# **`noConflict()` - (browser only) to reset global 'uuid' var**
uuid.noConflict = ->