Tinytest.add("spark - patch - basic", function(test) { var Patcher = Spark._Patcher; var div = function(html) { var n = document.createElement("DIV"); n.innerHTML = html; return n; }; var tag = function(node, tagName, which) { return node.getElementsByTagName(tagName)[which || 0]; }; var assert_html = function(actual, expected) { actual = (typeof actual === "string" ? actual : actual.innerHTML); expected = (typeof expected === "string" ? expected : expected.innerHTML); test.equal(actual.toLowerCase(), expected.toLowerCase()); }; var x,y,p,ret; x = div("foobar"); y = div("quxbaz"); p = new Patcher(x, y); ret = p.match(tag(x, 'u'), tag(y, 'u')); test.isTrue(ret); assert_html(x, "bar"); ret = p.finish(); test.isTrue(ret); assert_html(x, "barbaz"); x = div("foobar"); y = div("quxbaz"); p = new Patcher(x, y); ret = p.finish(); test.isTrue(ret); assert_html(x, "quxbaz"); x = div("foobar"); y = div( "123foo456789bar10111213"); p = new Patcher(x, y); ret = p.match(tag(x, 'u'), tag(y, 'u')); test.isTrue(ret); assert_html(x, "123foobar"); ret = p.match(tag(x, 's'), tag(y, 's')); test.isTrue(ret); assert_html( x, "123foo456789bar"); ret = p.finish(); test.isTrue(ret); assert_html( x, "123foo456789bar10111213"); // mismatched parents, detection and recovery x = div("foobar"); y = div("foobar"); p = new Patcher(x,y); ret = p.match(tag(x, 'i'), tag(y, 'i')); test.isTrue(ret); assert_html(x, "foobar"); ret = p.match(tag(x, 'u'), tag(y, 'u')); test.isFalse(ret); assert_html(x, "foobar"); ret = p.finish(); test.isTrue(ret); assert_html(x,"foobar"); x = div("foobar"); y = div("foobar"); p = new Patcher(x,y); ret = p.match(tag(x, 'i'), tag(y, 'i')); test.isTrue(ret); assert_html(x, "foobar"); ret = p.match(tag(x, 'u'), tag(y, 'u')); test.isFalse(ret); assert_html(x, "foobarbar"); ret = p.finish(); test.isTrue(ret); assert_html(x, "foobar"); // mismatched tag name, detection and recovery x = div("foobar"); y = div("barbaz"); p = new Patcher(x, y); ret = p.match(tag(x, 'u'), tag(y, 'u')); test.isFalse(ret); ret = p.finish(); test.isTrue(ret); assert_html(x, "barbaz"); var t = "_foo"; var liverange = function(start, end, inner) { return new LiveRange(t, start, end, inner); }; var rangeTest = function(extras) { var aaa = extras[0], zzz = extras[1]; x = div(aaa+"foobar"+zzz); y = div("barbaz"); var rng = liverange(tag(y, 'u')); var tgt = liverange(tag(x, 'b')); p = new Patcher(tgt.containerNode(), y, tgt.firstNode().previousSibling, tgt.lastNode().nextSibling); var copyCallback = function(tgt, src) { LiveRange.transplantTag(t, tgt, src); }; ret = p.match(tag(x, 'u'), tag(y, 'u'), copyCallback); test.isTrue(ret); assert_html(x, aaa+"bar"+zzz); ret = p.finish(); test.isTrue(ret); assert_html(x, aaa+"barbaz"+zzz); test.equal(rng.firstNode(), tag(x, 'u')); }; _.each([["aaa","zzz"], ["",""], ["aaa",""], ["","zzz"]], rangeTest); }); Tinytest.add("spark - patch - copyAttributes", function(test) { var attrTester = function(tagName, initial) { var node; var allAttrNames = {}; var lastAttrs; var self = { copy: function(kv) { var buf = []; buf.push('<', tagName); _.each(kv, function(v,k) { allAttrNames[k] = true; buf.push(' ', k); if (v !== 'NO_VALUE') buf.push('="', v, '"'); }); buf.push('>'); var nodeHtml = buf.join(''); var frag = DomUtils.htmlToFragment(nodeHtml); var n = frag.firstChild; n._sparkOriginalRenderedChecked = [n.checked]; if (! node) { node = n; } else { Spark._Patcher._copyAttributes(node, n); } lastAttrs = {}; _.each(allAttrNames, function(v,k) { lastAttrs[k] = false; }); _.each(kv, function(v,k) { if (k === "style") { lastAttrs[k] = n.style.cssText; } else { lastAttrs[k] = String(v); } }); return self; }, check: function() { _.each(lastAttrs, function(v,k) { var actualAttr; var expectedAttr = v || ""; if (k === "style") { actualAttr = node.style.cssText; } else if (k === "class") { actualAttr = node.className; } else if (k === "checked") { actualAttr = String(node.getAttribute(k) || ""); if (expectedAttr === "NO_VALUE") expectedAttr = "checked"; if (actualAttr === "true") actualAttr = "checked"; // save IE's butt } else { actualAttr = String(node.getAttribute(k) || ""); } test.equal(actualAttr, expectedAttr, k); }); }, node: function() { return node; } }; if (initial) self.copy(initial); return self; }; var a = attrTester('div', {id:'foo', 'class':'bar', style:'border:1px solid blue;', name:'baz'}); a.check(); test.equal(a.node().style.borderLeftColor, "blue"); a.copy({id: "foo", style:'border:1px solid red'}); a.check(); test.equal(a.node().style.borderLeftColor, "red"); a.copy({id: "foo", 'class':'ha'}); a.check(); test.equal(a.node().style.borderColor, ""); test.equal(a.node().className, "ha"); var obj = {}; a.node().nifty = obj; a.copy({id: "foo", 'class':'ha hee'}); a.check(); test.equal(a.node().nifty, obj, 'nifty'); // test object property preservation var c = attrTester('input', {type:'checkbox', name:'foo', checked:'checked'}); c.check(); test.equal(c.node().checked, true); c.copy({type:'checkbox', name:'foo'}); c.check(); test.equal(c.node().checked, false); c.copy({type:'checkbox', name:'foo', checked:'checked'}); c.check(); test.equal(c.node().checked, true); c.copy({type:'checkbox', name:'foo'}); c.check(); test.equal(c.node().checked, false); var d = attrTester('input', {type:'checkbox', name:'foo'}); test.equal(c.node().checked, false); c.copy({type:'checkbox', name:'foo', checked:'checked'}); c.check(); test.equal(c.node().checked, true); c.copy({type:'checkbox', name:'foo'}); c.check(); test.equal(c.node().checked, false); c.copy({type:'checkbox', name:'foo', checked:'checked'}); c.check(); test.equal(c.node().checked, true); c.copy({type:'checkbox', name:'foo'}); c.check(); test.equal(c.node().checked, false); c.copy({type:'checkbox', name:'foo', checked:'NO_VALUE'}); c.check(); test.equal(c.node().checked, true); c.copy({type:'checkbox', name:'bar'}); test.expect_fail(); // changing "name" on a form control won't take in IE test.equal(c.node().getAttribute("name"), 'bar'); c.copy({type:'radio', name:'foo'}); test.expect_fail(); // changing "type" on a form control won't take in IE test.equal(c.node().getAttribute("type"), 'radio'); });