blossom
Version:
Modern, Cross-Platform Application Framework
145 lines (114 loc) • 4.57 kB
JavaScript
// ==========================================================================
// Project: SproutCore Costello - Property Observing Library
// Copyright: ©2006-2011 Strobe Inc. and contributors.
// Portions ©2008-2010 Apple Inc. All rights reserved.
// License: Licensed under MIT license (see license.js)
// ==========================================================================
sc_require('system/object');
var SC = global.SC; // Required to allow foundation to be re-namespaced as BT
// when loaded by the buildtools.
// ........................................................................
// CHAIN OBSERVER
//
// This is a private class used by the observable mixin to support chained
// properties.
// ChainObservers are used to automatically monitor a property several
// layers deep.
// org.plan.name = SC._ChainObserver.create({
// target: this, property: 'org',
// next: SC._ChainObserver.create({
// property: 'plan',
// next: SC._ChainObserver.create({
// property: 'name', func: myFunc
// })
// })
// })
//
SC._ChainObserver = function(property) {
this.property = property ;
} ;
// This is the primary entry point. Configures the chain.
SC._ChainObserver.createChain = function(rootObject, path, target, method, context) {
// First we create the chain.
var parts = path.split('.'),
root = new SC._ChainObserver(parts[0]),
tail = root,
len = parts.length;
for(var idx=1;idx<len;idx++) {
tail = tail.next = new SC._ChainObserver(parts[idx]) ;
}
// Now root has the first observer and tail has the last one.
// Feed the rootObject into the front to setup the chain...
// do this BEFORE we set the target/method so they will not be triggered.
root.objectDidChange(rootObject);
// Finally, set the target/method on the tail so that future changes will
// trigger.
tail.target = target; tail.method = method ; tail.context = context ;
// and return the root to save
return root ;
};
SC._ChainObserver.prototype = {
isChainObserver: true,
// the object this instance is observing
object: null,
// the property on the object this link is observing.
property: null,
// if not null, this is the next link in the chain. Whenever the
// current property changes, the next observer will be notified.
next: null,
// if not null, this is the final target observer.
target: null,
// if not null, this is the final target method
method: null,
// invoked when the source object changes. removes observer on old
// object, sets up new observer, if needed.
objectDidChange: function(newObject) {
if (newObject === this.object) return; // nothing to do.
// if an old object, remove observer on it.
if (this.object && this.object.removeObserver) {
this.object.removeObserver(this.property, this, this.propertyDidChange);
}
// if a new object, add observer on it...
this.object = newObject ;
if (this.object && this.object.addObserver) {
this.object.addObserver(this.property, this, this.propertyDidChange);
}
// now, notify myself that my property value has probably changed.
this.propertyDidChange() ;
},
// the observer method invoked when the observed property changes.
propertyDidChange: function() {
// get the new value
var object = this.object ;
var property = this.property ;
var value = (object && object.get) ? object.get(property) : null ;
// if we have a next object in the chain, notify it that its object
// did change...
if (this.next) this.next.objectDidChange(value) ;
// if we have a target/method, call it.
var target = this.target,
method = this.method,
context = this.context ;
if (target && method) {
var rev = object ? object.propertyRevision : null ;
if (context) {
method.call(target, object, property, value, context, rev);
} else {
method.call(target, object, property, value, rev) ;
}
}
},
// teardown the chain...
destroyChain: function() {
// remove observer
var obj = this.object ;
if (obj && obj.removeObserver) {
obj.removeObserver(this.property, this, this.propertyDidChange) ;
}
// destroy next item in chain
if (this.next) this.next.destroyChain() ;
// and clear left overs...
this.next = this.target = this.method = this.object = this.context = null;
return null ;
}
} ;