- correct compilation and output of quotes and backslash, with object returned in path (so nested compilation)");
// ................................ Reset ................................
$("#result").empty();
res = "";
});
QUnit.test("{^{for}}", function(assert) {
// ................................ Reset ................................
person1._firstName = "Jo"; // reset Prop
person1.lastName = "One"; // reset Prop
settings.title = "Mr"; // reset Prop
settings.width = 30; // reset Prop
model.things = []; // reset Prop
jsv.views.settings.advanced({_jsv: true}); // For using viewsAndBindings($)
// =============================== Arrange ===============================
model.things = [{thing: "box"}]; // reset Prop
jsv.templates('{^{for things}}{{:thing}}{{/for}}')
.link("#result", model);
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(model.things).insert(0, {thing: "tree"});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, 'box|treebox',
'{^{for things}} binds to array changes on leaf array');
// ................................ Act ..................................
jsv.observable(model).setProperty({things: [{thing: "triangle"}, {thing: "circle"}]});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, 'trianglecircle',
'{^{for things}} binds to property change on path');
// ................................ Act ..................................
jsv.observable(model).setProperty({things: {thing: "square"}});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, 'square',
'{^{for things}} binds to property change on path - swapping from array to singleton object');
// ................................ Act ..................................
jsv.observable(model).setProperty({things: [{thing: "triangle2"}, {thing: "circle2"}]});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, 'triangle2circle2',
'{^{for things}} binds to property change on path - swapping from singleton back to array');
// ................................ Act ..................................
jsv.observable(model.things).insert([{thing: "oblong"}, {thing: "pentagon"}]);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, 'triangle2circle2oblongpentagon',
'{^{for things}} binds to array change on array after swapping from singleton back to array');
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
assert.equal(JSON.stringify(_jsv.cbBindings), "{}",
"Bindings all removed when content removed from DOM");
// =============================== Arrange ===============================
var things1 = [{thing: "box"}],
things2 = [{thing: "triangle"}, {thing: "circle"}],
square = {thing: "square"};
model.things = things1; // reset Prop
jsv.templates('{^{for things}}{{:thing}}{{/for}}')
.link("#result", model);
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(things1).insert(0, {thing: "tree"});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, 'box|treebox',
'{^{for things}} binds to array changes on leaf array');
// ................................ Act ..................................
jsv.observable(model).setProperty({things: things2});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, 'trianglecircle',
'{^{for things}} binds to property change on path');
// ................................ Act ..................................
jsv.observable(model).setProperty({things: square});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, 'square',
'{^{for things}} binds to property change on path - swapping from array to singleton object');
// ................................ Act ..................................
jsv.observable(model).setProperty({things: things2});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, 'trianglecircle',
'{^{for things}} binds to property change on path - swapping from singleton back to previous array');
// ................................ Act ..................................
jsv.observable(things2).insert([{thing: "oblong"}, {thing: "pentagon"}]);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, 'trianglecircleoblongpentagon',
'{^{for things}} binds to array change on array after swapping from singleton back to array');
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
// =============================== Arrange ===============================
things1 = [{thing: "box"}];
things2 = [{thing: "triangle"}, {thing: "circle"}];
square = {thing: "square"};
model.things = things1; // reset Prop
jsv.templates('
{^{for things}}{{:thing}} {{/for}} ')
.link("#result", model);
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(things1).insert(0, {thing: "tree"});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, 'box|treebox',
'{^{for things}} in element content binds to array changes on leaf array');
// ................................ Act ..................................
jsv.observable(model).setProperty({things: things2});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, 'trianglecircle',
'{^{for things}} binds to property change on path');
// ................................ Act ..................................
jsv.observable(model).setProperty({things: square});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, 'square',
'{^{for things}} binds to property change on path - swapping from array to singleton object');
// ................................ Act ..................................
jsv.observable(model).setProperty({things: things2});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, 'trianglecircle',
'{^{for things}} binds to property change on path - swapping from singleton back to previous array');
// ................................ Act ..................................
jsv.observable(things2).insert([{thing: "oblong"}, {thing: "pentagon"}]);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, 'trianglecircleoblongpentagon',
'{^{for things}} binds to array change on array after swapping from singleton back to array');
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
// =============================== Arrange ===============================
model.things = [{thing: "box"}, {thing: "table"}]; // reset Prop
jsv.templates('{^{:length}} {^{for #data}}{{:thing}}{{/for}}')
.link("#result", model.things, null, true);
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(model.things).insert(0, {thing: "tree"});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, "2 boxtable|3 treeboxtable",
'{^{for #data}} when #data is an array binds to array changes on #data');
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
// =============================== Arrange ===============================
model.things = [{thing: "box"}, {thing: "table"}]; // reset Prop
jsv.templates('{^{:length}} {^{for}}{{:thing}}{{/for}}')
.link("#result", model.things, null, true);
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(model.things).insert(0, {thing: "tree"});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, "2 boxtable|3 treeboxtable",
'{^{for}} when #data is an array binds to array changes on #data');
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
assert.equal(JSON.stringify(_jsv.cbBindings), "{}",
"Bindings all removed when content removed from DOM");
// =============================== Arrange ===============================
var ret = "";
var listData = {nodes: {list: ["A "]}};
jsv.templates('Plain: {^{for nodes^list}}{{:}}{{/for}}\
|SpanWithNext: {^{for nodes^list}}
{{:}} {{/for}}
nextElem \
|UlWithNext:
{^{for nodes^list}}{{:}} {{/for}}nextElem \
|Ul:
{^{for nodes^list}}{{:}} {{/for}} ')
.link('#result', listData); //"Plain: A |SpanWithNext: A nextElem|UlWithNext: A nextElem|Ul: A "
// ................................ Act ..................................
ret += "|1 " + $("#result").text() + " " + $._data(listData.nodes.list).events.arrayChange.length;
jsv.observable(listData).setProperty("nodes", {list: []});
ret += "|2 " + $("#result").text() + " " + $._data(listData.nodes.list).events.arrayChange.length;
jsv.observable(listData.nodes.list).insert("C ");
ret += "|3 " + $("#result").text() + " " + $._data(listData.nodes.list).events.arrayChange.length;
jsv.observable(listData.nodes).setProperty("list", []);
ret += "|4 " + $("#result").text() + " " + $._data(listData.nodes.list).events.arrayChange.length;
jsv.observable(listData.nodes.list).insert("C2 ");
ret += "|5 " + $("#result").text() + " " + $._data(listData.nodes.list).events.arrayChange.length;
// ............................... Assert .................................
assert.equal(ret, "|1 Plain: A |SpanWithNext: A nextElem|UlWithNext: AnextElem|Ul: A 4"
+ "|2 Plain: |SpanWithNext: nextElem|UlWithNext: nextElem|Ul: 4"
+ "|3 Plain: C |SpanWithNext: C nextElem|UlWithNext: CnextElem|Ul: C 4"
+ "|4 Plain: |SpanWithNext: nextElem|UlWithNext: nextElem|Ul: 4"
+ "|5 Plain: C2 |SpanWithNext: C2 nextElem|UlWithNext: C2nextElem|Ul: C2 4",
"Deep observable updates of array path on {^{for}} does not create additional array bindings");
// =============================== Arrange ===============================
model.things = []; // reset Prop
jsv.templates('{^{if things.length}}X {^{for things}}{{:thing}}{{/for}}{{/if}}')
.link("#result", model);
// ................................ Act ..................................
after = $("#result").text();
jsv.observable(model.things).insert({thing: "box "});
after += "|" + $("#result").text();
jsv.observable(model.things).insert({thing: "table "});
after += "|" + $("#result").text();
jsv.observable(model.things).insert(0, {thing: "tree "});
after += "|" + $("#result").text();
jsv.observable(model.things).remove();
after += "|" + $("#result").text();
jsv.observable(model.things).remove(0);
after += "|" + $("#result").text();
jsv.observable(model.things).remove();
after += "|" + $("#result").text();
jsv.observable(model.things).refresh([{thing: "pen "},{thing: "lamp "}]);
after += "|" + $("#result").text();
jsv.observable(model.things).move(0, 1);
after += "|" + $("#result").text();
jsv.observable(model.things).refresh([]);
after += "|" + $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|X box |X box table |X tree box table |X tree box |X box ||X pen lamp |X lamp pen |",
'{^{if things.length}}{^{for things}} content block bound to both array and array.length responds correctly to observable array changes');
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
// =============================== Arrange ===============================
model.things = []; // reset Prop
jsv.templates('{^{for things.length && things}}{{:thing}}{{/for}}')
.link("#result", model);
// ................................ Act ..................................
after = $("#result").text();
jsv.observable(model.things).insert({thing: "box "});
after += "|" + $("#result").text();
jsv.observable(model.things).insert({thing: "table "});
after += "|" + $("#result").text();
jsv.observable(model.things).insert(0, {thing: "tree "});
after += "|" + $("#result").text();
jsv.observable(model.things).remove();
after += "|" + $("#result").text();
jsv.observable(model.things).remove(0);
after += "|" + $("#result").text();
jsv.observable(model.things).remove();
after += "|" + $("#result").text();
jsv.observable(model.things).refresh([{thing: "pen "},{thing: "lamp "}]);
after += "|" + $("#result").text();
jsv.observable(model.things).move(0, 1);
after += "|" + $("#result").text();
jsv.observable(model.things).refresh([]);
after += "|" + $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|box |box table |tree box table |tree box |box ||pen lamp |lamp pen |",
'{^{for things.length && things}} content block bound to both array and array.length responds correctly to observable array changes');
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
// =============================== Arrange ===============================
model.things = []; // reset Prop
jsv.templates('{^{for things.length}}{^{for ~root.things}}{{:thing}}{{/for}}{{/for}}')
.link("#result", model);
// ................................ Act ..................................
after = $("#result").text();
jsv.observable(model.things).insert({thing: "box "});
after += "|" + $("#result").text();
jsv.observable(model.things).insert({thing: "table "});
after += "|" + $("#result").text();
jsv.observable(model.things).insert(0, {thing: "tree "});
after += "|" + $("#result").text();
jsv.observable(model.things).remove();
after += "|" + $("#result").text();
jsv.observable(model.things).remove(0);
after += "|" + $("#result").text();
jsv.observable(model.things).remove();
after += "|" + $("#result").text();
jsv.observable(model.things).refresh([{thing: "pen "},{thing: "lamp "}]);
after += "|" + $("#result").text();
jsv.observable(model.things).move(0, 1);
after += "|" + $("#result").text();
jsv.observable(model.things).refresh([]);
after += "|" + $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|box |box table |tree box table |tree box |box ||pen lamp |lamp pen |",
'{^{for things.length}}{^{for ~root.things}} content bound to both array and array.length responds correctly to observable array changes');
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
// =============================== Arrange ===============================
model.things = [{thing: "box"}, {thing: "table"}]; // reset Prop
jsv.templates('{{include things}}{^{:length}} {^{for}}{{:thing}}{{/for}}{{/include}}')
.link("#result", model);
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(model.things).insert(0, {thing: "tree"});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, "2 boxtable|3 treeboxtable",
'{{include things}} moves context to things array, and {^{for}} then iterates and binds to array');
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
// =============================== Arrange ===============================
model.things = [{thing: "box"}];
jsv.templates('{^{for things}}{{:thing}}{{else}}None{{/for}}')
.link("#result", model);
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(model.things).insert(0, {thing: "tree"});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, 'box|treebox',
'{^{for things}}{{else}}{{/for}} binds to array changes on leaf array');
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(model.things).remove(0, 2);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, 'treebox|None',
'{^{for things}}{{else}}{{/for}} renders {{else}} block when array is emptied');
// ................................ Act ..................................
jsv.observable(model.things).insert(0, {thing: "tree"});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, 'tree',
'{^{for things}}{{else}}{{/for}} removes {{else}} block when item is added again');
// ................................ Act ..................................
jsv.observable(model).setProperty({things: [{thing: "triangle"}, {thing: "circle"}]});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, 'trianglecircle',
'{^{for things}}{{else}}{{/for}} binds to property change on path');
// ................................ Act ..................................
jsv.observable(model).setProperty({things: {thing: "square"}});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, 'square',
'{^{for things}}{{else}}{{/for}} binds to property change on path - swapping from array to singleton object');
// ................................ Act ..................................
jsv.observable(model).removeProperty("things");
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, 'None',
'{^{for things}}{{else}}{{/for}} binds to removeProperty change on path - and renders {{else}} block');
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
// =============================== Arrange ===============================
model.things = [{thing: "box"}];
jsv.templates('
{^{for things}}{{:thing}} {{else}}None {{/for}} ')
.link("#result", model);
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(model.things).insert(0, {thing: "tree"});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, 'box|treebox',
'{^{for things}}{{else}}{{/for}} binds to array changes on leaf array');
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(model.things).remove(0, 2);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, 'treebox|None',
'{^{for things}}{{else}}{{/for}} renders {{else}} block when array is emptied');
// ................................ Act ..................................
jsv.observable(model.things).insert(0, {thing: "tree"});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, 'tree',
'{^{for things}}{{else}}{{/for}} removes {{else}} block when item is added again');
// ................................ Act ..................................
jsv.observable(model).setProperty({things: [{thing: "triangle"}, {thing: "circle"}]});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, 'trianglecircle',
'{^{for things}}{{else}}{{/for}} binds to property change on path');
// ................................ Act ..................................
jsv.observable(model).setProperty({things: {thing: "square"}});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, 'square',
'{^{for things}}{{else}}{{/for}} binds to property change on path - swapping from array to singleton object');
// ................................ Act ..................................
jsv.observable(model).removeProperty("things");
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, 'None',
'{^{for things}}{{else}}{{/for}} binds to removeProperty change on path - and renders {{else}} block');
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
// =============================== Arrange ===============================
jsv.templates('{^{for things}}{{:thing}}{{else}}None{{/for}}{^{if true}}_yes{{/if}}')
.link("#result", model);
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(model.things).insert(0, {thing: "tree"});
jsv.observable(model.things).insert(0, {thing: "box"});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, 'None_yes|boxtree_yes',
'{^{for things}}{{else}}{{/for}}{^{if ...}} starting with empty array binds to array inserts');
// See https://github.com/BorisMoore/jsviews/issues/326
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
// =============================== Arrange ===============================
// ................................ Act ..................................
jsv.templates("testTmpl", '{{if ~things.length}}
{{for ~things}}{{:thing}} {{/for}} {{/if}}');
jsv.templates('
top {^{for things ~things=things tmpl="testTmpl"/}}
')
.link("#result", model);
before = $("#result td").text();
jsv.observable(model.things).insert(0, {thing: "tree"});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, 'top|toptree',
'Complex template, with empty placeholder for tbody after thead, and subsequent data-linked insertion of tbody');
// ................................ Act ..................................
jsv.view("#result", true).refresh();
res = "" + (after === $("#result").text());
jsv.view("#result", true).views._2.refresh();
res += " " + (after === $("#result").text());
jsv.view("#result", true).views._2.views[0].refresh();
res += " " + (after === $("#result").text());
jsv.view("#result", true).views._2.views[0].views._2.refresh();
res += " " + (after === $("#result").text());
jsv.view("#result", true).views._2.views[0].views._2.views._2.refresh();
res += " " + (after === $("#result").text());
jsv.view("#result", true).views._2.views[0].views._2.views._2.views[0].refresh();
res += " " + (after === $("#result").text());
// ............................... Assert .................................
assert.equal(res, 'true true true true true true',
'view refresh at all levels correctly maintains content');
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
assert.equal(JSON.stringify(_jsv.cbBindings), "{}",
"Bindings all removed when content removed from DOM");
// =============================== Arrange ===============================
// ................................ Act ..................................
jsv.templates("testTmpl", '{{if ~things.length}}
{{for ~things}}{{:thing}} {{/for}}
{{/if}}');
jsv.templates('
top {^{for things ~things=things tmpl="testTmpl"/}}
')
.link("#result", model);
before = $("#result div").text();
jsv.observable(model.things).insert(0, {thing: "tree"});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, 'top|toptree',
'Complex template, with empty placeholder for span, and subsequent data-linked insertion of in div');
// ................................ Act ..................................
jsv.view("#result", true).refresh();
res = "" + (after === $("#result").text());
jsv.view("#result", true).views._2.refresh();
res += " " + (after === $("#result").text());
jsv.view("#result", true).views._2.views[0].refresh();
res += " " + (after === $("#result").text());
jsv.view("#result", true).views._2.views[0].views._2.refresh();
res += " " + (after === $("#result").text());
jsv.view("#result", true).views._2.views[0].views._2.views._2.refresh();
res += " " + (after === $("#result").text());
jsv.view("#result", true).views._2.views[0].views._2.views._2.views[0].refresh();
res += " " + (after === $("#result").text());
// ............................... Assert .................................
assert.equal(res, 'true true true true true true',
'view refresh at all levels correctly maintains content');
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
assert.equal(JSON.stringify(_jsv.cbBindings), "{}",
"Bindings all removed when content removed from DOM");
// =============================== Arrange ===============================
// ................................ Act ..................................
jsv.templates('
{^{for things}}{^{if expanded}}{{:thing}} {{/if}}{{/for}}
')
.link("#result", model);
jsv.observable(model.things).insert(0, [{thing: "tree", expanded: false}]);
res = $._data(model.things[0]).events.propertyChange.length;
jsv.view("#result", true).views._1.views[0].refresh();
res += "|" + $._data(model.things[0]).events.propertyChange.length;
$("#result").empty();
res += "|" + $._data(model.things[0]).events;
// ............................... Assert .................................
assert.equal(res, '1|1|undefined',
'Refreshing a view containing a tag which is bound to dependant data, and has no _prv node, removes the original binding and replaces it with a new one');
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
assert.equal(JSON.stringify(_jsv.cbBindings), "{}",
"Bindings all removed when content removed from DOM");
// =============================== Arrange ===============================
// ................................ Act ..................................
jsv.templates('
{^{for things}}{^{if expanded}}{{:thing}}{{/if}}{{/for}}
')
.link("#result", model);
jsv.observable(model.things).insert(0, [{thing: "tree", expanded: false}]);
res = $._data(model.things[0]).events.propertyChange.length;
jsv.view("#result", true).views._1.views[0].refresh();
res += "|" + $._data(model.things[0]).events.propertyChange.length;
$("#result").empty();
res += "|" + $._data(model.things[0]).events;
// ............................... Assert .................................
assert.equal(res, '1|1|undefined',
'Refreshing a view containing a tag which is bound to dependant data, and has no _prv node, removes the original binding and replaces it with a new one');
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
assert.equal(JSON.stringify(_jsv.cbBindings), "{}",
"Bindings all removed when content removed from DOM");
// =============================== Arrange ===============================
// ................................ Act ..................................
jsv.templates('
{{if true}}{^{:things.length||""}}{{/if}}
')
.link("#result", model);
before = $("#result div *").length;
jsv.view("#result div", true).refresh();
after = $("#result div *").length;
// ............................... Assert .................................
assert.equal(after, before,
'Refreshing a view containing non-elOnly content, with a data-bound tag with no rendered content removes the original script node markers for the tag and replace with the new ones');
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
assert.equal(JSON.stringify(_jsv.cbBindings), "{}",
"Bindings all removed when content removed from DOM");
// =============================== Arrange ===============================
// ................................ Act ..................................
jsv.templates("testTmpl", '{^{if expanded}}
{{:thing}} {{/if}}');
jsv.templates('
{^{for things tmpl="testTmpl"/}}
')
.link("#result", model);
res = $("#result td").text();
jsv.observable(model.things).insert(0, [{thing: "tree", expanded: false}, {thing: "bush", expanded: true}]);
res += "|" + $("#result td").text();
jsv.observable(model.things[0]).setProperty("expanded", true);
jsv.observable(model.things[1]).setProperty("expanded", false);
res += "|" + $("#result td").text();
// ............................... Assert .................................
assert.equal(res, '|bush|tree',
'Changing dependant data on bindings with deferred correctly triggers refreshTag and refreshes content with updated data binding');
// ................................ Act ..................................
jsv.view("#result tr").parent.refresh();
res = $("#result td").text();
jsv.view("#result tr").parent.parent.views[1].refresh();
res += "|" + $("#result td").text();
// ............................... Assert .................................
assert.equal(res, 'tree|tree',
'view refresh with deferred correctly refreshes content');
// ................................ Act ..................................
jsv.observable(model.things[1]).setProperty("expanded", true);
res = $("#result td").text();
jsv.observable(model.things[0]).setProperty("expanded", false);
res += "|" + $("#result td").text();
// ............................... Assert .................................
assert.equal(res, 'treebush|bush',
'Changing dependant data on bindings with deferred, after view refresh correctly triggers refreshTag and refreshes content with updated data binding');
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
assert.equal(JSON.stringify(_jsv.cbBindings), "{}",
"Bindings all removed when content removed from DOM");
// =============================== Arrange ===============================
// ................................ Act ..................................
jsv.templates("testTmpl", '
{^{if expanded}}{{:thing}} {{/if}} ');
jsv.templates('
{^{for things tmpl="testTmpl"/}}
')
.link("#result", model);
res = $("#result td").text();
jsv.observable(model.things).insert(0, [{thing: "tree", expanded: false}, {thing: "bush", expanded: true}]);
res += "|" + $("#result").text();
jsv.observable(model.things[0]).setProperty("expanded", true);
jsv.observable(model.things[1]).setProperty("expanded", false);
res += "|" + $("#result").text();
// ............................... Assert .................................
assert.equal(res, '|bush|tree',
'Changing dependant data on bindings with deferred correctly triggers refreshTag and refreshes content with updated data binding');
// ................................ Act ..................................
jsv.view("#result tr").refresh();
res = $("#result").text();
jsv.view("#result tr").parent.views[1].refresh();
res += "|" + $("#result").text();
// ............................... Assert .................................
assert.equal(res, 'tree|tree',
'view refresh with deferred correctly refreshes content');
// ................................ Act ..................................
jsv.observable(model.things[1]).setProperty("expanded", true);
res = $("#result").text();
jsv.observable(model.things[0]).setProperty("expanded", false);
res += "|" + $("#result").text();
// ............................... Assert .................................
assert.equal(res, 'treebush|bush',
'Changing dependant data on bindings with deferred, after view refresh correctly triggers refreshTag and refreshes content with updated data binding');
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
assert.equal(JSON.stringify(_jsv.cbBindings), "{}",
"Bindings all removed when content removed from DOM");
// =============================== Arrange ===============================
jsv.templates("
{{for}}Name: {{:firstName()}}. Width: {{:~settings.width}} {{/for}} ")
.link("#result", person1, {settings: settings});
// ................................ Act ..................................
before = $("#result ul li").html(); // The innerHTML will be Name: Sir compFirst. Width: 40
person1.fullName.set.call(person1, "compFirst compLast");
settings.title = "Sir";
settings.width = 40;
jsv.view("li").refresh();
after = $("#result ul li").html();
// ............................... Assert .................................
assert.equal(before + "|" + after,
'Name: Mr Jo. Width: 30|Name: Sir compFirst. Width: 40',
'Calling view("li").refresh() for a view in element-only content (elCnt true) updates correctly: "
"');
// ................................ Reset ................................
$("#result").empty();
person1._firstName = "Jo"; // reset Prop
person1.lastName = "One"; // reset Prop
settings.title = "Mr"; // reset Prop
settings.width = 30; // reset Prop
// =============================== Arrange ===============================
var data = {};
jsv.templates("
{^{for items}}insertBefore {{/for}} next ")
.link("#result", data);
// ................................ Act ..................................
before = $("#result ul").text(); // The innerHTML will be Name: Sir compFirst. Width: 40
jsv.observable(data).setProperty("items", []);
var deferredString = $("#result ul li")[0]._df || "";
jsv.observable(data.items).insert("X");
after = $("#result ul").text();
// ............................... Assert .................................
assert.equal(before + "|" + deferredString + "|" + after,
(' next||insertBefore next'),
'Inserting content before a next sibling element in element-only context does not set ._df, and subsequent insertion is correctly placed before the next sibling.');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
jsv.templates('{^{for things}}
#index: #view.index: {{:thing}} Nested:{{for true}}{{for true}} #get(\'item\').index: #parent.parent.index: |{{/for}}{{/for}}
{{/for}}')
.link("#result", model);
// ................................ Act ..................................
jsv.observable(model.things).insert(0, {thing: "tree"});
jsv.observable(model.things).insert(0, {thing: "bush"});
// ............................... Assert .................................
assert.equal($("#result").text(), "#index:0 #view.index:0 bush Nested: #get('item').index:0 #parent.parent.index:0|#index:1 #view.index:1 tree Nested: #get('item').index:1 #parent.parent.index:1|",
'Data-link to "#index" and "#get(\'item\').index" work correctly');
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
// =============================== Arrange ===============================
jsv.templates('{^{for things}}
{{:thing}} Nested:{{for}}{{for}} {{/for}}{{/for}}
|{{/for}}')
.link("#result", model);
// ................................ Act ..................................
jsv.observable(model.things).insert(0, {thing: "tree"});
jsv.observable(model.things).insert(0, {thing: "bush"});
// ............................... Assert .................................
assert.equal($("#result").text(), "bush Nested:0|tree Nested:1|",
'Data-link to "#getIndex()" works correctly');
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
// =============================== Arrange ===============================
jsv.templates('
{^{for things}}xxx {{/for}} ')
.link("#result", model);
// ................................ Act ..................................
$("#result div").empty();
// ............................... Assert .................................
assert.ok(viewsAndBindings($).split(" ").length === 7 // We removed view inside div, but still have the view for the outer template.
&& !$._data(model.things).events,
'$(container).empty removes listeners for empty tags in element-only content (_df="#n_/n_")');
// =============================== Arrange ===============================
data = {
list: [],
q: true
};
jsv.templates('
{^{if q}}{^{for list}}{{:#data}} {{/for}}{{/if}} ')
.link("#result", data);
// ................................ Act ..................................
jsv.observable(data).setProperty("q", false);
jsv.observable(data).setProperty("q", true);
jsv.observable(data.list).insert("added");
// ............................... Assert .................................
assert.ok(viewsAndBindings($).split(" ").length === 13 // We removed view inside div, but still have the view for the outer template.
&& $._data(data.list).events.arrayChange.length === 1
&& $("#result ul").text() === "added",
'In element-only content, updateContent calls disposeTokens on _df inner bindings');
// ................................ Reset ................................
$("#result").empty();
assert.equal(JSON.stringify(_jsv.cbBindings), "{}",
"Bindings all removed when content removed from DOM");
jsv.views.settings.advanced({_jsv: false});
});
QUnit.test("{^{for start end sort filter reverse}}", function(assert) {
jsv.views.settings.advanced({_jsv: true}); // For using cbBindings store
assert.equal(jsv.templates("{{for start=0 end=10}}{{:}} {{/for}}").render(), "0 1 2 3 4 5 6 7 8 9 ", "{{for start=0 end=10}}: Auto-create array");
assert.equal(jsv.templates("{{for start=5 end=9 reverse=1}}{{:}} {{/for}}").render(), "8 7 6 5 ", "{{for start=5 end=9 reverse=1}}: Auto-create array");
assert.equal(jsv.templates("{{for start=8 end=4 step=-1}}{{:}} {{/for}}").render(), "8 7 6 5 ", "{{for start=8 end=4 step=-1}}: Auto-create array");
assert.equal(jsv.templates("{{for start=8 end=4 step=-1 reverse=true}}{{:}} {{/for}}").render(), "5 6 7 8 ", "{{for start=8 end=4 step=-1 reverse=true}}: Auto-create array");
assert.equal(jsv.templates("{{for start=20 end='10' step=-2}}{{:}} {{/for}}").render(), "20 18 16 14 12 ", "{{for start=20 end='10' step=-2}}: Auto-create array");
assert.equal(jsv.templates("{{for start=20 end='10' step=2}}{{:}} {{/for}}").render(), "", "{{for start=20 end='10' step=2}}: Auto-create array (outputs nothing)");
assert.equal(jsv.templates("{{for start=2 end=-1.5 step=-.5}}{{:}} {{/for}}").render(), "2 1.5 1 0.5 0 -0.5 -1 ", "{{for start=0 end='10' step=-1}}: Auto-create array");
assert.equal(jsv.templates("{{for start=2}}{{:}} {{/for}}").render(), "", "{{for start=2}}: (outputs nothing)");
assert.equal(jsv.templates("{{for end=4}}{{:}} {{/for}}").render(), "0 1 2 3 ", "{{for end=4}}: (start defaults to 0)");
var myarray = [1, 9, 2, 8, 3, 7, 4, 6, 5, -100, 20, 100, -1];
var mypeople = [
{name: "Jo", details: {age: 22}},
{name: "Bob", details: {age: 2}},
{name: "Emma", details: {age: 12}},
{name: "Jeff", details: {age: 13.5}},
{name: "Julia", details: {age: 0.6}},
{name: "Xavier", details: {age: 0}}
];
var oddValue = function(item, index, items) { return item%2; };
var oddIndex = function(item, index, items) { return index%2; };
var under20 = function(item, index, items) {
return item.details.age < 20;
};
assert.equal(jsv.templates("{{for #data}}{{:}} {{/for}}").render(myarray, true), "1 9 2 8 3 7 4 6 5 -100 20 100 -1 ", "{{for #data}}");
assert.equal(jsv.templates("{{for #data sort=true}}{{:}} {{/for}}").render(myarray, true), "-100 -1 1 2 3 4 5 6 7 8 9 20 100 ", "{{for #data sort=true}}");
assert.equal(jsv.templates("{{for myarray reverse=true}}{{:}} {{/for}}").render({myarray: myarray}), "-1 100 20 -100 5 6 4 7 3 8 2 9 1 ", "{{for myarray reverse=true}}");
assert.equal(jsv.templates("{{for myarray start=1 end=-1}}{{:}} {{/for}}").render({myarray: myarray}, {oddIndex: oddIndex}), "9 2 8 3 7 4 6 5 -100 20 100 ", "{{for myarray start=1 end=-1}}");
assert.equal(jsv.templates("{{for myarray start=1}}{{:}} {{/for}}").render({myarray: myarray}, {oddIndex: oddIndex}), "9 2 8 3 7 4 6 5 -100 20 100 -1 ", "{{for myarray start=1}}");
assert.equal(jsv.templates("{{for myarray end=-1}}{{:}} {{/for}}").render({myarray: myarray}, {oddIndex: oddIndex}), "1 9 2 8 3 7 4 6 5 -100 20 100 ", "{{for myarray end=-1}}");
assert.equal(jsv.templates("{{for myarray}}{{:}} {{/for}}").render({myarray: myarray}), "1 9 2 8 3 7 4 6 5 -100 20 100 -1 ", "{{for myarray}}");
assert.equal(jsv.templates("{{for myarray reverse=true}}{{:}} {{/for}}").render({myarray: myarray}), "-1 100 20 -100 5 6 4 7 3 8 2 9 1 ", "{{for myarray reverse=true}}");
assert.equal(jsv.templates("{{for myarray sort=true}}{{:}} {{/for}}").render({myarray: myarray}), "-100 -1 1 2 3 4 5 6 7 8 9 20 100 ", "{{for myarray sort=true}}");
assert.equal(jsv.templates("{{for myarray sort=true reverse=true}}{{:}} {{/for}}").render({myarray: myarray}), "100 20 9 8 7 6 5 4 3 2 1 -1 -100 ", "{{for myarray sort=true reverse=true}}");
assert.equal(jsv.templates("{{for myarray filter=~oddValue}}{{:}} {{/for}}").render({myarray: myarray}, {oddValue: oddValue}), "1 9 3 7 5 -1 ", "{{for myarray filter=~oddValue}}!!!");
assert.equal(jsv.templates("{{for myarray filter=~oddIndex}}{{:}} {{/for}}").render({myarray: myarray}, {oddIndex: oddIndex}), "9 8 7 6 -100 100 ", "{{for myarray filter=~oddIndex}}");
assert.equal(jsv.templates("{{for myarray filter=~oddValue}}{{:}} {{/for}}").render({myarray: myarray}, {oddValue: oddValue}), "1 9 3 7 5 -1 ", "{{for myarray filter=~oddValue}}");
assert.equal(jsv.templates("{{for myarray sort=true filter=~oddValue}}{{:}} {{/for}}").render({myarray: myarray}, {oddValue: oddValue}), "-1 1 3 5 7 9 ", "{{for myarray sort=true filter=~oddValue}}");
assert.equal(jsv.templates("{{for myarray sort=true filter=~oddIndex}}{{:}} {{/for}}").render({myarray: myarray}, {oddIndex: oddIndex}), "-1 2 4 6 8 20 ", "{{for myarray sort=true filter=~oddIndex}}");
assert.equal(jsv.templates("{{for myarray sort=true filter=~oddIndex start=1 end=3}}{{:}} {{/for}}").render({myarray: myarray}, {oddIndex: oddIndex}), "2 4 ", "{{for myarray sort=true filter=~oddIndex start=1 end=3}}");
assert.equal(jsv.templates("{{for myarray sort=true filter=~oddIndex start=-3 end=-1}}{{:}} {{/for}}").render({myarray: myarray}, {oddIndex: oddIndex}), "6 8 ", "{{for myarray sort=true filter=~oddIndex start=-3 end=-1}} Negative start or end count from the end");
assert.equal(jsv.templates("{{for myarray sort=true filter=~oddIndex start=3 end=3}}{{:}} {{/for}}").render({myarray: myarray}, {oddIndex: oddIndex}), "", "{{for myarray sort=true filter=~oddIndex start=3 end=3}} (outputs nothing)");
assert.equal(jsv.templates("{{for mypeople sort='name'}}{{:name}}: age {{:details.age}} - {{/for}}").render({mypeople: mypeople}), "Bob: age 2 - Emma: age 12 - Jeff: age 13.5 - Jo: age 22 - Julia: age 0.6 - Xavier: age 0 - ", "{{for mypeople sort='name'}}");
assert.equal(jsv.templates("{{for mypeople sort='details.age'}}{{:name}}: age {{:details.age}} - {{/for}}").render({mypeople: mypeople}), "Xavier: age 0 - Julia: age 0.6 - Bob: age 2 - Emma: age 12 - Jeff: age 13.5 - Jo: age 22 - ", "{{for mypeople sort='details.age'}}");
assert.equal(jsv.templates("{{for mypeople sort='details.age' reverse=true filter=~under20}}{{:name}}: age {{:details.age}} - {{/for}}").render({mypeople: mypeople}, {under20: under20}), "Jeff: age 13.5 - Emma: age 12 - Bob: age 2 - Julia: age 0.6 - Xavier: age 0 - ", "{{for mypeople sort='details.age' reverse=true filter=~under20}}");
assert.equal(jsv.templates("{{for mypeople sort='details.age' reverse=true filter=~under20 start=1 end=-1}}{{:name}}: age {{:details.age}} - {{/for}}").render({mypeople: mypeople}, {under20: under20}), "Emma: age 12 - Bob: age 2 - Julia: age 0.6 - ", "{{for mypeople sort='details.age' reverse=true filter=~under20 start=1 end=-1}}");
// =============================== Arrange ===============================
model.things = [{ob: {thing: "box"}}];
var ctx = {};
jsv.templates('|All: {^{for things}}{{:ob.thing}} {{else}}None{{/for}}
\
|Sort: {^{for this=~ctx.sorted things sort="ob.thing"}}{{:ob.thing}} {{else}}None{{/for}}
\
|NotTreeReverse: {^{for things filter=~notTree reverse=true}}{{:ob.thing}} {{else}}None{{/for}}
\
|Start1: {^{for things start=1}}{{:ob.thing}} {{else}}None{{/for}}')
.link("#result", model, {
ctx: ctx,
notTree: function(item) {
return item.ob.thing !== "tree";
}
});
var after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All: box |Sort: box |NotTreeReverse: box |Start1: None",
'{^{for things}}{{else}}{{/for}} plus sorting and filtering');
// ................................ Act ..................................
jsv.observable(model.things).insert(0, {ob: {thing: "tree"}});
jsv.observable(model.things).insert([{ob: {thing: "apple"}}, {ob: {thing: "tree"}}]);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All: tree box apple tree |Sort: apple box tree tree |NotTreeReverse: apple box |Start1: box apple tree ",
'{^{for things}}{{else}}{{/for}} plus sorting and filtering binds to array changes on leaf array');
// ................................ Act ..................................
jsv.observable(model.things).remove(0, 3);
after = $("#result").text();
// ............................... Assert .................................6
assert.equal(after, "|All: tree |Sort: tree |NotTreeReverse: None|Start1: None",
'{^{for things}}{{else}}{{/for}} plus sorting and filtering renders {{else}} block when array is emptied');
// ................................ Act ..................................
jsv.observable(model.things).insert(0, {ob: {thing: "tree"}});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All: tree tree |Sort: tree tree |NotTreeReverse: None|Start1: tree ",
'{^{for things}}{{else}}{{/for}} plus sorting and filtering removes {{else}} block when item is added again');
// ................................ Act ..................................
jsv.observable(model).setProperty({things: [{ob: {thing: "triangle"}}, {ob: {thing: "circle"}}]});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All: triangle circle |Sort: circle triangle |NotTreeReverse: circle triangle |Start1: circle ",
'{^{for things}}{{else}}{{/for}} plus sorting and filtering binds to property change on path');
// ................................ Act ..................................
jsv.observable(model.things).insert([{ob: {thing: "square"}}, {ob: {thing: "tree"}}]);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All: triangle circle square tree |Sort: circle square tree triangle |NotTreeReverse: square circle triangle |Start1: circle square tree ",
'{^{for things}}{{else}}{{/for}} plus sorting and filtering inserts new items with sorting/filtering');
// ................................ Act ..................................
jsv.observable(model).setProperty({things: {ob: {thing: "tree"}}});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All: tree |Sort: tree |NotTreeReverse: tree |Start1: tree ",
'{^{for things}}{{else}}{{/for}} plus sorting and filtering binds to property change on path - swapping from array to singleton object');
// ................................ Act ..................................
jsv.observable(model).setProperty({things: [{ob: {thing: "square"}}, {ob: {thing: "apple"}}, {ob: {thing: "tree"}}]});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All: square apple tree |Sort: apple square tree |NotTreeReverse: apple square |Start1: apple tree ",
'{^{for things}}{{else}}{{/for}} plus sorting and filtering binds to property change on path - swapping from singleton object back to array');
// ................................ Act ..................................
jsv.observable(model).removeProperty("things");
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All: None|Sort: None|NotTreeReverse: None|Start1: None",
'{^{for things}}{{else}}{{/for}} plus sorting and filtering binds to removeProperty change on path - and renders {{else}} block');
// ................................ Act ..................................
jsv.observable(model).setProperty("things", [{ob: {thing: "circle"}}, {ob: {thing: "tree"}}, {ob: {thing: "square"}}]);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All: circle tree square |Sort: circle square tree |NotTreeReverse: square circle |Start1: tree square ",
'{^{for things}}{{else}}{{/for}} plus sorting and filtering binds to setProperty change on path - and renders {{for}} block again');
// =============================== Arrange ===============================
var tgt = ctx.sorted.tagCtx.map.tgt;
// ................................ Act ..................................
jsv.observable(tgt).insert([{ob: {thing: "red"}}, {ob: {thing: "green"}}, {ob: {thing: "blue"}}]);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All: circle tree square red green blue |Sort: circle square tree red green blue |NotTreeReverse: blue green red square circle |Start1: tree square red green blue ",
'{^{for things}} plus sorting and filtering support observable changes to target array tagCtx.map.tgt');
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
assert.equal(JSON.stringify(_jsv.cbBindings), "{}",
"Bindings all removed when content removed from DOM");
// =============================== Arrange ===============================
var movies = [{title: "a0"}, {title: "x0"}, {title: "b0"}, {title: "y0"}, {title: "c0"}, {title: "z0"}];
ctx = {};
var cnt = 0;
jsv.templates(
'|All:--- {^{for movies}}{{:title}} {{/for}}
\
|Sort:-- {^{for movies sort="title" reverse=true}}{{:title}} {{/for}}
\
|Filter: {^{for movies sort="title" reverse=true filter=~odd}}{{:title}} {{/for}}
\
|Slice:- {^{for movies sort="title" reverse=true filter=~odd start=1 end=-1 this=~ctx.target}}{{:title}} {{/for}}')
.link("#result", {movies: movies}, {
ctx: ctx,
odd: function(item, index, items) {
return index%2;
}
});
tgt = ctx.target.tagCtx.map.tgt; // This is the target array for the fourth (and last) {^{for}} tag above - Slice: {^{for ...}}
// ................................ Act ..................................
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- a0 x0 b0 y0 c0 z0 |Sort:-- z0 y0 x0 c0 b0 a0 |Filter: y0 c0 a0 |Slice:- c0 ",
'{{for}} with sorting, filtering, reverse, start and end settings');
// ................................ Act ..................................
jsv.observable(tgt).insert({title: "t" + cnt++}); // Append item to fourth {^{for}} tag instance above
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- a0 x0 b0 y0 c0 z0 t0 |Sort:-- z0 y0 x0 t0 c0 b0 a0 |Filter: y0 t0 b0 |Slice:- c0 t0 ",
'Appending of item in target array (sorted, filtered etc) - item is rendered without refreshing sort, filter etc.');
// But note that in our scenario above this will append an item to the source array movies, which will trigger refreshed
// rendering of the first three {^{for}} instance above
ctx.target.refresh();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- a0 x0 b0 y0 c0 z0 t0 |Sort:-- z0 y0 x0 t0 c0 b0 a0 |Filter: y0 t0 b0 |Slice:- t0 ",
'To refresh sort etc with new item included, call tag.refresh() ');
// ................................ Act ..................................
jsv.observable(tgt).insert(0, {title: "t" + cnt++});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- a0 x0 b0 y0 c0 z0 t0 t1 |Sort:-- z0 y0 x0 t1 t0 c0 b0 a0 |Filter: y0 t1 c0 a0 |Slice:- t1 t0 ",
'Insertion of item in target array (sorted, filtered etc) - item is rendered without refreshing sort, filter etc.');
// ................................ Act ..................................
jsv.observable(movies).insert(1, {title: "m" + cnt++});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- a0 m2 x0 b0 y0 c0 z0 t0 t1 |Sort:-- z0 y0 x0 t1 t0 m2 c0 b0 a0 |Filter: y0 t1 m2 b0 |Slice:- t1 m2 ",
'Insertion of item in source array will also refresh sort, filter etc.');
// ................................ Act ..................................
jsv.observable(tgt).insert(1, [{title: "t" + cnt++}, {title: "t" + cnt++}]);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- a0 m2 x0 b0 y0 c0 z0 t0 t1 t3 t4 |Sort:-- z0 y0 x0 t4 t3 t1 t0 m2 c0 b0 a0 |Filter: y0 t4 t1 m2 b0 |Slice:- t1 t3 t4 m2 ",
'Insertion of multiple items in target array (sorted, filtered etc) - items are rendered without refreshing sort, filter etc.');
// ................................ Act ..................................
jsv.observable(tgt).refresh([tgt[1], {title: "t" + cnt++}, tgt[0]]);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- a0 x0 b0 y0 c0 z0 t0 t1 t3 t5 |Sort:-- z0 y0 x0 t5 t3 t1 t0 c0 b0 a0 |Filter: y0 t5 t1 c0 a0 |Slice:- t3 t5 t1 ",
'Calling refresh() on target array will insert and remove items appropriately from source array and target array (and move items in target array) without refreshing sort, filter etc.');
// ................................ Act ..................................
jsv.observable(tgt).remove();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- a0 x0 b0 y0 c0 z0 t0 t3 t5 |Sort:-- z0 y0 x0 t5 t3 t0 c0 b0 a0 |Filter: y0 t5 t0 b0 |Slice:- t3 t5 ",
'Removing item in target array (sorted, filtered etc) - items are rendered without refreshing sort, filter etc.');
// ................................ Act ..................................
jsv.observable(movies).remove(0);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- x0 b0 y0 c0 z0 t0 t3 t5 |Sort:-- z0 y0 x0 t5 t3 t0 c0 b0 |Filter: y0 t5 t0 b0 |Slice:- t5 t0 ",
'Removal of item in source array will also refresh sort, filter etc.');
// ................................ Act ..................................
jsv.observable(tgt).move(0, 1);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- x0 b0 y0 c0 z0 t0 t3 t5 |Sort:-- z0 y0 x0 t5 t3 t0 c0 b0 |Filter: y0 t5 t0 b0 |Slice:- t0 t5 ",
'Moving items in target array (sorted, filtered etc) - items are moved in target but not in source, and this is without refreshing sort, filter etc.');
// ................................ Act ..................................
jsv.observable(tgt).refresh([{title: "t" + cnt++}]);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- x0 b0 y0 c0 z0 t3 t6 |Sort:-- z0 y0 x0 t6 t3 c0 b0 |Filter: y0 t6 c0 |Slice:- t6 ",
'Calling refresh() on target array will insert and remove items appropriately from source array and target array (and move items in target array) without refreshing sort, filter etc.');
// ................................ Act ..................................
jsv.observable(movies).insert(1, {title: "m" + cnt++});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- x0 m7 b0 y0 c0 z0 t3 t6 |Sort:-- z0 y0 x0 t6 t3 m7 c0 b0 |Filter: y0 t6 m7 b0 |Slice:- t6 m7 ",
'Insertion of item in source array will also refresh sort, filter etc.');
// ................................ Act ..................................
jsv.observable(tgt).insert(1, [{title: "t" + cnt++}, {title: "t" + cnt++}]);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- x0 m7 b0 y0 c0 z0 t3 t6 t8 t9 |Sort:-- z0 y0 x0 t9 t8 t6 t3 m7 c0 b0 |Filter: y0 t9 t6 m7 b0 |Slice:- t6 t8 t9 m7 ",
'Insertion of multiple items in target array (sorted, filtered etc) - items are rendered without refreshing sort, filter etc.');
// ................................ Act ..................................
jsv.observable(movies).move(1, 3, 2);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- x0 y0 c0 m7 b0 z0 t3 t6 t8 t9 |Sort:-- z0 y0 x0 t9 t8 t6 t3 m7 c0 b0 |Filter: y0 t9 t6 m7 b0 |Slice:- t9 t6 m7 ",
'Moving of items in source array will also refresh sort, filter etc.');
// ................................ Reset ................................
$("#result").empty();
assert.equal(JSON.stringify(_jsv.cbBindings), "{}",
"Bindings all removed when content removed from DOM");
// =============================== Arrange ===============================
movies = [{title: "a0"}, {title: "x0"}, {title: "b0"}, {title: "y0"}, {title: "c0"}, {title: "z0"}];
ctx = {};
cnt = 0;
jsv.templates(
'|All:--- {^{for movies}}{{:title}} {{/for}}
\
|Slice:- {^{for movies start=1 end=-1 this=~ctx.target}}{{:title}} {{/for}}')
.link("#result", {movies: movies}, {
ctx: ctx,
odd: function(item, index, items) {
return index%2;
}
});
tgt = ctx.target.tagCtx.map.tgt;
// ................................ Act ..................................
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- a0 x0 b0 y0 c0 z0 |Slice:- x0 b0 y0 c0 ",
'{{for}} with start and end settings ("sliced")');
// ................................ Act ..................................
jsv.observable(tgt).insert({title: "t" + cnt++});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- a0 x0 b0 y0 c0 z0 t0 |Slice:- x0 b0 y0 c0 t0 ",
'Appending of item in target array ("sliced") - item is rendered without refreshing sort, filter etc.');
ctx.target.refresh();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- a0 x0 b0 y0 c0 z0 t0 |Slice:- x0 b0 y0 c0 z0 ",
'To refresh correct start and end with new item included, call tag.refresh() ');
// ................................ Act ..................................
jsv.observable(tgt).insert(0, {title: "t" + cnt++});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- a0 x0 b0 y0 c0 z0 t0 t1 |Slice:- t1 x0 b0 y0 c0 z0 ",
'Insertion of item at specific position in target array ("sliced") - item is rendered at insert location, but item is simply appended to source array');
// ................................ Act ..................................
jsv.observable(movies).insert(1, {title: "m" + cnt++});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- a0 m2 x0 b0 y0 c0 z0 t0 t1 |Slice:- m2 x0 b0 y0 c0 z0 t0 ",
'Insertion of item in source array will also refresh "slicing"');
// ................................ Act ..................................
jsv.observable(tgt).insert(1, [{title: "t" + cnt++}, {title: "t" + cnt++}]);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- a0 m2 x0 b0 y0 c0 z0 t0 t1 t3 t4 |Slice:- m2 t3 t4 x0 b0 y0 c0 z0 t0 ",
'Insertion of items at specific position in target array ("sliced") - items are rendered at insert location, but simply appended to source array');
// ................................ Act ..................................
jsv.observable(tgt).refresh([tgt[1], {title: "t" + cnt++}, tgt[0], tgt[2]]);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- a0 m2 t1 t3 t4 t5 |Slice:- t3 t5 m2 t4 ",
'Calling refresh() on target array will append and remove items appropriately from source array and target array (and move items in target array)');
// ................................ Act ..................................
jsv.observable(tgt).remove();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- a0 m2 t1 t3 t5 |Slice:- t3 t5 m2 ",
'Removing item in target array ("sliced") - items are rendered without refreshing "slicing".');
// ................................ Act ..................................
jsv.observable(movies).remove(0);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- m2 t1 t3 t5 |Slice:- t1 t3 ",
'Removal of item in source array will also refresh "slicing".');
// ................................ Act ..................................
jsv.observable(tgt).move(0, 1);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- m2 t1 t3 t5 |Slice:- t3 t1 ",
'Moving items in target array ("slice") - items are moved in target but not in source, and this is without refreshing "slicing".');
// ................................ Act ..................................
jsv.observable(movies).move(1, 3, 2);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- m2 t5 t1 t3 |Slice:- t5 t1 ",
'Moving of items in source array will also refresh "slicing".');
// ................................ Reset ................................
$("#result").empty();
assert.equal(JSON.stringify(_jsv.cbBindings), "{}",
"Bindings all removed when content removed from DOM");
// =============================== Arrange ===============================
var team = {
members: [
{name: "one", phones:[]},
{name: "two", phones:[21, 22]}
]
};
jsv.templates(
'
{^{for start=0 members}}{^{for start=0 phones}}|{{:}} {{else}}|NoPhones {{/for}}|{{:name}} {{else}}|NoMembers {{/for}} ')
.link("#result", team);
var firstLi = $("li")[0];
// ............................... Assert .................................
assert.equal(
jsv.view("ul").views._1._prv === firstLi
&& jsv.view("ul").views._1.tag._prv === firstLi
&& jsv.view("ul").views._1.views["0"].views._1._prv === firstLi
&& jsv.view("ul").views._1.views["0"].views._1.tag._prv === firstLi
&& $("#result").text(), "|NoPhones|one|21|22|two",
"First li is _prv for {{for}} tags");
// ................................ Act ..................................
jsv.observable(team.members).move(1, 0);
// ............................... Assert .................................
firstLi = $("li")[0];
assert.equal(
jsv.view("ul").views._1._prv === firstLi
&& jsv.view("ul").views._1.tag._prv === firstLi
&& jsv.view("ul").views._1.views["0"].views._1._prv === firstLi
&& jsv.view("ul").views._1.views["0"].views._1.tag._prv === firstLi
&& $("#result").text(), "|21|22|two|NoPhones|one",
"After observable move, first li is _prv for {{for}} tags");
// ................................ Act ..................................
jsv.observable(team.members).remove(0);
// ............................... Assert .................................
firstLi = $("li")[0];
assert.equal(
jsv.view("ul").views._1._prv === firstLi
&& jsv.view("ul").views._1.tag._prv === firstLi
&& jsv.view("ul").views._1.views["0"].views._1._prv === firstLi
&& jsv.view("ul").views._1.views["0"].views._1.tag._prv === firstLi
&& $("#result").text(), "|NoPhones|one",
"After remove, first li is _prv for {{for}} tags");
// ................................ Act ..................................
jsv.observable(team.members).remove(0);
// ............................... Assert .................................
firstLi = $("li")[0];
assert.equal(
jsv.view("ul").views._2._prv === firstLi
&& jsv.view("ul").views._2.tag._prv === firstLi
&& jsv.view("ul").views._3._prv === firstLi
&& jsv.view("ul").views._3.tag._prv === firstLi
&& $("#result").text(), "|NoMembers",
"After removing all, first li is _prv for {{for}} tags");
//................................ Reset ................................
$("#result").empty();
// ............................... Assert .................................
assert.equal(JSON.stringify(_jsv.cbBindings), "{}",
"dataMap bindings all removed when tag disposed (content removed from DOM)");
jsv.views.settings.advanced({_jsv: false});
});
QUnit.test("{^{for}} with start end sort filter reverse: Incremental rendering", function(assert) {
var data = {
p_st: 2,
p_en: 4,
p_rev: false,
t_st: 0,
t_en: 4,
t_stp: undefined,
t_mapdeps: "flt",
flt: "l",
people: [
"Jo",
"Bob",
"Jane",
"Jeff",
"May",
"Alice"
],
things: [
{is: "table"},
{is: "porcelain"},
{is: "lamp"},
{is: "hat"}
]
},
out = "",
content = "",
people = data.people;
jsv.templates(
'{^{for skip}}{{:~rndr(#data)}}'
+ '{{else people ~foo="test" start=p_st end=p_en reverse=p_rev}}|{{:~rndr(#data)}}'
+ '{{else things sort=t_srt filter=t_flt start=t_st end=t_en step=t_stp mapDepends=t_mapdeps}}|{{:~rndr(is)}}'
+ '{{else}}None{{/for}}'
)
.link("#result", data, {
rndr: function(value) {
out += "|" + value;
return value;
}
});
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|Jane|Jeff::|Jane|Jeff", 'initial render, first {{else}} block');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("skip", "SkipTheList");
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "SkipTheList::|SkipTheList", 'move to initial block (no mapped list');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("skip", undefined);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|Jane|Jeff::|Jane|Jeff", 'move back to {{else people}} block');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("p_st", 1);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|Bob|Jane|Jeff::|Bob", 'incremental, on reducing start');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("p_en", 5);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|Bob|Jane|Jeff|May::|May", 'incremental, on increasing end');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("p_en", 3);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|Bob|Jane::", 'incremental, on reducing end');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("p_st", 4);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|table|porcelain|lamp|hat::|table|porcelain|lamp|hat", 'incremental, on increasing start, no items, moves to {{else}} block');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("t_st", -1);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|hat::", 'incremental, on changing start - integer from end');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("t_en", -2);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "None::", 'incremental, on changing end - integer from end, no items, moves to final {{else}}');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty({p_st: 1, p_en:40});
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|Bob|Jane|Jeff|May|Alice::|Bob|Jane|Jeff|May|Alice", 'incremental, on changing start/end - moves to first block');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("p_rev", true);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|May|Jeff|Jane|Bob|Jo::|Jo", 'incremental, set reverse=true');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty({p_en: 0, t_st: 0, t_en: 10});
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|table|porcelain|lamp|hat::|table|porcelain|lamp|hat", 'incremental, moves to second block');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("t_srt", "is");
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|hat|lamp|porcelain|table::", 'incremental, on changing start');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("t_flt", function(item, index, items) {
return index%2 === 1; // Include only odd index items
});
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|lamp|table::", 'incremental, on setting filter');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty({t_st: 0, t_en: 10, t_flt: false, t_srt: function(a, b) {
return a.is.length> b.is.length? 1 : a.is.length< b.is.length? -1 : 0; // Sort by string length of items
}});
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|hat|lamp|table|porcelain::|hat|porcelain", 'incremental, on setting sort function');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("t_flt", function(item, index, items) {
var flt = this.view.data.flt;
return flt ? item.is.toLowerCase().indexOf(flt.toLowerCase()) !== -1 : true;
});
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|lamp|table|porcelain::", 'incremental, on setting filter, with flt="l"');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("flt", "t");
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|hat|table::|hat", 'incremental, on setting flt to "t"');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("flt", "e");
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|table|porcelain::|porcelain", 'incremental, on setting flt to "e"');
// ................................ Act ..................................
out = "";
jsv.observable(data.things).insert(2, [{is: "cupboard"}, {is: "window"}]);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|table|porcelain::", 'incremental, on inserting items');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("flt", "");
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|hat|lamp|table|window|cupboard|porcelain::|hat|lamp|window|cupboard", 'incremental, on setting flt to ""');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("t_srt", false);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|table|porcelain|cupboard|window|lamp|hat::", 'incremental, on setting sort to false');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("t_stp", 3);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|table|window::", 'incremental, on setting step to 3');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("t_stp", 2);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|table|cupboard|lamp::|cupboard|lamp", 'incremental, on setting step to 2');
// ................................ Act ..................................
out = "";
jsv.observable(data.things).move(1, 4, 2);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|table|lamp|porcelain::|porcelain", 'incremental, on using move(1, 4, 2)');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("t_stp", false);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|table|window|lamp|hat|porcelain|cupboard::|window|hat|cupboard", 'incremental, on setting step to false');
// ................................ Act ..................................
out = "";
jsv.observable(data.things).refresh([data.things[4], data.things[2], data.things[0], data.things[3]]);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|porcelain|lamp|table|hat::", 'incremental, on using refresh(...)');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("t_st", 10);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "None::", 'incremental, move to final {{else}} block');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
var data = {
p_rev: false,
people: [
"Jo",
"Bob",
"Jane"
]
},
out = "",
content = "",
people = data.people;
jsv.templates(
'{^{for people sort=p_srt reverse=p_rev}}|{{:~rndr(#data)}}{{/for}}'
)
.link("#result", data, {
rndr: function(value) {
out += "|" + value;
return value;
}
});
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|Jo|Bob|Jane::|Jo|Bob|Jane", 'initial render, (no initial DataMap use)');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("p_rev", true);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|Jane|Bob|Jo::", 'reverse order (DataMap used - incremental re-order)');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("p_srt", true);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|Jo|Jane|Bob::", 'reverse sort (DataMap used - incremental re-order)');
// ................................ Reset ................................
$("#result").empty();
});
QUnit.test("{^{props}} with start end sort filter reverse: Incremental rendering", function(assert) {
var data = {
p_st: 2,
p_en: 4,
p_rev: false,
t_st: 0,
t_en: 4,
t_stp: undefined,
t_mapdeps: "flt",
flt: "l",
people: {
one: "Jo",
b: "Bob",
x: "Jane",
two: "Jeff",
m: "May",
last: "Alice"
},
things: {
a: {is: "table"},
b: {is: "porcelain"},
c: {is: "lamp"},
d: {is: "hat"}
}
},
out = "",
content = "",
people = data.people;
jsv.templates(
'{^{props skip}}{{:~rndr(prop)}}'
+ '{{else people start=p_st end=p_en reverse=p_rev}}|{{:~rndr(prop)}}'
+ '{{else things sort=t_srt filter=t_flt start=t_st end=t_en step=t_stp mapDepends=t_mapdeps}}|{{:~rndr(prop.is)}}'
+ '{{else}}None{{/props}}'
)
.link("#result", data, {
rndr: function(value) {
out += "|" + value;
return value;
}
});
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|Jane|Jeff::|Jane|Jeff", 'initial render, first {{else}} block');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("skip", {is: "SkipTheList"});
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "SkipTheList::|SkipTheList", 'move to initial block (no mapped list');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("skip", undefined);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|Jane|Jeff::|Jane|Jeff", 'move back to {{else people}} block');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("p_st", 1);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|Bob|Jane|Jeff::|Bob", 'incremental, on reducing start');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("p_en", 5);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|Bob|Jane|Jeff|May::|May", 'incremental, on increasing end');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("p_en", 3);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|Bob|Jane::", 'incremental, on reducing end');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("p_st", 4);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|table|porcelain|lamp|hat::|table|porcelain|lamp|hat", 'incremental, on increasing start, no items, moves to {{else}} block');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("t_st", -1);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|hat::", 'incremental, on changing start - integer from end');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("t_en", -2);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "None::", 'incremental, on changing end - integer from end, no items, moves to final {{else}}');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty({p_st: 1, p_en:40});
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|Bob|Jane|Jeff|May|Alice::|Bob|Jane|Jeff|May|Alice", 'incremental, on changing start/end - moves to first block');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("p_rev", true);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|May|Jeff|Jane|Bob|Jo::|Jo", 'incremental, set reverse=true');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty({p_en: 0, t_st: 0, t_en: 10});
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|table|porcelain|lamp|hat::|table|porcelain|lamp|hat", 'incremental, moves to second block');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("t_srt", "prop.is");
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|hat|lamp|porcelain|table::", 'incremental, on changing start');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("t_flt", function(item, index, items) {
return index%2 === 1; // Include only odd index items
});
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|lamp|table::", 'incremental, on setting filter');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty({t_st: 0, t_en: 10, t_flt: false, t_srt: function(a, b) {
return a.prop.is.length> b.prop.is.length? 1 : a.prop.is.length< b.prop.is.length? -1 : 0; // Sort by string length of items
}});
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|hat|lamp|table|porcelain::|hat|porcelain", 'incremental, on setting sort function');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("t_flt", function(item, index, items) {
var flt = this.view.data.flt;
return flt ? item.prop.is.toLowerCase().indexOf(flt.toLowerCase()) !== -1 : true;
});
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|lamp|table|porcelain::", 'incremental, on setting filter, with flt="l"');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("flt", "t");
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|hat|table::|hat", 'incremental, on setting flt to "t"');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("flt", "e");
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|table|porcelain::|porcelain", 'incremental, on setting flt to "e"');
// ................................ Act ..................................
out = "";
jsv.observable(data.things).setProperty({e: {is: "cupboard"}, f: {is: "window"}});
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|table|porcelain::", 'incremental, on inserting items');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("flt", "");
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|hat|lamp|table|window|cupboard|porcelain::|hat|lamp|window|cupboard", 'incremental, on setting flt to ""');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("t_srt", "prop.is");
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|cupboard|hat|lamp|porcelain|table|window::", 'incremental, on setting sort to "prop.is"');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("t_srt", false);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|hat|lamp|table|window|cupboard|porcelain::", 'incremental, on setting sort to false');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("t_stp", 3);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|hat|window::", 'incremental, on setting step to 3');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("t_stp", 2);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|hat|table|cupboard::|table|cupboard", 'incremental, on setting step to 2');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("t_stp", false);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "|hat|lamp|table|window|cupboard|porcelain::|lamp|window|porcelain", 'incremental, on setting step to false');
// ................................ Act ..................................
out = "";
jsv.observable(data).setProperty("t_st", 10);
// ............................... Assert .................................
assert.equal($("#result").text() + "::" + out, "None::", 'incremental, move to final {{else}} block');
// ................................ Reset ................................
$("#result").empty();
});
QUnit.test("{^{if}}...{{else}}...{{/if}}", function(assert) {
jsv.views.settings.advanced({_jsv: true}); // For using cbBindings store
// =============================== Arrange ===============================
var data = {one: true, two: false, three: true},
boundIfElseTmpl = jsv.templates(
'{^{if one pane=0}}'
+ '{^{if two pane=0}}'
+ '{^{if three pane=0}}ONE TWO THREE {{else}}ONE TWO notThree {{/if}}'
+ '{{else}}ONE notTwo {^{if three}}THREE {{/if}}{^{if !three}}notThree {{/if}}{{/if}}'
+ '{{else three pane=1}}'
+ '{^{if two pane=0}}notOne TWO THREE{{else}}notOne notTwo THREE {{/if}}'
+ '{{else}}'
+ '{^{if two pane=0}}notOne TWO notThree {{else}}notOne TWO notThree {{/if}}'
+ '{{/if}}');
// ................................ Act ..................................
boundIfElseTmpl.link("#result", data);
// ............................... Assert .................................
after = $("#result").text();
assert.equal(after, boundIfElseTmpl.render(data),
'Bound if and else with link render the same as unbound, when using the JsRender render() method');
// ............................... Assert .................................
assert.equal(after, "ONE notTwo THREE ",
'Bound if and else render correct blocks based on boolean expressions');
// ................................ Act ..................................
jsv.observable(data).setProperty({one: false, two: false, three: true});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "notOne notTwo THREE ",
'Bound if and else render correct blocks based on boolean expressions');
// ................................ Act ..................................
jsv.observable(data).setProperty({one: false, two: true, three: false});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "notOne TWO notThree ",
'Bound if and else render correct blocks based on boolean expressions');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
data = {expanded: true};
var deepIfTmpl = jsv.templates(
'
'
+ '{^{if expanded}}'
+ 'DeepContent '
+ '{{/if}} '
+ 'afterDeep '
+ '
');
// ................................ Act ..................................
deepIfTmpl.link("#result", data);
jsv.observable(data).setProperty("expanded", false);
jsv.observable(data).setProperty("expanded", true);
// ............................... Assert .................................
after = $("#result").text();
var deferredString = $("#result tr")[0]._df; // "/226_/322^"
// With deep version, the tokens for the {^{if}} binding had to be deferred - we test the format:
deferredString = /\/\d+\_\/\d+\^/.test(deferredString);
assert.equal(deferredString && after, 'DeepContentafterDeep',
'With deep bound {^{if}} tag, there is deferred binding and binding behaves correctly after removing and inserting');
// ................................ Act ..................................
jsv.observable(data).setProperty("expanded", false);
// ............................... Assert .................................
after = $("#result").text();
deferredString = $("#result tr")[0]._df; // "#322^/322^"
// With deep version, the tokens for the {^{if}} binding had to be deferred - we test the format:
deferredString = /#(\d+\^)\/\1/.test(deferredString);
assert.equal(deferredString && after, 'afterDeep',
'With deep bound {^{if}} tag, there is deferred binding and binding behaves correctly after further remove');
// =============================== Arrange ===============================
var shallowIfTmpl = jsv.templates(
'
'
+ '{^{if expanded}}'
+ 'ShallowContent '
+ '{{/if}}'
+ 'afterShallow '
+ '
');
// ................................ Act ..................................
shallowIfTmpl.link("#result", data);
jsv.observable(data).setProperty("expanded", false);
jsv.observable(data).setProperty("expanded", true);
// ............................... Assert .................................
after = $("#result").text();
deferredString = $("#result tr")[0]._df; // ""
// With shallow version, no deferred binding
assert.equal(!deferredString && after, 'ShallowContentafterShallow',
'With shallow bound {^{if}} tag, there is no deferred binding, and binding behaves correctly after removing and inserting');
// ................................ Act ..................................
jsv.observable(data).setProperty("expanded", false);
// ............................... Assert .................................
after = $("#result").text();
deferredString = $("#result tr")[0]._df; // ""
// With shallow version, no deferred binding
assert.equal(!deferredString && after, 'afterShallow',
'With shallow bound {^{if}} tag, there is no deferred binding and binding behaves correctly after further remove');
// ................................ Reset ................................
$("#result").empty();
res = "";
jsv.views.settings.advanced({_jsv: false});
});
QUnit.test("{^{props}} basic", function(assert) {
jsv.views.settings.advanced({_jsv: true}); // For using cbBindings store
// =============================== Arrange ===============================
var root = {
objA: {propA1: "valA1a"},
objB: {propB1: "valB1a"}
};
jsv.templates('{^{props objA}}{^{:key}}:{^{:prop}},{{/props}}')
.link("#result", root);
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(root.objA).setProperty({propA1: "valA1b"});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, 'propA1:valA1a,|propA1:valA1b,',
'{^{props}} - set existing property');
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(root.objA).setProperty({propA1: "valA1c", propA2: "valA2a"});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, 'propA1:valA1b,|propA1:valA1c,propA2:valA2a,',
'{^{props}} - set new property');
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(root.objA).setProperty({propA1: "", propA2: null});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, 'propA1:valA1c,propA2:valA2a,|propA1:,propA2:,',
'{^{props}} - set property to empty string or null');
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(root.objA).setProperty({propA1: null});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, 'propA1:,propA2:,|propA1:,propA2:,',
'{^{props}} - all properties null');
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(root.objA).removeProperty("propA1").removeProperty("propA2");
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, 'propA1:,propA2:,|',
'{^{props}} - all properties removed');
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(root.objA).setProperty({propA1: "valA1b"});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, "|propA1:valA1b,",
'{^{props}} - set property where there were none');
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(root).setProperty({objA: {}});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, "propA1:valA1b,|",
'{^{props}} - set whole object to empty object');
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(root).setProperty({objA: {propX: "XX"}});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, "|propX:XX,",
'{^{props}} - set whole object to different object');
//................................ Reset ................................
$("#result").empty();
// ............................... Assert .................................
assert.equal(JSON.stringify(_jsv.cbBindings), "{}",
"{^{props}} dataMap bindings all removed when tag disposed (content removed from DOM)");
jsv.views.settings.advanced({_jsv: false});
});
QUnit.test("{^{props}} modifying content, through arrayChange/propertyChange on target array", function(assert) {
var done;
if (assert.async) { done = assert.async() } else { stop() }
jsv.views.settings.advanced({_jsv: true}); // For using cbBindings store
// =============================== Arrange ===============================
var root = {
objA: {propA1: "valA1a"}
};
jsv.templates(
'{^{props objA}}'
+ '{^{:key}}:{^{:prop}},'
+ '
remove '
+ '
add ,'
+ '
change ,'
+ '
'
+ '
'
+ '{{/props}}')
.link("#result", root, {
add: function(ev, eventArgs) {
var view = eventArgs.view,
arr = view.get("array").data;
jsv.observable(arr).insert({key: "addkey", prop: "addprop"});
},
remove: function(ev, eventArgs) {
var view = eventArgs.view,
arr = view.get("array").data,
index = view.index;
jsv.observable(arr).remove(index);
},
change: function(ev, eventArgs) {
var view = eventArgs.view,
item = view.data;
jsv.observable(item).setProperty({key: "changed", prop: "changedValue"});
}
});
// ................................ Act ..................................
before = $("#result").text();
$(".addProp").click();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, "propA1:valA1a,removeadd,change,|propA1:valA1a,removeadd,change,addkey:addprop,removeadd,change,",
'{^{props}} - add properties to props target array');
// ................................ Act ..................................
before = $("#result").text();
$(".removeProp:first()").click();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, "propA1:valA1a,removeadd,change,addkey:addprop,removeadd,change,|addkey:addprop,removeadd,change,",
'{^{props}} - remove properties from props target array');
// ................................ Act ..................................
before = $("#result").text();
$(".changeProp").click();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, "addkey:addprop,removeadd,change,|changed:changedValue,removeadd,change,",
'{^{props}} - change value of key and prop in props target array');
// ................................ Act ..................................
before = $("#result").text();
keydown($(".changePropInput").val("newValue"));
setTimeout(function() {
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after + "|" + JSON.stringify(root.objA), "changed:changedValue,removeadd,change,|changed:newValue,removeadd,change,|{\"changed\":\"newValue\"}",
'{^{props}} - change value of input bound to prop in props target array');
// ................................ Act ..................................
before = $("#result").text();
keydown($(".changeKeyInput").val("newKey"));
setTimeout(function() {
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after + "|" + JSON.stringify(root.objA), "changed:newValue,removeadd,change,|newKey:newValue,removeadd,change,|{\"newKey\":\"newValue\"}",
'{^{props}} - change value of input bound to key in props target array');
// ................................ Reset ................................
before = "" + $._data(root).events.propertyChange.length + "-" + $._data(root.objA).events.propertyChange.length;
$("#result").empty();
after = "" + ($._data(root).events === undefined) + "-" + ($._data(root.objA).events === undefined) + " -" + JSON.stringify(_jsv.cbBindings);
// ............................... Assert .................................
assert.equal(before + "|" + after, "1-1|true-true -{}",
'{^{props}} dataMap bindings all removed when tag disposed (content removed from DOM)');
jsv.views.settings.advanced({_jsv: false});
if (assert.async) { done() } else { start() }
}, 0);
}, 0);
});
QUnit.test("{^{props}}...{{else}} ...", function(assert) {
jsv.views.settings.advanced({_jsv: true}); // For using cbBindings store
// =============================== Arrange ===============================
var root = {
objA: {propA1: "valA1"},
objB: {propB1: "valb1", propB2: "valb2"}
};
jsv.templates('{^{props objA}}{^{:key}}:{^{:prop}},'
+ '{{else objB}}{^{:key}}:{^{:prop}},'
+ '{{else}}'
+ 'NONE'
+ '{{/props}}')
.link("#result", root);
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(root.objA).setProperty("propA2", "valA2");
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, "propA1:valA1,|propA1:valA1,propA2:valA2,",
'{^{props}} - set new property on objA - shows additional property');
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(root.objA).removeProperty("propA1").removeProperty("propA2");
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, "propA1:valA1,propA2:valA2,|propB1:valb1,propB2:valb2,",
'{^{props}} - remove properties from objA - switches to {{else objB}}');
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(root.objB).removeProperty("propB1").removeProperty("propB2");
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, "propB1:valb1,propB2:valb2,|NONE",
'{^{props}} - remove properties from objB - switches to {{else}}');
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(root.objB).removeProperty("NotAProperty");
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, "NONE|NONE",
'{^{props}} - remove inexistant property from objB - remains on {{else}}');
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(root.objB).setProperty("newProp", "");
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, "NONE|newProp:,",
'{^{props}} - set property on objB to undefined - render {{else objB}}');
// ................................ Reset ................................
$("#result").empty();
// ............................... Assert .................................
assert.equal(JSON.stringify(_jsv.cbBindings), "{}",
"{^{props}} dataMap bindings all removed when tag disposed (content removed from DOM)");
// =============================== Arrange ===============================
root = {
objA: {propA1: "valA1"},
objB: {propB1: "valb1", propB2: "valb2"}
};
jsv.templates('{^{props objA}}{^{:key}}:{^{:prop}},'
+ '
remove ,'
+ '{{else objB}}{^{:key}}:{^{:prop}},'
+ '
remove ,'
+ '{{else}}'
+ 'NONE'
+ '{{/props}}')
.link("#result", root, {
remove: function(ev, eventArgs) {
var view = eventArgs.view,
arr = view.get("array").data,
index = view.index;
jsv.observable(arr).remove(index);
}
});
// ................................ Act ..................................
before = $("#result").text();
$(".removePropA").click();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, "propA1:valA1,remove,|propB1:valb1,remove,propB2:valb2,remove,",
'{^{props}} - remove properties from objA target array - switches to {{else objB}}');
// ................................ Act ..................................
before = $("#result").text();
$(".removePropB").click();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after, "propB1:valb1,remove,propB2:valb2,remove,|NONE",
'{^{props}} - remove properties from objB target array - switches to {{else}}');
// ................................ Reset ................................
$("#result").empty();
// ............................... Assert .................................
assert.equal(JSON.stringify(_jsv.cbBindings), "{}",
"{^{props}} dataMap bindings all removed when tag disposed (content removed from DOM)");
jsv.views.settings.advanced({_jsv: false});
});
QUnit.test("{^{props start end sort filter reverse}}...{{else}} ...", function(assert) {
jsv.views.settings.advanced({_jsv: true}); // For using _jsv
// =============================== Arrange ===============================
var movies = {keyf: {title: "a0"}, keye: {title: "x0"}, keyd: {title: "b0"}, keyc: {title: "y0"}, keyb: {title: "c0"}, keya: {title: "z0"}},
ctx = {},
cnt = 0;
jsv.templates(
'|All:--- {^{props movies}}{{:key}}:{{:prop.title}} {{/props}}
\
|Sort:-- {^{props movies sort="prop.title" reverse=true}}{{:prop.title}} {{/props}}
\
|Filter: {^{props movies sort="prop.title" reverse=true filter=~odd}}{{:prop.title}} {{/props}}
\
|Slice:- {^{props movies sort="prop.title" reverse=true filter=~odd start=1 end=-1 this=~ctx.target}}{{:prop.title}} {{/props}}')
.link("#result", {movies: movies}, {
ctx: ctx,
odd: function(item, index, items) {
return index%2;
}
});
var tgt = ctx.target.tagCtx.contentView.data
function newProp(title) {
return {key: "key"+cnt++, prop: {title: title}}
}
// ................................ Act ..................................
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- keyf:a0 keye:x0 keyd:b0 keyc:y0 keyb:c0 keya:z0 |Sort:-- z0 y0 x0 c0 b0 a0 |Filter: y0 c0 a0 |Slice:- c0 ",
'{{props}} with Sorting, filtering, reverse, start and end settings');
// ................................ Act ..................................
jsv.observable(tgt).insert(newProp("t" + cnt));
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- keyf:a0 keye:x0 keyd:b0 keyc:y0 keyb:c0 keya:z0 key0:t0 |Sort:-- z0 y0 x0 t0 c0 b0 a0 |Filter: y0 t0 b0 |Slice:- c0 t0 ",
'Appending of item in target array (sorted, filtered etc) - item is rendered without refreshing sort, filter etc.');
ctx.target.refresh();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- keyf:a0 keye:x0 keyd:b0 keyc:y0 keyb:c0 keya:z0 key0:t0 |Sort:-- z0 y0 x0 t0 c0 b0 a0 |Filter: y0 t0 b0 |Slice:- t0 ",
'To refresh, sort, slice etc with new item included, call tag.refresh() ');
// ................................ Act ..................................
jsv.observable(tgt).insert(0, newProp("t" + cnt));
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- keyf:a0 keye:x0 keyd:b0 keyc:y0 keyb:c0 keya:z0 key0:t0 key1:t1 |Sort:-- z0 y0 x0 t1 t0 c0 b0 a0 |Filter: y0 t1 c0 a0 |Slice:- t1 t0 ",
'Insertion of item in target array (sorted, filtered etc) - item is rendered without refreshing sort, filter etc.');
// ................................ Act ..................................
jsv.observable(movies).setProperty("key" + cnt++, {title: "m" + cnt});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after,
"|All:--- keyf:a0 keye:x0 keyd:b0 keyc:y0 keyb:c0 keya:z0 key0:t0 key1:t1 key2:m3 |Sort:-- z0 y0 x0 t1 t0 m3 c0 b0 a0 |Filter: y0 t1 m3 b0 |Slice:- t1 m3 ",
'Insertion of item in source array will also refresh sort, filter etc.');
// ................................ Act ..................................
jsv.observable(tgt).insert(1, [newProp("t" + cnt), newProp("t" + cnt)]);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- keyf:a0 keye:x0 keyd:b0 keyc:y0 keyb:c0 keya:z0 key0:t0 key1:t1 key2:m3 key4:t4 key3:t3 |Sort:-- z0 y0 x0 t4 t3 t1 t0 m3 c0 b0 a0 |Filter: y0 t4 t1 m3 b0 |Slice:- t1 t3 t4 m3 ",
'Insertion of multiple items in target array (sorted, filtered etc) - items are rendered without refreshing sort, filter etc.');
// ................................ Act ..................................
jsv.observable(tgt).refresh([tgt[1], newProp("t" + cnt), tgt[0]]);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- keyf:a0 keye:x0 keyd:b0 keyc:y0 keyb:c0 keya:z0 key0:t0 key1:t1 key3:t3 key5:t5 |Sort:-- z0 y0 x0 t5 t3 t1 t0 c0 b0 a0 |Filter: y0 t5 t1 c0 a0 |Slice:- t3 t5 t1 ",
'Calling refresh() on target array will insert and remove items appropriately from source array and target array (and move items in target array) without refreshing sort, filter etc.');
// ................................ Act ..................................
jsv.observable(tgt).remove();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- keyf:a0 keye:x0 keyd:b0 keyc:y0 keyb:c0 keya:z0 key0:t0 key3:t3 key5:t5 |Sort:-- z0 y0 x0 t5 t3 t0 c0 b0 a0 |Filter: y0 t5 t0 b0 |Slice:- t3 t5 ",
'Removing item in target array (sorted, filtered etc) - items are rendered without refreshing sort, filter etc.');
// ................................ Act ..................................
jsv.observable(movies).removeProperty("keyf");
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- keye:x0 keyd:b0 keyc:y0 keyb:c0 keya:z0 key0:t0 key3:t3 key5:t5 |Sort:-- z0 y0 x0 t5 t3 t0 c0 b0 |Filter: y0 t5 t0 b0 |Slice:- t5 t0 ",
'Removal of item in source array will also refresh sort, filter etc.');
// ................................ Act ..................................
jsv.observable(tgt).move(0, 1);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- keye:x0 keyd:b0 keyc:y0 keyb:c0 keya:z0 key0:t0 key3:t3 key5:t5 |Sort:-- z0 y0 x0 t5 t3 t0 c0 b0 |Filter: y0 t5 t0 b0 |Slice:- t0 t5 ",
'Moving items in target array (sorted, filtered etc) - items are moved in target but not in source, and this is without refreshing sort, filter etc.');
// ................................ Act ..................................
jsv.observable(tgt).refresh([newProp("t" + cnt)]);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- keye:x0 keyd:b0 keyc:y0 keyb:c0 keya:z0 key3:t3 key6:t6 |Sort:-- z0 y0 x0 t6 t3 c0 b0 |Filter: y0 t6 c0 |Slice:- t6 ",
'Calling refresh() on target array will insert and remove items appropriately from source array and target array (and move items in target array) without refreshing sort, filter etc.');
// ................................ Act ..................................
jsv.observable(movies).setProperty("key" + cnt++, {title: "m" + cnt});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- keye:x0 keyd:b0 keyc:y0 keyb:c0 keya:z0 key3:t3 key6:t6 key7:m8 |Sort:-- z0 y0 x0 t6 t3 m8 c0 b0 |Filter: y0 t6 m8 b0 |Slice:- t6 m8 ",
'Insertion of item in source array will also refresh sort, filter etc.');
// ................................ Act ..................................
jsv.observable(tgt).insert(1, [newProp("t" + cnt), newProp("t" + cnt)]);
after = $("#result").text();
// ............................... Assert .................................
assert.equal(after, "|All:--- keye:x0 keyd:b0 keyc:y0 keyb:c0 keya:z0 key3:t3 key6:t6 key7:m8 key9:t9 key8:t8 |Sort:-- z0 y0 x0 t9 t8 t6 t3 m8 c0 b0 |Filter: y0 t9 t6 m8 b0 |Slice:- t6 t8 t9 m8 ",
'Insertion of multiple items in target array (sorted, filtered etc) - items are rendered without refreshing sort, filter etc.');
// ................................ Reset ................................
$("#result").empty();
assert.equal(JSON.stringify(_jsv.cbBindings), "{}",
"Bindings all removed when content removed from DOM");
jsv.views.settings.advanced({_jsv: false});
});
QUnit.test('data-link="{on ...', function(assert) {
jsv.views.settings.advanced({_jsv: true}); // For using _jsv
// =============================== Arrange ===============================
function swap(ev, eventArgs) {
jsv.observable(this).setProperty("type", this.type === "shape" ? "line" : "shape");
}
var thing = {
type: "shape",
swap: swap
};
jsv.templates('
{^{:type}}
')
.link("#result", thing);
// ................................ Act ..................................
before = $("#result").text();
$("#result div").click();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"shape|line",
'{on swap} calls swap method on click, with "this" pointer context on data object');
// ................................ Reset ................................
$("#result").empty();
thing.type = "shape";
// =============================== Arrange ===============================
jsv.templates('
{^{:type}}
')
.link("#result", thing, {swap: swap});
// ................................ Act ..................................
before = $("#result").text();
$("#result div").click();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"shape|line",
'{on ~swap} calls swap helper method on click, with "this" pointer context defaulting to current data object');
// ................................ Reset ................................
$("#result").empty();
thing.type = "shape";
// =============================== Arrange ===============================
jsv.templates('
{^{:type}} {^{:check}}
')
.link("#result", thing, {
util:
{
swap: function(ev, eventArgs) {
jsv.observable(this.data).setProperty({
type: this.data.type === "shape" ? "line" : "shape",
check: this.data === eventArgs.view.data
});
},
data: thing
}
});
// ................................ Act ..................................
before = $("#result").text();
$("#result div").click();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"shape |line true",
'{on ~util.swap} calls util.swap helper method on click, with ~util as this pointer');
// ................................ Reset ................................
$("#result").empty();
thing.type = "shape";
delete thing.check;
// =============================== Arrange ===============================
jsv.templates('
{^{:type}}
')
.link("#result", thing, {
util:
{
swap: swap
}
});
// ................................ Act ..................................
before = $("#result").text();
$("#result div").click();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"shape|line",
'{on ~util.swap context=#data} calls util.swap helper method on click, with current data object as this pointer');
// ................................ Reset ................................
$("#result").empty();
thing.type = "shape";
// =============================== Arrange ===============================
jsv.templates('
{^{:type}} {^{:check}}
')
.link("#result", thing, {
util:
{
swap: function(ev, eventArgs) {
jsv.observable(this.data).setProperty({
type: this.data.type === "shape" ? "line" : "shape",
check: this.data === eventArgs.view.data
});
},
data: thing,
swapCtx: {
data: thing
}
}
});
// ................................ Act ..................................
before = $("#result").text();
$("#result div").click();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"shape |line true",
'{on ~util.swap context=~util.swapCtx} calls util.swap helper method on click, with util.swapCtx as this pointer');
// ................................ Reset ................................
$("#result").empty();
thing.type = "shape";
delete thing.check;
// =============================== Arrange ===============================
jsv.templates('
{^{:type}} {^{:check}}
')
.link("#result", thing, {
util:
{
swap: function(ev, eventArgs) {
jsv.observable(ev.data).setProperty({
type: ev.data.type === "shape" ? "line" : "shape",
check: ev.data === eventArgs.view.data
});
},
data: thing,
swapCtx: {
data: thing
}
}
});
// ................................ Act ..................................
before = $("#result").text();
$("#result div").click();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"shape |line true",
'{on ~util.swap data=#data} calls util.swap helper method on click, and passes current data #data as ev.data');
// ................................ Reset ................................
$("#result").empty();
thing.type = "shape";
delete thing.check;
// =============================== Arrange ===============================
jsv.templates('
{^{:type}}
')
.link("#result", thing);
// ................................ Act ..................................
before = $("#result").text();
$("#result div").mouseup();
after = $("#result").text();
$("#result div").mousedown();
after += $("#result").text();
$("#result div").blur();
after += $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"shape|lineshapeline",
"{on 'mouseup mousedown blur' swap} calls util method on mouseup, mousedown and blur");
// ................................ Reset ................................
$("#result").empty();
thing.type = "shape";
delete thing.check;
// =============================== Arrange ===============================
jsv.templates('
{^{:type}}
')
.link("#result", thing, {
util:
[{
swap: swap,
data: thing
}]
});
// ................................ Act ..................................
before = $("#result").text();
$("#result div").click();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"shape |line ",
'{on ~util[0].swap} calls util[0].swap helper method on click');
// ................................ Reset ................................
$("#result").empty();
thing.type = "shape";
// =============================== Arrange ===============================
var res = "1: ";
jsv.templates(
"
"
+ " "
+ " "
+ "
")
.link("#result", {
unbind: function(ev, eventArgs) {
res += "unbind ";
eventArgs.linkCtx.tag.onUnbind();
},
refresh: function(ev, eventArgs) {
res += "refresh ";
eventArgs.linkCtx.tag.refresh();
},
test: function()
{
res += "test ";
}
});
// ................................ Act ..................................
var events = $._data($("#divForOn")[0]).events,
eventBindings = "before: " + events.keydown.length + events.keyup.length + events.mouseup.length + events.mousedown.length;
$("#divForOn #inputB").mouseup();
res += "2: ";
$("#divForOn .inputA").mouseup();
res += "3: ";
$("#divForOn #inputB").keyup();
res += "4: ";
$("#divForOn #inputB").keyup();
res += "5: ";
$("#divForOn .inputA").keydown();
res += "6: ";
$("#divForOn .inputA").keyup();
res += "7: ";
$("#divForOn #inputB").mouseup();
res += "8: ";
$("#divForOn #inputB").mousedown();
eventBindings += " | after: " + events.keydown + events.keyup + events.mouseup.length + events.mousedown.length;
// ............................... Assert .................................
assert.equal(res,
"1: test 2: test 3: unbind 4: 5: unbind 6: 7: test 8: refresh ",
"multiple {on events selector method} bindings on container attach events on delegated elements. Also, tag.onDispose on tag instances removes specific handlers for corresponding elements/selectors");
// ............................... Assert .................................
assert.equal(eventBindings,
"before: 1211 | after: undefinedundefined11",
"onDispose removes specific delegated events");
// ................................ Act ..................................
res = "1: ";
$("#divForOn").html("
");
$("#divForOn #newlyAdded").mouseup();
res += "2: ";
$("#divForOn #newlyAdded").keyup();
// ............................... Assert .................................
assert.equal(res,
"1: test 2: ",
"delegated {on events selector method} binding allows additional elements added to content to bind correctly");
// ................................ Act ..................................
$("#result").empty();
eventBindings = "" + events.keydown + events.keyup + events.mouseup + JSON.stringify([_jsv.cbBindings, _jsv.bindings]);
// ............................... Assert .................................
assert.equal(eventBindings,
"undefinedundefinedundefined[{},{}]",
"Removing the element removes all associated attached {on } handlers");
// =============================== Arrange ===============================
var tmpl = jsv.templates("
\
Do it \
\
"),
data = {
name: "Jo",
role: "Advisor",
option: {
allow: true
},
thisIsTheMethod: function(role, text, isFoo, compile, amount, root, ev, eventArgs) {
if (compile) {
compile.call(root, role, text, isFoo, amount, ev.data.allow, eventArgs.linkCtx.tag.tagCtx.args[2]);
}
},
process: function(role, text, isFoo, amount, allow, extraParam) {
jsv.observable(this).setProperty("res", this.res + role + text + isFoo + amount + " allow:" + allow + " extraParam: " + extraParam + "|");
},
res: ""
};
tmpl.link("#result", data);
// ................................ Act ..................................
$("#doIt").click();
data.option.allow = false;
jsv.observable(data).setProperty("role", "Follower");
$("#doIt").click();
// ............................... Assert .................................
assert.equal(data.res, "Advisorheytrue33 allow:true extraParam: 754|Followerheytrue33 allow:false extraParam: 754|",
"{on 'click' selector otherParams... method params...} : supports passing params to method, of any type, as well as setting data and context for the function call");
// =============================== Arrange ===============================
res = "1: ";
$("#result").html("
"
+ " "
+ " "
+ "
");
jsv.link(true, "#result", {
unbind: function(ev, eventArgs) {
res += "unbind ";
eventArgs.linkCtx.tag.onUnbind();
},
refresh: function(ev, eventArgs) {
res += "refresh ";
eventArgs.linkCtx.tag.refresh();
},
test: function() {
res += "test ";
}
});
// ................................ Act ..................................
events = $._data($("#divForOn")[0]).events;
eventBindings = "before: " + events.keydown.length + events.keyup.length + events.mouseup.length + events.mousedown.length;
$("#divForOn #inputB").mouseup();
res += "2: ";
$("#divForOn .inputA").mouseup();
res += "3: ";
$("#divForOn #inputB").keyup();
res += "4: ";
$("#divForOn #inputB").keyup();
res += "5: ";
$("#divForOn .inputA").keydown();
res += "6: ";
$("#divForOn .inputA").keyup();
res += "7: ";
$("#divForOn #inputB").mouseup();
res += "8: ";
$("#divForOn #inputB").mousedown();
eventBindings += " | after: " + events.keydown + events.keyup + events.mouseup.length + events.mousedown.length;
// ............................... Assert .................................
assert.equal(res,
"1: test 2: test 3: unbind 4: 5: unbind 6: 7: test 8: refresh ",
"Top-level {on }: multiple {on events selector method} top-level bindings on container attach events on delegated elements. Also, tag.onDispose on tag instances removes specific handlers for corresponding elements/selectors");
// ............................... Assert .................................
assert.equal(eventBindings,
"before: 1211 | after: undefinedundefined11",
"Top-level {on }: onDispose removes specific delegated events");
// ................................ Act ..................................
res = "1: ";
$("#divForOn").html("
");
$("#divForOn #newlyAdded").mouseup();
res += "2: ";
$("#divForOn #newlyAdded").keyup();
// ............................... Assert .................................
assert.equal(res,
"1: test 2: ",
"Top-level {on }: delegated {on events selector method} binding allows additional elements added to content to bind correctly");
// ................................ Act ..................................
$("#result").empty();
eventBindings = "" + events.keydown + events.keyup + events.mouseup + JSON.stringify([_jsv.cbBindings, _jsv.bindings]);
// ............................... Assert .................................
assert.equal(eventBindings,
"undefinedundefinedundefined[{},{}]",
"Top-level {on }: Removing the element removes all associated attached {on } handlers");
// =============================== Arrange ===============================
$("#result").html("
\
Do it \
\
");
data = {
name: "Jo",
role: "Advisor",
option: {
allow: true
},
thisIsTheMethod: function(role, text, isFoo, compile, amount, root, ev, eventArgs) {
if (compile) {
compile.call(root, role, text, isFoo, amount, ev.data.allow, eventArgs.linkCtx.tag.tagCtx.args[2]);
}
},
process: function(role, text, isFoo, amount, allow, extraParam) {
jsv.observable(this).setProperty("res", this.res + role + text + isFoo + amount + " allow:" + allow + " extraParam: " + extraParam + "|");
},
res: ""
};
jsv.link(true, "#result", data);
// ................................ Act ..................................
$("#doIt").click();
data.option.allow = false;
jsv.observable(data).setProperty("role", "Follower");
$("#doIt").click();
// ............................... Assert .................................
assert.equal(data.res, "Advisorheytrue33 allow:true extraParam: 754|Followerheytrue33 allow:false extraParam: 754|",
"Top-level {on 'click' selector method params...} : supports passing params to method, of any type, as well as setting data and context for the function call");
// =============================== Arrange ===============================
res = "1: ";
data = {
unbind: function(ev, eventArgs) {
res += "unbind ";
eventArgs.linkCtx.tag.onUnbind();
},
refresh: function(ev, eventArgs) {
res += "refresh ";
eventArgs.linkCtx.tag.refresh();
},
test: function() {
res += "test ";
}
};
$("#result").html("
oldcontent
");
jsv.link(true, "#linkTgt", data);
events = $._data($("#linkTgt")[0]).events;
// ................................ Act ..................................
$("#linkTgt").mousedown();
res += "2: ";
$("#linkTgt").mouseup();
res += "3: ";
$("#linkTgt").click();
res += "4: ";
$("#linkTgt").mousedown();
res += "5: ";
$("#linkTgt").mouseup();
res += "6: ";
$("#linkTgt").click();
// ............................... Assert .................................
assert.equal(res,
"1: test 2: test 3: refresh 4: test 5: test 6: refresh ",
'jsv.link(true, "#linkTgt", data): top-level linking to element (not container) links correctly, including \'{on }\' bindings');
// ............................... Assert .................................
eventBindings = "" + events.mouseup.length + events.mousedown.length + events.click.length;
assert.equal(eventBindings,
"111",
'jsv.link(true, "#linkTgt", data): top-level linking to element (not container) adds {on } binding handlers correctly - including calling refresh() on {on } tag');
// ................................ Act ..................................
jsv.unlink("#linkTgt");
// ............................... Assert .................................
eventBindings = "" + events.mouseup + events.mousedown + events.click + JSON.stringify([_jsv.cbBindings, _jsv.bindings]);
assert.equal(eventBindings,
"undefinedundefinedundefined[{},{}]",
'jsv.unlink("#linkTgt"): directly on top-level data-linked element (not through container) removes all \'{on }\' handlers');
jsv.views.settings.advanced({_jsv: false});
});
QUnit.test('{^{radiogroup}}', function(assert) {
// =============================== Arrange ===============================
var tmpl = jsv.templates(
'{^{radiogroup selected}}'
+ '{^{for people}}'
+ '
:{{:name}}'
+ '{{/for}}'
+'{{/radiogroup}}'
);
var model = {
selected: "Jim",
people: [
{name: "Bob"},
{name: "Jim"}
]
},
newName = "new";
// ............................... Act .................................
tmpl.link("#result", model);
res = $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).insert({
name: newName
});
jsv.observable(model).setProperty("selected", newName);
res += $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).remove(2);
res += $("#result input:checked").parent().text() + "|";
$("#result input").first().prop("checked", true).change(); // Check first radio button
res += model.selected + "-" + $("#result input:checked").parent().text() + "|";
// ............................... Assert .................................
assert.equal(res, ":Jim|:new||Bob-:Bob|",
'{^{radiogroup selected}}{^{for ...}}...
');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
var tmpl = jsv.templates(
'{^{radiogroup selected}}'
+ '
:NONE'
+ '{^{for people}}'
+ '
:{^{:name^toUpperCase()}}'
+ '{{/for}}'
+'{{/radiogroup}}'
);
model = {
selected: "Jim",
people: [
{name: "Bob"},
{name: "Jim"}
]
};
// ............................... Act .................................
tmpl.link("#result", model);
res = $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).insert({
name: newName
});
jsv.observable(model).setProperty("selected", newName);
res += $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).remove(2);
res += $("#result input:checked").parent().text() + "|";
$("#result input").first().prop("checked", true).change(); // Check first radio button
res += model.selected + "-" + $("#result input:checked").parent().text() + "|";
// ............................... Assert .................................
assert.equal(res, ":JIM|:NEW||None-:NONE|",
'{^{radiogroup selected}}...
...{^{for ...}}...
');
// ............................... Act .................................
jsv.observable(model.people[1]).setProperty("name", "jimUpdated");
res = $("#result").text() + "|";
$("#result input").eq(2).prop("checked", true).change(); // Check third radio button
res += model.selected + "-" + $("#result input:checked").parent().text() + "|";
assert.equal(res, ":NONE:BOB:JIMUPDATED|jimUpdated-:JIMUPDATED|",
'{^{radiogroup selected}}...{^{for ...}}...
- updated label and value');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
tmpl = jsv.templates({markup:
'{^{radiogroup selected convert=~lower convertBack="upper" linkTo=selectedOut}}'
+ '
:none'
+ '{^{for people}}'
+ '
:{^{:name}}'
+ '{{/for}}'
+'{{/radiogroup}}',
converters: {
upper: function(val) {
return val.toUpperCase();
}
}
});
model = {
selected: "JIM",
people: [
{name: "bob"},
{name: "jim"}
]
};
// ............................... Act .................................
tmpl.link("#result", model, {
lower: function(val) {
return val.toLowerCase();
}
});
res = $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).insert({
name: newName
});
jsv.observable(model).setProperty("selected", newName);
res += $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).remove(2);
res += $("#result input:checked").parent().text() + "|";
$("#result input").first().prop("checked", true).change(); // Check first radio button
res += model.selected + "-" + model.selectedOut + "-" + $("#result input:checked").parent().text() + "|";
// ............................... Assert .................................
assert.equal(res, ":jim|:new||new-NONE-:none|",
'{^{radiogroup selected convert=... convertBack=... linkTo=...}}');
// ............................... Act .................................
jsv.observable(model.people[1]).setProperty("name", "jimUpdated");
res = $("#result").text() + "|";
$("#result input").eq(2).prop("checked", true).change(); // Check third radio button
res += model.selected + "-" + model.selectedOut + "-" + $("#result input:checked").parent().text() + "|";
assert.equal(res, ":none:bob:jimUpdated|new-JIMUPDATED-:jimUpdated|",
'{^{radiogroup selected convert=... convertBack=... linkTo=...}} - updated label and value');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
tmpl = jsv.templates(
'{^{radiogroup selected}}'
+ '
:
NONE '
+ '{^{for people}}'
+ '
:
'
+ '{{/for}}'
+'{{/radiogroup}}'
);
model = {
selected: "Jim",
people: [
{name: "Bob"},
{name: "Jim"}
]
};
// ............................... Act .................................
tmpl.link("#result", model);
res = $("#" + $("#result input:checked").prop("id") + "Lbl").text() + "|";
jsv.observable(model.people).insert({
name: newName
});
jsv.observable(model).setProperty("selected", newName);
res += $("#" + $("#result input:checked").prop("id") + "Lbl").text() + "|";
jsv.observable(model.people).remove(2);
res += $("#" + $("#result input:checked").prop("id") + "Lbl").text() + "|";
$("#result input").first().prop("checked", true).change(); // Check first radio button
res += model.selected + "-" + $("#" + $("#result input:checked").prop("id") + "Lbl").text() + "|";
// ............................... Assert .................................
assert.equal(res, "JIM|NEW||None-NONE|",
'{^{radiogroup selected}} with labels by for/id');
// ............................... Act .................................
jsv.observable(model.people[1]).setProperty("name", "jimUpdated");
res = $("#result").text() + "|";
$("#result input").eq(2).prop("checked", true).change(); // Check third radio button
res += model.selected + "-" + $("#" + $("#result input:checked").prop("id") + "Lbl").text() + "|";
assert.equal(res, ":NONE:BOB:JIMUPDATED|jimUpdated-JIMUPDATED|",
'{^{radiogroup selected}} with labels by for/id - updated label and value');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
tmpl = jsv.templates(
'{^{radiogroup selected}}'
+ '
:NONE'
+ '{^{for people}}'
+ '
:{^{:name^toUpperCase()}}'
+ '{{/for}}'
+'{{/radiogroup}}'
+ '{^{radiogroup selected}}'
+ '
:NONE'
+ '{^{for people}}'
+ '
:{^{:name^toUpperCase()}}'
+ '{{/for}}'
+'{{/radiogroup}}'
);
model = {
selected: "Jim",
people: [
{name: "Bob"},
{name: "Jim"}
]
};
// ............................... Act .................................
tmpl.link("#result", model);
res = $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).insert({
name: newName
});
jsv.observable(model).setProperty("selected", newName);
res += $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).remove(2);
res += $("#result input:checked").parent().text() + "|";
$("#result input").first().prop("checked", true).change(); // Check first radio button
res += model.selected + "-" + $("#result input:checked").parent().text() + "|";
// ............................... Assert .................................
assert.equal(res, ":JIM:JIM|:NEW:NEW||None-:NONE:NONE|",
'{^{radiogroup selected}} - two radiogroups with same selected bindings');
// ............................... Act .................................
jsv.observable(model.people[1]).setProperty("name", "jimUpdated");
res = $("#result").text() + "|";
$("#result input").eq(2).prop("checked", true).change(); // Check third radio button
res += model.selected + "-" + $("#result input:checked").parent().text() + "|";
assert.equal(res, ":NONE:BOB:JIMUPDATED:NONE:BOB:JIMUPDATED|jimUpdated-:JIMUPDATED:JIMUPDATED|",
'{^{radiogroup selected}} - two radiogroups with same selected bindings - updated label and value');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
tmpl = jsv.templates(
'{^{radiogroup selected}}'
+ '{^{for people}}'
+ '
:{^{:name^toUpperCase()}}'
+ '{{/for}}'
+'{{/radiogroup}}'
+ '{^{radiogroup selected}}'
+ '
:NONE'
+ '{^{for people}}'
+ '
:{^{:name^toUpperCase()}}'
+ '{{/for}}'
+'{{/radiogroup}}'
);
model = {
selected: "Jim",
people: []
};
// ............................... Act .................................
tmpl.link("#result", model);
res = $("#result").text() + "|" + $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).insert([{name: "Bob"},{name: "Jim"},{name: newName}]);
res += $("#result").text() + "|" + $("#result input:checked").parent().text() + "|";
jsv.observable(model).setProperty("selected", newName);
res += $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).remove(2);
res += $("#result input:checked").parent().text() + "|";
$("#result input").first().prop("checked", true).change(); // Check first radio button
res += model.selected + "-" + $("#result input:checked").parent().text() + "|";
// ............................... Assert .................................
assert.equal(res, ":NONE||:BOB:JIM:NEW:NONE:BOB:JIM:NEW|:JIM:JIM|:NEW:NEW||Bob-:BOB:BOB|",
'{^{radiogroup selected}} - two radiogroups with same selected bindings - starting out with no items, so no radio buttons');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
tmpl = jsv.templates(
'{^{radiogroup selected name="rad1"}}'
+ '{^{for people}}'
+ '
:{^{:name^toUpperCase()}}'
+ '{{/for}}'
+'{{/radiogroup}}'
+ '{^{radiogroup selected name="rad1"}}'
+ '
:NONE'
+ '{^{for people}}'
+ '
:{^{:name^toUpperCase()}}'
+ '{{/for}}'
+'{{/radiogroup}}'
);
model = {
selected: "Jim",
people: []
};
// ............................... Act .................................
tmpl.link("#result", model);
res = $("#result").text() + "|" + $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).insert([{name: "Bob"},{name: "Jim"},{name: newName}]);
res += $("#result").text() + "|" + $("#result input:checked").parent().text() + "|";
jsv.observable(model).setProperty("selected", newName);
res += $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).remove(2);
res += $("#result input:checked").parent().text() + "|";
$("#result input").first().prop("checked", true).change(); // Check first radio button
res += model.selected + "-" + $("#result input:checked").parent().text() + "|" + $("#result input:checked")[0].name + "|" + $("#result input:checked")[1].name;
// ............................... Assert .................................
assert.equal(res, ":NONE||:BOB:JIM:NEW:NONE:BOB:JIM:NEW|:JIM:JIM|:NEW:NEW||Bob-:BOB:BOB|rad1|rad2",
'{^{radiogroup selected}} - name for group can be specified rather than auto-generated - on item or on radiogroup tag');
// =============================== Arrange ===============================
tmpl = jsv.templates(
'{^{if rg1}}'
+ '{^{radiogroup selected name="rad1"}}'
+ '{^{for people}}'
+ '
:{^{:name^toUpperCase()}}'
+ '{{/for}}'
+'{{/radiogroup}}'
+ '{{/if}}'
+ '{^{if rg2}}'
+ '{^{radiogroup selected name="rad1"}}'
+ '
:NONE'
+ '{^{for people}}'
+ '
:{^{:name^toUpperCase()}}'
+ '{{/for}}'
+'{{/radiogroup}}'
+ '{{/if}}'
);
model = {
rg1: true,
rg2: true,
selected: "Jim",
people: []
};
newName = "newName";
// ............................... Act .................................
tmpl.link("#result", model);
res = ">" + $("#result").text() + "|" + $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).insert([{name: "Bob"},{name: "Jim"},{name: newName}]);
res += $("#result").text() + "|" + $("#result input:checked").parent().text() + "|";
jsv.observable(model).setProperty("selected", newName);
res += $("#result input:checked").parent().text() + "|";
// ............................... Assert .................................
assert.equal(res, ">:NONE||:BOB:JIM:NEWNAME:NONE:BOB:JIM:NEWNAME|:JIM:JIM|:NEWNAME:NEWNAME|",
'{^{radiogroup selected}} - two radiogroups wrapped in {{if}} blocks - starting out with no items');
jsv.observable(model.people).refresh([]);
jsv.observable(model).setProperty("rg1", false);
res = ">" + $("#result").text() + "|" + $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).insert([{name: "Bob"},{name: "Jim"},{name: newName}]);
res += $("#result").text() + "|" + $("#result input:checked").parent().text() + "|";
jsv.observable(model).setProperty("selected", newName);
res += $("#result input:checked").parent().text() + "|";
// ............................... Assert .................................
assert.equal(res, ">:NONE||:NONE:BOB:JIM:NEWNAME|:NEWNAME|:NEWNAME|",
'{^{radiogroup selected}} - two radiogroups wrapped in {{if}} blocks - one set to false');
jsv.observable(model.people).refresh([]);
jsv.observable(model).setProperty("selected", "Jim");
jsv.observable(model).setProperty("rg1", true);
res = ">" + $("#result").text() + "|" + $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).insert([{name: "Bob"},{name: "Jim"},{name: newName}]);
res += $("#result").text() + "|" + $("#result input:checked").parent().text() + "|";
jsv.observable(model).setProperty("selected", newName);
res += $("#result input:checked").parent().text() + "|";
// ............................... Assert .................................
assert.equal(res, ">:NONE||:BOB:JIM:NEWNAME:NONE:BOB:JIM:NEWNAME|:JIM:JIM|:NEWNAME:NEWNAME|",
'{^{radiogroup selected}} - two radiogroups wrapped in {{if}} blocks - one set back to true');
// ................................ Reset ................................
$("#result").empty();
});
QUnit.test('{^{checkboxgroup}}', function(assert) {
jsv.views.settings.advanced({_jsv: true});
// =============================== Arrange ===============================
var tmpl = jsv.templates(
'{^{checkboxgroup selectedPeople}}'
+ '{^{for people}}'
+ '
:{{:name}}'
+ '{{/for}}'
+'{{/checkboxgroup}}'
);
var model = {
selectedPeople: ["Jim"],
people: [
{name: "Bob"},
{name: "Jim"}
]
},
newName = "new";
// ............................... Act .................................
tmpl.link("#result", model);
res = $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).insert({
name: newName
});
jsv.observable(model).setProperty("selectedPeople", [newName, "Jim"]);
res += $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).remove(2);
res += $("#result input:checked").parent().text() + "|";
$("#result input").first().prop("checked", true).change(); // Check first checkbox
res += model.selectedPeople + "-" + $("#result input:checked").parent().text() + "|";
jsv.observable(model.selectedPeople).remove();
res += model.selectedPeople + "-" + $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).insert({name: "new"});
res += model.selectedPeople + "-" + $("#result input:checked").parent().text() + "|";
// ............................... Assert .................................
assert.equal(res, ":Jim|:Jim:new|:Jim|new,Jim,Bob-:Bob:Jim|new,Jim-:Jim|new,Jim-:Jim:new|",
'{^{checkboxgroup selectedPeople}}{^{for ...}}...
');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
var tmpl = jsv.templates(
'{^{checkboxgroup selectedPeople}}'
+ '
:NONE'
+ '{^{for people}}'
+ '
:{^{:name^toUpperCase()}}'
+ '{{/for}}'
+'{{/checkboxgroup}}'
);
model = {
selectedPeople: ["Jim"],
people: [
{name: "Bob"},
{name: "Jim"}
]
};
// ............................... Act .................................
tmpl.link("#result", model);
res = $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).insert({
name: newName
});
jsv.observable(model).setProperty("selectedPeople", [newName, "Bob"]);
res += $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).remove(2);
res += $("#result input:checked").parent().text() + "|";
$("#result input").first().prop("checked", true).change(); // Check first checkbox
res += model.selectedPeople + "-" + $("#result input:checked").parent().text() + "|";
// ............................... Assert .................................
assert.equal(res, ":JIM|:BOB:NEW|:BOB|new,Bob,None-:NONE:BOB|",
'{^{checkboxgroup selectedPeople}}...
...{^{for ...}}...
');
// ............................... Act .................................
jsv.observable(model.people[1]).setProperty("name", "jimUpdated");
res = $("#result").text() + "|";
$("#result input").eq(2).prop("checked", true).change(); // Check third checkbox
res += model.selectedPeople + "-" + $("#result input:checked").parent().text() + "|";
assert.equal(res, ":NONE:BOB:JIMUPDATED|new,Bob,None,jimUpdated-:NONE:BOB:JIMUPDATED|",
'{^{checkboxgroup selectedPeople}}...{^{for ...}}...
- updated label and value');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
tmpl = jsv.templates({markup:
'{^{checkboxgroup selectedPeople convert=~lower convertBack="upper" linkTo=selectedOut}}'
+ '
:none'
+ '{^{for people}}'
+ '
:{^{:name}}'
+ '{{/for}}'
+'{{/checkboxgroup}}',
converters: {
upper: function(val) {
return val.map(function(x) {return x.toUpperCase();})
}
}
});
model = {
selectedPeople: ["Jim"],
people: [
{name: "bob"},
{name: "jim"}
]
};
// ............................... Act .................................
tmpl.link("#result", model, {
lower: function(val) {
return val.map(function(x) {return x.toLowerCase();})
}
});
res = $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).insert({
name: newName
});
jsv.observable(model).setProperty("selectedPeople", [newName]);
res += $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).remove(2);
res += $("#result input:checked").parent().text() + "|";
$("#result input").first().prop("checked", true).change(); // Check first checkbox
res += model.selectedPeople + "-" + model.selectedOut + "-" + $("#result input:checked").parent().text() + "|";
// ............................... Assert .................................
assert.equal(res, ":jim|:new||new-NONE-:none|",
'{^{checkboxgroup selectedPeople convert=... convertBack=... linkTo=...}}');
// ............................... Act .................................
jsv.observable(model.people[1]).setProperty("name", "jimUpdated");
res = $("#result").text() + "|";
$("#result input").eq(2).prop("checked", true).change(); // Check third checkbox
res += model.selectedPeople + "-" + model.selectedOut + "-" + $("#result input:checked").parent().text() + "|";
assert.equal(res, ":none:bob:jimUpdated|new-NONE,JIMUPDATED-:none:jimUpdated|",
'{^{checkboxgroup selectedPeople convert=... convertBack=... linkTo=...}} - updated label and value');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
tmpl = jsv.templates(
'{^{checkboxgroup selectedPeople}}'
+ '
:
NONE '
+ '{^{for people}}'
+ '
:
'
+ '{{/for}}'
+'{{/checkboxgroup}}'
);
model = {
selectedPeople: ["Jim"],
people: [
{name: "Bob"},
{name: "Jim"}
]
};
// ............................... Act .................................
tmpl.link("#result", model);
res = $("#" + $("#result input:checked").prop("id") + "Lbl").text() + "|";
jsv.observable(model.people).insert({
name: newName
});
jsv.observable(model).setProperty("selectedPeople", [newName]);
res += $("#" + $("#result input:checked").prop("id") + "Lbl").text() + "|";
jsv.observable(model.people).remove(2);
res += $("#" + $("#result input:checked").prop("id") + "Lbl").text() + "|";
$("#result input").first().prop("checked", true).change(); // Check first checkbox
res += model.selectedPeople + "-" + $("#" + $("#result input:checked").prop("id") + "Lbl").text() + "|";
// ............................... Assert .................................
assert.equal(res, "JIM|NEW||new,None-NONE|",
'{^{checkboxgroup selectedPeople}} with labels by for/id');
// ............................... Act .................................
jsv.observable(model.people[1]).setProperty("name", "jimUpdated");
res = $("#result").text() + "|";
$("#result input").eq(2).prop("checked", true).change(); // Check third checkbox
res += model.selectedPeople + "-" + $("#" + $("#result input:checked").prop("id") + "Lbl").text() + "|";
assert.equal(res, ":NONE:BOB:JIMUPDATED|new,None,jimUpdated-NONE|",
'{^{checkboxgroup selectedPeople}} with labels by for/id - updated label and value');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
tmpl = jsv.templates(
'{^{checkboxgroup selectedPeople}}'
+ '
:NONE'
+ '{^{for people}}'
+ '
:{^{:name^toUpperCase()}}'
+ '{{/for}}'
+'{{/checkboxgroup}}'
+ '{^{checkboxgroup selectedPeople}}'
+ '
:NONE'
+ '{^{for people}}'
+ '
:{^{:name^toUpperCase()}}'
+ '{{/for}}'
+'{{/checkboxgroup}}'
);
model = {
selectedPeople: ["Jim"],
people: [
{name: "Bob"},
{name: "Jim"}
]
};
// ............................... Act .................................
tmpl.link("#result", model);
res = $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).insert({
name: newName
});
jsv.observable(model).setProperty("selectedPeople", [newName]);
res += $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).remove(2);
res += $("#result input:checked").parent().text() + "|";
$("#result input").first().prop("checked", true).change(); // Check first checkbox
res += model.selectedPeople + "-" + $("#result input:checked").parent().text() + "|";
// ............................... Assert .................................
assert.equal(res, ":JIM:JIM|:NEW:NEW||new,None-:NONE:NONE|",
'{^{checkboxgroup selectedPeople}} - two checkboxgroups with same selectedPeople bindings');
// ............................... Act .................................
jsv.observable(model.people[1]).setProperty("name", "jimUpdated");
res = $("#result").text() + "|";
$("#result input").eq(2).prop("checked", true).change(); // Check third checkbox
res += model.selectedPeople + "-" + $("#result input:checked").parent().text() + "|";
assert.equal(res, ":NONE:BOB:JIMUPDATED:NONE:BOB:JIMUPDATED|new,None,jimUpdated-:NONE:JIMUPDATED:NONE:JIMUPDATED|",
'{^{checkboxgroup selectedPeople}} - two checkboxgroups with same selected bindings - updated label and value');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
tmpl = jsv.templates(
'{^{checkboxgroup selectedPeople}}'
+ '{^{for people}}'
+ '
:{^{:name^toUpperCase()}}'
+ '{{/for}}'
+'{{/checkboxgroup}}'
+ '{^{checkboxgroup selectedPeople}}'
+ '
:NONE'
+ '{^{for people}}'
+ '
:{^{:name^toUpperCase()}}'
+ '{{/for}}'
+'{{/checkboxgroup}}'
);
model = {
selectedPeople: ["Jim"],
people: []
};
// ............................... Act .................................
tmpl.link("#result", model);
res = $("#result").text() + "|" + $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).insert([{name: "Bob"},{name: "Jim"},{name: newName}]);
res += $("#result").text() + "|" + $("#result input:checked").parent().text() + "|";
jsv.observable(model).setProperty("selectedPeople", [newName]);
res += $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).remove(2);
res += $("#result input:checked").parent().text() + "|";
$("#result input").first().prop("checked", true).change(); // Check first checkbox
res += model.selectedPeople + "-" + $("#result input:checked").parent().text() + "|";
// ............................... Assert .................................
assert.equal(res, ":NONE||:BOB:JIM:NEW:NONE:BOB:JIM:NEW|:JIM:JIM|:NEW:NEW||new,Bob-:BOB:BOB|",
'{^{checkboxgroup selectedPeople}} - two checkboxgroups with same selected bindings - starting out with no items, so no checkboxes');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
tmpl = jsv.templates(
'{^{checkboxgroup selectedPeople name="rad1"}}'
+ '{^{for people}}'
+ '
:{^{:name^toUpperCase()}}'
+ '{{/for}}'
+'{{/checkboxgroup}}'
+ '{^{checkboxgroup selectedPeople name="rad1"}}'
+ '
:NONE'
+ '{^{for people}}'
+ '
:{^{:name^toUpperCase()}}'
+ '{{/for}}'
+'{{/checkboxgroup}}'
);
model = {
selectedPeople: ["Jim"],
people: []
};
// ............................... Act .................................
tmpl.link("#result", model);
res = $("#result").text() + "|" + $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).insert([{name: "Bob"},{name: "Jim"},{name: newName}]);
res += $("#result").text() + "|" + $("#result input:checked").parent().text() + "|";
jsv.observable(model).setProperty("selectedPeople", [newName]);
res += $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).remove(2);
res += $("#result input:checked").parent().text() + "|";
$("#result input").first().prop("checked", true).change(); // Check first checkbox
res += model.selectedPeople + "-" + $("#result input:checked").parent().text() + "|" + $("#result input:checked")[0].name + "|" + $("#result input:checked")[1].name;
// ............................... Assert .................................
assert.equal(res, ":NONE||:BOB:JIM:NEW:NONE:BOB:JIM:NEW|:JIM:JIM|:NEW:NEW||new,Bob-:BOB:BOB|rad1|rad2",
'{^{checkboxgroup selectedPeople}} - name for group can be specified rather than auto-generated - on item or on checkboxgroup tag');
// =============================== Arrange ===============================
tmpl = jsv.templates(
'{^{if rg1}}'
+ '{^{checkboxgroup selectedPeople name="rad1"}}'
+ '{^{for people}}'
+ '
:{^{:name^toUpperCase()}}'
+ '{{/for}}'
+'{{/checkboxgroup}}'
+ '{{/if}}'
+ '{^{if rg2}}'
+ '{^{checkboxgroup selectedPeople name="rad1"}}'
+ '
:NONE'
+ '{^{for people}}'
+ '
:{^{:name^toUpperCase()}}'
+ '{{/for}}'
+'{{/checkboxgroup}}'
+ '{{/if}}'
);
model = {
rg1: true,
rg2: true,
selectedPeople: ["Jim"],
people: []
};
newName = "new";
// ............................... Act .................................
tmpl.link("#result", model);
res = ">" + $("#result").text() + "|" + $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).insert([{name: "Bob"},{name: "Jim"},{name: newName}]);
res += $("#result").text() + "|" + $("#result input:checked").parent().text() + "|";
jsv.observable(model).setProperty("selectedPeople", [newName]);
res += $("#result input:checked").parent().text() + "|";
// ............................... Assert .................................
assert.equal(res, ">:NONE||:BOB:JIM:NEW:NONE:BOB:JIM:NEW|:JIM:JIM|:NEW:NEW|",
'{^{checkboxgroup selectedPeople}} - two checkboxgroups wrapped in {{if}} blocks - starting out with no items');
jsv.observable(model.people).refresh([]);
jsv.observable(model).setProperty("rg1", false);
res = ">" + $("#result").text() + "|" + $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).insert([{name: "Bob"},{name: "Jim"},{name: newName}]);
res += $("#result").text() + "|" + $("#result input:checked").parent().text() + "|";
jsv.observable(model).setProperty("selectedPeople", [newName]);
res += $("#result input:checked").parent().text() + "|";
// ............................... Assert .................................
assert.equal(res, ">:NONE||:NONE:BOB:JIM:NEW|:NEW|:NEW|",
'{^{checkboxgroup selectedPeople}} - two checkboxgroups wrapped in {{if}} blocks - one set to false');
jsv.observable(model.people).refresh([]);
jsv.observable(model).setProperty("selectedPeople", ["Jim"]);
jsv.observable(model).setProperty("rg1", true);
res = ">" + $("#result").text() + "|" + $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).insert([{name: "Bob"},{name: "Jim"},{name: newName}]);
res += $("#result").text() + "|" + $("#result input:checked").parent().text() + "|";
jsv.observable(model).setProperty("selectedPeople", [newName]);
res += $("#result input:checked").parent().text() + "|";
// ............................... Assert .................................
assert.equal(res, ">:NONE||:BOB:JIM:NEW:NONE:BOB:JIM:NEW|:JIM:JIM|:NEW:NEW|",
'{^{checkboxgroup selectedPeople}} - two checkboxgroups wrapped in {{if}} blocks - one set back to true');
// ................................ Reset ................................
$("#result").empty();
assert.equal(JSON.stringify(_jsv.cbBindings), "{}", "All bindings removed when content removed from DOM");
jsv.views.settings.advanced({_jsv: false});
});
QUnit.test('radio buttons without {{radiogroup}}', function(assert) {
// =============================== Arrange ===============================
var tmpl = jsv.templates(
'{^{for people}}'
+ '
:{{:name}}'
+ '{{/for}}'
);
var model = {
selected: "Jim",
people: [
{name: "Bob"},
{name: "Jim"}
]
},
newName = "new";
// ............................... Act .................................
tmpl.link("#result", model);
res = $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).insert({
name: newName
});
jsv.observable(model).setProperty("selected", newName);
res += $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).remove(2);
res += $("#result input:checked").parent().text() + "|";
$("#result input").first().prop("checked", true).change(); // Check first radio button
res += model.selected + "-" + $("#result input:checked").parent().text() + "|";
// ............................... Assert .................................
assert.equal(res, ":Jim|:new||Bob-:Bob|",
'{^{for ...}}...
');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
tmpl = jsv.templates(
'
:NONE'
+ '{^{for people}}'
+ '
:{^{:name^toUpperCase()}}'
+ '{{/for}}'
);
model = {
selected: "Jim",
people: [
{name: "Bob"},
{name: "Jim"}
]
};
// ............................... Act .................................
tmpl.link("#result", model);
res = $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).insert({
name: newName
});
jsv.observable(model).setProperty("selected", newName);
res += $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).remove(2);
res += $("#result input:checked").parent().text() + "|";
$("#result input").first().prop("checked", true).change(); // Check first radio button
res += model.selected + "-" + $("#result input:checked").parent().text() + "|";
// ............................... Assert .................................
assert.equal(res, ":JIM|:NEW||None-:NONE|",
'
...{^{for ...}}...
');
// ............................... Act .................................
jsv.observable(model.people[1]).setProperty("name", "jimUpdated");
res = $("#result").text() + "|";
$("#result input").eq(2).prop("checked", true).change(); // Check third radio button
res += model.selected + "-" + $("#result input:checked").parent().text() + "|";
assert.equal(res, ":NONE:BOB:JIMUPDATED|jimUpdated-:JIMUPDATED|",
'{^{for ...}}...
- updated label and value');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
tmpl = jsv.templates({markup:
'
:none'
+ '{^{for people}}'
+ '
:{^{:name}}'
+ '{{/for}}',
converters: {
upper: function(val) {
return val.toUpperCase();
}
}
});
model = {
selected: "JIM",
people: [
{name: "bob"},
{name: "jim"}
]
};
// ............................... Act .................................
tmpl.link("#result", model, {
lower: function(val) {
return val.toLowerCase();
}
});
res = $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).insert({
name: newName
});
jsv.observable(model).setProperty("selected", newName);
res += $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).remove(2);
res += $("#result input:checked").parent().text() + "|";
$("#result input").first().prop("checked", true).change(); // Check first radio button
res += model.selected + "-" + model.selectedOut + "-" + $("#result input:checked").parent().text() + "|";
// ............................... Assert .................................
assert.equal(res, ":jim|:new||new-NONE-:none|",
'data-link="{:select convert=... convertBack=... linkTo=...:}"');
// ............................... Act .................................
jsv.observable(model.people[1]).setProperty("name", "jimUpdated");
res = $("#result").text() + "|";
$("#result input").eq(2).prop("checked", true).change(); // Check third radio button
res += model.selected + "-" + model.selectedOut + "-" + $("#result input:checked").parent().text() + "|";
assert.equal(res, ":none:bob:jimUpdated|new-JIMUPDATED-:jimUpdated|",
'data-link="{:select convert=... convertBack=... linkTo=...:}" - updated label and value');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
tmpl = jsv.templates(
'
:
NONE '
+ '{^{for people}}'
+ '
:
'
+ '{{/for}}'
);
model = {
selected: "Jim",
people: [
{name: "Bob"},
{name: "Jim"}
]
};
// ............................... Act .................................
tmpl.link("#result", model);
res = $("#" + $("#result input:checked").prop("id") + "Lbl").text() + "|";
jsv.observable(model.people).insert({
name: newName
});
jsv.observable(model).setProperty("selected", newName);
res += $("#" + $("#result input:checked").prop("id") + "Lbl").text() + "|";
jsv.observable(model.people).remove(2);
res += $("#" + $("#result input:checked").prop("id") + "Lbl").text() + "|";
$("#result input").first().prop("checked", true).change(); // Check first radio button
res += model.selected + "-" + $("#" + $("#result input:checked").prop("id") + "Lbl").text() + "|";
// ............................... Assert .................................
assert.equal(res, "JIM|NEW||None-NONE|",
'data-link="selected" with labels by for/id');
// ............................... Act .................................
jsv.observable(model.people[1]).setProperty("name", "jimUpdated");
res = $("#result").text() + "|";
$("#result input").eq(2).prop("checked", true).change(); // Check third radio button
res += model.selected + "-" + $("#" + $("#result input:checked").prop("id") + "Lbl").text() + "|";
assert.equal(res, ":NONE:BOB:JIMUPDATED|jimUpdated-JIMUPDATED|",
'data-link="selected" with labels by for/id - updated label and value');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
tmpl = jsv.templates(
'
:NONE'
+ '{^{for people}}'
+ '
:{^{:name^toUpperCase()}}'
+ '{{/for}}'
+ '
:NONE'
+ '{^{for people}}'
+ '
:{^{:name^toUpperCase()}}'
+ '{{/for}}'
);
model = {
selected: "Jim",
people: [
{name: "Bob"},
{name: "Jim"}
]
};
// ............................... Act .................................
tmpl.link("#result", model);
res = $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).insert({
name: newName
});
jsv.observable(model).setProperty("selected", newName);
res += $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).remove(2);
res += $("#result input:checked").parent().text() + "|";
$("#result input").first().prop("checked", true).change(); // Check first radio button
res += model.selected + "-" + $("#result input:checked").parent().text() + "|";
// ............................... Assert .................................
assert.equal(res, ":JIM:JIM|:NEW:NEW||None-:NONE:NONE|",
'data-link="selected" - two radiogroups with same selected bindings');
// ............................... Act .................................
jsv.observable(model.people[1]).setProperty("name", "jimUpdated");
res = $("#result").text() + "|";
$("#result input").eq(2).prop("checked", true).change(); // Check third radio button
res += model.selected + "-" + $("#result input:checked").parent().text() + "|";
assert.equal(res, ":NONE:BOB:JIMUPDATED:NONE:BOB:JIMUPDATED|jimUpdated-:JIMUPDATED:JIMUPDATED|",
'data-link="selected" - two radiogroups with same selected bindings - updated label and value');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
tmpl = jsv.templates(
'{^{for people}}'
+ '
:{^{:name^toUpperCase()}}'
+ '{{/for}}'
+ '
:NONE'
+ '{^{for people}}'
+ '
:{^{:name^toUpperCase()}}'
+ '{{/for}}'
);
model = {
selected: "Jim",
people: []
};
// ............................... Act .................................
tmpl.link("#result", model);
res = $("#result").text() + "|" + $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).insert([{name: "Bob"},{name: "Jim"},{name: "newName"}]);
res += $("#result").text() + "|" + $("#result input:checked").parent().text() + "|";
jsv.observable(model).setProperty("selected", newName);
res += $("#result input:checked").parent().text() + "|";
jsv.observable(model.people).remove(2);
res += $("#result input:checked").parent().text() + "|";
$("#result input").first().prop("checked", true).change(); // Check first radio button
res += model.selected + "-" + $("#result input:checked").parent().text() + "|";
// ............................... Assert .................................
assert.equal(res, ":NONE||:BOB:JIM:NEWNAME:NONE:BOB:JIM:NEWNAME|:JIM:JIM|||Bob-:BOB:BOB|",
'data-link="selected" - two radiogroups with same selected bindings - starting out with no items, so no radio buttons');
// ................................ Reset ................................
$("#result").empty();
});
QUnit.test('{^{on}}', function(assert) {
jsv.views.settings.advanced({_jsv: true}); // For using _jsv
// =============================== Arrange ===============================
function swap(ev, eventArgs) {
jsv.observable(this).setProperty("type", this.type === "shape" ? "line" : "shape");
}
var thing = {
type: "shape",
swap: swap
};
jsv.templates('{^{on swap/}} {^{:type}}')
.link("#result", thing);
// ................................ Act ..................................
before = $("#result").text();
$("#result button").click();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"swap shape|swap line",
'{^{on swap/}} renders as button with label "swap", and calls swap method on click, with "this" pointer context on data object');
// ................................ Reset ................................
$("#result").empty();
thing.type = "shape";
// =============================== Arrange ===============================
jsv.templates('{^{on missingMethod/}} {^{:type}}')
.link("#result", thing);
// ................................ Act ..................................
before = $("#result").text();
$("#result button").click();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"noop shape|noop shape",
'{^{on missingMethod/}} renders as button with label "noop", and is noop on click');
// ................................ Reset ................................
$("#result").empty();
thing.type = "shape";
// =============================== Arrange ===============================
var tmpla = jsv.templates('{^{on swap}} clickme {{/on}} {^{:type}}');
tmpla.link("#result", thing);
// ................................ Act ..................................
before = $("#result").text();
$("#result button").click();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"clickme shape|clickme line",
'{^{on swap}} clickme {{/on}} renders as button with label "clickme", and calls swap method on click');
// ................................ Reset ................................
$("#result").empty();
thing.type = "shape";
// =============================== Arrange ===============================
jsv.templates('{^{on swap tmpl=~label}} clickme {{/on}} {^{:type}}')
.link("#result", thing, {label: "clickagain"});
// ................................ Act ..................................
before = $("#result").text();
$("#result button").click();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"clickagain shape|clickagain line",
'{^{on swap tmpl=stringValue renders as button with label stringValue, and calls swap method on click');
// ................................ Reset ................................
$("#result").empty();
thing.type = "shape";
// =============================== Arrange ===============================
jsv.templates('{^{on swap}}
clickme {{/on}} {^{:type}}')
.link("#result", thing);
// ................................ Act ..................................
before = $("#result").text();
$("#result span").click();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"clickme shape|clickme line",
'{^{on swap}}
clickme {{/on}} renders as span with label clickme, and calls swap method on click');
// ................................ Reset ................................
$("#result").empty();
thing.type = "shape";
// =============================== Arrange ===============================
jsv.templates('{^{on ~swap/}} {^{:type}}')
.link("#result", thing, {swap: swap});
// ................................ Act ..................................
before = $("#result").text();
$("#result button").click();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"~swap shape|~swap line",
'{^{on ~swap/}} calls swap helper method on click, with "this" pointer context defaulting to current data object');
// ................................ Reset ................................
$("#result").empty();
thing.type = "shape";
// =============================== Arrange ===============================
jsv.templates('{^{on ~util.swap/}} {^{:type}} {^{:check}}')
.link("#result", thing, {
util:
{
swap: function(ev, eventArgs) {
jsv.observable(this.data).setProperty({
type: this.data.type === "shape" ? "line" : "shape",
check: this.data === eventArgs.view.data
});
},
data: thing
}
});
// ................................ Act ..................................
before = $("#result").text();
$("#result button").click();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"~util.swap shape |~util.swap line true",
'{^{on ~util.swap/}} calls util.swap helper method on click, with ~util as this pointer');
// ................................ Reset ................................
$("#result").empty();
thing.type = "shape";
delete thing.check;
// =============================== Arrange ===============================
jsv.templates('{^{on ~util.swap context=#data/}} {^{:type}}')
.link("#result", thing, {
util:
{
swap: swap
}
});
// ................................ Act ..................................
before = $("#result").text();
$("#result button").click();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"~util.swap shape|~util.swap line",
'{^{on ~util.swap context=#data/}} calls util.swap helper method on click, with current data object as this pointer');
// ................................ Reset ................................
$("#result").empty();
thing.type = "shape";
// =============================== Arrange ===============================
jsv.templates('{^{on ~util.swap context=~util.swapCtx/}} {^{:type}} {^{:check}}')
.link("#result", thing, {
util:
{
swap: function(ev, eventArgs) {
jsv.observable(this.data).setProperty({
type: this.data.type === "shape" ? "line" : "shape",
check: this.data === eventArgs.view.data
});
},
data: thing,
swapCtx: {
data: thing
}
}
});
// ................................ Act ..................................
before = $("#result").text();
$("#result button").click();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"~util.swap shape |~util.swap line true",
'{^{on ~util.swap context=~util.swapCtx/}} calls util.swap helper method on click, with util.swapCtx as this pointer');
// ................................ Reset ................................
$("#result").empty();
thing.type = "shape";
delete thing.check;
// =============================== Arrange ===============================
jsv.templates('{^{on ~util.swap data=#data/}} {^{:type}} {^{:check}}')
.link("#result", thing, {
util:
{
swap: function(ev, eventArgs) {
jsv.observable(ev.data).setProperty({
type: ev.data.type === "shape" ? "line" : "shape",
check: ev.data === eventArgs.view.data
});
},
data: thing,
swapCtx: {
data: thing
}
}
});
// ................................ Act ..................................
before = $("#result").text();
$("#result button").click();
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"~util.swap shape |~util.swap line true",
'{^{on ~util.swap data=#data/}} calls util.swap helper method on click, and passes current data #data as ev.data');
// ................................ Reset ................................
$("#result").empty();
thing.type = "shape";
delete thing.check;
// =============================== Arrange ===============================
jsv.templates('{^{on \'mouseup mousedown blur\' swap/}} {^{:type}}')
.link("#result", thing);
// ................................ Act ..................................
before = $("#result").text();
$("#result button").mouseup();
after = $("#result").text();
$("#result button").mousedown();
after += $("#result").text();
$("#result button").blur();
after += $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"swap shape|swap lineswap shapeswap line",
"{^{on 'mouseup mousedown blur' swap/}} calls util method on mouseup, mousedown and blur");
// ................................ Reset ................................
$("#result").empty();
thing.type = "shape";
delete thing.check;
// =============================== Arrange ===============================
var res = "1: ";
jsv.templates(
"
{^{on 'keyup keydown' '.inputA' unbind}}{^{on 'keyup' '#inputB' unbind}}{^{on 'mouseup' 'input' test}}{^{on 'mousedown' '#inputB' refresh}}\">"
+ " "
+ " "
+ "{{/on}}{{/on}}{{/on}}{{/on}}
")
.link("#result", {
unbind: function(ev, eventArgs) {
res += "unbind ";
eventArgs.linkCtx.tag.onUnbind();
},
refresh: function(ev, eventArgs) {
res += "refresh ";
eventArgs.linkCtx.tag.refresh();
},
test: function() {
res += "test ";
}
});
// ................................ Act ..................................
var events = $._data($("#divForOn")[0]).events,
eventBindings = "before: " + events.keydown.length + events.keyup.length + events.mouseup.length + events.mousedown.length;
$("#divForOn #inputB").mouseup();
res += "2: ";
$("#divForOn .inputA").mouseup();
res += "3: ";
$("#divForOn #inputB").keyup();
res += "4: ";
$("#divForOn #inputB").keyup();
res += "5: ";
$("#divForOn .inputA").keydown();
res += "6: ";
$("#divForOn .inputA").keyup();
res += "7: ";
$("#divForOn #inputB").mouseup();
res += "8: ";
$("#divForOn #inputB").mousedown();
eventBindings += " | after: " + events.keydown + events.keyup + events.mouseup.length + events.mousedown.length;
// ............................... Assert .................................
assert.equal(res,
"1: test 2: test 3: unbind 4: 5: unbind 6: 7: test 8: refresh ",
"multiple {^{on events selector method}} bindings on container attach events on delegated elements. Also, tag.onDispose on tag instances removes specific handlers for corresponding elements/selectors");
// ............................... Assert .................................
assert.equal(eventBindings,
"before: 1211 | after: undefinedundefined11",
"onDispose removes specific delegated events");
// ................................ Act ..................................
res = "1: ";
$("#divForOn #inputB").after("
");
$("#divForOn #newlyAdded").mouseup();
res += "2: ";
$("#divForOn #newlyAdded").keyup();
// ............................... Assert .................................
assert.equal(res,
"1: test 2: ",
"delegated {^{on events selector method}} binding allows additional elements added to content to bind correctly");
// ................................ Act ..................................
$("#result").empty();
eventBindings = "" + events.keydown + events.keyup + events.mouseup + JSON.stringify([_jsv.cbBindings, _jsv.bindings]);
// ............................... Assert .................................
assert.equal(eventBindings,
"undefinedundefinedundefined[{},{}]",
"Removing the element removes all associated attached {on } handlers");
// =============================== Arrange ===============================
var tmpl = jsv.templates("{^{on 'click' '#doIt' 754 thisIsTheMethod role 'hey' true process data=option 33 #data context=option}}\
Do it \
\
{{/on}}"),
data = {
name: "Jo",
role: "Advisor",
option: {
allow: true
},
thisIsTheMethod: function(role, text, isFoo, compile, amount, root, ev, eventArgs) {
if (compile) {
compile.call(root, role, text, isFoo, amount, ev.data.allow, eventArgs.linkCtx.tag.tagCtx.args[2]);
}
},
process: function(role, text, isFoo, amount, allow, extraParam) {
jsv.observable(this).setProperty("res", this.res + role + text + isFoo + amount + " allow:" + allow + " extraParam: " + extraParam + "|");
},
res: ""
};
tmpl.link("#result", data);
// ................................ Act ..................................
$("#doIt").click();
data.option.allow = false;
jsv.observable(data).setProperty("role", "Follower");
$("#doIt").click();
// ............................... Assert .................................
assert.equal(data.res, "Advisorheytrue33 allow:true extraParam: 754|Followerheytrue33 allow:false extraParam: 754|",
"{^{on 'click' selector otherParams... method params...}} : supports passing params to method, of any type, as well as setting data and context for the function call");
// ................................ Act ..................................
$("#result").empty();
// =============================== Arrange ===============================
var tmpl = jsv.templates("{^{on doit id='doIt' class='red' width='100' height='100'/}}"),
res = "",
data = {
name: "Jo",
doit: function() {
var button = $("button");
res = (Math.round(button.width())) + "|" + (Math.round(button.height())) + "|" + button[0].id + "|" + button[0].className;
}
};
tmpl.link("#result", data);
// ................................ Act ..................................
$("#doIt").click();
// ............................... Assert .................................
assert.equal(res, "100|100|doIt|red",
"{^{on ... id=... class=... width=... height=...}} : supports setting id, class, height and width");
// ................................ Act ..................................
$("#result").empty();
jsv.views.settings.advanced({_jsv: false});
});
QUnit.test('data-link="{tag...} and {^{tag}} in same template"', function(assert) {
var done;
if (assert.async) { done = assert.async() } else { stop() }
// ................................ Reset ................................
person1._firstName = "Jo"; // reset Prop
person1.lastName = "One"; // reset Prop
settings.title = "Mr"; // reset Prop
settings.width = 30; // reset Prop
jsv.views.settings.advanced({_jsv: true}); // For using viewsAndBindings($)
// =============================== Arrange ===============================
jsv.templates('{^{tmplTag/}}-{^{:lastName}}
-
')
.link("#result", person1);
// ................................ Act ..................................
before = $("#result").text() + $("#result input").val();
jsv.observable(person1).setProperty({firstName: "newFirst", lastName: "newLast"});
jsv.observable(settings).setProperty({title: "Sir", width: 40});
after = $("#result").text() + $("#result input").val();
// ............................... Assert .................................
assert.equal(before + "|" + after,
'Name: Mr Jo. Width: 30-One Name: Mr Jo. Width: 30-OneOne|Name: Sir newFirst. Width: 40-newLast Name: Sir newFirst. Width: 40-newLastnewLast',
'Data link using: {^{tmplTag/}} {^{:lastName}}
');
// ................................ Reset ................................
$("#result").empty();
person1._firstName = "Jo"; // reset Prop
person1.lastName = "One"; // reset Prop
settings.title = "Mr"; // reset Prop
settings.width = 30; // reset Prop
// =============================== Arrange ===============================
jsv.templates('prop:{^{:lastName}}
computed:{^{:fullName()}}
Tag:{^{tmplTag/}}
')
.link("#result", person1);
// ................................ Act ..................................
before = $("#result").text() + $("#last").val() + $("#full").val();
jsv.observable(person1).setProperty({firstName: "newFirst", lastName: "newLast"});
jsv.observable(settings).setProperty({title: "Sir", width: 40});
jsv.observable(person1).setProperty({fullName: "compFirst compLast"});
after = $("#result").text() + $("#last").val() + $("#full").val();
// ............................... Assert .................................
assert.equal(before + "|" + after,
'prop:OneOne computed:Mr Jo OneMr Jo One Tag:Name: Mr Jo. Width: 30Name: Mr Jo. Width: 30OneMr Jo One|prop:compLastcompLast computed:Sir compFirst compLastSir compFirst compLast Tag:Name: Sir compFirst. Width: 40Name: Sir compFirst. Width: 40compLastSir compFirst compLast',
'Data link using: {^{:lastName}}
{^{:fullName()}}
{^{tmplTag/}}
');
// ................................ Act ..................................
keydown($("#full").val("newFirst newLast"));
setTimeout(function() {
after = $("#result").text() + $("#last").val() + $("#full").val();
// ............................... Assert .................................
assert.equal(after,
"prop:newLastnewLast computed:Sir newFirst newLastSir newFirst newLast Tag:Name: Sir newFirst. Width: 40Name: Sir newFirst. Width: 40newLastnewFirst newLast",
'Two-way binding to a computed observable correctly calls the setter');
// =============================== Arrange ===============================
// ................................ Act ..................................
$("#result").empty();
// ............................... Assert .................................
assert.ok(!viewsAndBindings($) && !$._data(person1).events && !$._data(settings).events,
"$(container).empty removes the views and current listeners from that content");
// ................................ Reset ................................
person1._firstName = "Jo"; // reset Prop
person1.lastName = "One"; // reset Prop
settings.title = "Mr"; // reset Prop
settings.width = 30; // reset Prop
// =============================== Arrange ===============================
jsv.templates('prop:{^{:lastName}}
computed:{^{:fullName()}}
Tag:{^{tmplTag/}}
')
.link("#result", person1);
// ................................ Act ..................................
before = $("#result").text() + $("#last").val() + $("#full").val();
jsv.observable(person1).setProperty({firstName: "newFirst", lastName: "newLast"});
jsv.observable(settings).setProperty({title: "Sir", width: 40});
jsv.observable(person1).setProperty({fullName: "compFirst compLast"});
after = $("#result").text() + $("#last").val() + $("#full").val();
// ............................... Assert .................................
res = 'prop:OneOne computed:Mr Jo OneMr Jo One Tag:Name: Mr Jo. Width: 30Name: Mr Jo. Width: 30OneMr Jo One|prop:compLastcompLast computed:Sir compFirst compLastSir compFirst compLast Tag:Name: Sir compFirst. Width: 40Name: Sir compFirst. Width: 40compLastSir compFirst compLast';
// ................................ Act ..................................
jsv.unlink("#result");
// ............................... Assert .................................
assert.ok(before + "|" + after === res && !viewsAndBindings($) && !$._data(person1).events && !$._data(settings).events,
"jsv.unlink(container) removes the views and current listeners from that content");
// ................................ Reset ................................
person1._firstName = "Jo"; // reset Prop
person1.lastName = "One"; // reset Prop
settings.title = "Mr"; // reset Prop
settings.width = 30; // reset Prop
// =============================== Arrange ===============================
jsv.templates('prop:{^{:lastName}}
computed:{^{:fullName()}}
Tag:{^{tmplTag/}}
')
.link("#result", person1);
// ................................ Act ..................................
$("#result").unlink();
// ............................... Assert .................................
assert.ok(!viewsAndBindings($) && !$._data(person1).events && !$._data(settings).events,
"$(container).unlink() removes the views and current listeners from that content");
// =============================== Arrange ===============================
jsv.templates('prop:{^{:lastName}}
computed:{^{:fullName()}}
Tag:{^{tmplTag/}}
')
.link("#result", person1);
// ................................ Act ..................................
before = $("#result").text() + $("#last").val() + $("#full").val();
jsv.observable(person1).setProperty({firstName: "newFirst", lastName: "newLast"});
jsv.observable(settings).setProperty({title: "Sir", width: 40});
jsv.observable(person1).setProperty({fullName: "compFirst compLast"});
after = $("#result").text() + $("#last").val() + $("#full").val();
// ............................... Assert .................................
res = 'prop:OneOne computed:Mr Jo OneMr Jo One Tag:Name: Mr Jo. Width: 30Name: Mr Jo. Width: 30OneMr Jo One|prop:compLastcompLast computed:Sir compFirst compLastSir compFirst compLast Tag:Name: Sir compFirst. Width: 40Name: Sir compFirst. Width: 40compLastSir compFirst compLast';
// ................................ Act ..................................
viewContent = viewsAndBindings($);
jsv.unobserve(person1, "*", settings, "*");
// ............................... Assert .................................
assert.ok(before + "|" + after === res && viewContent === viewsAndBindings($) && !$._data(person1).events && !$._data(settings).events,
'jsv.unobserve(person1, "*", settings, "*") removes the current listeners from that content, but leaves the views');
// ................................ Reset ................................
person1._firstName = "Jo"; // reset Prop
person1.lastName = "One"; // reset Prop
settings.title = "Mr"; // reset Prop
settings.width = 30; // reset Prop
// =============================== Arrange ===============================
jsv.templates('prop:{^{:lastName}}
computed:{^{:fullName()}}
Tag:{^{tmplTag/}}
')
.link("#result", person1);
// ................................ Act ..................................
before = $("#result").text() + $("#last").val() + $("#full").val();
jsv.observable(person1).setProperty({firstName: "newFirst", lastName: "newLast"});
jsv.observable(settings).setProperty({title: "Sir", width: 40});
jsv.observable(person1).setProperty({fullName: "compFirst compLast"});
after = $("#result").text() + $("#last").val() + $("#full").val();
// ............................... Assert .................................
res = 'prop:OneOne computed:Mr Jo OneMr Jo One Tag:Name: Mr Jo. Width: 30Name: Mr Jo. Width: 30OneMr Jo One|prop:compLastcompLast computed:Sir compFirst compLastSir compFirst compLast Tag:Name: Sir compFirst. Width: 40Name: Sir compFirst. Width: 40compLastSir compFirst compLast';
// ................................ Act ..................................
jsv.unlink();
// ............................... Assert .................................
assert.ok(before + "|" + after === res && !viewsAndBindings($) && !$._data(person1).events && !$._data(settings).events,
'jsv.unlink() removes all views and listeners from the page');
// ................................ Reset ................................
person1._firstName = "Jo"; // reset Prop
person1.lastName = "One"; // reset Prop
settings.title = "Mr"; // reset Prop
settings.width = 30; // reset Prop
jsv.views.settings.advanced({_jsv: false});
// TODO ADDITIONAL TESTS:
// 1: link(null, data) to link whole document
if (assert.async) { done() } else { start() }
}, 0);
});
QUnit.test("Fallbacks for missing or undefined paths: using {^{:some.path onError = 'fallback'}}, etc.", function(assert) {
jsv.views.settings.advanced({_jsv: true}); // For using viewsAndBindings($)
// =============================== Arrange ===============================
jsv.views.tags({
mytag1: function(val) {return val + " from my tag1"; },
mytag2: {
template: "{{:}} from my tag2"
}
}).converters({
upper: function(val) {
return val.toUpperCase();
}
});
var initial = {a: {b: null}},
updated = {c: {val: 'leaf'}};
jsv.templates(
"{^{:a.b^c.val onError=~error + 'A '}} "
+ "{^{upper:a.b^c.val onError=~error + 'B '}} "
+ "{^{>a.b^c.val onError=~error + 'C '}} "
+ "{^{if a.b^c onError=~error + 'D '}}
{{:a.b.c.val}} {{/if}} "
+ "{^{for a.b^c onError=~error + 'E ' ~foo='foo'}}
{{:val + ~foo}} {{/for}} "
+ "{^{mytag1 a.b^c.val onError=~error + 'F '/}} "
+ "{^{mytag2 a.b^c.val onError=~error + 'G '/}} "
+ "
"
+ "
"
+ "
"
+ "
")
.link("#result", initial, {error: "err:"});
// ................................ Act ..................................
before = "" + $._data(initial.a).events.propertyChange.length + " " + !$._data(updated).events + " " + !$._data(updated.c).events + "|"
+ $("#result").text() + "|";
jsv.observable(initial.a).setProperty('b', updated);
after = $("#result").text() + "|";
jsv.observable(initial.a.b.c).setProperty('val', "leaf2");
after += $("#result").text() + "|";
jsv.observable(initial.a.b).setProperty('c', {val: "leaf3"});
after += $("#result").text() + "|";
jsv.observable(initial.a).setProperty('b', {c: {val: "leaf4"}});
after += $("#result").text() + "|";
after += "" + $._data(initial.a).events.propertyChange.length + " " + $._data(initial.a.b).events.propertyChange.length + " " + $._data(initial.a.b.c).events.propertyChange.length
+ " " + !$._data(updated).events + " " + !$._data(updated.c).events + "|"
+ $("#result").text() + "|";
var prevB = initial.a.b;
jsv.observable(initial.a).setProperty('b', null);
after += "" + $._data(initial.a).events.propertyChange.length + " " + !$._data(prevB).events + " " + !$._data(prevB.c).events + "|"
+ $("#result").text() + "|";
jsv.observable(initial.a).setProperty('b', updated);
after += $("#result").text() + "|";
assert.equal(before + after,
"11 true true|"
+ "err:A ERR:B err:C err:D err:E err:F err:G err:H err:I ERR:J err:K |"
+ "leaf LEAF leaf leaf leaffoo leaf from my tag1 leaf from my tag2 leaf leaf from my tag1 LEAF LEAF |"
+ "leaf2 LEAF2 leaf2 leaf leaffoo leaf2 from my tag1 leaf2 from my tag2 leaf2 leaf2 from my tag1 LEAF2 LEAF2 |"
+ "leaf3 LEAF3 leaf3 leaf leaf3foo leaf3 from my tag1 leaf3 from my tag2 leaf3 leaf3 from my tag1 LEAF3 LEAF3 |"
+ "leaf4 LEAF4 leaf4 leaf leaf4foo leaf4 from my tag1 leaf4 from my tag2 leaf4 leaf4 from my tag1 LEAF4 LEAF4 |"
+ "11 11 9 true true|"
+ "leaf4 LEAF4 leaf4 leaf leaf4foo leaf4 from my tag1 leaf4 from my tag2 leaf4 leaf4 from my tag1 LEAF4 LEAF4 |"
+ "11 true true|"
+ "err:A ERR:B err:C err:D err:E err:F err:G err:H err:I ERR:J ERR:K |"
+ "leaf3 LEAF3 leaf3 leaf3 leaf3foo leaf3 from my tag1 leaf3 from my tag2 leaf3 leaf3 from my tag1 LEAF3 LEAF3 |",
"deep linking in templates, using onError - correctly re-link to data when missing objects are dynamically replaced");
// ................................ Act ..................................
jsv.unlink();
// ............................... Assert .................................
assert.ok(!viewsAndBindings($) && !$._data(initial.a).events && !$._data(initial.a.b).events,
'jsv.unlink() removes all views and listeners from the page');
// =============================== Arrange ===============================
function Item(value, title) {
this.title = title;
this._value = value;
this.value = function(val) {
if (!arguments.length) {
return this._value;
} else {
this._value = val;
}
};
this.value.set = true;
}
initial = new Item("string1", "A");
jsv.templates(
"{^{:value() onError='error1'}} {^{:title}} "
+ "{^{:value()^value() onError='error2'}} {^{:value()^title onError='error2b'}} "
+ "{^{:value()^value().value() onError='error3'}} {^{:value()^value().title onError='error3b'}} "
+ "{^{:value()^value().value().value() onError='error4'}} {^{:value()^value().value().title onError='error4b'}} "
).link("#result", initial);
// ................................ Act ..................................
var B, C, D, a, b, c, d, e;
before = $("#result").text() + "|";
jsv.observable(initial).setProperty('value', B = new Item("string2", "B"));
after = $("#result").text() + "|";
jsv.observable(initial.value()).setProperty('value', C = new Item("string3", "C"));
after += $("#result").text() + "|";
jsv.observable(initial.value().value()).setProperty('value', D = new Item("string4", "D"));
after += $("#result").text() + "|";
jsv.observable(initial).removeProperty('value');
after += $("#result").text() + "|";
jsv.observable(initial).setProperty('value', a = new Item(b = new Item(c = new Item(d = new Item(e = new Item("string4", "e"), "d"), "c"), "b"), "a"));
after += $("#result").text() + "|";
assert.equal(before + after,
"string1 A error2 error3 error3b error4 error4b |"
+ "[object Object] A string2 B error3 error4 error4b |"
+ "[object Object] A [object Object] B string3 C error4 |"
+ "[object Object] A [object Object] B [object Object] C string4 D |"
+ "[object Object] A error2 error3 error3b error4 error4b |"
+ "[object Object] A [object Object] a [object Object] b [object Object] c |",
"deep linking in templates, using onError - correctly re-link to data when missing objects are dynamically replaced");
// ................................ Act ..................................
jsv.unlink();
// ............................... Assert .................................
assert.ok(!viewsAndBindings($) && !$._data(initial.value()).events && !$._data(initial.value().value()).events,
'jsv.unlink() removes all views and listeners from the page');
jsv.views.settings.advanced({_jsv: false});
});
QUnit.test("contextual parameter", function(assert) {
// =============================== Arrange ===============================
var teams = [
{title: "The A Team", members: [{name: "Jeff"}, {name: "Maria"}]},
{title: "The B Team", members: [{name: "Francis"}]}
];
// ................................ Act ..................................
jsv.templates(
"{{if members.length ~teamTitle=title ~teamData=#data ~teamIndex=#index}}"
+ "{{for members itemVar='~member'}}"
+ "{{:~teamTitle}} "
+ "{{:~teamData.title}} "
+ "{{:~teamIndex}} "
+ "{{:~member.name}} "
+ "{{/for}}"
+ "{{/if}}"
).link("#result", teams);
// ............................... Assert .................................
assert.equal($("#result").text(),
"The A Team The A Team 0 Jeff The A Team The A Team 0 Maria The B Team The B Team 1 Francis ",
"contextual parameter passing to inner context");
// =============================== Arrange ===============================
var ct = 1;
// ................................ Act ..................................
jsv.templates("{^{include ~f=~hlp('foo') }}{^{:~f}}{{/include}}").link("#result", {}, {
hlp: function() {
return ct++;
}
});
// ............................... Assert .................................
assert.equal($("#result").text(),
"1",
"contextual parameter function (with quotes) is cached and called only once");
// See https://github.com/BorisMoore/jsviews/issues/440#issuecomment-660853490
// ................................ Act ..................................
jsv.templates("{^{if 1 ~a='A'+\"B\"+'\"'+\"'\"+\"\\'\"}}{^{:'Inner'+~a}}{{/if}}").link("#result");
// ............................... Assert .................................
assert.equal($("#result").text(),
"InnerAB\"'\\'",
"contextual parameter correctly escaping quotes and backslash");
// ................................ Reset ................................
$("#result").empty();
});
QUnit.test('Bound tag properties and contextual parameters', function(assert) {
// =============================== Arrange ===============================
var things = [
{
type: "shape",
form: "circle"
},
{
type: "line",
form: "square",
thickness: "1"
}
];
jsv.templates('Tag: {^{include ^tmpl=~typeTemplates[type]/}} Elem:
')
.link("#result", things, {
typeTemplates: {
shape: "Shape: {^{:form}}\n",
line: "Line: {^{:form}} {^{:thickness}}\n"
}
}
);
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(things[0]).setProperty({type: "line", thickness: 5});
jsv.observable(things[1]).setProperty({type: "shape"});
jsv.observable(things[1]).removeProperty("thickness");
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"Tag: Shape: circle\n Elem: Shape: circle\n Tag: Line: square 1\n Elem: Line: square 1\n |Tag: Line: circle 5\n Elem: Line: circle 5\n Tag: Shape: square\n Elem: Shape: square\n ",
'binding to ^tmpl=... :{^{include ^tmpl=~typeTemplates[type]... and data-link="{include ^tmpl=~typeTemplates[type]...');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
var things = [
{
type: "shape",
form: "circle"
},
{
type: "line",
form: "square",
thickness: "1"
}
];
jsv.templates('Tag: {^{if true ^tmpl=~typeTemplates[type]/}} Elem:
')
.link("#result", things, {
typeTemplates: {
shape: "Shape: {^{:form}}\n",
line: "Line: {^{:form}} {^{:thickness}}\n"
}
}
);
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(things[0]).setProperty({type: "line", thickness: 5});
jsv.observable(things[1]).setProperty({type: "shape"});
jsv.observable(things[1]).removeProperty("thickness");
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"Tag: Shape: circle\n Elem: Shape: circle\n Tag: Line: square 1\n Elem: Line: square 1\n |Tag: Line: circle 5\n Elem: Line: circle 5\n Tag: Shape: square\n Elem: Shape: square\n ",
'binding to ^tmpl=... :{^{if true ^tmpl=~typeTemplates[type]... and data-link="{if true ^tmpl=~typeTemplates[type]...');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
var things = [
{
type: "shape",
form: "circle"
},
{
type: "line",
form: "square",
thickness: "1"
}
];
jsv.templates('Tag: {^{if false}}{{else ^tmpl=~typeTemplates[type]}}{{/if}} Elem:
')
.link("#result", things, {
typeTemplates: {
shape: "Shape: {^{:form}}\n",
line: "Line: {^{:form}} {^{:thickness}}\n"
}
}
);
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(things[0]).setProperty({type: "line", thickness: 5});
jsv.observable(things[1]).setProperty({type: "shape"});
jsv.observable(things[1]).removeProperty("thickness");
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"Tag: Shape: circle\n Elem: Shape: circle\n Tag: Line: square 1\n Elem: Line: square 1\n |Tag: Line: circle 5\n Elem: Line: circle 5\n Tag: Shape: square\n Elem: Shape: square\n ",
'binding to ^tmpl=... :{^{if false}}{{else ^tmpl=~typeTemplates[type]... and data-link="{if false}{else ^tmpl=~typeTemplates[type]...');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
var things = [
{
type: "shape",
form: "circle"
},
{
type: "line",
form: "square",
thickness: "1"
}
];
jsv.templates('Tag: {^{for undefined}}{{else ^tmpl=~typeTemplates[type]}}{{/for}} Elem:
')
.link("#result", things, {
typeTemplates: {
shape: "Shape: {^{:form}}\n",
line: "Line: {^{:form}} {^{:thickness}}\n"
}
}
);
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(things[0]).setProperty({type: "line", thickness: 5});
jsv.observable(things[1]).setProperty({type: "shape"});
jsv.observable(things[1]).removeProperty("thickness");
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"Tag: Shape: circle\n Elem: Shape: circle\n Tag: Line: square 1\n Elem: Line: square 1\n |Tag: Line: circle 5\n Elem: Line: circle 5\n Tag: Shape: square\n Elem: Shape: square\n ",
'binding to ^tmpl=... :{^{for undefined}}{{else ^tmpl=~typeTemplates[type]... and data-link="{for undefined}{else ^tmpl=~typeTemplates[type]...');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
things = [
{
type: "shape",
form: "circle"
},
{
type: "line",
form: "square",
thickness: "1"
}
];
jsv.templates('Bound condition: {^{include ^~condition=type==="shape"}}{{:type}} {{:~condition}} {{/include}}'
+ 'Unbound condition: {^{include ~condition=type==="shape"}}{{:type}} {{:~condition}} {{/include}}')
.link("#result", things);
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(things[0]).setProperty({type: "line", thickness: 5});
jsv.observable(things[1]).setProperty({type: "shape"});
jsv.observable(things[1]).removeProperty("thickness");
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"Bound condition: shape true Unbound condition: shape true Bound condition: line false Unbound condition: line false |Bound condition: line false Unbound condition: shape true Bound condition: shape true Unbound condition: line false ",
'Binding to contextual parameter {^{include ^~condition=... triggers update. Unbound contextual parameter {^{include ~condition=... does not trigger updated content');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
things = [
{
type: "shape",
form: "circle"
},
{
type: "line",
form: "square",
thickness: "1"
}
];
jsv.views.tags({
updatingTag: {
},
nonUpdatingTag: {
onUpdate: function() {
return false;
}
}
});
jsv.templates('Updating: {^{updatingTag ^condition=type==="shape"}}{{:type}} {^{:~tagCtx.props.condition}} {{/updatingTag}} '
+ 'Non updating: {^{nonUpdatingTag ^condition=type==="shape"}}{{:type}} {^{:~tagCtx.props.condition}} {{/nonUpdatingTag}}')
.link("#result", things);
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(things[0]).setProperty({type: "line", thickness: 5});
jsv.observable(things[1]).setProperty({type: "shape"});
jsv.observable(things[1]).removeProperty("thickness");
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"Updating: shape true Non updating: shape true Updating: line false Non updating: line false |Updating: line false Non updating: shape false Updating: shape true Non updating: line true ",
'Binding to property triggers update {^{updatingTag ^condition=... unless tag is non-updating: {^{nonUpdatingTag ^condition=...');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
jsv.templates({
myTmpl: "{{>name}} lead:{^{>~team.lead}} - "
});
var model = {
lead: "Jim",
people: [
{name: "Bob"},
{name: "Jim"}
]
};
// ............................... Act .................................
jsv.templates("
").link("#result", model);
res = $("#result").text();
jsv.observable(model.people).insert({
name: "newName"
});
jsv.observable(model).setProperty("lead", "newName");
res += "|" + $("#result").text();
// ............................... Assert .................................
assert.equal(res, ("Bob lead:Jim - Jim lead:Jim - |Bob lead:newName - Jim lead:newName - newName lead:newName - "),
"data-link allows passing in new contextual parameters to template: data-link=\"{for people ~team=#data tmpl=...");
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
model = {
sortby: "role",
cols: ["name", "role"]
};
// ............................... Act .................................
jsv.templates('{^{for cols itemVar="~col"}}{^{:~root.sortby === ~col}} {^{:~col}} {{/for}}').link("#result", model);
res = $("#result").text();
jsv.observable(model).setProperty("sortby", model.sortby === "role" ? "name" : "role");
res += "|" + $("#result").text();
jsv.observable(model).setProperty("sortby", model.sortby === "role" ? "name" : "role");
res += "|" + $("#result").text();
jsv.observable(model.cols).insert("other");
res += "|" + $("#result").text();
// ............................... Assert .................................
assert.equal(res, "false name true role |true name false role |false name true role |false name true role false other ",
"itemVar variables in item list are distinct variables");
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
var data = {items1: [], items: [{name: "Jo"}, {name: "Bob"}]};
// ............................... Act .................................
jsv.templates(
'{^{for items1}}{{else items itemVar="~currentItem"}}' +
'{^{:name}}
' +
'{^{:~currentItem.name}}
' +
'{^{include}}' +
'{^{:~currentItem.name}}
' +
'{{/include}}' +
'{^{for itemVar="~currentItem2"}}' +
'{^{:name}}
' +
'{^{:~currentItem2.name}}
' +
'{^{if true}}' +
'{^{:~currentItem2.name}}
' +
'{{/if}}' +
'{{/for}}' +
'{^{for ~currentItem itemVar="~currentItem2"}}' +
'{^{:name}}
' +
'{^{:~currentItem2.name}}
' +
'{^{include}}' +
'{^{:~currentItem2.name}}
' +
'{{/include}}' +
'{{/for}}' +
'{{/for}}').link("#result", data);
jsv.observable(data.items).insert({name: "Jeff"});
var inputs = $("#result input");
res = "|" + $("#result").text() + inputs[0].value;
keydown($(inputs[0]).val("Jo0"));
res += "|" + $("#result").text() + inputs[1].value;
// ............................... Assert .................................
assert.equal(res, "|Jo Jo Jo Jo Jo Jo Jo Jo Jo Bob Bob Bob Bob Bob Bob Bob Bob Bob Jeff Jeff Jeff Jeff Jeff Jeff Jeff Jeff Jeff Jo|Jo0 Jo0 Jo0 Jo0 Jo0 Jo0 Jo0 Jo0 Jo0 Bob Bob Bob Bob Bob Bob Bob Bob Bob Jeff Jeff Jeff Jeff Jeff Jeff Jeff Jeff Jeff Jo0",
"itemVar in nested contexts, on else blocks, etc. with two-way binding, works correctly");
// ................................ Reset ................................
$("#result").empty();
});
QUnit.test('Data-linking helpers and contextual parameters', function(assert) {
jsv.views.settings.trigger(false);
// =============================== Arrange ===============================
var data = {name: "Jo", address: {}};
res = "";
jsv.templates(
'{{for address ~nm=name}}'
+ '
'
+ '
'
+ '{{/for}}'
)
.link("#result", data);
// ................................ Act ..................................
res += $("#result").text() + " | ";
jsv.observable(data).setProperty("name", "new");
res += $("#result").text() + " | ";
$("#input1").val('changed').change();
res += $("#result").text() + " | ";
// ............................... Assert .................................
assert.equal(res,
"Jo | new | changed | ",
'contextual parameter two-way binding');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
var things = [
{
type: "shape",
form: "circle"
},
{
type: "line",
form: "square",
thickness: "1"
}
];
jsv.templates('{^{include ~condition=type==="shape"}}{{:type}} {^{:type}} {^{:~condition}} {{/include}}')
.link("#result", things);
// ................................ Act ..................................
before = $("#result").text();
jsv.observable(things[0]).setProperty({type: "line", thickness: 5});
jsv.observable(things[1]).setProperty({type: "shape"});
after = $("#result").text();
// ............................... Assert .................................
assert.equal(before + "|" + after,
"shape shape true line line false |shape line false line shape true ",
'contextual parameter {^{include ~condition=... does not trigger update but references are bound');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
var getContent = function() {
$("#result input").each(function() {
res += this.value;
});
res += $("#result").text() + "|";
}
var tmpl = jsv.templates(
'
\
\
\
\
{^{include ~f1 = foo}}\
\
{^{include ~f2 = #data.foo}}\
\
\
{^{include ~f3 = #view.data.foo}}\
{^{:~f1}}\
{^{:~f2}}\
{^{:~f3}}\
\
\
\
{{/include}}\
{{/include}}\
{{/include}}\
');
// ............................... Act .................................
tmpl.link("#result", {foo: "F"});
var cnt = 0;
res = "";
getContent();
$("#result input").each(function() {
$("#result input").val(cnt++).change();
getContent();
});
assert.equal(res, "FFFFFFFFFFFF|000000000000|111111111111|222222222222|333333333333|444444444444|"
+ "555555555555|666666666666|777777777777|888888888888|",
'Two-way binding to contextual parameters');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
getContent = function getContent() {
return $("#outerInput").val() + ":" + $("#innerInput").val() + ":" + $("#innerDiv").text();
}
var tmpl = jsv.templates(
'{^{if true ~street=address(name)}}\
\
{^{for address ~street=~street^street}}\
\
\
{{/for}}\
{{/if}}');
// ............................... Act .................................
var address = {street: "add"};
var altAddress = {street: "alt"};
function getAddress(name) {
return (name.length > 2 || data.alt) ? altAddress : address;
}
getAddress.depends = "alt";
var data = {name: "Jo", address: getAddress, alt: false};
tmpl.link("#result", data);
before = getContent();
$("#outerInput").val("addOuter").change();
after = before + "|" + getContent();
$("#innerInput").val("addInner").change();
after += "|" + getContent();
jsv.observable(data).setProperty("name", "John");
after += "|" + getContent();
$("#outerInput").val("altOuter").change();
after += "|" + getContent();
$("#innerInput").val("altInner").change();
after += "|" + getContent();
jsv.observable(data).setProperty("name", "Me");
after += "|" + getContent();
jsv.observable(data).setProperty("alt", true);
after += "|" + getContent();
assert.equal(after, "add:add:add|addOuter:addOuter:addOuter|addInner:addInner:addInner"
+ "|alt:alt:alt|altOuter:altOuter:altOuter|altInner:altInner:altInner"
+ "|addInner:addInner:addInner|altInner:altInner:altInner",
'Two-way binding to contextual parameters with computed values');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
var tmpl = jsv.templates(
'{^{for items}}\
{{for 22 ~ind=#index}}\
{{for 33 ~ind=~ind+1}}\
{{for 33 ~ind=~ind*2}}\
{{if true}}\
{^{:~ind}}\
{{/if}}\
{{/for}}\
{{/for}}\
{{/for}}\
{{/for}}'
);
// ............................... Act .................................
var data = {items: [1,2,3]};
tmpl.link("#result", data);
before = $("#result").text();
jsv.observable(data.items).remove();
after = before + "|" + $("#result").text();
jsv.observable(data.items).remove();
after += "|" + $("#result").text();
jsv.observable(data.items).insert(4);
after += "|" + $("#result").text();
jsv.observable(data.items).insert(5);
after += "|" + $("#result").text();
jsv.observable(data.items).insert(6);
after += "|" + $("#result").text();
assert.equal(after, "246|24|2|24|246|2468",
'Contextual parameter for index with array change');
// ................................ Reset ................................
$("#result").empty();
jsv.views.settings.trigger(true);
});
QUnit.test("JsViews ArrayChange: insert()", function(assert) {
// =============================== Arrange ===============================
jsv.views.tags({
liTag: function() {
return "
Tag ";
}
});
model.things = [{thing: "Orig"}]; // reset Prop
jsv.templates('
{^{liTag/}}{^{for things}}{{:thing}} {^{liTag/}}{{/for}}|after ')
.link("#result", model);
// ................................ Act ..................................
jsv.observable(model.things).insert(0, {thing: "First"});
jsv.observable(model.things).insert(1, {thing: "Last"});
jsv.observable(model.things).insert(1, {thing: "Middle"});
// ............................... Assert .................................
assert.equal($("#result").text(), "TagFirstTagMiddleTagLastTagOrigTag|after",
'Within element only content, insertion maintains correctly prevNode, nextNode, element order and binding on views and tags');
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
// =============================== Arrange ===============================
jsv.views.tags({
spanTag: function() {
return "
Tag ";
}
});
model.things = [{thing: "Orig"}]; // reset Prop
jsv.templates('
{^{spanTag/}}{^{for things}}{{:thing}} {^{spanTag/}}{{/for}}|after
')
.link("#result", model);
// ................................ Act ..................................
jsv.observable(model.things).insert(0, {thing: "First"});
jsv.observable(model.things).insert(1, {thing: "Last"});
jsv.observable(model.things).insert(1, {thing: "Middle"});
// ............................... Assert .................................
assert.equal($("#result").text(), "TagFirstTagMiddleTagLastTagOrigTag|after",
'Within regular content, insertion finds correctly the previous view, prevNode, nextNode, etc and establishes correct element/textNode order and binding');
// ................................ Reset ................................
$("#result").empty();
model.things = [{thing: "Orig"}]; // reset Prop
// =============================== Arrange ===============================
jsv.templates('
{^{for things}}{{:thing}} {{/for}}
')
.link("#result", model);
// ................................ Act ..................................
jsv.observable(model.things).insert(0, {thing: "First"});
// ............................... Assert .................................
assert.equal($("#result").text(), "FirstOrig",
'Within element only content, insertion maintains correctly prevNode, nextNode, element order and binding on views and tags');
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
});
QUnit.test("JsViews ArrayChange: remove()", function(assert) {
// =============================== Arrange ===============================
jsv.views.tags({
liTag: function() {
return "
Tag ";
}
});
model.things = [{thing: "Orig"}, {thing: "First"}, {thing: "Middle"}, {thing: "Last"}]; // reset Prop
jsv.templates('
{^{liTag/}}{^{for things}}{{:thing}} {^{liTag/}}{{/for}}|after ')
.link("#result", model); // -> TagOrigTagFirstTagMiddleTagLastTag|after
// ................................ Act ..................................
jsv.observable(model.things).remove(1);
// ............................... Assert .................................
assert.equal($("#result").text(), "TagOrigTagMiddleTagLastTag|after",
'Within element only content, remove(1) maintains correctly prevNode, nextNode, element order and binding on views and tags');
// ................................ Act ..................................
jsv.observable(model.things).remove();
// ............................... Assert .................................
assert.equal($("#result").text(), "TagOrigTagMiddleTag|after",
'Within element only content, remove maintains correctly prevNode, nextNode, element order and binding on views and tags');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
model.things = [{thing: "Orig"}, {thing: "First"}, {thing: "Middle"}, {thing: "Last"}]; // reset Prop
jsv.templates('
{^{for things start=0}}{{:thing}} {^{liTag/}}{{/for}}|after ')
.link("#result", model); // -> OrigTagFirstTagMiddleTagLastTag|after
// See https://github.com/BorisMoore/jsviews/issues/442
// ................................ Act ..................................
jsv.observable(model.things).remove(1);
// ............................... Assert .................................
assert.equal($("#result").text(), "OrigTagMiddleTagLastTag|after",
'Within element only content, remove(1) maintains correctly prevNode etc. on views and tags, even when using start=0, and with linked {on} tag');
// ................................ Act ..................................
jsv.observable(model.things).remove();
// ............................... Assert .................................
assert.equal($("#result").text(), "OrigTagMiddleTag|after",
'Within element only content, remove maintains correctly prevNode etc. on views and tags, even when using start=0, and with linked {on} tag');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
model.things = [{thing: "Orig"}, {thing: "First"}, {thing: "Middle"}, {thing: "Last"}]; // reset Prop
jsv.templates("liTmpl", "
{{:thing}} {^{liTag/}}");
jsv.templates('
')
.link("#result", model); // -> OrigTagFirstTagMiddleTagLastTag
// See https://github.com/BorisMoore/jsviews/issues/442
// ................................ Act ..................................
jsv.observable(model.things).remove(1);
// ............................... Assert .................................
assert.equal($("#result").text(), "OrigTagMiddleTagLastTag",
'Within element only content, using data-linked element {for}, remove(1) maintains correctly even when using start=0, and with linked {on} tag');
// ................................ Act ..................................
jsv.observable(model.things).remove();
// ............................... Assert .................................
assert.equal($("#result").text(), "OrigTagMiddleTag",
'Within element only content, using data-linked element {for}, remove maintains correctly even when using start=0, and with linked {on} tag');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
jsv.views.tags({
spanTag: function() {
return "
Tag ";
}
});
model.things = [{thing: "Orig"}, {thing: "First"}, {thing: "Middle"}, {thing: "Last"}]; // reset Prop
jsv.templates('
{^{spanTag/}}{^{for things}}{{:thing}} {^{spanTag/}}{{/for}}|after
')
.link("#result", model);
// ................................ Act ..................................
jsv.observable(model.things).remove(1);
// ............................... Assert .................................
assert.equal($("#result").text(), "TagOrigTagMiddleTagLastTag|after",
'Within regular content, remove(1) finds correctly the previous view, prevNode, nextNode, etc and establishes correct element/textNode order and binding');
// ................................ Act ..................................
jsv.observable(model.things).remove();
// ............................... Assert .................................
assert.equal($("#result").text(), "TagOrigTagMiddleTag|after",
'Within regular content, remove() finds correctly the previous view, prevNode, nextNode, etc and establishes correct element/textNode order and binding');
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
});
QUnit.test("JsViews ArrayChange: move()", function(assert) {
// =============================== Arrange ===============================
jsv.views.tags({
liTag: function() {
return "
Tag ";
}
});
model.things = [{thing: "Orig"}, {thing: "First"}, {thing: "Middle"}, {thing: "Last"}]; // reset Prop
jsv.templates('
{^{liTag/}}{^{for things}}{{:thing}} {^{liTag/}}{{/for}}|after ')
.link("#result", model); // -> TagOrigTagFirstTagMiddleTagLastTag|after
// ................................ Act ..................................
jsv.observable(model.things).move(2, 0, 2);
// ............................... Assert .................................
assert.equal($("#result").text(), "TagMiddleTagLastTagOrigTagFirstTag|after",
'Within element only content, move(2, 0, 2) maintains correctly prevNode, nextNode, element order and binding on views and tags');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
function addResult() {
result += "|" + $("#result").text();
}
var result = "";
model.things = [{thing: "Orig"}, {thing: "First"}, {thing: "Middle"}, {thing: "Last"}];
model.one = false;
model.two = false;
jsv.templates('
{^{if one}}One {{/if}}{^{if two}}Two {{/if}}{^{for things}}{{:thing}} {{/for}} ')
.link("#result", model);
// ................................ Act ..................................
jsv.observable(model.things).move(2, 0, 2);
addResult();
jsv.observable(model).setProperty("one", true);
addResult();
jsv.observable(model).setProperty("one", false);
addResult();
jsv.observable(model).setProperty("one", true);
addResult();
jsv.observable(model).setProperty("two", true);
addResult();
jsv.observable(model).setProperty("two", false);
addResult();
jsv.observable(model).setProperty("two", true);
addResult();
jsv.observable(model).setProperty("one", false);
addResult();
jsv.observable(model).setProperty("two", false);
addResult();
// ............................... Assert .................................
assert.equal(result, "|MiddleLastOrigFirst|OneMiddleLastOrigFirst|MiddleLastOrigFirst|OneMiddleLastOrigFirst|OneTwoMiddleLastOrigFirst|OneMiddleLastOrigFirst|OneTwoMiddleLastOrigFirst|TwoMiddleLastOrigFirst|MiddleLastOrigFirst",
'In element only content with preceding collapsible {{if}} blocks, move(2, 0, 2) maintains correctly prevNode, nextNode, element order and binding on views and tags');
// ................................ Act ..................................
result = ""
jsv.observable(model.things).refresh([{thing: "Orig"}, {thing: "First"}, {thing: "Middle"}, {thing: "Last"}]);
addResult();
jsv.observable(model).setProperty("one", true);
addResult();
jsv.observable(model).setProperty("one", false);
addResult();
jsv.observable(model).setProperty("one", true);
addResult();
jsv.observable(model).setProperty("two", true);
addResult();
jsv.observable(model).setProperty("two", false);
addResult();
jsv.observable(model).setProperty("two", true);
addResult();
jsv.observable(model).setProperty("one", false);
addResult();
jsv.observable(model).setProperty("two", false);
addResult();
// ............................... Assert .................................
assert.equal(result, "|OrigFirstMiddleLast|OneOrigFirstMiddleLast|OrigFirstMiddleLast|OneOrigFirstMiddleLast|OneTwoOrigFirstMiddleLast|OneOrigFirstMiddleLast|OneTwoOrigFirstMiddleLast|TwoOrigFirstMiddleLast|OrigFirstMiddleLast",
'In element only content with preceding collapsible {{if}} blocks, refresh(...) maintains correctly prevNode, nextNode, element order and binding on views and tags');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
result = "";
model.things = [{thing: "Orig"}, {thing: "First"}, {thing: "Middle"}, {thing: "Last"}]; // reset Prop
model.one = [];
model.two = [];
jsv.templates('
{^{for one}}{{:}} {{/for}}{^{for two}}{{:}} {{/for}}{^{for things}}{{:thing}} {{/for}} ')
.link("#result", model);
// ................................ Act ..................................
jsv.observable(model.things).move(2, 0, 2);
addResult();
jsv.observable(model.one).insert("one");
addResult();
jsv.observable(model.one).remove();
addResult();
jsv.observable(model.one).insert("one");
addResult();
jsv.observable(model.two).insert("two");
addResult();
jsv.observable(model.two).remove();
addResult();
jsv.observable(model.two).insert("two");
addResult();
jsv.observable(model.one).remove();
addResult();
jsv.observable(model.two).remove();
addResult();
// ............................... Assert .................................
assert.equal(result, "|MiddleLastOrigFirst|oneMiddleLastOrigFirst|MiddleLastOrigFirst|oneMiddleLastOrigFirst|onetwoMiddleLastOrigFirst|oneMiddleLastOrigFirst|onetwoMiddleLastOrigFirst|twoMiddleLastOrigFirst|MiddleLastOrigFirst",
'In element only content with preceding collapsible {{for}} blocks, move(2, 0, 2) maintains correctly prevNode, nextNode, element order and binding on views and tags');
// ................................ Act ..................................
result = ""
jsv.observable(model.things).refresh([{thing: "Orig"}, {thing: "First"}, {thing: "Middle"}, {thing: "Last"}]);
addResult();
jsv.observable(model.one).insert("one");
addResult();
jsv.observable(model.one).remove();
addResult();
jsv.observable(model.one).insert("one");
addResult();
jsv.observable(model.two).insert("two");
addResult();
jsv.observable(model.two).remove();
addResult();
jsv.observable(model.two).insert("two");
addResult();
jsv.observable(model.one).remove();
addResult();
jsv.observable(model.two).remove();
addResult();
// ............................... Assert .................................
assert.equal(result, "|OrigFirstMiddleLast|oneOrigFirstMiddleLast|OrigFirstMiddleLast|oneOrigFirstMiddleLast|onetwoOrigFirstMiddleLast|oneOrigFirstMiddleLast|onetwoOrigFirstMiddleLast|twoOrigFirstMiddleLast|OrigFirstMiddleLast",
'In element only content with preceding collapsible {{for}} blocks, refresh(...) maintains correctly prevNode, nextNode, element order and binding on views and tags');
// ................................ Reset ................................
$("#result").empty();
// =============================== Arrange ===============================
jsv.views.tags({
spanTag: function() {
return "
Tag ";
}
});
model.things = [{thing: "Orig"}, {thing: "First"}, {thing: "Middle"}, {thing: "Last"}]; // reset Prop
jsv.templates('
{^{spanTag/}}{^{for things}}{{:thing}} {^{spanTag/}}{{/for}}|after
')
.link("#result", model);
// ................................ Act ..................................
jsv.observable(model.things).move(2, 0, 2);
// ............................... Assert .................................
assert.equal($("#result").text(), "TagMiddleTagLastTagOrigTagFirstTag|after",
'Within regular content, move(2, 0, 2) finds correctly the previous view, prevNode, nextNode, etc and establishes correct element/textNode order and binding');
// ................................ Reset ................................
$("#result").empty();
model.things = []; // reset Prop
// =============================== Arrange ===============================
function move(from, to, number) {
jsv.observable(model.items).move(from, to, number);
}
function remove(index, number) {
jsv.observable(model.items).remove(index, number);
}
function insert(index, item) {
jsv.observable(model.items).insert(index, item);
}
function findRed() {
return " at: "
+ $(".wrap").find("span[style]").view().index + "/" +
+ $(".wrap2").find("li[style]").view().index + "/" +
+ $(".wrap3").find("li[style]").view().index;
}
function current() {
var seq = "",
redItemAt;
for (var i=0; i
Start ' +
'{^{for items}}' +
'{^{:#index}} {{:}}|' +
'{{/for}}' +
' End ' +
'Start ' +
'{^{for items}}' +
'{^{if true}}' +
'{^{if true}}' +
'{^{if false}}{{else}}' +
'{^{tag}}' +
' {{:}} |' +
'{{/tag}}' +
'{{/if}}' +
'{{/if}}' +
'{{/if}}' +
'{{/for}}' +
'End
' +
'Start