Skip to content

Instantly share code, notes, and snippets.

@trxcllnt
Created April 20, 2013 22:26
Show Gist options
  • Save trxcllnt/5427669 to your computer and use it in GitHub Desktop.
Save trxcllnt/5427669 to your computer and use it in GitHub Desktop.
A dynamic Observable dictionary.
/*
* 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