Created
April 20, 2013 22:26
-
-
Save trxcllnt/5427669 to your computer and use it in GitHub Desktop.
A dynamic Observable dictionary.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2013 the original author or authors | |
* | |
* Permission is hereby granted to use, modify, and distribute this file | |
* in accordance with the terms of the license agreement accompanying it. | |
*/ | |
package org.tinytlf.observables | |
{ | |
import asx.array.flatten; | |
import asx.fn.args; | |
import asx.fn.ifElse; | |
import asx.fn.noop; | |
import flash.utils.*; | |
import raix.reactive.*; | |
import raix.reactive.scheduling.*; | |
use namespace flash_proxy; | |
/** | |
* <p> | |
* Values is a dynamic Observable Dictionary. | |
* </p> | |
* | |
* <p> | |
* Since it extends Proxy, it overrides the flash_proxy functions for setting | |
* and retrieving data. | |
* </p> | |
*/ | |
public dynamic class Values extends DynObservable implements ICancelable | |
{ | |
public function Values(styleObject:Object = null) | |
{ | |
mergeWith(styleObject); | |
} | |
private const subscribers:Array = []; | |
override public function subscribeWith(observer:IObserver):ICancelable { | |
subscribers.push(observer); | |
return Cancelable.create(function():void { | |
const i:int = subscribers.indexOf(observer); | |
if(i != -1) subscribers.splice(i, 1); | |
}); | |
} | |
public function cancel():void { | |
dispatch(callProperty('onCompleted')); | |
subscribers.length = 0; | |
} | |
public function observe(...properties):IObservable { | |
properties = flatten(properties); | |
if(properties.length == 0) throw new Error('...well this is awkward :/'); | |
return filter(function(changes:Array):Boolean { | |
return properties.indexOf(changes[1]) != -1; | |
}); | |
} | |
public function combine(...properties):IObservable { | |
properties = flatten(properties); | |
if(properties.length == 0) throw new Error('...well this is awkward :/'); | |
const filtered:IObservable = observe(properties[0]); | |
const obs:IObservable = hasOwnProperty(properties[0]) ? | |
filtered.startWith(this[properties[0]]) : | |
filtered; | |
return properties.length == 1 ? | |
obs : | |
obs.combineLatest(combine(properties.slice(1)), args); | |
} | |
public function clearStyles():Values | |
{ | |
properties = {}; | |
propNames.length = 0; | |
return this; | |
} | |
public function mergeWith(object:Object):Values | |
{ | |
for(var prop:String in object) | |
mergeProperty(prop, object); | |
return this; | |
} | |
public function applyTo(object:Object, dynamic:Boolean = false):Values | |
{ | |
for(var prop:String in properties) | |
applyProperty(prop, object, dynamic); | |
return this; | |
} | |
protected var properties:Object = {}; | |
protected var propNames:Array = []; | |
public function toString():String | |
{ | |
var str:String = "{"; | |
propNames.forEach(function(property:String, ...args):void{ | |
str = str.concat(property, ":", properties[property].toString(), ";"); | |
}); | |
return str.concat("}"); | |
} | |
protected function mergeProperty(property:String, source:Object):void | |
{ | |
this[property] = source[property]; | |
} | |
protected function applyProperty(property:String, destination:Object, dynamic:Boolean = false):void | |
{ | |
if(!destination.hasOwnProperty(property) && !dynamic) | |
return; | |
const isFunction:Boolean = destination[property] is Function; | |
if(isFunction) | |
return; | |
destination[property] = this[property]; | |
} | |
protected function update(name:String, o:*, n:*):Values { | |
return dispatch(callProperty('onNext', [this, name, o, n])); | |
} | |
protected function dispatch(action:Function):Values { | |
const self:Values = this; | |
const observers:Array = subscribers.concat(); | |
const empty:Function = function():Boolean { return observers.length <= 0; }; | |
var schedule:Function = function(reschedule:Function):void { | |
schedule = reschedule; | |
const observer:IObserver = observers.shift(); | |
try { | |
action(observer); | |
} catch(e:Error) { | |
observer.onError(e); | |
} finally { | |
recurse(); | |
} | |
}; | |
const recurse:Function = ifElse(empty, noop, schedule); | |
recurse(); | |
return self; | |
} | |
override flash_proxy function getProperty(name:*):* | |
{ | |
return properties[name]; | |
} | |
override flash_proxy function hasProperty(name:*):Boolean | |
{ | |
return properties.hasOwnProperty(name); | |
} | |
override flash_proxy function callProperty(name:*, ... parameters):* | |
{ | |
if(properties.hasOwnProperty(name) == false) return null; | |
const val:* = properties[name]; | |
return (val is Function) ? val.apply(this, parameters) : val; | |
} | |
override flash_proxy function deleteProperty(name:*):Boolean | |
{ | |
const val:* = properties[name]; | |
if(delete properties[name]) | |
{ | |
propNames.splice(propNames.indexOf(name.toString()), 1); | |
update(name, val, undefined); | |
return true; | |
} | |
return false; | |
} | |
override flash_proxy function setProperty(name:*, value:*):void | |
{ | |
const val:* = properties[name]; | |
if(!properties.hasOwnProperty(name)) | |
propNames.push(name.toString()); | |
properties[name] = value; | |
update(name, val, value); | |
} | |
override flash_proxy function nextName(index:int):String | |
{ | |
return propNames[index - 1]; | |
} | |
override flash_proxy function nextNameIndex(index:int):int | |
{ | |
return index < propNames.length ? index + 1 : 0; | |
} | |
override flash_proxy function nextValue(index:int):* | |
{ | |
return properties[propNames[index - 1]]; | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment