UNPKG

core-state

Version:

127 lines (123 loc) 4.43 kB
if typeof EventEmitter is "undefined" EventEmitter = Leaf?.EventEmitter or require("eventex").EventEmitter class ObservePortal extends EventEmitter constructor:(@adapter)-> super() @buffer = {} @watches = {} @adapter.listenBy this,"change",@handleChange.bind(this) @adapter.listenBy this,"init",@handleChange.bind(this) @adapter.listenBy this,"delete",@handleChange.bind(this) observeBy:(who,path,callback)-> path = @normalize(path) @watches[path] ?= [] watch = @watches[path] exists = watch.some (item)-> return item.owner is who and callback is callback if not exists watch.push { owner:who callback:callback } @_observe path,callback stopObserveBy:(who,path)-> if path watch = @watches[path] watches = [watch] else watches = (@watches[prop] for prop of @watches) for watch,index in watches for item in watch if item.owner is who watch.splice(index,1) break for path of @watches if @watches[path].length is 0 @_stopObserve path _observe:(path,callback = ()->)-> @adapter.observe path,(err,results)=> # I do not emit change event here, no matter the property change # or not. # If every thing goes right, # 1. the first observe will not need a event since nobody is listen. # 2. if not first observe then change event will already be sent before # # so no bother to fire even here. if results and results not instanceof Array results = [results] else if not results results = [] for result in results @setBufferProperty result.path,result callback err,results _stopObserve:(path,callback = ()->)-> @adapter.stopObserve path,callback normalize:(path)-> path = path.split(".") group = path.shift() if not group return name = path.shift() if not name or name is "*" name = "" path = "#{group}.#{name}" return path handleChange:(change)-> if change.type is "delete" @deleteBufferProperty change.path else if change.type in ["init","change"] @setBufferProperty change.path,change.property @emit "#{change.type}/#{change.path}" @emit change.type,change get:(path)-> detail = @getProperty(path) if detail instanceof Array return detail.map (item)->item.data else if detail return detail.data return null getProperty:(path)-> routes = path.split(".") groupName = routes.shift() propertyName = routes.shift() if not groupName return null group = @buffer[groupName] or {} if not propertyName or propertyName is "*" return (group[prop] for prop of group) property = group[propertyName] if not property return null return property setBufferProperty:(path,property)-> [group,name] = path.split(".") if not group or not name return @buffer[group] ?= {} @buffer[group][name] = property deleteBufferProperty:(path)-> [group,name] = path.split(".") delete @buffer[group]?[name] class Adapter extends EventEmitter constructor:()-> super() observe:(path,callback)-> stopObserve:(path,callback)-> class ExampleAdapter extends Adapter constructor:(@observeWindow)-> super() @observeWindow.on "delete",(path)=> @emit "delete",{type:"delete",path} @observeWindow.on "init",(prop)=> prop.type = "init" @emit "init",prop @observeWindow.on "change",(prop)=> prop.type = "change" @emit "change",prop observe:(path,callback)-> callback null,@observeWindow.observe path stopObserve:(path,callback)-> callback null,@observeWindow.stopObserve path module.exports.ObservePortal = ObservePortal module.exports.Adapter = Adapter module.exports.ExampleAdapter = ExampleAdapter