Skip to content

Instantly share code, notes, and snippets.

@sgss
Last active December 16, 2015 16:40
Show Gist options
  • Save sgss/5464760 to your computer and use it in GitHub Desktop.
Save sgss/5464760 to your computer and use it in GitHub Desktop.
class Application extends Observable
self = @initialize()
@static
stack: []
@property
delegate:
defaults: null
@property
processes:
attributes: readonly clones bindable
defaults: []
@property
startedTime:
attributes: readonly
defaults: 0
@property
processedTime:
attributes: readonly
defaults: 0
@property
consumedTime:
attributes: readonly
defaults: 0
@property
frameRate:
attributes: readonly
defaults: 0
@property
active:
attributes: bindable
defaults: yes
converter: Boolean
afterChange: (value) -> if value then @activate() else @deactivate()
@property
started:
attributes: bindable
defaults: no
converter: Boolean
afterChange: (value) -> if value then @start() else @stop()
@property
pasteboard:
attributes: bindable
defaults: null
#---------------------------------------------------------------------------
# Initialization
# method: initialize [parameters]
# returns: this
initialize: (parameters) ->
super()
@setProperties parameters
@
#---------------------------------------------------------------------------
# Activating Application
# method: activate
# returns: this
activate: ->
if application isnt @
application?.setActive no
application = @
@setActive yes
@_delegate?.applicationActivated? @
self.stack.push @
@
# method: deactivate
# returns: this
deactivate: ->
if application is @
application = null
@setActive no
@_delegate?.applicationDeactivated? @
self.stack.pop()
if self.stack.length
self.stack.last().activate()
@
#---------------------------------------------------------------------------
# Controlling Application
# method: start
# returns: this
start: ->
if not @_intervalTimer?
@_startedTime = new Date().getTime()
@_intervalTimer = setInterval @process, self.interval
@setStarted yes
@_delegate?.applicationStarted? @
@
# method: stop
# returns: this
stop: ->
if @_intervalTimer?
clearInterval @_intervalTimer
@_intervalTimer = null
@setStarted no
@_delegate?.applicationStopped? @
@
#---------------------------------------------------------------------------
# Managing Application Cycle
# method: process
# returns: this
process: =>
before = new Date().getTime()
@main()
process.process() for process in @_processes
after = new Date().getTime()
@_processedTime = before
@_consumedTime = after - before
@
# method: main
# returns: this
main: -> @
#---------------------------------------------------------------------------
# Managing Process
# method: attachProcess <process>
# returns: this
attachProcess: (process) ->
if process instanceof Process and @_processes.indexOf(process) is -1
@willChangeProperty 'processes'
@_processes.push process
@didChangeProperty 'processes'
@_delegate?.applicationAttachedProcess? @, process
@
# method: detachProcess <process>
# returns: this
detachProcess: (process) ->
index = @_processes.indexOf process
if index isnt -1
@willChangeProperty 'processes'
@_processes.splice index, 1
@didChangeProperty 'processes'
@_delegate?.applicationDetachedProcess? @, process
@
window.application = new Application
"use strict"
window.sgss ?= {}
#-------------------------------------------------------------------------------
# Object Manipulation
# function: merge <target>, <sources...>
# returns: Object
sgss.merge =
merge = (target, sources...) ->
if not target? then target = {}
for source in sources
for name, value of source
target[name] = value
target
# function: merge <objs...>
# returns: Object
sgss.combine =
combine = (objs...) ->
result = {}
for obj in objs
for name, value of obj
result[name] = value
result
# function: clone <obj>, [deep]
# returns: Object
sgss.clone =
clone = (obj, deep) ->
if obj? and isFunction obj.clone
obj.clone deep
else
_.clone obj, deep
# function: equal <a>, <b>
# returns: Boolean
sgss.equal =
equal = (a, b) ->
if a? and isFunction a.equals
a.equals b
else if b? and isFunction b.equals
b.equals a
else
_.isEqual a, b
# function: equalEach <objs...>
# returns: Boolean
sgss.equalEach =
equalEach = (objs...) ->
for value, i in objs by 2
a = objs[i]
b = objs[i + 1]
if a? and isFunction a.equals
return no unless a.equals b
else if b? and isFunction b.equals
return no unless b.equals a
else
return no unless _.isEqual a, b
yes
# function: equalAll <obj>, <others...>
# returns: Boolean
sgss.equalAll =
equalAll = (obj, others...) ->
if obj? and isFunction obj.equals
for other in others
return no unless obj.equals other
else
for other in others
if other? and isFunction other.equals
return no unless other.equals obj
else
return no unless _.isEqual obj, other
yes
# function: equalAny <obj>, <others...>
# returns: Boolean
sgss.equalAny =
equalAny = (obj, others...) ->
if obj? and isFunction obj.equals
for other in others
return yes if obj.equals other
else
for other in others
if other? and isFunction other.equals
return yes if other.equals obj
else
return yes if _.isEqual obj, other
no
# function: slice <array>, [start], [end]
# returns: Array
sgss.slice =
slice = Function::call.bind Array::slice
#-------------------------------------------------------------------------------
# Property Definitions
# function: clones [linker]
# returns: Object
clones = (linker) ->
attribute = { clones: yes }
if isFunction linker
merge do linker, attribute
else if linker?
merge linker, attribute
else
attribute
# function: readonly [linker]
# returns: Object
readonly = (linker) ->
attribute = { readonly: yes }
if isFunction linker
merge do linker, attribute
else if linker?
merge linker, attribute
else
attribute
# function: writeonly [linker]
# returns: Object
writeonly = (linker) ->
attribute = { writeonly: yes }
if isFunction linker
merge do linker, attribute
else if linker?
merge linker, attribute
else
attribute
# function: informative [linker]
# returns: Object
informative = (linker) ->
attribute = { informative: yes }
if isFunction linker
merge do linker, attribute
else if linker?
merge linker, attribute
else
attribute
# function: bindable [linker]
# returns: Object
bindable = (linker) ->
attribute = { bindable: yes }
if isFunction linker
merge do linker, attribute
else if linker?
merge linker, attribute
else
attribute
# function: parametric [linker]
# returns: Object
parametric = (linker) ->
attribute = { parametric: yes }
if isFunction linker
merge do linker, attribute
else if linker?
merge linker, attribute
else
attribute
#-------------------------------------------------------------------------------
# Type Tests
# function: isArray <obj>
# returns: Boolean
sgss.isArray =
isArray = Object.isArray
# function: isBoolean <obj>
# returns: Boolean
sgss.isBoolean =
isBoolean = Object.isBoolean
# function: isNaN <obj>
# returns: Boolean
sgss.isNaN =
isNaN = Object.isNaN
# function: isDate <obj>
# returns: Boolean
sgss.isDate =
isDate = Object.isDate
# function: isFunction <obj>
# returns: Boolean
sgss.isFunction =
isFunction = Object.isFunction
# function: isNumber <obj>
# returns: Boolean
sgss.isNumber =
isNumber = Object.isNumber
# function: isObject <obj>
# returns: Boolean
sgss.isObject =
isObject = Object.isObject
# function: isString <obj>
# returns: Boolean
sgss.isString =
isString = Object.isString
# function: isRegExp <obj>
# returns: Boolean
sgss.isRegExp =
isRegExp = Object.isRegExp
# function: isEmpty <obj>
# returns: Boolean
sgss.isEmpty =
isEmpty = Object.isEmpty
#-------------------------------------------------------------------------------
# Debugging
# method: log [messages...]
sgss.log =
log = (messages...) ->
console.log messages...
# method: error [messages...]
sgss.error =
error = (messages...) ->
console.error messages...
# method: assert <condition>, [messages...]
sgss.assert =
assert = (condition, messages...) ->
if not condition
console.error messages...
window.sgss ?= {}
sgss.Intrinsic =
class Intrinsic
self = @
# function: initialize
# returns: Function
@initialize: -> @
# function: implements <implementation>
# returns: Function
@implements: (implementation) ->
merge @, implementation
merge @::, implementation::
@
# function: static <definition>
# returns: Function
@static: (definition) ->
merge @, definition
@
# function: getSuper
# returns: Function
@getSuper: -> @__super__?.constructor
# function: getName
# returns: String
@getName: -> @name
#---------------------------------------------------------------------------
# Managing Class and Instance
# method: getClass
# returns: Function
getClass: -> @__proto__.constructor
#---------------------------------------------------------------------------
# Managing Instance Variable
# method: getInternal <name>
# method: getInternal <names>
# method: getInternal <name:>
# returns: mixed
getInternal: (arg0) ->
# getInternal <name>
if isString arg0
@['_' + arg0]
# getInternal <names>
else if isArray arg0
result = {}
for name in arg0
result[name] = @['_' + name]
result
# getInternal <name:>
else if isObject arg0
result = {}
for name of arg0
result[name] = @['_' + name]
result
# aliases: getInternal
getInternals: @::getInternal
# method: setInternal <name>, <value>
# method: setInternal <names>, <value>
# method: setInternal <name: value>, <name: default>
# method: setInternal <name: value>
# returns: this
setInternal: (arg0, arg1) ->
# setInternal <name>, <value>
if isString arg0
@['_' + arg0] = arg1
# setInternal <names>, <value>
else if isArray arg0
for name in arg0
@['_' + name] = arg1
# setInternal <name: value>, <name: default>
else if isObject arg1
unless arg0?
for name, defaults of arg1
@['_' + name] = defaults
else
for name, defaults of arg1
@['_' + name] = if name of arg0 then arg0[name] else defaults
# setInternal <name: value>
else if isObject arg0
for name, value of arg0
@['_' + name] = value
@
# aliases: setInternal
setInternals: @::setInternal
#---------------------------------------------------------------------------
# Debugging
# method: log [messages...]
# returns: this
log: (messages...) ->
messages.unshift "#{@getClass().getName()}:"
console.log messages...
@
# method: error [messages...]
# returns: this
error: (messages...) ->
messages.unshift "#{@getClass().getName()}:"
console.error messages...
@
# method: assert <condition>, [messages...]
# returns: this
assert: (condition, messages...) ->
if not condition
messages.unshift "#{@getClass().getName()}:"
console.error messages...
@
window.sgss ?= {}
sgss.Manageable =
class Manageable extends Intrinsic
self = @initialize()
@__identifier : 0
@__properties : {}
@__propertyOptions :
clones : no
readonly : no
writeonly : no
informative : no
bindable : no
parametric : no
internal : null
getter : null
setter : null
defaults : null
converter : null
inflector : null
beforeChange : null
afterChange : null
# function: property <definition>
# returns: Function
@property: (definition) ->
for name, value of definition
if isFunction value
definition[name] = value()
else if value.attributes?
if isFunction value.attributes
value.attributes = value.attributes()
merge value, value.attributes
delete value.attributes
@createProperty definition
@
# function: initialize
# returns: Function
@initialize: ->
@__properties = clone @__properties
@
# function: getProperty <name>
# returns: Object
@getProperty: (name) -> @__properties[name]
# function: getProperty
# returns: Object
@getProperties: -> @__properties
# function: getParameter <name>
# returns: Object
@getParameter: (name) ->
property = @__properties[name]
if property?.parametric
property
else
null
# function: getParameters
# returns: Object
@getParameters: ->
result = {}
for name, property of @__properties
if property.parametric
result[name] = property
result
# function: getReadableProperty <name>
# returns: Object
@getReadableProperty: (name) ->
property = @__properties[name]
if property? and not property.writeonly
property
else
null
# function: getReadableProperties
# returns: Object
@getReadableProperties: ->
result = {}
for name, property of @__properties
if not property.writeonly
result[name] = property
result
# function: getWritableProperty <name>
# returns: Object
@getWritableProperty: (name) ->
property = @__properties[name]
if property? and not property.readonly
property
else
null
# function: getWritableProperties
# returns: Object
@getWritableProperties: ->
result = {}
for name, property of @__properties
if not property.readonly
result[name] = property
result
# function: createProperty <name>, <options>
# function: createProperty <name: options>
# returns: Function
@createProperty: (arg0, arg1) ->
# createProperty <name>, <options>
if isString arg0
options = arg1
options[name] = value for name, value of @__propertyOptions when not options[name]?
internal = '_' + arg0
capital = arg0.first().capitalize() + arg0.from 1
if not options.internal
options.internal = internal
if not options.writeonly and not options.getter?
if arg0.startsWith(/can|should|needs|draws|uses|has/)
options.getter = arg0
else if options.converter is Boolean
options.getter = 'is' + capital
else
options.getter = 'get' + capital
if not options.readonly and not options.setter?
options.setter = 'set' + capital
if not options.writeonly and not @::[options.getter]
@::[options.getter] = @::propertyGetter.fill arg0, options
if not options.readonly and not @::[options.setter]
@::[options.setter] = @::propertySetter.fill arg0, options
@__properties[arg0] = options
# createProperty <name: options>
else if isObject arg0
@createProperty name, options for name, options of arg0
@
#---------------------------------------------------------------------------
# Initialization
# function: Manageable
constructor: ->
@preinitialize()
@initialize arguments...
@postinitialize()
# method: preinitialize
# returns: this
preinitialize: ->
@__identifier = ++self.__identifier
@__initialized = no
@__deferreds = {}
for name, options of @getClass().__properties
if options.defaults?
if options.readonly
@[options.internal] = clone options.defaults
else if setter = @getSetter name
@setAfterInitialization name, [clone options.defaults], setter
@
# method: initialize
# returns: this
initialize: -> @
# method: postinitialize
# returns: this
postinitialize: ->
@__initialized = yes
for name, callback of @__deferreds
callback()
@__deferreds = null
@
# method: setAfterInitialization <name>, <args>, [setter]
# returns: this
setAfterInitialization: (name, args, setter) ->
if not setter?
setter = @getSetter name
if setter?
@__deferreds[name] = _.bind.apply _, [setter, @].concat args
@
#---------------------------------------------------------------------------
# Managing Class and Instance
# method: getIdentifier
# returns: Number
getIdentifier: -> @__identifier;
#---------------------------------------------------------------------------
# Templating Property
# method: propertyGetter <name>, <options>
# returns: mixed
propertyGetter: (name, options) ->
# pass the value to inflector
if options.inflector?
if isString options.inflector
return @[options.inflector].apply @, [@[options.internal]].concat slice(arguments, 2)
else
return options.inflector.apply @, [@[options.internal]].concat slice(arguments, 2)
# clone if needed
if options.clones
return clone @[options.internal]
return @[options.internal]
# method: propertySetter <name>, <options>, <value>
# returns: this
propertySetter: (name, options, value) ->
# pass the value to converter
if options.converter?
value =
if isString options.converter
@[options.converter].apply @, slice(arguments, 2)
else
options.converter.apply @, slice(arguments, 2)
if not equal @[options.internal], value
# call before change callback
if options.beforeChange?
if isString options.beforeChange
@[options.beforeChange].apply @, [value, @[options.internal]]
else
options.beforeChange.apply @, [value, @[options.internal]]
oldValue = @[options.internal]
@[options.internal] = if options.clones then clone value else value
# inform change
if options.informative
@[name + 'PropertyChanged']? value, oldValue
# post property change notification
if options.bindable
@notifyPropertyChange name, value, oldValue
# call after change callback
if options.afterChange?
if isString options.afterChange
@[options.afterChange].apply @, [value, oldValue]
else
options.afterChange.apply @, [value, oldValue]
@
#---------------------------------------------------------------------------
# Managing Property
# method: validateProperty <name>
# returns: Boolean
validateProperty: (name) -> @getClass().getProperty(name)?
# method: getGetter <name>
# returns: Function
getGetter: (name) ->
capital = name[0].toUpperCase() + name.substr 1
if isFunction @['get' + capital]
@['get' + capital]
else
@getPropertyGetter name
# method: getPropertyGetter <name>
# returns: Function
getPropertyGetter: (name) ->
property = @getClass().getProperty name
if property? and not property.writeonly
@[property.getter]
else
null
# method: getSetter <name>
# returns: Function
getSetter: (name) ->
capital = name[0].toUpperCase() + name.substr 1
if isFunction @['set' + capital]
@['set' + capital]
else
@getPropertySetter name
# method: getPropertySetter <name>
# returns: Function
getPropertySetter: (name) ->
property = @getClass().getProperty name
if property? and not property.readonly
@[property.setter]
else
null
# method: getProperty <name>, [args...]
# method: getProperty <names>, [args...]
# method: getProperty <name:>, [args...]
# method: getProperty [args...]
# returns: mixed
getProperty: (arg0, args...) ->
# getProperty <name>, [args...]
if isString arg0
if getter = @getGetter arg0
getter.apply @, args
# getProperty <names>, [args...]
else if isArray arg0
result = {}
for name in arg0
if getter = @getGetter name
result[name] = getter.apply @, args
result
# getProperty <name:>, [args...]
else if isObject arg0
result = {}
for name of arg0
if getter = @getGetter name
result[name] = getter.apply @, args
result
# getProperty [args...]
else
properties = @getClass().getReadableProperties()
args = [arg0].concat args
result = {}
for name, property of properties
result[name] = @[property.getter].apply @, args
result
# aliases: getProperty
getProperties: @::getProperty
# method: setProperty <name>, <args...>
# method: setProperty <names>, <args...>
# method: setProperty <name: value>, <name: default>
# method: setProperty <name: value>
# returns: this
setProperty: (arg0, args...) ->
arg1 = args[0]
# setProperty <name>, <value>
if isString arg0
if @__initialized
if setter = @getSetter arg0
setter.apply @, args
else
if setter = @getSetter arg0
@setAfterInitialization arg0, args, setter
# setProperty <names>, <value>
else if isArray arg0
if @__initialized
for name in arg0
if setter = @getSetter name
setter.apply @, args
else
for name in arg0
if setter = @getSetter name
@setAfterInitialization name, args, setter
# setProperty <name: value>, <name: default>
else if isObject arg1
if @__initialized
unless arg0?
for name, defaults of arg1
if setter = @getSetter name
setter.apply @, [defaults]
else
for name, defaults of arg1
if setter = @getSetter name
setter.apply @, [if name of arg0 then arg0[name] else defaults]
else
unless arg0?
for name, defaults of arg1
if setter = @getSetter name
@setAfterInitialization name, [defaults], setter
else
for name, defaults of arg1
if setter = @getSetter name
@setAfterInitialization name, [if name of arg0 then arg0[name] else defaults], setter
# setProperty <name: value>
else if isObject arg0
if @__initialized
for name, value of arg0
if setter = @getSetter name
setter.apply @, [value]
else
for name, value of arg0
if setter = @getSetter name
@setAfterInitialization name, [value], setter
@
# aliases: setProperty
setProperties: @::setProperty
window.sgss ?= {}
sgss.Observable =
class Observable extends Manageable
self = @initialize()
@__regexpOfPattern: []
#---------------------------------------------------------------------------
# Initialization
# method: preinitialize
# returns: this
# overrides: Manageable.preinitialize
preinitialize: ->
super()
@__propertyObserversOfName = {}
@__propertyObserversOfPattern = {}
@__propertyObservers = {}
@__notificationObservers = {}
@__notificationObserversOfType = {}
@__notificationObserversOfPattern = {}
@__valuesOfPropertyWillChange = {}
@
#---------------------------------------------------------------------------
# Observing Notification
# method: observe <target>, <type>, <callback>
# method: observe <target>, <type: callback>
# method: observe <target>, <pattern>, <callback>
# method: observe <target>, <types>, <callback>
# method: observe <target>, <callback>
# returns: this
observe: (target, arg1, arg2) ->
@assert target?, 'cannot observe non-object type of target:', target
# observe <target>, <type>, <callback>
if isString arg1
target.addNotificationObserverWithType @, arg1, arg2
# observe <target>, <type: callback>
else if isObject arg1
target.addNotificationObserverWithType @, type, callback for type, callback of arg1
# observe <target>, <pattern>, <callback>
else if isRegExp arg1
target.addNotificationObserverWithPattern @, arg1, arg2
# observe <target>, <types>, <callback>
else if isArray arg1
for each1 in arg1
if isString each1
target.addNotificationObserverWithType @, each1, arg2
else if isRegExp each1
target.addNotificationObserverWithPattern @, each1, arg2
else
@error 'neither name nor pattern specified in array:', each1
# observe <target>, <callback>
else
target.addNotificationObserverToAll @, arg1
@
# method: dismiss <target>, <type>, [callback]
# method: dismiss <target>, <type: callback>
# method: dismiss <target>, <pattern>, [callback]
# method: dismiss <target>, <types>, [callback]
# method: dismiss <target>, [callback]
# returns: this
dismiss: (target, arg1, arg2) ->
@assert target?, 'cannot dismiss non-object type of target:', target
# dismiss <target>, <type>, [callback]
if isString arg1
target.removeNotificationObserverWithType @, arg1, arg2
# dismiss <target>, <type: callback>
else if isObject arg1
target.removeNotificationObserverWithType @, type, callback for type, callback of arg1
# dismiss <target>, <pattern>, [callback]
else if isRegExp arg1
target.removeNotificationObserverWithPattern @, arg1, arg2
# dismiss <target>, <types>, [callback]
else if isArray arg1
for each1 in arg1
if isString each1
target.removeNotificationObserverWithType @, each1, arg2
else if isRegExp each1
target.removeNotificationObserverWithPattern @, each1, arg2
else
@error 'neither name nor pattern specified in array:', each1
# dismiss <target>, [callback]
else
target.removeNotificationObserverFromAll @, arg1
@
# method: addObserver <observer>, <type>, <callback>
# method: addObserver <observer>, <type: callback>
# method: addObserver <observer>, <pattern>, <callback>
# method: addObserver <observer>, <types>, <callback>
# method: addObserver <observer>, <callback>
# returns: this
addObserver: (observer, arg1, arg2) ->
@assert observer?, 'cannot add non-object type of observer:', observer
# addObserver <observer>, <type>, <callback>
if isString arg1
@addNotificationObserverWithType observer, arg1, arg2
# addObserver <observer>, <type: callback>
else if isObject arg1
for type, callback of arg1
@addNotificationObserverWithType observer, type, callback
# addObserver <observer>, <pattern>, <callback>
else if isRegExp arg1
@addNotificationObserverWithPattern observer, arg1, arg2
# addObserver <observer>, <types>, <callback>
else if isArray arg1
for each1 in arg1
if isString each1
@addNotificationObserverWithType observer, each1, arg2
else if isRegExp each1
@addNotificationObserverWithPattern observer, each1, arg2
else
@error 'neither name nor pattern specified in array:', each1
# addObserver <observer>, <callback>
else
@addNotificationObserverToAll observer, arg1
@
# method: removeObserver <observer>, <type>, [callback]
# method: removeObserver <observer>, <type: callback>
# method: removeObserver <observer>, <pattern>, [callback]
# method: removeObserver <observer>, <types>, [callback]
# method: removeObserver <observer>, [callback]
# method: removeObserver
# returns: this
removeObserver: (observer, arg1, arg2) ->
# removeObserver <observer>, <type>, [callback]
if isString arg1
@removeNotificationObserverWithType observer, arg1, arg2
# removeObserver <observer>, <type: callback>
else if isObject arg1
@removeNotificationObserverWithType observer, type, callback for type, callback of arg1
# removeObserver <observer>, <pattern>, [callback]
else if isRegExp arg1
@removeNotificationObserverWithPattern observer, arg1, arg2
# removeObserver <observer>, <types>, [callback]
else if isArray arg1
for each1 in arg1
if isString each1
@removeNotificationObserverWithType observer, each1, arg2
else if isRegExp each1
@removeNotificationObserverWithPattern observer, each1, arg2
else
@error 'neither name nor pattern specified in array:', each1
# removeObserver <observer>, [callback]
else if observer?
@removeNotificationObserverFromAll observer, arg1
# removeObserver
else
@removeAllNotificationObservers()
@
# method: removeAllNotificationObservers
# return: this
removeAllNotificationObservers: ->
@__notificationObserversOfType = {}
@__notificationObserversOfPattern = {}
@__notificationObservers = {}
@
#---------------------------------------------------------------------------
# Notifying
# method: notify <type>, [data]
# method: notify <type: data>
# method: notify <types>, [data]
notify: (arg0, arg1) ->
# notify <type>, [data]
if isString arg0
@postNotification new Notification @, arg0, arg1
# notify <type: data>
else if isObject arg0
@postNotificationWithType new Notification @, type, data for type, data of arg0
# notify <types>, [data]
else if isArray arg0
@postNotification new Notification @, type, arg1 for type in arg0
else
@error 'insufficient argument to notify:', arguments
@
# method: postNotification <notification>
# return: this
postNotification: (notification) ->
type = notification.type
args = [notification]
observers = @__notificationObserversOfType[type]
if observers?
for identifier, entry of observers
{context, callbacks} = entry
for callback in callbacks
callback.apply context, args
for pattern, observers of @__notificationObserversOfPattern
if self.__regexpOfPattern[pattern].test type
for identifier, entry of observers
{context, callbacks} = entry
for callback in callbacks
callback.apply context, args
for identifier, entry of @__notificationObservers
{context, callbacks} = entry
for callback in callbacks
callback.apply context, args
@
#---------------------------------------------------------------------------
# Managing Notification Observer
# method: addNotificationObserverWithType <observer>, <type>, <callback>
# return: this
addNotificationObserverWithType: (observer, type, callback) ->
if isFunction callback
observers = @__notificationObserversOfType[type]
unless observers?
observers = @__notificationObserversOfType[type] = {}
entry = observers[observer.__identifier]
unless entry?
entry = observers[observer.__identifier] =
context: observer
callbacks: [callback]
else if entry.callbacks.indexOf(callback) is -1
entry.callbacks.push callback
else
@error 'attempting to add duplicated observer callback of type:', type, 'observer:', observer
else
@error 'cannot add non-function type of observer callback:', callback, 'type:', type, 'observer:', observer
@
# method: addNotificationObserverWithPattern <observer>, <pattern>, <callback>
# return: this
addNotificationObserverWithPattern: (observer, pattern, callback) ->
if isRegExp pattern
regexp = pattern
pattern = regexp.toString()
else
regexp = new RegExp pattern
self.__regexpOfPattern[pattern] = regexp
if isFunction callback
observers = @__notificationObserversOfPattern[pattern]
unless observers?
observers = @__notificationObserversOfPattern[pattern] = {}
entry = observers[observer.__identifier]
unless entry?
entry = observers[observer.__identifier] =
context: observer
callbacks: [callback]
else if entry.callbacks.indexOf(callback) is -1
entry.callbacks.push callback
else
@error 'attempting to add duplicated observer callback of pattern:', pattern, 'observer:', observer
else
@error 'cannot add non-function type of observer callback:', callback, 'pattern:', pattern, 'observer:', observer
@
# method: addNotificationObserverToAll <observer>, <callback>
# return: this
addNotificationObserverToAll: (observer, callback) ->
if isFunction callback
entry = @__notificationObservers[observer.__identifier]
unless entry?
entry = @__notificationObservers[observer.__identifier] =
context: observer
callbacks: [callback]
else if entry.callbacks.indexOf(callback) is -1
entry.callbacks.push callback
else
@error 'attempting to add duplicated observer callback of observer:', observer
else
@error 'cannot add non-function type of observer callback:', callback, 'observer:', observer
@
# method: removeNotificationObserverWithType <observer>, <type>, [callback]
# return: this
removeNotificationObserverWithType: (observer, type, callback) ->
observers = @__notificationObserversOfType[type]
if observers?
unless callback?
delete observers[observer.__identifier]
else
entry = observers[observer.__identifier]
if entry?
callbacks = entry.callbacks
index = callbacks.indexOf callback
if index isnt -1
callbacks.splice index, 1
unless callbacks.length
delete observers[observer.__identifier]
if isEmpty observers
delete @__notificationObserversOfType[type]
@
# method: removeNotificationObserverWithPattern <observer>, <pattern>, [callback]
# return: this
removeNotificationObserverWithPattern: (observer, pattern, callback) ->
if isRegExp pattern
pattern = pattern.toString()
observers = @__notificationObserversOfPattern[pattern]
if observers?
unless callback?
delete observers[observer.__identifier]
else
entry = observers[observer.__identifier]
if entry?
callbacks = entry.callbacks
index = callbacks.indexOf callback
if index isnt -1
callbacks.splice index, 1
unless callbacks.length
delete observers[observer.__identifier]
if isEmpty observers
delete @__notificationObserversOfPattern[pattern]
@
# method: removeNotificationObserver <observer>, [callback]
# return: this
removeNotificationObserverFromAll: (observer, callback) ->
unless callbacks?
for type, observers of @__notificationObserversOfType
delete observers[observer.__identifier]
if isEmpty observers
delete @__notificationObserversOfType[type]
for pattern, observers of @__notificationObserversOfPattern
delete observers[observer.__identifier]
if isEmpty observers
delete @__notificationObserversOfPattern[pattern]
@__notificationObservers = []
else
for type, observers of @__notificationObserversOfType
entry = observers[observer.__identifier]
if entry?
callbacks = entry.callbacks
index = callbacks.indexOf callback
if index isnt -1
callbacks.splice index, 1
unless callbacks.length
delete observers[observer.__identifier]
if isEmpty observers
delete @__notificationObserversOfType[type]
for pattern, observers of @__notificationObserversOfPattern
entry = observers[observer.__identifier]
if entry?
callbacks = entry.callbacks
index = callbacks.indexOf callback
if index isnt -1
callbacks.splice index, 1
unless callbacks.length
delete observers[observer.__identifier]
if isEmpty observers
delete @__notificationObserversOfPattern[pattern]
entry = @__notificationObservers[observer.__identifier]
if entry?
callbacks = entry.callbacks
index = callbacks.indexOf callback
if index isnt -1
callbacks.splice index, 1
unless callbacks.length
delete @__notificationObservers[observer.__identifier]
@
#---------------------------------------------------------------------------
# Observing Property Change
# method: observeProperty <target>, <name>, <callback>
# method: observeProperty <target>, <name: callback>
# method: observeProperty <target>, <pattern>, <callback>
# method: observeProperty <target>, <names>, <callback>
# method: observeProperty <target>, <callback>
# returns: this
observeProperty: (target, arg1, arg2) ->
@assert target?, 'cannot observe property of non-object type of target:', target
# observeProperty <target>, <name>, <callback>
if isString arg1
target.addPropertyObserverWithName @, arg1, arg2
# observeProperty <target>, <name: callback>
else if isObject arg1
target.addPropertyObserverWithName @, name, callback for name, callback of arg1
# observeProperty <target>, <pattern>, <callback>
else if isRegExp arg1
target.addPropertyObserverWithPattern @, arg1, arg2
# observeProperty <target>, <names>, <callback>
else if isArray arg1
for each1 in arg1
if isString each1
target.addPropertyObserverWithName @, each1, arg2
else if isRegExp each1
target.addPropertyObserverWithPattern @, each1, arg2
else
@error 'neither name nor pattern specified in array:', each1
# observeProperty <target>, <callback>
else
target.addPropertyObserverToAll @, arg1
@
# aliases: observeProperty
observeProperties: @::observeProperty
# method: dismissProperty <target>, <name>, [callback]
# method: dismissProperty <target>, <name: callback>
# method: dismissProperty <target>, <pattern>, [callback]
# method: dismissProperty <target>, <names>, [callback]
# method: dismissProperty <target>, [callback]
# returns: this
dismissProperty: (target, arg1, arg2) ->
@assert target?, 'cannot dismiss property of non-object type of target:', target
# dismissProperty <target>, <name>, [callback]
if isString arg1
target.removePropertyObserverWithName @, arg1, arg2
# dismissProperty <target>, <name: callback>
else if isObject arg1
target.removePropertyObserverWithName @, type, callback for type, callback of arg1
# dismissProperty <target>, <pattern>, [callback]
else if isRegExp arg1
target.removePropertyObserverWithPattern @, arg1, arg2
# dismissProperty <target>, <names>, [callback]
else if isArray arg1
for each1 in arg1
if isString each1
target.removePropertyObserverWithName @, each1, arg2
else if isRegExp each1
target.removePropertyObserverWithPattern @, each1, arg2
else
@error 'neither name nor pattern specified in array:', each1
# dismissProperty <target>, [callback]
else
target.removePropertyObserverFromAll @, arg1
@
# aliases: dismissProperty
dismissProperties: @::dismissProperty
# method: addPropertyObserver <observer>, <name>, <callback>
# method: addPropertyObserver <observer>, <name: callback>
# method: addPropertyObserver <observer>, <pattern>, <callback>
# method: addPropertyObserver <observer>, <names>, <callback>
# method: addPropertyObserver <observer>, <callback>
# returns: this
addPropertyObserver: (observer, arg1, arg2) ->
@assert observer?, 'cannot add non-object type of property observer:', observer
# addPropertyObserver <observer>, <name>, <callback>
if isString arg1
@addPropertyObserverWithName observer, arg1, arg2
# addPropertyObserver <observer>, <name: callback>
else if isObject arg1
@addPropertyObserverWithName observer, name, callback for name, callback of arg1
# addPropertyObserver <observer>, <pattern>, <callback>
else if isRegExp arg1
@addPropertyObserverWithPattern observer, arg1, arg2
# addPropertyObserver <observer>, <names>, <callback>
else if isArray arg1
for each1 in arg1
if isString each1
@addPropertyObserverWithName observer, each1, arg2
else if isRegExp each1
@addPropertyObserverWithPattern observer, each1, arg2
else
@error 'neither name nor pattern specified in array:', each1
# addPropertyObserver <observer>, <callback>
else
@addPropertyObserverToAll observer, arg1
@
# method: removePropertyObserver <observer>, <name>, [callback]
# method: removePropertyObserver <observer>, <name: callback>
# method: removePropertyObserver <observer>, <pattern>, [callback]
# method: removePropertyObserver <observer>, <names>, [callback]
# method: removePropertyObserver <observer>, [callback]
# method: removePropertyObserver
# returns: this
removePropertyObserver: (observer, arg1, arg2) ->
# removePropertyObserver <observer>, <name>, [callback]
if isString arg1
@removePropertyObserverWithName observer, arg1, arg2
# removePropertyObserver <observer>, <name: callback>
else if isObject arg1
@removePropertyObserverWithName observer, name, callback for name, callback of arg1
# removePropertyObserver <observer>, <pattern>, [callback]
else if isRegExp arg1
@removePropertyObserverWithPattern observer, arg1, arg2
# removePropertyObserver <observer>, <names>, [callback]
else if isArray arg1
for each1 in arg1
if isString each1
@removePropertyObserverWithName observer, each1, arg2
else if isRegExp each1
@removePropertyObserverWithPattern observer, each1, arg2
else
@error 'neither name nor pattern specified in array:', each1
# removePropertyObserver <observer>, [callback]
else if observer?
@removePropertyObserverFromAll observer, arg1
# removePropertyObserver
else
@removeAllPropertyObservers()
@
# method: removeAllPropertyObservers
# return: this
removeAllPropertyObservers: ->
@__propertyObserversOfName = {}
@__propertyObserversOfPattern = {}
@__propertyObservers = {}
@
#---------------------------------------------------------------------------
# Notifying Property Change
# method: willChangeProperty <names...>
# method: willChangeProperty
# returns: this
willChangeProperty: (names...) ->
properties = @getClass().__properties
unless names.length
names = (name for name of properties)
names = names.flatten()
for name in names
property = properties[name]
if property? and property.bindable
@__valuesOfPropertyWillChange[name] =
if property.writeonly
clone @[property.internal]
else if property.clones
@[property.getter]()
else
clone @[property.getter]()
@
# aliases: willChangeProperty
willChangeProperties: @::willChangeProperty
# method: didChangeProperty <names...>
# method: didChangeProperty
# returns: this
didChangeProperty: (names...) ->
properties = @getClass().__properties
unless names.length
names = (name for name of properties)
names = names.flatten()
for name in names
if name of @__valuesOfPropertyWillChange
property = properties[name]
value =
if property.writeonly
@[property.internal]
else
@[property.getter]()
oldValue = @__valuesOfPropertyWillChange[name]
delete @__valuesOfPropertyWillChange[name]
@notifyPropertyChange name, value, oldValue
else
@error 'cannot determine change of unbalanced property:', name, 'object:', @
@
# aliases: didChangeProperty
didChangeProperties: @::didChangeProperty
# method: notifyPropertyChange <name>, <value>, <oldValue>
# returns: this
notifyPropertyChange: (name, value, oldValue) ->
@postPropertyChangeNotification new Notification @, 'propertyChanged',
name: name
value: value
oldValue: oldValue
@
# method: postPropertyChangeNotification <notification>
# return: this
postPropertyChangeNotification: (notification) ->
name = notification.name
args = [notification]
observers = @__propertyObserversOfName[name]
if observers?
for identifier, entry of observers
{context, callbacks} = entry
for callback in callbacks
callback.apply context, args
for pattern, observers of @__propertyObserversOfPattern
if self.__regexpOfPattern[pattern].test name
for identifier, entry of observers
{context, callbacks} = entry
for callback in callbacks
callback.apply context, args
for identifier, entry of @__propertyObservers
{context, callbacks} = entry
for callback in callbacks
callback.apply context, args
@
#---------------------------------------------------------------------------
# Managing Property Observer
# method: addPropertyObserverWithName <observer>, <name>, <callback>
# return: this
addPropertyObserverWithName: (observer, name, callback) ->
if isFunction callback
observers = @__propertyObserversOfName[name]
unless observers?
observers = @__propertyObserversOfName[name] = {}
entry = observers[observer.__identifier]
unless entry?
entry = observers[observer.__identifier] =
context: observer
callbacks: [callback]
else if entry.callbacks.indexOf(callback) is -1
entry.callbacks.push callback
else
@error 'attempting to add duplicated property observer callback of name:', name, 'observer:', observer
else
@error 'cannot add non-function type of property observer callback:', callback, 'name:', name, 'observer:', observer
@
# method: addPropertyObserverWithPattern <observer>, <pattern>, <callback>
# return: this
addPropertyObserverWithPattern: (observer, pattern, callback) ->
if isRegExp pattern
regexp = pattern
pattern = regexp.toString()
else
regexp = new RegExp pattern
self.__regexpOfPattern[pattern] = regexp
if isFunction callback
observers = @__propertyObserversOfPattern[pattern]
unless observers?
observers = @__propertyObserversOfPattern[pattern] = {}
entry = observers[observer.__identifier]
unless entry?
entry = observers[observer.__identifier] =
context: observer
callbacks: [callback]
else if entry.callbacks.indexOf(callback) is -1
entry.callbacks.push callback
else
@error 'attempting to add duplicated property observer callback of pattern:', pattern, 'observer:', observer
else
@error 'cannot add non-function type of property observer callback:', callback, 'pattern:', pattern, 'observer:', observer
@
# method: addPropertyObserverToAll <observer>, <callback>
# return: this
addPropertyObserverToAll: (observer, callback) ->
if isFunction callback
entry = @__propertyObservers[observer.__identifier]
unless entry?
entry = @__propertyObservers[observer.__identifier] =
context: observer
callbacks: [callback]
else if entry.callbacks.indexOf(callback) is -1
entry.callbacks.push callback
else
@error 'attempting to add duplicated property observer callback of observer:', observer
else
@error 'cannot add non-function type of property observer callback:', callback, 'observer:', observer
@
# method: removePropertyObserverWithName <observer>, <name>, [callback]
# return: this
removePropertyObserverWithName: (observer, name, callback) ->
observers = @__propertyObserversOfName[name]
if observers?
unless callback?
delete observers[observer.__identifier]
else
entry = observers[observer.__identifier]
if entry?
callbacks = entry.callbacks
index = callbacks.indexOf callback
if index isnt -1
callbacks.splice index, 1
unless callbacks.length
delete observers[observer.__identifier]
if isEmpty observers
delete @__propertyObserversOfName[name]
@
# method: removePropertyObserverWithPattern <observer>, <pattern>, [callback]
# return: this
removePropertyObserverWithPattern: (observer, pattern, callback) ->
if isRegExp pattern
pattern = pattern.toString()
observers = @__propertyObserversOfPattern[pattern]
if observers?
unless callback?
delete observers[observer.__identifier]
else
entry = observers[observer.__identifier]
if entry?
callbacks = entry.callbacks
index = callbacks.indexOf callback
if index isnt -1
callbacks.splice index, 1
unless callbacks.length
delete observers[observer.__identifier]
if isEmpty observers
delete @__propertyObserversOfPattern[pattern]
@
# method: removePropertyObserverFromAll <observer>, [callback]
# return: this
removePropertyObserverFromAll: (observer, callback) ->
unless callbacks?
for name, observers of @__propertyObserversOfName
delete observers[observer.__identifier]
if isEmpty observers
delete @__propertyObserversOfName[name]
for pattern, observers of @__propertyObserversOfPattern
delete observers[observer.__identifier]
if isEmpty observers
delete @__propertyObserversOfPattern[pattern]
@__propertyObservers = []
else
for name, observers of @__propertyObserversOfName
entry = observers[observer.__identifier]
if entry?
callbacks = entry.callbacks
index = callbacks.indexOf callback
if index isnt -1
callbacks.splice index, 1
unless callbacks.length
delete observers[observer.__identifier]
if isEmpty observers
delete @__propertyObserversOfName[name]
for pattern, observers of @__propertyObserversOfPattern
entry = observers[observer.__identifier]
if entry?
callbacks = entry.callbacks
index = callbacks.indexOf callback
if index isnt -1
callbacks.splice index, 1
unless callbacks.length
delete observers[observer.__identifier]
if isEmpty observers
delete @__propertyObserversOfPattern[pattern]
entry = @__propertyObservers[observer.__identifier]
if entry?
callbacks = entry.callbacks
index = callbacks.indexOf callback
if index isnt -1
callbacks.splice index, 1
unless callbacks.length
delete @__propertyObservers[observer.__identifier]
@
class Process extends Observable
self = @initialize()
@property
started:
attributes: bindable
defaults: yes
converter: Boolean
@property
needsUpdate:
defaults: yes
converter: Boolean
#---------------------------------------------------------------------------
# Initialization
# method : initialize [parameters]
# returns : this
initialize: (parameters) ->
super()
@setProperties parameters
application.attachProcess @
@
#---------------------------------------------------------------------------
# Managing Process Cycle
# method: process
# returns: this
process: ->
if @_needsUpdate
@setNeedsUpdate no
@update()
@
# method: update
# returns: this
update: -> @
# method: invalidate
# returns: this
invalidate: ->
application.detachProcess @
@
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment