//= require ./core
//= require_self

(function () {

	"use strict";

	var assertEqual = function (obj1, obj2) {
		if (obj1 === obj2) {
			return true;
		}

		if (typeof obj1 !== typeof obj2) {
			return false;
		}

		if (!obj1 || !obj2) {
			return false;
		}

		if (typeof obj1 === "object") {
			for (var k in obj1) {
				if (obj1.hasOwnProperty(k)) {
					if ( !assertEqual(obj1[k], obj2[k]) ) {
						return false;
					}
				}
			}

			for (k in obj2) {
				if (obj2.hasOwnProperty(k)) {
					if ( !assertEqual(obj2[k], obj1[k]) ) {
						return false;
					}
				}
			}
			return true;
		} else {
			return obj1 === obj2;
		}
	};

	var calculateDiff = function (keypath, value) {
		var __hasChanges = Marbles.Utils.extend({}, this.__hasChanges);
		if (assertEqual(this.__originalValues[keypath], value)) {
			__hasChanges[keypath] = false;
		} else {
			__hasChanges[keypath] = true;
		}
		this.set("__hasChanges", __hasChanges);
	};

	Marbles.DirtyTracking = {
		willInitialize: function () {
			var keypaths = this.constructor.dirtyTrackingKeypaths;
			if ( !Array.isArray(keypaths) ) {
				throw new Error(this.constructor.displayName +": `dirtyTrackingKeypaths` property (Array) on ctor required, is "+ JSON.stringify(keypaths) +"!");
			}

			this.resetDirtyTracking();

			// track changes
			keypaths.forEach(function (keypath) {
				var __parts = keypath.split('.');
				var __keypath = "";
				__parts.forEach(function (__part) {
					__keypath = __keypath ? (__keypath +"."+ __part) : __part;
					this.on("change:"+ __keypath, function () {
						calculateDiff.call(this, keypath, this.get(keypath));
					}.bind(this));
				}.bind(this));

				// changes via child objects
				this.on("change", function (value, oldValue, kpath) {
					var __value, k;
					if (kpath !== keypath && kpath.substr(0, keypath.length) === keypath) {
						k = kpath.substring(keypath.length+1);
						__value = this.get(keypath);
						calculateDiff.call(this, keypath, __value);
					}
				}.bind(this));
			}.bind(this));
		},

		didInitialize: function () {
			this.resetDirtyTracking();
		},

		proto: {
			resetDirtyTracking: function (keypath) {
				var keypaths;
				if (keypath) {
					keypaths = [keypath];
				} else {
					keypaths = this.constructor.dirtyTrackingKeypaths;
					this.__originalValues = {};
					this.__hasChanges = {};
				}
				keypaths.forEach(function (keypath) {
					this.__originalValues[keypath] = this.get(keypath);
					this.__hasChanges[keypath] = false;
				}.bind(this));
			},

			isDirty: function (keypath) {
				if (keypath) {
					return !!this.__hasChanges[keypath];
				} else {
					for (var k in this.__hasChanges) {
						if (this.__hasChanges.hasOwnProperty(k)) {
							if (this.__hasChanges[k] === true) {
								return true;
							}
						}
					}
					return false;
				}
			}
		}
	};

})();

@@ -2716,41 +2716,111 @@
 %09%09%09%09
-if (this.__hasChanges%5Bk%5D === true
+// call isDirty so overriding logic for a single keypath%0A%09%09%09%09%09%09%09// is easier%0A%09%09%09%09%09%09%09if (this.isDirty(k)
 ) %7B%0A

Update DirtyTracking: Call isDirty(keypath) for each tracked keypath