Business objects have no built-in equals; comparing two complex variables means comparing their serialised forms or walking the fields. Three practical variants:
// 1. quick equality: serialise both (field order is the BO definition order, so the strings are comparable)
function same(a, b) { return JSON.stringify(a) === JSON.stringify(b); } // BAW 20+; 8.5.x: a.toXMLString() === b.toXMLString()
tw.local.changed = !same(tw.local.original, tw.local.edited);
// 2. field-level diff (returns the list of changed paths) - ignores the "@metadata" style internals
function diff(a, b, path, out) {
path = path || ""; out = out || [];
if (a === b) return out;
if (a == null || b == null || typeof a !== "object" || typeof b !== "object") { out.push({ path: path, from: a, to: b }); return out; }
var isListA = a.listLength !== undefined, keys = {};
if (isListA) { var n = Math.max(a.listLength, b.listLength || 0); for (var i = 0; i < n; i++) diff(a[i], b[i], path + "[" + i + "]", out); return out; }
for (var k in a) if (typeof a[k] !== "function" && k.charAt(0) !== "@") keys[k] = 1;
for (var k2 in b) if (typeof b[k2] !== "function" && k2.charAt(0) !== "@") keys[k2] = 1;
for (var key in keys) diff(a[key], b[key], path ? path + "." + key : key, out);
return out;
}
var changes = diff(tw.local.original, tw.local.edited);
tw.local.summary = changes.map(function (c) { return c.path + ": " + c.from + " -> " + c.to; }).join("\n");
// 3. business identity instead of structural equality
tw.local.sameOrder = tw.local.a.number === tw.local.b.number && tw.local.a.version === tw.local.b.version;Pitfalls: dates compare as strings after serialisation (fine when both went through the same path; otherwise compare getTime()); decimals may differ by representation (10 vs 10.0 - round or compare numerically); list order matters for structural equality (sort by a key first when order is irrelevant); shared business objects compare by reference on the server (same object when both variables point to the same shared instance). In coaches, keep a copy of the original (JSON.parse(JSON.stringify(tw.local.order)) at load) to show "unsaved changes" with the diff above.
References