Sys.Observer = function() {
#if DEBUG
throw Error.invalidOperation();
#endif
}
Sys.Observer.registerClass("Sys.Observer");
Sys.Observer.makeObservable = function(target) {
/// Makes an object directly observable by adding observable methods to it.
/// The object, array, or DOM element to make observable.
/// The observable object.
var isArray = target instanceof Array,
o = Sys.Observer;
##DEBUG Sys.Observer._ensureObservable(target);
if (target.setValue === o._observeMethods.setValue) return target;
o._addMethods(target, o._observeMethods);
if (isArray) {
o._addMethods(target, o._arrayMethods);
}
return target;
}
#if DEBUG
Sys.Observer._ensureObservable = function(target) {
var type = typeof target;
if ((type === "string") || (type === "number") || (type === "boolean") || (type === "date")) {
throw Error.invalidOperation(String.format(Sys.Res.notObservable, type));
}
}
#endif
Sys.Observer._addMethods = function(target, methods) {
for (var m in methods) {
#if DEBUG
if (target[m] && (target[m] !== methods[m])) {
throw Error.invalidOperation(String.format(Sys.Res.observableConflict, m));
}
#endif
target[m] = methods[m];
}
}
// Make use of private version for significant perf improvement in Binding.
// See references of these functions in binding.js for more details.
Sys.Observer._addEventHandler = function(target, eventName, handler) {
Sys.Observer._getContext(target, true).events._addHandler(eventName, handler);
}
Sys.Observer.addEventHandler = function(target, eventName, handler) {
/// Adds an observable event handler to the target.
///
///
///
##DEBUG Sys.Observer._ensureObservable(target);
Sys.Observer._addEventHandler(target, eventName, handler);
}
// Make use of private version for significant perf improvement in Binding.
// See references of these functions in binding.js for more details.
Sys.Observer._removeEventHandler = function(target, eventName, handler) {
Sys.Observer._getContext(target, true).events._removeHandler(eventName, handler);
}
Sys.Observer.removeEventHandler = function(target, eventName, handler) {
/// Adds an observable event handler to the target.
///
///
///
##DEBUG Sys.Observer._ensureObservable(target);
Sys.Observer._removeEventHandler(target, eventName, handler);
}
Sys.Observer.raiseEvent = function(target, eventName, eventArgs) {
/// Raises an observable event on the target.
///
///
///
##DEBUG Sys.Observer._ensureObservable(target);
var ctx = Sys.Observer._getContext(target);
if (!ctx) return;
var handler = ctx.events.getHandler(eventName);
if (handler) {
handler(target, eventArgs);
}
}
Sys.Observer.addPropertyChanged = function(target, handler) {
/// Adds a propertyChanged event handler to the target.
/// The object to observe.
/// The event handler.
##DEBUG Sys.Observer._ensureObservable(target);
Sys.Observer._addEventHandler(target, "propertyChanged", handler);
}
Sys.Observer.removePropertyChanged = function(target, handler) {
/// Removes a propertyChanged event handler from the target.
/// The object to observe.
/// The event handler.
##DEBUG Sys.Observer._ensureObservable(target);
Sys.Observer._removeEventHandler(target, "propertyChanged", handler);
}
Sys.Observer.beginUpdate = function(target) {
///
///
##DEBUG Sys.Observer._ensureObservable(target);
Sys.Observer._getContext(target, true).updating = true;
}
Sys.Observer.endUpdate = function(target) {
///
///
##DEBUG Sys.Observer._ensureObservable(target);
var ctx = Sys.Observer._getContext(target);
if (!ctx || !ctx.updating) return;
ctx.updating = false;
var dirty = ctx.dirty;
ctx.dirty = false;
if (dirty) {
if (target instanceof Array) {
var changes = ctx.changes;
ctx.changes = null;
Sys.Observer.raiseCollectionChanged(target, changes);
}
Sys.Observer.raisePropertyChanged(target, "");
}
}
Sys.Observer.isUpdating = function(target) {
///
///
///
##DEBUG Sys.Observer._ensureObservable(target);
var ctx = Sys.Observer._getContext(target);
return ctx ? ctx.updating : false;
}
// Make use of private version for significant perf improvement in Binding.
// See references of these functions in binding.js for more details.
Sys.Observer._setValue = function(target, propertyName, value) {
var getter, setter, mainTarget = target, path = propertyName.split('.');
for (var i = 0, l = (path.length - 1); i < l ; i++) {
var name = path[i];
getter = target["get_" + name];
if (typeof (getter) === "function") {
target = getter.call(target);
}
else {
target = target[name];
}
var type = typeof (target);
if ((target === null) || (type === "undefined")) {
throw Error.invalidOperation(String.format(Sys.Res.nullReferenceInPath, propertyName));
}
}
var currentValue, lastPath = path[l];
getter = target["get_" + lastPath];
setter = target["set_" + lastPath];
if (typeof(getter) === 'function') {
currentValue = getter.call(target);
}
else {
currentValue = target[lastPath];
}
if (typeof(setter) === 'function') {
setter.call(target, value);
}
else {
target[lastPath] = value;
}
if (currentValue !== value) {
var ctx = Sys.Observer._getContext(mainTarget);
if (ctx && ctx.updating) {
ctx.dirty = true;
return;
};
Sys.Observer.raisePropertyChanged(mainTarget, path[0]);
}
}
Sys.Observer.setValue = function(target, propertyName, value) {
/// Sets a property or field on the target in an observable manner.
/// The object to set a property on.
/// The name of the property to field to set.
/// The value to set.
##DEBUG Sys.Observer._ensureObservable(target);
Sys.Observer._setValue(target, propertyName, value);
}
Sys.Observer.raisePropertyChanged = function(target, propertyName) {
/// Raises a change notification event.
///
/// The object to raise the event on.
/// The name of the property that changed.
Sys.Observer.raiseEvent(target, "propertyChanged", new Sys.PropertyChangedEventArgs(propertyName));
}
Sys.Observer.addCollectionChanged = function(target, handler) {
///
///
///
Sys.Observer._addEventHandler(target, "collectionChanged", handler);
}
Sys.Observer.removeCollectionChanged = function(target, handler) {
///
///
///
Sys.Observer._removeEventHandler(target, "collectionChanged", handler);
}
Sys.Observer._collectionChange = function(target, change) {
var ctx = Sys.Observer._getContext(target);
if (ctx && ctx.updating) {
ctx.dirty = true;
var changes = ctx.changes;
if (!changes) {
ctx.changes = changes = [change];
}
else {
changes.push(change);
}
}
else {
Sys.Observer.raiseCollectionChanged(target, [change]);
Sys.Observer.raisePropertyChanged(target, 'length');
}
}
Sys.Observer.add = function(target, item) {
/// Adds an item to the collection in an observable manner.
/// The array to add to.
/// The item to add.
var change = new Sys.CollectionChange(Sys.NotifyCollectionChangedAction.add, [item], target.length);
Array.add(target, item);
Sys.Observer._collectionChange(target, change);
}
Sys.Observer.addRange = function(target, items) {
/// Adds items to the collection in an observable manner.
/// The array to add to.
/// The array of items to add.
var change = new Sys.CollectionChange(Sys.NotifyCollectionChangedAction.add, items, target.length);
Array.addRange(target, items);
Sys.Observer._collectionChange(target, change);
}
Sys.Observer.clear = function(target) {
/// Clears the array of its elements in an observable manner.
/// The array to clear.
var oldItems = Array.clone(target);
Array.clear(target);
Sys.Observer._collectionChange(target, new Sys.CollectionChange(Sys.NotifyCollectionChangedAction.reset, null, -1, oldItems, 0));
}
Sys.Observer.insert = function(target, index, item) {
/// Inserts an item at the specified index in an observable manner.
/// The array to insert into.
/// The index where the item will be inserted.
/// The item to insert.
Array.insert(target, index, item);
Sys.Observer._collectionChange(target, new Sys.CollectionChange(Sys.NotifyCollectionChangedAction.add, [item], index));
}
Sys.Observer.remove = function(target, item) {
/// Removes the first occurence of an item from the array in an observable manner.
/// The array to remove from.
/// The item to remove.
/// True if the item was found.
var index = Array.indexOf(target, item);
if (index !== -1) {
Array.remove(target, item);
Sys.Observer._collectionChange(target, new Sys.CollectionChange(Sys.NotifyCollectionChangedAction.remove, null, -1, [item], index));
return true;
}
return false;
}
Sys.Observer.removeAt = function(target, index) {
/// Removes the item at the specified index from the array in an observable manner.
/// The array to remove from.
/// The index of the item to remove.
if ((index > -1) && (index < target.length)) {
var item = target[index];
Array.removeAt(target, index);
Sys.Observer._collectionChange(target, new Sys.CollectionChange(Sys.NotifyCollectionChangedAction.remove, null, -1, [item], index));
}
}
Sys.Observer.raiseCollectionChanged = function(target, changes) {
/// Raises the collectionChanged event.
///
/// The collection to raise the event on.
/// A list of changes that were performed on the collection since the last event.
Sys.Observer.raiseEvent(target, "collectionChanged", new Sys.NotifyCollectionChangedEventArgs(changes));
}
// note: triple-slash comments in these methods works with the preprocessor despite them being defined in this unique way
Sys.Observer._observeMethods = {
add_propertyChanged: function(handler) {
Sys.Observer._addEventHandler(this, "propertyChanged", handler);
},
remove_propertyChanged: function(handler) {
Sys.Observer._removeEventHandler(this, "propertyChanged", handler);
},
addEventHandler: function(eventName, handler) {
/// Adds an observable event handler.
///
///
Sys.Observer._addEventHandler(this, eventName, handler);
},
removeEventHandler: function(eventName, handler) {
/// Removes an observable event handler.
///
///
Sys.Observer._removeEventHandler(this, eventName, handler);
},
get_isUpdating: function() {
///
///
///
return Sys.Observer.isUpdating(this);
},
beginUpdate: function() {
///
///
Sys.Observer.beginUpdate(this);
},
endUpdate: function() {
///
///
Sys.Observer.endUpdate(this);
},
setValue: function(name, value) {
/// Sets a property or field on the target in an observable manner.
/// The name of the property to field to set.
/// The value to set.
Sys.Observer._setValue(this, name, value);
},
raiseEvent: function(eventName, eventArgs) {
/// Raises an observable event.
///
///
///
Sys.Observer.raiseEvent(this, eventName, eventArgs);
},
raisePropertyChanged: function(name) {
/// Raises a change notification event.
///
/// The name of the property that changed.
Sys.Observer.raiseEvent(this, "propertyChanged", new Sys.PropertyChangedEventArgs(name));
}
}
Sys.Observer._arrayMethods = {
add_collectionChanged: function(handler) {
Sys.Observer._addEventHandler(this, "collectionChanged", handler);
},
remove_collectionChanged: function(handler) {
Sys.Observer._removeEventHandler(this, "collectionChanged", handler);
},
add: function(item) {
/// Adds an item to the collection in an observable manner.
///
/// The item to add.
Sys.Observer.add(this, item);
},
addRange: function(items) {
/// Adds items to the collection in an observable manner.
///
/// The array of items to add.
Sys.Observer.addRange(this, items);
},
clear: function() {
/// Clears the array of its elements in an observable manner.
///
Sys.Observer.clear(this);
},
insert: function(index, item) {
/// Inserts an item at the specified index in an observable manner.
///
/// The index where the item will be inserted.
/// The item to insert.
Sys.Observer.insert(this, index, item);
},
remove: function(item) {
/// Removes the first occurence of an item from the array in an observable manner.
///
/// The item to remove.
/// True if the item was found.
return Sys.Observer.remove(this, item);
},
removeAt: function(index) {
/// Removes the item at the specified index from the array in an observable manner.
///
/// The index of the item to remove.
Sys.Observer.removeAt(this, index);
},
raiseCollectionChanged: function(changes) {
/// Raises the collectionChanged event.
///
/// A list of changes that were performed on the collection since the last event.
Sys.Observer.raiseEvent(this, "collectionChanged", new Sys.NotifyCollectionChangedEventArgs(changes));
}
}
Sys.Observer._getContext = function(obj, create) {
var ctx = obj._observerContext;
if (ctx) return ctx();
if (create) {
return (obj._observerContext = Sys.Observer._createContext())();
}
return null;
}
Sys.Observer._createContext = function() {
// instead of attaching an EventHandlerList, etc directly onto the observed object, we attach a function
// which returns it as a closure. This prevents the need to attach a field to the object which is not a
// function, which could have a negative impact on serializers, whereas they typically always skip functions.
// It's also better for sparse arrays, where a for-var-in loop need only ignore functions.
// Return an object instead of the handler list directly so we have a place to put other fields without having
// to add separate getX() methods for each.
var ctx = {
events: new Sys.EventHandlerList()
};
return function() {
return ctx;
}
}