adamhalasz/diet

Proposal Replace object.observe by Kefir.Stream or the IBM Shim

frank-dspeed opened this issue ยท 1 comments

lets use Kefir Streams ๐Ÿ‘
Or the IBM Code
This Closes: #60 #42

Source from IBM decor

IBM decor Shim for OberservableArray

/** @module liaison/ObservableArray */
define([
	"requirejs-dplugins/has",
	"./Observable"
], function (has, Observable) {
	"use strict";

	/**
	 * The same argument list of Array, taking the length of the new array or the initial list of array elements.
	 * @typedef {number|...Anything} module:liaison/ObservableArray~CtorArguments
	 */

	/**
	 * An observable array, working as a shim
	 * of {@link http://wiki.ecmascript.org/doku.php?id=harmony:observe ECMAScript Harmony Array.observe()}.
	 * @class
	 * @alias module:liaison/ObservableArray
	 * @augments module:decor/Observable
	 * @param {module:decor/ObservableArray~CtorArguments} [args]
	 *     The length of the new array or the initial list of array elements.
	 */
	var ObservableArray,
		augmentedMethods,
		defineProperty = Object.defineProperty,
		EMPTY_ARRAY = [],
		REGEXP_GLOBAL_OBJECT = /\[\s*object\s+global\s*\]/i; // Global object in node.js

	(function () {
		var observableArrayMarker = "_observableArray";

		if (has("object-observe-api")) {
			// For useNative case, make ObservableArray an instance of Array instead of an inheritance,
			// so that Array.observe() emits splices for .length update
			ObservableArray = function (length) {
				var self = [];
				Observable.call(self);
				// Make ObservableArray marker not enumerable, configurable or writable
				defineProperty(self, observableArrayMarker, {value: 1});
				defineProperty(self, "set", Object.getOwnPropertyDescriptor(Observable.prototype, "set"));
				if (typeof length === "number" && arguments.length === 1) {
					self.length = length;
				} else {
					EMPTY_ARRAY.push.apply(self, arguments);
				}
				return self;
			};
		} else {
			// TODO(asudoh):
			// Document that ObservableArray cannot be observed by Observable.observe()
			// without "splice" in accept list.
			// We need to create large amount of change records to do so,
			// when splice happens with large amount of removals/adds
			ObservableArray = function (length) {
				var beingConstructed = this && !REGEXP_GLOBAL_OBJECT.test(this) && !this.hasOwnProperty("length"),
					// If this is called as regular function (instead of constructor), work with a new instance
					self = beingConstructed ? [] : new ObservableArray();
				if (beingConstructed) {
					Observable.call(self);
					// Make ObservableArray marker not enumerable, configurable or writable
					defineProperty(self, observableArrayMarker, {value: 1});
					// Make those methods not enumerable
					for (var s in augmentedMethods) {
						defineProperty(self, s, {
							value: augmentedMethods[s],
							configurable: true,
							writable: true
						});
					}
				}
				if (typeof length === "number" && arguments.length === 1) {
					self.length = length;
				} else {
					EMPTY_ARRAY.push.apply(self, arguments);
				}
				return self;
			};
		}

		/**
		 * @method module:liaison/ObservableArray.test
		 * @param {Array} a The array to test.
		 * @returns {boolean} true if o is an instance of {@link module:liaison/ObservableArray ObservableArray}.
		 */
		ObservableArray.test = function (a) {
			return a && a[observableArrayMarker];
		};
	})();

	/**
	 * @method module:liaison/ObservableArray.canObserve
	 * @param {Array} a The array to test.
	 * @returns {boolean}
	 *     true if o can be observed with {@link module:liaison/ObservableArray.observe ObservableArray.observe()}.
	 */
	if (has("object-observe-api")) {
		ObservableArray.canObserve = function (a) {
			return typeof (a || {}).splice === "function";
		};
	} else {
		ObservableArray.canObserve = ObservableArray.test;
	}

	if (!has("object-observe-api")) {
		(function () {
			/**
			 * Adds and/or removes elements from an array
			 * and automatically emits a change record compatible
			 * with {@link http://wiki.ecmascript.org/doku.php?id=harmony:observe ECMAScript Harmony Array.observe()}.
			 * @param {number} index Index at which to start changing the array.
			 * @param {number} removeCount [An integer indicating the number of old array elements to remove.
			 * @param {...Anything} [var_args] The elements to add to the array.
			 * @return {Array} An array containing the removed elements.
			 * @memberof module:liaison/ObservableArray#
			 */
			function splice(index, removeCount) {
				/* jshint validthis: true */
				if (index < 0) {
					index = this.length + index;
				}
				var oldLength = this.length,
					changeRecord = {
						index: index,
						removed: this.slice(index, index + removeCount),
						addedCount: arguments.length - 2
					},
					result = EMPTY_ARRAY.splice.apply(this, arguments),
					lengthRecord = oldLength !== this.length && {
						type: "update",
						object: this,
						name: "length",
						oldValue: oldLength
					},
					notifier = Observable.getNotifier(this);
				notifier.performChange("splice", function () {
					lengthRecord && notifier.notify(lengthRecord);
					return changeRecord;
				});
				return result;
			}

			augmentedMethods = /** @lends module:liaison/ObservableArray# */ {
				splice: splice,

				/**
				 * Sets a value and automatically emits change record(s)
				 * compatible with
				 * {@link http://wiki.ecmascript.org/doku.php?id=harmony:observe ECMAScript Harmony Array.observe()}.
				 * @param {string} name The property name.
				 * @param value The property value.
				 * @returns The value set.
				 */
				set: function (name, value) {
					var args;
					if (name === "length") {
						args = new Array(Math.max(value - this.length, 0));
						args.unshift(Math.min(this.length, value), Math.max(this.length - value, 0));
						splice.apply(this, args);
					} else if (!isNaN(name) && +name >= this.length) {
						args = new Array(name - this.length);
						args.push(value);
						args.unshift(this.length, 0);
						splice.apply(this, args);
					} else {
						Observable.prototype.set.call(this, name, value);
					}
					return value;
				},

				/**
				 * Removes the last element from an array
				 * and automatically emits a change record compatible with
				 * {@link http://wiki.ecmascript.org/doku.php?id=harmony:observe ECMAScript Harmony Array.observe()}.
				 * @returns The element removed.
				 */
				pop: function () {
					return splice.call(this, -1, 1)[0];
				},

				/**
				 * Adds one or more elements to the end of an array
				 * and automatically emits a change record compatible with
				 * {@link http://wiki.ecmascript.org/doku.php?id=harmony:observe ECMAScript Harmony Array.observe()}.
				 * @param {...Anything} var_args The elements to add to the end of the array.
				 * @returns The new length of the array.
				 */
				push: function () {
					var args = [this.length, 0];
					EMPTY_ARRAY.push.apply(args, arguments);
					splice.apply(this, args);
					return this.length;
				},

				/**
				 * Reverses the order of the elements of an array
				 * and automatically emits a splice type of change record.
				 * @returns {Array} The array itself.
				 */
				reverse: function () {
					var changeRecord = {
							type: "splice",
							object: this,
							index: 0,
							removed: this.slice(),
							addedCount: this.length
						},
						result = EMPTY_ARRAY.reverse.apply(this, arguments);
					// Treat this change as a splice instead of updates in each entry
					Observable.getNotifier(this).notify(changeRecord);
					return result;
				},

				/**
				 * Removes the first element from an array
				 * and automatically emits a change record compatible with
				 * {@link http://wiki.ecmascript.org/doku.php?id=harmony:observe ECMAScript Harmony Array.observe()}.
				 * @returns The element removed.
				 */
				shift: function () {
					return splice.call(this, 0, 1)[0];
				},

				/**
				 * Sorts the elements of an array in place
				 * and automatically emits a splice type of change record.
				 * @returns {Array} The array itself.
				 */
				sort: function () {
					var changeRecord = {
							type: "splice",
							object: this,
							index: 0,
							removed: this.slice(),
							addedCount: this.length
						},
						result = EMPTY_ARRAY.sort.apply(this, arguments);
					// Treat this change as a splice instead of updates in each entry
					Observable.getNotifier(this).notify(changeRecord);
					return result;
				},

				/**
				 * Adds one or more elements to the front of an array
				 * and automatically emits a change record compatible with
				 * {@link http://wiki.ecmascript.org/doku.php?id=harmony:observe ECMAScript Harmony Array.observe()}.
				 * @param {...Anything} var_args The elements to add to the front of the array.
				 * @returns The new length of the array.
				 */
				unshift: function () {
					var args = [0, 0];
					EMPTY_ARRAY.push.apply(args, arguments);
					splice.apply(this, args);
					return this.length;
				}
			};
		})();
	}

	/**
	 * Observes an ObservableArray for changes.
	 * Internally calls {@link module:decor/Observable.observe Observable.observe()}
	 * observing for the following types of change records:
	 * [
	 *     "add",
	 *     "update",
	 *     "delete",
	 *     "splice"
	 * ]
	 * All change records will be converted to "splice" and are sorted by index and merged to smaller number
	 * of change records.
	 * @method
	 * @param {Object} observable The {@link module:liaison/ObservableArray ObservableArray} to observe.
	 * @param {module:decor/Observable~ChangeCallback} callback The change callback.
	 * @returns {Handle} The handle to stop observing.
	 * @throws {TypeError} If the 1st argument is non-object or null.
	 */
	ObservableArray.observe = (function () {
		function intersect(start1, end1, start2, end2) {
			return end1 <= start2 ? end1 - start2 : // Adjacent or distant
				end2 <= start1 ? end2 - start1 : // Adjacent or distant
				Math.min(end1, end2) - Math.max(start1, start2); // Intersected or contained
		}
		function normalize(record) {
			return record.type !== "add" && record.type !== "update" ? record :
				{
					type: "splice",
					object: record.object,
					index: +record.name,
					removed: [record.oldValue],
					addedCount: 1
				};
		}
		function observeSpliceCallback(callback, records) {
			var merged = [];
			records.forEach(function (incoming) {
				incoming = normalize(incoming);
				var doneIncoming = false,
					indexAdjustment = 0;
				for (var i = 0; i < merged.length; ++i) {
					var entry;
					if (!has("object-observe-api") || !Object.isFrozen(merged[i])) {
						entry = merged[i];
						entry.index += indexAdjustment;
					} else {
						entry = merged[i] = {
							type: "splice",
							object: merged[i].object,
							index: merged[i].index + indexAdjustment,
							removed: merged[i].removed,
							addedCount: merged[i].addedCount
						};
					}
					/* jshint maxlen:150 */
					var amount = intersect(entry.index, entry.index + entry.addedCount, incoming.index, incoming.index + incoming.removed.length);
					if (amount >= 0) {
						// Merge splices
						merged.splice(i--, 1);
						var removed,
							addedCount = entry.addedCount - amount + incoming.addedCount;
						if (entry.index < incoming.index) {
							removed = incoming.removed.slice(Math.max(amount, 0));
							EMPTY_ARRAY.unshift.apply(removed, entry.removed);
						} else {
							removed = incoming.removed.slice(0, amount > 0 ? entry.index - incoming.index : incoming.length);
							EMPTY_ARRAY.push.apply(removed, entry.removed);
							// Append happens when second splice's range contains first splice's range
							EMPTY_ARRAY.push.apply(removed, incoming.removed.slice(entry.index + entry.addedCount - incoming.index));
						}
						/* jshint maxlen:120 */
						if (removed.length === 0 && addedCount === 0) {
							doneIncoming = true;
						} else {
							incoming = {
								type: "splice",
								object: entry.object,
								index: Math.min(entry.index, incoming.index),
								removed: removed,
								addedCount: addedCount
							};
						}
						indexAdjustment -= entry.addedCount - entry.removed.length; // entry is subsumed by incoming
					} else if (incoming.index < entry.index) {
						// Insert the new splice
						var adjustment = incoming.addedCount - incoming.removed.length;
						entry.index += adjustment;
						indexAdjustment += adjustment;
						merged.splice(i++, 0, incoming);
						doneIncoming = true;
					}
				}
				if (!doneIncoming) {
					merged.push(incoming);
				}
			});
			if (merged.length > 0) {
				callback(merged);
			}
		}
		if (has("object-observe-api")) {
			return function (observableArray, callback) {
				Array.observe(observableArray, callback = observeSpliceCallback.bind(observableArray, callback));
				return {
					deliver: Object.deliverChangeRecords.bind(Object, callback),
					remove: Array.unobserve.bind(Array, observableArray, callback)
				};
			};
		} else {
			return function (observableArray, callback) {
				var h = Object.create(Observable.observe(observableArray,
					callback = observeSpliceCallback.bind(observableArray, callback), [
					"add",
					"update",
					"delete",
					"splice"
				]));
				h.deliver = Observable.deliverChangeRecords.bind(Observable, callback);
				return h;
			};
		}
	})();

	return ObservableArray;
});

IBM Decor Shim for Observeable

/** @module decor/Observable */
define([
	"./features",
	"./features!object-observe-api?:./schedule"
], function (has, schedule) {
	"use strict";

	/**
	 * An observable object, working as a shim
	 * of {@link http://wiki.ecmascript.org/doku.php?id=harmony:observe ECMAScript Harmony Object.observe()}.
	 * @class
	 * @alias module:decor/Observable
	 * @param {Object} o The object to mix-into the new Observable.
	 * @example
	 *     var observable = new Observable({foo: "Foo0"});
	 *     Observable.observe(observable, function (changeRecords) {
	 *         // Called at the end of microtask with:
	 *         //     [
	 *         //         {
	 *         //             type: "update",
	 *         //             object: observable,
	 *         //             name: "foo",
	 *         //             oldValue: "Foo0"
	 *         //         },
	 *         //         {
	 *         //             type: "add",
	 *         //             object: observable,
	 *         //             name: "bar"
	 *         //         }
	 *         //     ]
	 *     });
	 *     observable.set("foo", "Foo1");
	 *     observable.set("bar", "Bar0");
	 */
	var Observable,
		defineProperty = Object.defineProperty,
		getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;

	/**
	 * The default list of change record types, which is:
	 * [
	 *     "add",
	 *     "update",
	 *     "delete",
	 *     "reconfigure",
	 *     "setPrototype",
	 *     "preventExtensions"
	 * ]
	 * @constant {Array.<module:decor/Observable~ChangeType>}
	 *     module:decor/Observable~DEFAULT_CHANGETYPES
	 */
	var DEFAULT_ACCEPT_CHANGETYPES = {
		"add": 1,
		"update": 1,
		"delete": 1,
		"reconfigure": 1,
		"setPrototype": 1,
		"preventExtensions": 1
	}; // Observable#set() only supports the first two

	/**
	 * Change record type.
	 * One of:
	 * * "add"
	 * * "update"
	 * * "delete"
	 * * "reconfigure"
	 * * "setPrototype"
	 * * "preventExtensions"
	 * * "splice"
	 * @typedef {string} module:decor/Observable~ChangeType
	 */

	/**
	 * Change record seen in Observable.observe().
	 * @typedef {Object} module:decor/Observable~ChangeRecord
	 * @property {module:decor/Observable~ChangeType} type The type of change record.
	 * @property {Object} object The changed object.
	 * @property {string} [name] The changed property name. Set only for non-splice type of change records.
	 * @property {number} [index] The array index of splice. Set only for splice type of change records.
	 * @property {Array} [removed] The removed array elements. Set only for splice type of change records.
	 * @property {number} [addedCount] The count of added array elements. Set only for splice type of change records.
	 */

	/**
	 * Change callback.
	 * @callback module:decor/Observable~ChangeCallback
	 * @param {Array.<module:decor/Observable~ChangeRecord>} changeRecords The change records.
	 */

	Observable = function (o) {
		// Make Observable marker not enumerable, configurable or writable
		if (!this._observable) { // In case this constructor is called manually
			defineProperty(this, "_observable", {value: 1});
		}
		o && Observable.assign(this, o);
	};

	/**
	 * @method module:decor/Observable.test
	 * @param {Object} o The object to test.
	 * @returns {boolean} true if o is an instance of Observable.
	 */
	Observable.test = function (o) {
		return o && o._observable;
	};

	/**
	 * @method module:decor/Observable.is
	 * @returns {boolean} true if the given two values are the same, considering NaN as well as +0 vs. -0.
	 */
	Observable.is = has("object-is-api") ? Object.is : function (lhs, rhs) {
		return lhs === rhs && (lhs !== 0 || 1 / lhs === 1 / rhs) || lhs !== lhs && rhs !== rhs;
	};

	/**
	 * Copy properties of given source objects to given target object.
	 * If target object has {@link module:decor/Observable#set set()} function for the property, uses it.
	 * @function module:decor/Observable.assign
	 * @param {Object} dst The target object.
	 * @param {...Object} var_args The source objects.
	 * @returns {Object} The target object.
	 */
	Observable.assign = function (dst) {
		if (dst == null) {
			throw new TypeError("Can't convert " + dst + " to object.");
		}
		dst = Object(dst);
		for (var i = 1, l = arguments.length; i < l; ++i) {
			var src = Object(arguments[i]),
				props = Object.getOwnPropertyNames(src);
			for (var j = 0, m = props.length; j < m; ++j) {
				var prop = props[j];
				Observable.prototype.set.call(dst, prop, src[prop]);
			}
		}
		return dst;
	};

	/**
	 * @method module:decor/Observable.canObserve
	 * @param {Object} o The object to test.
	 * @returns {boolean} true if o can be observed with {@link module:decor/Observable.observe Observable.observe()}.
	 */
	if (has("object-observe-api")) {
		Observable.canObserve = function (o) {
			return typeof o === "object" && o != null;
		};
	} else {
		Observable.canObserve = Observable.test;
	}

	if (has("object-observe-api")) {
		defineProperty(Observable.prototype, "set", { // Make set() not enumerable
			value: function (name, value) {
				this[name] = value;
				return value;
			},
			configurable: true,
			writable: true
		});

		Observable.observe = function (object, callback, accept) {
			Object.observe.call(this, object, callback, accept);
			return {
				remove: function () {
					Object.unobserve(object, callback);
				}
			};
		};

		Observable.getNotifier = Object.getNotifier;
		Observable.deliverChangeRecords = Object.deliverChangeRecords;
	} else {
		defineProperty(Observable.prototype, "set", { // Make set() not enumerable
			/**
			 * Sets a value.
			 * Automatically emits change record(s)
			 * compatible with {@link http://wiki.ecmascript.org/doku.php?id=harmony:observe Object.observe()}
			 * if no ECMAScript setter is defined for the given property.
			 * If ECMAScript setter is defined for the given property, use
			 * {@link module:decor/Observable~Notifier#notify Observable.getNotifier(observable).notify(changeRecord)}
			 * to manually emit a change record.
			 * @method module:decor/Observable#set
			 * @param {string} name The property name.
			 * @param value The property value.
			 * @returns The value set.
			 */
			value: function (name, value) {
				var type = name in this ? "update" : "add",
					oldValue = this[name],
					// For defining setter, ECMAScript setter should be used
					setter = (getOwnPropertyDescriptor(this, name) || {}).set;
				this[name] = value;
				if (!Observable.is(value, oldValue) && setter === undefined) {
					// Auto-notify if there is no setter defined for the property.
					// Application should manually call Observable.getNotifier(observable).notify(changeRecord)
					// if a setter is defined.
					var changeRecord = {
						type: type,
						object: this,
						name: name + ""
					};
					if (type === "update") {
						changeRecord.oldValue = oldValue;
					}
					Observable.getNotifier(this).notify(changeRecord);
				}
				return value;
			},
			configurable: true,
			writable: true
		});

		var seq = 0,
			hotCallbacks = {},
			deliverHandle = null,
			deliverAllByTimeout = function () {
				/* global Platform */
				has("polymer-platform") && Platform.performMicrotaskCheckpoint(); // For Polymer watching for Observable
				for (var anyWorkDone = true; anyWorkDone;) {
					anyWorkDone = false;
					// Observation may stop during observer callback
					var callbacks = [];
					for (var s in hotCallbacks) {
						callbacks.push(hotCallbacks[s]);
					}
					hotCallbacks = {};
					callbacks = callbacks.sort(function (lhs, rhs) {
						return lhs._seq - rhs._seq;
					});
					for (var i = 0, l = callbacks.length; i < l; ++i) {
						if (callbacks[i]._changeRecords.length > 0) {
							Observable.deliverChangeRecords(callbacks[i]);
							anyWorkDone = true;
						}
					}
				}
				deliverHandle = null;
			},
			removeGarbageCallback = function (callback) {
				if (callback._changeRecords.length === 0 && callback._refCountOfNotifier === 0) {
					callback._seq = undefined;
				}
			};

		/**
		 * Notifier object for Observable.
		 * This is an internal function and cannot be used directly.
		 * @class module:decor/Observable~Notifier
		 */
		var Notifier = function (target) {
			this.target = target;
			this.observers = {};
			this._activeChanges = {};
		};

		Notifier.prototype = /** @lends module:decor/Observable~Notifier */ {
			/**
			 * Queue up a change record.
			 * It will be notified at the end of microtask,
			 * or when {@link module:decor/Observable.deliverChangeRecords Observable.deliverChangeRecords()}
			 * is called.
			 * @method module:decor/Observable~Notifier#notify
			 * @param {module:decor/Observable~ChangeRecord} changeRecord
			 *     The change record to queue up for notification.
			 */
			notify: function (changeRecord) {
				function shouldDeliver(activeChanges, acceptTable, changeType) {
					if (changeType in acceptTable) {
						for (var s in acceptTable) {
							if (activeChanges[s] > 0) {
								return false;
							}
						}
						return true;
					}
				}
				for (var s in this.observers) {
					if (shouldDeliver(this._activeChanges, this.observers[s].acceptTable, changeRecord.type)) {
						var callback = this.observers[s].callback;
						callback._changeRecords.push(changeRecord);
						hotCallbacks[callback._seq] = callback;
						if (!deliverHandle) {
							deliverHandle = schedule(deliverAllByTimeout);
						}
					}
				}
			},
			/**
			 * Let the series of changes made in the given callback be represented
			 * by a synthetic change of the given change type.
			 * The callback may return the synthetic change record,
			 * which will be of the `type` and automatically emitted.
			 * Otherwise, the caller can emit the synthetic record manually
			 * via {@link module:decor/Observable~Notifier#notify notify()}.
			 * @param {string} type The change type of synthetic change record.
			 * @param {Function} callback The callback function.
			 */
			performChange: function (type, callback) {
				this._activeChanges[type] = (this._activeChanges[type] || 0) + 1;
				var source = callback.call(undefined);
				--this._activeChanges[type];
				if (source) {
					var target = {
						type: type,
						object: this.target
					};
					for (var s in source) {
						if (!(s in target)) {
							target[s] = source[s];
						}
					}
					this.notify(target);
				}
			}
		};

		/**
		 * Obtains a notifier object for the given {@link module:decor/Observable Observable}.
		 * @method module:decor/Observable.getNotifier
		 * @param {Object} observable The {@link module:decor/Observable Observable} to get a notifier object of.
		 * @returns {module:decor/Observable~Notifier}
		 */
		Observable.getNotifier = function (observable) {
			if (!getOwnPropertyDescriptor(observable, "_notifier")) {
				// Make the notifier reference not enumerable, configurable or writable
				defineProperty(observable, "_notifier", {
					value: new Notifier(observable)
				});
			}
			return observable._notifier;
		};

		/**
		 * Observes an {@link module:decor/Observable Observable} for changes.
		 * @method module:decor/Observable.observe
		 * @param {Object} observable The {@link module:decor/Observable Observable} to observe.
		 * @param {module:decor/Observable~ChangeCallback} callback The change callback.
		 * @param {Array.<module:decor/Observable~ChangeType>}
		 *     [accept={@link module:decor/Observable~DEFAULT_CHANGETYPES}]
		 *     The list of change record types to observe.
		 * @returns {Handle} The handle to stop observing.
		 * @throws {TypeError} If the 1st argument is non-object or null.
		 */
		Observable.observe = function (observable, callback, accept) {
			if (Object(observable) !== observable) {
				throw new TypeError("Observable.observe() cannot be called on non-object.");
			}
			if (!("_seq" in callback)) {
				callback._seq = seq++;
				callback._changeRecords = [];
				callback._refCountOfNotifier = 0;
			}
			var acceptTable = accept ? accept.reduce(function (types, type) {
					types[type] = 1;
					return types;
				}, {}) : DEFAULT_ACCEPT_CHANGETYPES,
				notifier = Observable.getNotifier(observable);
			if (!(callback._seq in notifier.observers)) {
				notifier.observers[callback._seq] = {
					acceptTable: acceptTable,
					callback: callback
				};
				++callback._refCountOfNotifier;
			} else {
				notifier.observers[callback._seq].acceptTable = acceptTable;
			}
			return {
				remove: function () {
					if (callback._seq in notifier.observers) {
						delete notifier.observers[callback._seq];
						--callback._refCountOfNotifier;
					}
				}
			};
		};

		/**
		 * Delivers change records immediately.
		 * @method module:decor/Observable.deliverChangeRecords
		 * @param {Function} callback The change callback to deliver change records of.
		 */
		Observable.deliverChangeRecords = function (callback) {
			var length = callback._changeRecords.length;
			try {
				callback(callback._changeRecords.splice(0, length));
			} catch (e) {
				has("console-api") && console.error("Error occured in observer callback: " + (e.stack || e));
			}
			removeGarbageCallback(callback);
		};
	}

	return Observable;
});

Just published a new version with the ES6 Proxy which replaces object.observe which should fix this.