summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/jquery.simulate.js150
-rw-r--r--slick.grid.js1
-rw-r--r--slick.model.js12
-rw-r--r--tests/dataview.js384
-rw-r--r--tests/dataview/dataview.js886
-rw-r--r--tests/dataview/index.html28
-rw-r--r--tests/grid/grid.js67
-rw-r--r--tests/grid/index.html41
-rw-r--r--tests/index.html30
9 files changed, 1202 insertions, 397 deletions
diff --git a/lib/jquery.simulate.js b/lib/jquery.simulate.js
new file mode 100644
index 0000000..be41246
--- /dev/null
+++ b/lib/jquery.simulate.js
@@ -0,0 +1,150 @@
+/*
+ * jquery.simulate - simulate browser mouse and keyboard events
+ *
+ * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ */
+
+;(function($) {
+
+$.fn.extend({
+ simulate: function(type, options) {
+ return this.each(function() {
+ var opt = $.extend({}, $.simulate.defaults, options || {});
+ new $.simulate(this, type, opt);
+ });
+ }
+});
+
+$.simulate = function(el, type, options) {
+ this.target = el;
+ this.options = options;
+
+ if (/^drag$/.test(type)) {
+ this[type].apply(this, [this.target, options]);
+ } else {
+ this.simulateEvent(el, type, options);
+ }
+}
+
+$.extend($.simulate.prototype, {
+ simulateEvent: function(el, type, options) {
+ var evt = this.createEvent(type, options);
+ this.dispatchEvent(el, type, evt, options);
+ return evt;
+ },
+ createEvent: function(type, options) {
+ if (/^mouse(over|out|down|up|move)|(dbl)?click$/.test(type)) {
+ return this.mouseEvent(type, options);
+ } else if (/^key(up|down|press)$/.test(type)) {
+ return this.keyboardEvent(type, options);
+ }
+ },
+ mouseEvent: function(type, options) {
+ var evt;
+ var e = $.extend({
+ bubbles: true, cancelable: (type != "mousemove"), view: window, detail: 0,
+ screenX: 0, screenY: 0, clientX: 0, clientY: 0,
+ ctrlKey: false, altKey: false, shiftKey: false, metaKey: false,
+ button: 0, relatedTarget: undefined
+ }, options);
+
+ var relatedTarget = $(e.relatedTarget)[0];
+
+ if ($.isFunction(document.createEvent)) {
+ evt = document.createEvent("MouseEvents");
+ evt.initMouseEvent(type, e.bubbles, e.cancelable, e.view, e.detail,
+ e.screenX, e.screenY, e.clientX, e.clientY,
+ e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
+ e.button, e.relatedTarget || document.body.parentNode);
+ } else if (document.createEventObject) {
+ evt = document.createEventObject();
+ $.extend(evt, e);
+ evt.button = { 0:1, 1:4, 2:2 }[evt.button] || evt.button;
+ }
+ return evt;
+ },
+ keyboardEvent: function(type, options) {
+ var evt;
+
+ var e = $.extend({ bubbles: true, cancelable: true, view: window,
+ ctrlKey: false, altKey: false, shiftKey: false, metaKey: false,
+ keyCode: 0, charCode: 0
+ }, options);
+
+ if ($.isFunction(document.createEvent)) {
+ try {
+ evt = document.createEvent("KeyEvents");
+ evt.initKeyEvent(type, e.bubbles, e.cancelable, e.view,
+ e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
+ e.keyCode, e.charCode);
+ } catch(err) {
+ evt = document.createEvent("Events");
+ evt.initEvent(type, e.bubbles, e.cancelable);
+ $.extend(evt, { view: e.view,
+ ctrlKey: e.ctrlKey, altKey: e.altKey, shiftKey: e.shiftKey, metaKey: e.metaKey,
+ keyCode: e.keyCode, charCode: e.charCode
+ });
+ }
+ } else if (document.createEventObject) {
+ evt = document.createEventObject();
+ $.extend(evt, e);
+ }
+ if ($.browser.msie || $.browser.opera) {
+ evt.keyCode = (e.charCode > 0) ? e.charCode : e.keyCode;
+ evt.charCode = undefined;
+ }
+ return evt;
+ },
+
+ dispatchEvent: function(el, type, evt) {
+ if (el.dispatchEvent) {
+ el.dispatchEvent(evt);
+ } else if (el.fireEvent) {
+ el.fireEvent('on' + type, evt);
+ }
+ return evt;
+ },
+
+ drag: function(el) {
+ var self = this, center = this.findCenter(this.target),
+ options = this.options, x = Math.floor(center.x), y = Math.floor(center.y),
+ dx = options.dx || 0, dy = options.dy || 0, target = this.target;
+ var coord = { clientX: x, clientY: y };
+ this.simulateEvent(target, "mousedown", coord);
+ coord = { clientX: x + 1, clientY: y + 1 };
+ this.simulateEvent(document, "mousemove", coord);
+ coord = { clientX: x + dx, clientY: y + dy };
+ this.simulateEvent(document, "mousemove", coord);
+ this.simulateEvent(document, "mousemove", coord);
+ this.simulateEvent(target, "mouseup", coord);
+ },
+ findCenter: function(el) {
+ var el = $(this.target), o = el.offset();
+ return {
+ x: o.left + el.outerWidth() / 2,
+ y: o.top + el.outerHeight() / 2
+ };
+ }
+});
+
+$.extend($.simulate, {
+ defaults: {
+ speed: 'sync'
+ },
+ VK_TAB: 9,
+ VK_ENTER: 13,
+ VK_ESC: 27,
+ VK_PGUP: 33,
+ VK_PGDN: 34,
+ VK_END: 35,
+ VK_HOME: 36,
+ VK_LEFT: 37,
+ VK_UP: 38,
+ VK_RIGHT: 39,
+ VK_DOWN: 40
+});
+
+})(jQuery);
diff --git a/slick.grid.js b/slick.grid.js
index 281e0ed..2790830 100644
--- a/slick.grid.js
+++ b/slick.grid.js
@@ -270,7 +270,6 @@ if (!jQuery.fn.drag) {
var currentEditor = null;
var editController;
-
var rowsCache = {};
var renderedRows = 0;
var numVisibleRows;
diff --git a/slick.model.js b/slick.model.js
index 1bbf8d1..4294677 100644
--- a/slick.model.js
+++ b/slick.model.js
@@ -29,7 +29,7 @@ function EventHelper() {
var self = this;
// private
- var idProperty = "id"; // property holding a unique row id
+ var idProperty = "id"; // property holding a unique row id
var items = []; // data by index
var rows = []; // data by row
var idxById = {}; // indexes by id
@@ -112,10 +112,10 @@ function EventHelper() {
Object.prototype.toString = (typeof field == "function")?field:function() { return this[field] };
// an extra reversal for descending sort keeps the sort stable
// (assuming a stable native sort implementation, which isn't true in some cases)
- if (!ascending) items.reverse();
+ if (ascending === false) items.reverse();
items.sort();
Object.prototype.toString = oldToString;
- if (!ascending) items.reverse();
+ if (ascending === false) items.reverse();
refreshIdxById();
refresh();
}
@@ -150,6 +150,8 @@ function EventHelper() {
}
function updateItem(id, item) {
+ if (idxById[id] === undefined || id !== item[idProperty])
+ throw "Invalid or non-matching id";
items[idxById[id]] = item;
if (!updated) updated = {};
updated[id] = true;
@@ -169,6 +171,8 @@ function EventHelper() {
}
function deleteItem(id) {
+ if (idxById[id] === undefined)
+ throw "Invalid id";
items.splice(idxById[id], 1);
refreshIdxById(); // TODO: optimize
refresh();
@@ -231,7 +235,7 @@ function EventHelper() {
if (totalRowsBefore != totalRows) onPagingInfoChanged.notify(getPagingInfo());
if (countBefore != rows.length) onRowCountChanged.notify({previous:countBefore, current:rows.length});
- if (diff.length > 0 || countBefore != rows.length) onRowsChanged.notify(diff);
+ if (diff.length > 0) onRowsChanged.notify(diff);
}
diff --git a/tests/dataview.js b/tests/dataview.js
deleted file mode 100644
index 9b44166..0000000
--- a/tests/dataview.js
+++ /dev/null
@@ -1,384 +0,0 @@
-
-module("basic");
-
-function assertEmpty(dv) {
- ok(dv.rows !== null, ".rows is not null");
- ok(dv.rows !== undefined, ".rows is not undefined");
- same(0, dv.rows.length, ".rows is initialized to an empty array");
- same(dv.getItems().length, 0, "getItems().length");
- same(undefined, dv.getIdxById("id"), "getIdxById should return undefined if not found");
- same(undefined, dv.getRowById("id"), "getRowById should return undefined if not found");
- same(undefined, dv.getItemById("id"), "getItemById should return undefined if not found");
- same(undefined, dv.getItemByIdx(0), "getItemByIdx should return undefined if not found");
-}
-
-function assertConsistency(dv,idProperty) {
- idProperty = idProperty || "id";
- var items = dv.getItems(),
- filteredOut = 0,
- row,
- id;
-
- for (var i=0; i<items.length; i++) {
- id = items[i][idProperty];
- same(dv.getItemByIdx(i), items[i], "getItemByIdx");
- same(dv.getItemById(id), items[i], "getItemById");
- same(dv.getIdxById(id), i, "getIdxById");
-
- row = dv.getRowById(id);
- if (row === undefined)
- filteredOut++;
- else
- same(dv.rows[row], items[i], "getRowById");
- }
-
- same(items.length-dv.rows.length, filteredOut, "filtered rows");
-}
-
-test("initial setup", function() {
- var dv = new Slick.Data.DataView();
- assertEmpty(dv);
-});
-
-test("initial setup, refresh", function() {
- var dv = new Slick.Data.DataView();
- dv.refresh();
- assertEmpty(dv);
-});
-
-
-module("setItems");
-
-test("empty", function() {
- var dv = new Slick.Data.DataView();
- dv.setItems([]);
- assertEmpty(dv);
-});
-
-test("basic", function() {
- var dv = new Slick.Data.DataView();
- dv.setItems([{id:0},{id:1}]);
- same(2, dv.rows.length, "rows.length");
- same(dv.getItems().length, 2, "getItems().length");
- assertConsistency(dv);
-});
-
-test("alternative idProperty", function() {
- var dv = new Slick.Data.DataView();
- dv.setItems([{uid:0},{uid:1}], "uid");
- assertConsistency(dv,"uid");
-});
-
-test("requires an id on objects", function() {
- var dv = new Slick.Data.DataView();
- try {
- dv.setItems([1,2,3]);
- ok(false, "exception expected")
- }
- catch (ex) {}
-});
-
-test("requires a unique id on objects", function() {
- var dv = new Slick.Data.DataView();
- try {
- dv.setItems([{id:0},{id:0}]);
- ok(false, "exception expected")
- }
- catch (ex) {}
-});
-
-test("requires a unique id on objects (alternative idProperty)", function() {
- var dv = new Slick.Data.DataView();
- try {
- dv.setItems([{uid:0},{uid:0}], "uid");
- ok(false, "exception expected")
- }
- catch (ex) {}
-});
-
-test("events fired on setItems", function() {
- var count = 0;
- var dv = new Slick.Data.DataView();
- dv.onRowsChanged.subscribe(function() {
- ok(true, "onRowsChanged called");
- count++;
- });
- dv.onRowCountChanged.subscribe(function(args) {
- ok(true, "onRowCountChanged called");
- same(args.previous, 0, "previous arg");
- same(args.current, 2, "current arg");
- count++;
- });
- dv.onPagingInfoChanged.subscribe(function(args) {
- ok(true, "onPagingInfoChanged called");
- same(args.pageSize, 0, "pageSize arg");
- same(args.pageNum, 0, "pageNum arg");
- same(args.totalRows, 2, "totalRows arg");
- count++;
- });
- dv.setItems([{id:0},{id:1}]);
- dv.refresh();
- same(3, count, "3 events should have been called");
-});
-
-test("no events on setItems([])", function() {
- var dv = new Slick.Data.DataView();
- dv.onRowsChanged.subscribe(function() { ok(false, "onRowsChanged called") });
- dv.onRowCountChanged.subscribe(function() { ok(false, "onRowCountChanged called") });
- dv.onPagingInfoChanged.subscribe(function() { ok(false, "onPagingInfoChanged called") });
- dv.setItems([]);
- dv.refresh();
-});
-
-test("no events on setItems followed by refresh", function() {
- var dv = new Slick.Data.DataView();
- dv.setItems([{id:0},{id:1}]);
- dv.onRowsChanged.subscribe(function() { ok(false, "onRowsChanged called") });
- dv.onRowCountChanged.subscribe(function() { ok(false, "onRowCountChanged called") });
- dv.onPagingInfoChanged.subscribe(function() { ok(false, "onPagingInfoChanged called") });
- dv.refresh();
-});
-
-test("no refresh while suspended", function() {
- var dv = new Slick.Data.DataView();
- dv.beginUpdate();
- dv.onRowsChanged.subscribe(function() { ok(false, "onRowsChanged called") });
- dv.onRowCountChanged.subscribe(function() { ok(false, "onRowCountChanged called") });
- dv.onPagingInfoChanged.subscribe(function() { ok(false, "onPagingInfoChanged called") });
- dv.setItems([{id:0},{id:1}]);
- dv.setFilter(function(o) { return true });
- dv.refresh();
- same(dv.rows.length, 0, "rows aren't updated until resumed");
-});
-
-test("refresh fires after resume", function() {
- var dv = new Slick.Data.DataView();
- dv.beginUpdate();
- dv.setItems([{id:0},{id:1}]);
- same(dv.getItems().length, 2, "items updated immediately");
- dv.setFilter(function(o) { return true });
- dv.refresh();
-
- var count = 0;
- dv.onRowsChanged.subscribe(function(args) {
- ok(true, "onRowsChanged called");
- same(args, [0,1], "args");
- count++;
- });
- dv.onRowCountChanged.subscribe(function(args) {
- ok(true, "onRowCountChanged called");
- same(args.previous, 0, "previous arg");
- same(args.current, 2, "current arg");
- count++;
- });
- dv.onPagingInfoChanged.subscribe(function(args) {
- ok(true, "onPagingInfoChanged called");
- same(args.pageSize, 0, "pageSize arg");
- same(args.pageNum, 0, "pageNum arg");
- same(args.totalRows, 2, "totalRows arg");
- count++;
- });
- dv.endUpdate();
- same(count, 3, "events fired");
- same(dv.getItems().length, 2, "items are the same");
- same(dv.rows.length, 2, "rows updated");
-});
-
-module("sort");
-
-test("happy path", function() {
- var count = 0;
- var items = [{id:2,val:2},{id:1,val:1},{id:0,val:0}];
- var dv = new Slick.Data.DataView();
- dv.setItems(items);
- dv.onRowsChanged.subscribe(function() {
- ok(true, "onRowsChanged called");
- count++;
- });
- dv.onRowCountChanged.subscribe(function() { ok(false, "onRowCountChanged called") });
- dv.onPagingInfoChanged.subscribe(function() { ok(false, "onPagingInfoChanged called") });
- dv.sort(function(x,y) { return x.val-y.val }, true);
- same(count, 1, "events fired");
- same(dv.getItems(), items, "original array should get sorted");
- same(items, [{id:0,val:0},{id:1,val:1},{id:2,val:2}], "sort order");
- assertConsistency(dv);
-});
-
-test("asc by default", function() {
- var items = [{id:2,val:2},{id:1,val:1},{id:0,val:0}];
- var dv = new Slick.Data.DataView();
- dv.setItems(items);
- dv.sort(function(x,y) { return x.val-y.val });
- same(items, [{id:0,val:0},{id:1,val:1},{id:2,val:2}], "sort order");
-});
-
-test("desc", function() {
- var items = [{id:0,val:0},{id:2,val:2},{id:1,val:1}];
- var dv = new Slick.Data.DataView();
- dv.setItems(items);
- dv.sort(function(x,y) { return x.val-y.val }, false);
- same(items, [{id:2,val:2},{id:1,val:1},{id:0,val:0}], "sort order");
-});
-
-test("sort is stable", function() {
- var items = [{id:0,val:0},{id:2,val:2},{id:3,val:2},{id:1,val:1}];
- var dv = new Slick.Data.DataView();
- dv.setItems(items);
-
- dv.sort(function(x,y) { return x.val-y.val });
- same(items, [{id:0,val:0},{id:1,val:1},{id:2,val:2},{id:3,val:2}], "sort order");
-
- dv.sort(function(x,y) { return x.val-y.val }, false);
- same(items, [{id:2,val:2},{id:3,val:2},{id:1,val:1},{id:0,val:0}], "sort order");
-
-});
-
-
-module("filtering");
-
-test("applied immediately", function() {
- var count = 0;
- var dv = new Slick.Data.DataView();
- dv.setItems([{id:0,val:0},{id:1,val:1},{id:2,val:2}]);
- dv.onRowsChanged.subscribe(function(args) {
- ok(true, "onRowsChanged called");
- same(args, [0], "args");
- count++;
- });
- dv.onRowCountChanged.subscribe(function(args) {
- ok(true, "onRowCountChanged called");
- same(args.previous, 3, "previous arg");
- same(args.current, 1, "current arg");
- count++;
- });
- dv.onPagingInfoChanged.subscribe(function(args) {
- ok(true, "onPagingInfoChanged called");
- same(args.pageSize, 0, "pageSize arg");
- same(args.pageNum, 0, "pageNum arg");
- same(args.totalRows, 1, "totalRows arg");
- count++;
- });
- dv.setFilter(function(o) { return o.val === 1 });
- same(count, 3, "events fired");
- same(dv.getItems().length, 3, "original data is still there");
- same(dv.rows.length, 1, "rows are filtered");
- assertConsistency(dv);
-});
-
-test("re-applied on refresh", function() {
- var count = 0;
- var filterVal = 0;
- var dv = new Slick.Data.DataView();
- dv.setItems([{id:0,val:0},{id:1,val:1},{id:2,val:2}]);
- dv.setFilter(function(o) { return o.val >= filterVal });
- same(dv.rows.length, 3, "nothing is filtered out");
- assertConsistency(dv);
-
- dv.onRowsChanged.subscribe(function(args) {
- ok(true, "onRowsChanged called");
- same(args, [0], "args");
- count++;
- });
- dv.onRowCountChanged.subscribe(function(args) {
- ok(true, "onRowCountChanged called");
- same(args.previous, 3, "previous arg");
- same(args.current, 1, "current arg");
- count++;
- });
- dv.onPagingInfoChanged.subscribe(function(args) {
- ok(true, "onPagingInfoChanged called");
- same(args.pageSize, 0, "pageSize arg");
- same(args.pageNum, 0, "pageNum arg");
- same(args.totalRows, 1, "totalRows arg");
- count++;
- });
- filterVal = 2;
- dv.refresh();
- same(count, 3, "events fired");
- same(dv.getItems().length, 3, "original data is still there");
- same(dv.rows.length, 1, "rows are filtered");
- assertConsistency(dv);
-});
-
-test("re-applied on sort", function() {
- var dv = new Slick.Data.DataView();
- dv.setItems([{id:0,val:0},{id:1,val:1},{id:2,val:2}]);
- dv.setFilter(function(o) { return o.val === 1 });
- same(dv.rows.length, 1, "one row is remaining");
-
- dv.onRowsChanged.subscribe(function() { ok(false, "onRowsChanged called") });
- dv.onRowCountChanged.subscribe(function() { ok(false, "onRowCountChanged called") });
- dv.onPagingInfoChanged.subscribe(function() { ok(false, "onPagingInfoChanged called") });
- dv.sort(function(x,y) { return x.val-y.val }, false);
- same(dv.getItems().length, 3, "original data is still there");
- same(dv.rows.length, 1, "rows are filtered");
- assertConsistency(dv);
-});
-
-test("all", function() {
- var count = 0;
- var dv = new Slick.Data.DataView();
- dv.setItems([{id:0,val:0},{id:1,val:1},{id:2,val:2}]);
- dv.onRowsChanged.subscribe(function(args) {
- ok(true, "onRowsChanged called");
- same(args, [], "args");
- count++;
- });
- dv.onRowCountChanged.subscribe(function(args) {
- ok(true, "onRowCountChanged called");
- same(args.previous, 3, "previous arg");
- same(args.current, 0, "current arg");
- count++;
- });
- dv.onPagingInfoChanged.subscribe(function(args) {
- ok(true, "onPagingInfoChanged called");
- same(args.pageSize, 0, "pageSize arg");
- same(args.pageNum, 0, "pageNum arg");
- same(args.totalRows, 0, "totalRows arg");
- count++;
- });
- dv.setFilter(function(o) { return false });
- same(count, 3, "events fired");
- same(dv.getItems().length, 3, "original data is still there");
- same(dv.rows.length, 0, "rows are filtered");
- assertConsistency(dv);
-});
-
-test("all then none", function() {
- var filterResult = false;
- var count = 0;
- var dv = new Slick.Data.DataView();
- dv.setItems([{id:0,val:0},{id:1,val:1},{id:2,val:2}]);
- dv.setFilter(function(o) { return filterResult });
- same(dv.rows.length, 0, "all rows are filtered out");
-
- dv.onRowsChanged.subscribe(function(args) {
- ok(true, "onRowsChanged called");
- same(args, [0,1,2], "args");
- count++;
- });
- dv.onRowCountChanged.subscribe(function(args) {
- ok(true, "onRowCountChanged called");
- same(args.previous, 0, "previous arg");
- same(args.current, 3, "current arg");
- count++;
- });
- dv.onPagingInfoChanged.subscribe(function(args) {
- ok(true, "onPagingInfoChanged called");
- same(args.pageSize, 0, "pageSize arg");
- same(args.pageNum, 0, "pageNum arg");
- same(args.totalRows, 3, "totalRows arg");
- count++;
- });
- filterResult = true;
- dv.refresh();
- same(count, 3, "events fired");
- same(dv.getItems().length, 3, "original data is still there");
- same(dv.rows.length, 3, "all rows are back");
- assertConsistency(dv);
-});
-
-// TODO: paging
-// TODO: fast sort
-// TODO: events on refresh on boundary conditions
-// TODO: add/insert/update/delete \ No newline at end of file
diff --git a/tests/dataview/dataview.js b/tests/dataview/dataview.js
new file mode 100644
index 0000000..a5b56ac
--- /dev/null
+++ b/tests/dataview/dataview.js
@@ -0,0 +1,886 @@
+
+module("basic");
+
+function assertEmpty(dv) {
+ ok(dv.rows !== null, ".rows is not null");
+ ok(dv.rows !== undefined, ".rows is not undefined");
+ same(0, dv.rows.length, ".rows is initialized to an empty array");
+ same(dv.getItems().length, 0, "getItems().length");
+ same(undefined, dv.getIdxById("id"), "getIdxById should return undefined if not found");
+ same(undefined, dv.getRowById("id"), "getRowById should return undefined if not found");
+ same(undefined, dv.getItemById("id"), "getItemById should return undefined if not found");
+ same(undefined, dv.getItemByIdx(0), "getItemByIdx should return undefined if not found");
+}
+
+function assertConsistency(dv,idProperty) {
+ idProperty = idProperty || "id";
+ var items = dv.getItems(),
+ filteredOut = 0,
+ row,
+ id;
+
+ for (var i=0; i<items.length; i++) {
+ id = items[i][idProperty];
+ same(dv.getItemByIdx(i), items[i], "getItemByIdx");
+ same(dv.getItemById(id), items[i], "getItemById");
+ same(dv.getIdxById(id), i, "getIdxById");
+
+ row = dv.getRowById(id);
+ if (row === undefined)
+ filteredOut++;
+ else
+ same(dv.rows[row], items[i], "getRowById");
+ }
+
+ same(items.length-dv.rows.length, filteredOut, "filtered rows");
+}
+
+test("initial setup", function() {
+ var dv = new Slick.Data.DataView();
+ assertEmpty(dv);
+});
+
+test("initial setup, refresh", function() {
+ var dv = new Slick.Data.DataView();
+ dv.refresh();
+ assertEmpty(dv);
+});
+
+
+module("setItems");
+
+test("empty", function() {
+ var dv = new Slick.Data.DataView();
+ dv.setItems([]);
+ assertEmpty(dv);
+});
+
+test("basic", function() {
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{id:0},{id:1}]);
+ same(2, dv.rows.length, "rows.length");
+ same(dv.getItems().length, 2, "getItems().length");
+ assertConsistency(dv);
+});
+
+test("alternative idProperty", function() {
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{uid:0},{uid:1}], "uid");
+ assertConsistency(dv,"uid");
+});
+
+test("requires an id on objects", function() {
+ var dv = new Slick.Data.DataView();
+ try {
+ dv.setItems([1,2,3]);
+ ok(false, "exception expected")
+ }
+ catch (ex) {}
+});
+
+test("requires a unique id on objects", function() {
+ var dv = new Slick.Data.DataView();
+ try {
+ dv.setItems([{id:0},{id:0}]);
+ ok(false, "exception expected")
+ }
+ catch (ex) {}
+});
+
+test("requires a unique id on objects (alternative idProperty)", function() {
+ var dv = new Slick.Data.DataView();
+ try {
+ dv.setItems([{uid:0},{uid:0}], "uid");
+ ok(false, "exception expected")
+ }
+ catch (ex) {}
+});
+
+test("events fired on setItems", function() {
+ var count = 0;
+ var dv = new Slick.Data.DataView();
+ dv.onRowsChanged.subscribe(function() {
+ ok(true, "onRowsChanged called");
+ count++;
+ });
+ dv.onRowCountChanged.subscribe(function(args) {
+ ok(true, "onRowCountChanged called");
+ same(args.previous, 0, "previous arg");
+ same(args.current, 2, "current arg");
+ count++;
+ });
+ dv.onPagingInfoChanged.subscribe(function(args) {
+ ok(true, "onPagingInfoChanged called");
+ same(args.pageSize, 0, "pageSize arg");
+ same(args.pageNum, 0, "pageNum arg");
+ same(args.totalRows, 2, "totalRows arg");
+ count++;
+ });
+ dv.setItems([{id:0},{id:1}]);
+ dv.refresh();
+ same(3, count, "3 events should have been called");
+});
+
+test("no events on setItems([])", function() {
+ var dv = new Slick.Data.DataView();
+ dv.onRowsChanged.subscribe(function() { ok(false, "onRowsChanged called") });
+ dv.onRowCountChanged.subscribe(function() { ok(false, "onRowCountChanged called") });
+ dv.onPagingInfoChanged.subscribe(function() { ok(false, "onPagingInfoChanged called") });
+ dv.setItems([]);
+ dv.refresh();
+});
+
+test("no events on setItems followed by refresh", function() {
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{id:0},{id:1}]);
+ dv.onRowsChanged.subscribe(function() { ok(false, "onRowsChanged called") });
+ dv.onRowCountChanged.subscribe(function() { ok(false, "onRowCountChanged called") });
+ dv.onPagingInfoChanged.subscribe(function() { ok(false, "onPagingInfoChanged called") });
+ dv.refresh();
+});
+
+test("no refresh while suspended", function() {
+ var dv = new Slick.Data.DataView();
+ dv.beginUpdate();
+ dv.onRowsChanged.subscribe(function() { ok(false, "onRowsChanged called") });
+ dv.onRowCountChanged.subscribe(function() { ok(false, "onRowCountChanged called") });
+ dv.onPagingInfoChanged.subscribe(function() { ok(false, "onPagingInfoChanged called") });
+ dv.setItems([{id:0},{id:1}]);
+ dv.setFilter(function(o) { return true });
+ dv.refresh();
+ same(dv.rows.length, 0, "rows aren't updated until resumed");
+});
+
+test("refresh fires after resume", function() {
+ var dv = new Slick.Data.DataView();
+ dv.beginUpdate();
+ dv.setItems([{id:0},{id:1}]);
+ same(dv.getItems().length, 2, "items updated immediately");
+ dv.setFilter(function(o) { return true });
+ dv.refresh();
+
+ var count = 0;
+ dv.onRowsChanged.subscribe(function(args) {
+ ok(true, "onRowsChanged called");
+ same(args, [0,1], "args");
+ count++;
+ });
+ dv.onRowCountChanged.subscribe(function(args) {
+ ok(true, "onRowCountChanged called");
+ same(args.previous, 0, "previous arg");
+ same(args.current, 2, "current arg");
+ count++;
+ });
+ dv.onPagingInfoChanged.subscribe(function(args) {
+ ok(true, "onPagingInfoChanged called");
+ same(args.pageSize, 0, "pageSize arg");
+ same(args.pageNum, 0, "pageNum arg");
+ same(args.totalRows, 2, "totalRows arg");
+ count++;
+ });
+ dv.endUpdate();
+ equal(count, 3, "events fired");
+ same(dv.getItems().length, 2, "items are the same");
+ same(dv.rows.length, 2, "rows updated");
+});
+
+module("sort");
+
+test("happy path", function() {
+ var count = 0;
+ var items = [{id:2,val:2},{id:1,val:1},{id:0,val:0}];
+ var dv = new Slick.Data.DataView();
+ dv.setItems(items);
+ dv.onRowsChanged.subscribe(function() {
+ ok(true, "onRowsChanged called");
+ count++;
+ });
+ dv.onRowCountChanged.subscribe(function() { ok(false, "onRowCountChanged called") });
+ dv.onPagingInfoChanged.subscribe(function() { ok(false, "onPagingInfoChanged called") });
+ dv.sort(function(x,y) { return x.val-y.val }, true);
+ equal(count, 1, "events fired");
+ same(dv.getItems(), items, "original array should get sorted");
+ same(items, [{id:0,val:0},{id:1,val:1},{id:2,val:2}], "sort order");
+ assertConsistency(dv);
+});
+
+test("asc by default", function() {
+ var items = [{id:2,val:2},{id:1,val:1},{id:0,val:0}];
+ var dv = new Slick.Data.DataView();
+ dv.setItems(items);
+ dv.sort(function(x,y) { return x.val-y.val });
+ same(items, [{id:0,val:0},{id:1,val:1},{id:2,val:2}], "sort order");
+});
+
+test("desc", function() {
+ var items = [{id:0,val:0},{id:2,val:2},{id:1,val:1}];
+ var dv = new Slick.Data.DataView();
+ dv.setItems(items);
+ dv.sort(function(x,y) { return x.val-y.val }, false);
+ same(items, [{id:2,val:2},{id:1,val:1},{id:0,val:0}], "sort order");
+});
+
+test("sort is stable", function() {
+ var items = [{id:0,val:0},{id:2,val:2},{id:3,val:2},{id:1,val:1}];
+ var dv = new Slick.Data.DataView();
+ dv.setItems(items);
+
+ dv.sort(function(x,y) { return x.val-y.val });
+ same(items, [{id:0,val:0},{id:1,val:1},{id:2,val:2},{id:3,val:2}], "sort order");
+
+ dv.sort(function(x,y) { return x.val-y.val });
+ same(items, [{id:0,val:0},{id:1,val:1},{id:2,val:2},{id:3,val:2}], "sorting on the same column again doesn't change the order");
+
+ dv.sort(function(x,y) { return x.val-y.val }, false);
+ same(items, [{id:2,val:2},{id:3,val:2},{id:1,val:1},{id:0,val:0}], "sort order");
+});
+
+
+
+module("fastSort");
+
+test("happy path", function() {
+ var count = 0;
+ var items = [{id:2,val:2},{id:1,val:1},{id:0,val:0}];
+ var dv = new Slick.Data.DataView();
+ dv.setItems(items);
+ dv.onRowsChanged.subscribe(function() {
+ ok(true, "onRowsChanged called");
+ count++;
+ });
+ dv.onRowCountChanged.subscribe(function() { ok(false, "onRowCountChanged called") });
+ dv.onPagingInfoChanged.subscribe(function() { ok(false, "onPagingInfoChanged called") });
+ dv.fastSort("val", true);
+ equal(count, 1, "events fired");
+ same(dv.getItems(), items, "original array should get sorted");
+ same(items, [{id:0,val:0},{id:1,val:1},{id:2,val:2}], "sort order");
+ assertConsistency(dv);
+});
+
+test("asc by default", function() {
+ var items = [{id:2,val:2},{id:1,val:1},{id:0,val:0}];
+ var dv = new Slick.Data.DataView();
+ dv.setItems(items);
+ dv.fastSort("val");
+ same(items, [{id:0,val:0},{id:1,val:1},{id:2,val:2}], "sort order");
+});
+
+test("desc", function() {
+ var items = [{id:0,val:0},{id:2,val:2},{id:1,val:1}];
+ var dv = new Slick.Data.DataView();
+ dv.setItems(items);
+ dv.fastSort("val", false);
+ same(items, [{id:2,val:2},{id:1,val:1},{id:0,val:0}], "sort order");
+});
+
+test("sort is stable", function() {
+ var items = [{id:0,val:0},{id:2,val:2},{id:3,val:2},{id:1,val:1}];
+ var dv = new Slick.Data.DataView();
+ dv.setItems(items);
+
+ dv.fastSort("val");
+ same(items, [{id:0,val:0},{id:1,val:1},{id:2,val:2},{id:3,val:2}], "sort order");
+
+ dv.fastSort("val");
+ same(items, [{id:0,val:0},{id:1,val:1},{id:2,val:2},{id:3,val:2}], "sorting on the same column again doesn't change the order");
+
+ dv.fastSort("val", false);
+ same(items, [{id:2,val:2},{id:3,val:2},{id:1,val:1},{id:0,val:0}], "sort order");
+});
+
+test("w/ function param", function() {
+ var count = 0;
+ var items = [{id:2,val:2},{id:1,val:10},{id:0,val:0}];
+ var dv = new Slick.Data.DataView();
+ dv.setItems(items);
+ dv.onRowsChanged.subscribe(function() {
+ ok(true, "onRowsChanged called");
+ count++;
+ });
+ dv.onRowCountChanged.subscribe(function() { ok(false, "onRowCountChanged called") });
+ dv.onPagingInfoChanged.subscribe(function() { ok(false, "onPagingInfoChanged called") });
+ var numericValueFn = function() {
+ var val = this["val"];
+ if (val < 10)
+ return "00" + val;
+ else if (val < 100)
+ return "0" + val;
+ else
+ return val;
+ };
+ dv.fastSort(numericValueFn, true);
+ equal(count, 1, "events fired");
+ same(dv.getItems(), items, "original array should get sorted");
+ same(items, [{id:0,val:0},{id:2,val:2},{id:1,val:10}], "sort order");
+ assertConsistency(dv);
+});
+
+module("filtering");
+
+test("applied immediately", function() {
+ var count = 0;
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{id:0,val:0},{id:1,val:1},{id:2,val:2}]);
+ dv.onRowsChanged.subscribe(function(args) {
+ ok(true, "onRowsChanged called");
+ same(args, [0], "args");
+ count++;
+ });
+ dv.onRowCountChanged.subscribe(function(args) {
+ ok(true, "onRowCountChanged called");
+ same(args.previous, 3, "previous arg");
+ same(args.current, 1, "current arg");
+ count++;
+ });
+ dv.onPagingInfoChanged.subscribe(function(args) {
+ ok(true, "onPagingInfoChanged called");
+ same(args.pageSize, 0, "pageSize arg");
+ same(args.pageNum, 0, "pageNum arg");
+ same(args.totalRows, 1, "totalRows arg");
+ count++;
+ });
+ dv.setFilter(function(o) { return o.val === 1 });
+ equal(count, 3, "events fired");
+ same(dv.getItems().length, 3, "original data is still there");
+ same(dv.rows.length, 1, "rows are filtered");
+ assertConsistency(dv);
+});
+
+test("re-applied on refresh", function() {
+ var count = 0;
+ var filterVal = 0;
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{id:0,val:0},{id:1,val:1},{id:2,val:2}]);
+ dv.setFilter(function(o) { return o.val >= filterVal });
+ same(dv.rows.length, 3, "nothing is filtered out");
+ assertConsistency(dv);
+
+ dv.onRowsChanged.subscribe(function(args) {
+ ok(true, "onRowsChanged called");
+ same(args, [0], "args");
+ count++;
+ });
+ dv.onRowCountChanged.subscribe(function(args) {
+ ok(true, "onRowCountChanged called");
+ same(args.previous, 3, "previous arg");
+ same(args.current, 1, "current arg");
+ count++;
+ });
+ dv.onPagingInfoChanged.subscribe(function(args) {
+ ok(true, "onPagingInfoChanged called");
+ same(args.pageSize, 0, "pageSize arg");
+ same(args.pageNum, 0, "pageNum arg");
+ same(args.totalRows, 1, "totalRows arg");
+ count++;
+ });
+ filterVal = 2;
+ dv.refresh();
+ equal(count, 3, "events fired");
+ same(dv.getItems().length, 3, "original data is still there");
+ same(dv.rows.length, 1, "rows are filtered");
+ assertConsistency(dv);
+});
+
+test("re-applied on sort", function() {
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{id:0,val:0},{id:1,val:1},{id:2,val:2}]);
+ dv.setFilter(function(o) { return o.val === 1 });
+ same(dv.rows.length, 1, "one row is remaining");
+
+ dv.onRowsChanged.subscribe(function() { ok(false, "onRowsChanged called") });
+ dv.onRowCountChanged.subscribe(function() { ok(false, "onRowCountChanged called") });
+ dv.onPagingInfoChanged.subscribe(function() { ok(false, "onPagingInfoChanged called") });
+ dv.sort(function(x,y) { return x.val-y.val }, false);
+ same(dv.getItems().length, 3, "original data is still there");
+ same(dv.rows.length, 1, "rows are filtered");
+ assertConsistency(dv);
+});
+
+test("all", function() {
+ var count = 0;
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{id:0,val:0},{id:1,val:1},{id:2,val:2}]);
+ dv.onRowsChanged.subscribe(function(args) {
+ ok(false, "onRowsChanged called");
+ });
+ dv.onRowCountChanged.subscribe(function(args) {
+ ok(true, "onRowCountChanged called");
+ same(args.previous, 3, "previous arg");
+ same(args.current, 0, "current arg");
+ count++;
+ });
+ dv.onPagingInfoChanged.subscribe(function(args) {
+ ok(true, "onPagingInfoChanged called");
+ same(args.pageSize, 0, "pageSize arg");
+ same(args.pageNum, 0, "pageNum arg");
+ same(args.totalRows, 0, "totalRows arg");
+ count++;
+ });
+ dv.setFilter(function(o) { return false });
+ equal(count, 2, "events fired");
+ same(dv.getItems().length, 3, "original data is still there");
+ same(dv.rows.length, 0, "rows are filtered");
+ assertConsistency(dv);
+});
+
+test("all then none", function() {
+ var filterResult = false;
+ var count = 0;
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{id:0,val:0},{id:1,val:1},{id:2,val:2}]);
+ dv.setFilter(function(o) { return filterResult });
+ same(dv.rows.length, 0, "all rows are filtered out");
+
+ dv.onRowsChanged.subscribe(function(args) {
+ ok(true, "onRowsChanged called");
+ same(args, [0,1,2], "args");
+ count++;
+ });
+ dv.onRowCountChanged.subscribe(function(args) {
+ ok(true, "onRowCountChanged called");
+ same(args.previous, 0, "previous arg");
+ same(args.current, 3, "current arg");
+ count++;
+ });
+ dv.onPagingInfoChanged.subscribe(function(args) {
+ ok(true, "onPagingInfoChanged called");
+ same(args.pageSize, 0, "pageSize arg");
+ same(args.pageNum, 0, "pageNum arg");
+ same(args.totalRows, 3, "totalRows arg");
+ count++;
+ });
+ filterResult = true;
+ dv.refresh();
+ equal(count, 3, "events fired");
+ same(dv.getItems().length, 3, "original data is still there");
+ same(dv.rows.length, 3, "all rows are back");
+ assertConsistency(dv);
+});
+
+module("updateItem");
+
+test("basic", function() {
+ var count = 0;
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{id:0,val:0},{id:1,val:1},{id:2,val:2}]);
+
+ dv.onRowsChanged.subscribe(function(args) {
+ ok(true, "onRowsChanged called");
+ same(args, [1], "args");
+ count++;
+ });
+ dv.onRowCountChanged.subscribe(function(args) {
+ ok(false, "onRowCountChanged called");
+ });
+ dv.onPagingInfoChanged.subscribe(function(args) {
+ ok(false, "onPagingInfoChanged called");
+ });
+
+ dv.updateItem(1,{id:1,val:1337});
+ equal(count, 1, "events fired");
+ same(dv.rows[1], {id:1,val:1337}, "item updated");
+ assertConsistency(dv);
+});
+
+test("updating an item not passing the filter", function() {
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{id:0,val:0},{id:1,val:1},{id:2,val:2},{id:3,val:1337}]);
+ dv.setFilter(function(o) { return o["val"] !== 1337 });
+ dv.onRowsChanged.subscribe(function(args) {
+ ok(false, "onRowsChanged called");
+ });
+ dv.onRowCountChanged.subscribe(function(args) {
+ ok(false, "onRowCountChanged called");
+ });
+ dv.onPagingInfoChanged.subscribe(function(args) {
+ ok(false, "onPagingInfoChanged called");
+ });
+ dv.updateItem(3,{id:3,val:1337});
+ same(dv.getItems()[3], {id:3,val:1337}, "item updated");
+ assertConsistency(dv);
+});
+
+test("updating an item to pass the filter", function() {
+ var count = 0;
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{id:0,val:0},{id:1,val:1},{id:2,val:2},{id:3,val:1337}]);
+ dv.setFilter(function(o) { return o["val"] !== 1337 });
+ dv.onRowsChanged.subscribe(function(args) {
+ ok(true, "onRowsChanged called");
+ same(args, [3], "args");
+ count++;
+ });
+ dv.onRowCountChanged.subscribe(function(args) {
+ ok(true, "onRowCountChanged called");
+ equal(args.previous, 3, "previous arg");
+ equal(args.current, 4, "current arg");
+ count++;
+ });
+ dv.onPagingInfoChanged.subscribe(function(args) {
+ ok(true, "onPagingInfoChanged called");
+ same(args.pageSize, 0, "pageSize arg");
+ same(args.pageNum, 0, "pageNum arg");
+ same(args.totalRows, 4, "totalRows arg");
+ count++;
+ });
+ dv.updateItem(3,{id:3,val:3});
+ equal(count, 3, "events fired");
+ same(dv.getItems()[3], {id:3,val:3}, "item updated");
+ assertConsistency(dv);
+});
+
+test("updating an item to not pass the filter", function() {
+ var count = 0;
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{id:0,val:0},{id:1,val:1},{id:2,val:2},{id:3,val:3}]);
+ dv.setFilter(function(o) { return o["val"] !== 1337 });
+ dv.onRowsChanged.subscribe(function(args) {
+ console.log(args)
+ ok(false, "onRowsChanged called");
+ });
+ dv.onRowCountChanged.subscribe(function(args) {
+ ok(true, "onRowCountChanged called");
+ equal(args.previous, 4, "previous arg");
+ equal(args.current, 3, "current arg");
+ count++;
+ });
+ dv.onPagingInfoChanged.subscribe(function(args) {
+ ok(true, "onPagingInfoChanged called");
+ same(args.pageSize, 0, "pageSize arg");
+ same(args.pageNum, 0, "pageNum arg");
+ same(args.totalRows, 3, "totalRows arg");
+ count++;
+ });
+ dv.updateItem(3,{id:3,val:1337});
+ equal(count, 2, "events fired");
+ same(dv.getItems()[3], {id:3,val:1337}, "item updated");
+ assertConsistency(dv);
+});
+
+
+module("addItem");
+
+test("must have id", function() {
+ var count = 0;
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{id:0,val:0},{id:1,val:1},{id:2,val:2}]);
+ try {
+ dv.addItem({val:1337});
+ ok(false, "exception thrown");
+ }
+ catch (ex) {}
+});
+
+test("must have id (custom)", function() {
+ var count = 0;
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{uid:0,val:0},{uid:1,val:1},{uid:2,val:2}], "uid");
+ try {
+ dv.addItem({id:3,val:1337});
+ ok(false, "exception thrown");
+ }
+ catch (ex) {}
+});
+
+test("basic", function() {
+ var count = 0;
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{id:0,val:0},{id:1,val:1},{id:2,val:2}]);
+ dv.onRowsChanged.subscribe(function(args) {
+ ok(true, "onRowsChanged called");
+ same(args, [3], "args");
+ count++;
+ });
+ dv.onRowCountChanged.subscribe(function(args) {
+ ok(true, "onRowCountChanged called");
+ equal(args.previous, 3, "previous arg");
+ equal(args.current, 4, "current arg");
+ count++;
+ });
+ dv.onPagingInfoChanged.subscribe(function(args) {
+ ok(true, "onPagingInfoChanged called");
+ equal(args.pageSize, 0, "pageSize arg");
+ equal(args.pageNum, 0, "pageNum arg");
+ equal(args.totalRows, 4, "totalRows arg");
+ count++;
+ });
+ dv.addItem({id:3,val:1337});
+ equal(count, 3, "events fired");
+ same(dv.getItems()[3], {id:3,val:1337}, "item updated");
+ same(dv.rows[3], {id:3,val:1337}, "item updated");
+ assertConsistency(dv);
+});
+
+test("add an item not passing the filter", function() {
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{id:0,val:0},{id:1,val:1},{id:2,val:2}]);
+ dv.setFilter(function(o) { return o["val"] !== 1337 });
+ dv.onRowsChanged.subscribe(function(args) {
+ ok(false, "onRowsChanged called");
+ });
+ dv.onRowCountChanged.subscribe(function(args) {
+ ok(false, "onRowCountChanged called");
+ });
+ dv.onPagingInfoChanged.subscribe(function(args) {
+ ok(false, "onPagingInfoChanged called");
+ });
+ dv.addItem({id:3,val:1337});
+ same(dv.getItems()[3], {id:3,val:1337}, "item updated");
+ assertConsistency(dv);
+});
+
+module("insertItem");
+
+test("must have id", function() {
+ var count = 0;
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{id:0,val:0},{id:1,val:1},{id:2,val:2}]);
+ try {
+ dv.insertItem(0,{val:1337});
+ ok(false, "exception thrown");
+ }
+ catch (ex) {}
+});
+
+test("must have id (custom)", function() {
+ var count = 0;
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{uid:0,val:0},{uid:1,val:1},{uid:2,val:2}], "uid");
+ try {
+ dv.insertItem(0,{id:3,val:1337});
+ ok(false, "exception thrown");
+ }
+ catch (ex) {}
+});
+
+test("insert at the beginning", function() {
+ var count = 0;
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{id:0,val:0},{id:1,val:1},{id:2,val:2}]);
+ dv.onRowsChanged.subscribe(function(args) {
+ ok(true, "onRowsChanged called");
+ same(args, [0,1,2,3], "args");
+ count++;
+ });
+ dv.onRowCountChanged.subscribe(function(args) {
+ ok(true, "onRowCountChanged called");
+ equal(args.previous, 3, "previous arg");
+ equal(args.current, 4, "current arg");
+ count++;
+ });
+ dv.onPagingInfoChanged.subscribe(function(args) {
+ ok(true, "onPagingInfoChanged called");
+ equal(args.pageSize, 0, "pageSize arg");
+ equal(args.pageNum, 0, "pageNum arg");
+ equal(args.totalRows, 4, "totalRows arg");
+ count++;
+ });
+ dv.insertItem(0, {id:3,val:1337});
+ equal(count, 3, "events fired");
+ same(dv.rows[0], {id:3,val:1337}, "item updated");
+ equal(dv.getItems().length, 4, "items updated");
+ equal(dv.rows.length, 4, "rows updated");
+ assertConsistency(dv);
+});
+
+test("insert in the middle", function() {
+ var count = 0;
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{id:0,val:0},{id:1,val:1},{id:2,val:2}]);
+ dv.onRowsChanged.subscribe(function(args) {
+ ok(true, "onRowsChanged called");
+ same(args, [2,3], "args");
+ count++;
+ });
+ dv.onRowCountChanged.subscribe(function(args) {
+ ok(true, "onRowCountChanged called");
+ equal(args.previous, 3, "previous arg");
+ equal(args.current, 4, "current arg");
+ count++;
+ });
+ dv.onPagingInfoChanged.subscribe(function(args) {
+ ok(true, "onPagingInfoChanged called");
+ equal(args.pageSize, 0, "pageSize arg");
+ equal(args.pageNum, 0, "pageNum arg");
+ equal(args.totalRows, 4, "totalRows arg");
+ count++;
+ });
+ dv.insertItem(2,{id:3,val:1337});
+ equal(count, 3, "events fired");
+ same(dv.rows[2], {id:3,val:1337}, "item updated");
+ equal(dv.getItems().length, 4, "items updated");
+ equal(dv.rows.length, 4, "rows updated");
+ assertConsistency(dv);
+});
+
+test("insert at the end", function() {
+ var count = 0;
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{id:0,val:0},{id:1,val:1},{id:2,val:2}]);
+ dv.onRowsChanged.subscribe(function(args) {
+ ok(true, "onRowsChanged called");
+ same(args, [3], "args");
+ count++;
+ });
+ dv.onRowCountChanged.subscribe(function(args) {
+ ok(true, "onRowCountChanged called");
+ equal(args.previous, 3, "previous arg");
+ equal(args.current, 4, "current arg");
+ count++;
+ });
+ dv.onPagingInfoChanged.subscribe(function(args) {
+ ok(true, "onPagingInfoChanged called");
+ equal(args.pageSize, 0, "pageSize arg");
+ equal(args.pageNum, 0, "pageNum arg");
+ equal(args.totalRows, 4, "totalRows arg");
+ count++;
+ });
+ dv.insertItem(3,{id:3,val:1337});
+ equal(count, 3, "events fired");
+ same(dv.rows[3], {id:3,val:1337}, "item updated");
+ equal(dv.getItems().length, 4, "items updated");
+ equal(dv.rows.length, 4, "rows updated");
+ assertConsistency(dv);
+});
+
+module("deleteItem");
+
+test("must have id", function() {
+ var count = 0;
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{id:0,val:0},{id:1,val:1},{id:2,val:2}]);
+ try {
+ dv.deleteItem(-1);
+ ok(false, "exception thrown");
+ }
+ catch (ex) {}
+ try {
+ dv.deleteItem(undefined);
+ ok(false, "exception thrown");
+ }
+ catch (ex) {}
+ try {
+ dv.deleteItem(null);
+ ok(false, "exception thrown");
+ }
+ catch (ex) {}
+ try {
+ dv.deleteItem(3);
+ ok(false, "exception thrown");
+ }
+ catch (ex) {}
+});
+
+test("must have id (custom)", function() {
+ var count = 0;
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{uid:0,id:-1,val:0},{uid:1,id:3,val:1},{uid:2,id:null,val:2}], "uid");
+ try {
+ dv.deleteItem(-1);
+ ok(false, "exception thrown");
+ }
+ catch (ex) {}
+ try {
+ dv.deleteItem(undefined);
+ ok(false, "exception thrown");
+ }
+ catch (ex) {}
+ try {
+ dv.deleteItem(null);
+ ok(false, "exception thrown");
+ }
+ catch (ex) {}
+ try {
+ dv.deleteItem(3);
+ ok(false, "exception thrown");
+ }
+ catch (ex) {}
+});
+
+test("delete at the beginning", function() {
+ var count = 0;
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{id:05,val:0},{id:15,val:1},{id:25,val:2}]);
+ dv.onRowsChanged.subscribe(function(args) {
+ ok(true, "onRowsChanged called");
+ same(args, [0,1], "args");
+ count++;
+ });
+ dv.onRowCountChanged.subscribe(function(args) {
+ ok(true, "onRowCountChanged called");
+ equal(args.previous, 3, "previous arg");
+ equal(args.current, 2, "current arg");
+ count++;
+ });
+ dv.onPagingInfoChanged.subscribe(function(args) {
+ ok(true, "onPagingInfoChanged called");
+ equal(args.pageSize, 0, "pageSize arg");
+ equal(args.pageNum, 0, "pageNum arg");
+ equal(args.totalRows, 2, "totalRows arg");
+ count++;
+ });
+ dv.deleteItem(05);
+ equal(count, 3, "events fired");
+ equal(dv.getItems().length, 2, "items updated");
+ equal(dv.rows.length, 2, "rows updated");
+ assertConsistency(dv);
+});
+
+test("delete in the middle", function() {
+ var count = 0;
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{id:05,val:0},{id:15,val:1},{id:25,val:2}]);
+ dv.onRowsChanged.subscribe(function(args) {
+ ok(true, "onRowsChanged called");
+ same(args, [1], "args");
+ count++;
+ });
+ dv.onRowCountChanged.subscribe(function(args) {
+ ok(true, "onRowCountChanged called");
+ equal(args.previous, 3, "previous arg");
+ equal(args.current, 2, "current arg");
+ count++;
+ });
+ dv.onPagingInfoChanged.subscribe(function(args) {
+ ok(true, "onPagingInfoChanged called");
+ equal(args.pageSize, 0, "pageSize arg");
+ equal(args.pageNum, 0, "pageNum arg");
+ equal(args.totalRows, 2, "totalRows arg");
+ count++;
+ });
+ dv.deleteItem(15);
+ equal(count, 3, "events fired");
+ equal(dv.getItems().length, 2, "items updated");
+ equal(dv.rows.length, 2, "rows updated");
+ assertConsistency(dv);
+});
+
+test("delete at the end", function() {
+ var count = 0;
+ var dv = new Slick.Data.DataView();
+ dv.setItems([{id:05,val:0},{id:15,val:1},{id:25,val:2}]);
+ dv.onRowsChanged.subscribe(function(args) {
+ ok(false, "onRowsChanged called");
+ });
+ dv.onRowCountChanged.subscribe(function(args) {
+ ok(true, "onRowCountChanged called");
+ equal(args.previous, 3, "previous arg");
+ equal(args.current, 2, "current arg");
+ count++;
+ });
+ dv.onPagingInfoChanged.subscribe(function(args) {
+ ok(true, "onPagingInfoChanged called");
+ equal(args.pageSize, 0, "pageSize arg");
+ equal(args.pageNum, 0, "pageNum arg");
+ equal(args.totalRows, 2, "totalRows arg");
+ count++;
+ });
+ dv.deleteItem(25);
+ equal(count, 2, "events fired");
+ equal(dv.getItems().length, 2, "items updated");
+ equal(dv.rows.length, 2, "rows updated");
+ assertConsistency(dv);
+});
+
+// TODO: paging
+// TODO: combination \ No newline at end of file
diff --git a/tests/dataview/index.html b/tests/dataview/index.html
new file mode 100644
index 0000000..860b3e7
--- /dev/null
+++ b/tests/dataview/index.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+ <title></title>
+ <link rel="stylesheet" href="../../lib/qunit.css" type="text/css"/>
+</head>
+<body>
+<h1 id="qunit-header">QUnit Test Suite</h1>
+<h2 id="qunit-banner"></h2>
+<div id="qunit-testrunner-toolbar"></div>
+<h2 id="qunit-userAgent"></h2>
+<ol id="qunit-tests"></ol>
+
+
+
+<script type="text/javascript" src="../../lib/qunit.js"></script>
+<script type="text/javascript" src="../../lib/jquery-1.4.2.min.js"></script>
+<script type="text/javascript" src="../../slick.model.js"></script>
+
+
+<script type="text/javascript" src="dataview.js"></script>
+
+
+
+
+</body>
+</html> \ No newline at end of file
diff --git a/tests/grid/grid.js b/tests/grid/grid.js
new file mode 100644
index 0000000..4d52afe
--- /dev/null
+++ b/tests/grid/grid.js
@@ -0,0 +1,67 @@
+var grid;
+var el, offsetBefore, offsetAfter, dragged;
+
+var drag = function(handle, dx, dy) {
+ offsetBefore = el.offset();
+ $(handle).simulate("drag", {
+ dx: dx || 0,
+ dy: dy || 0
+ });
+ dragged = { dx: dx, dy: dy };
+ offsetAfter = el.offset();
+}
+
+var moved = function (dx, dy, msg) {
+ msg = msg ? msg + "." : "";
+ var actual = { left: offsetAfter.left, top: offsetAfter.top };
+ var expected = { left: offsetBefore.left + dx, top: offsetBefore.top + dy };
+ same(actual, expected, 'dragged[' + dragged.dx + ', ' + dragged.dy + '] ' + msg);
+}
+
+
+var ROWS = 500, COLS = 10;
+var data = [], row;
+for (var i=0; i<ROWS; i++) {
+ row = {};
+ row["id"] = "id_" + i;
+ for (var j=0; j<COLS; j++) {
+ row["col_" + j] = i + "." + j;
+ }
+ data.push(row);
+}
+
+var cols = [], col;
+for (var i=0; i<COLS; i++) {
+ col = {};
+ col["id"] = "col" + i;
+ col["field"] = "col_" + i;
+ col["name"] = "col_" + i;
+ cols.push(col);
+}
+
+cols[0].minWidth = 70;
+
+grid = new Slick.Grid($("#container"), data, cols);
+grid.render();
+
+
+
+module("grid - column resizing");
+
+test("minWidth is respected", function() {
+ var firstCol = $("#container .slick-header-column:first");
+ firstCol.find(".slick-resizable-handle:first").simulate("drag", {dx:100,dy:0});
+ firstCol.find(".slick-resizable-handle:first").simulate("drag", {dx:-200,dy:0});
+ equal(firstCol.outerWidth(), 70, "width set to minWidth");
+});
+
+
+test("onColumnsResized is fired on column resize", function() {
+ expect(2);
+ grid.onColumnsResized = function() { ok(true,"onColumnsResized called") };
+ var oldWidth = cols[0].width;
+ $("#container .slick-resizable-handle:first").simulate("drag", {dx:100,dy:0});
+ equal(cols[0].width, oldWidth+100-1, "columns array is updated");
+
+});
+
diff --git a/tests/grid/index.html b/tests/grid/index.html
new file mode 100644
index 0000000..6a391bb
--- /dev/null
+++ b/tests/grid/index.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+ <title></title>
+ <link rel="stylesheet" href="../../lib/qunit.css" type="text/css"/>
+ <link rel="stylesheet" href="../../slick.grid.css" type="text/css"/>
+ <link rel="stylesheet" href="../../examples/slick-default-theme.css" type="text/css" />
+
+</head>
+<body>
+<h1 id="qunit-header">QUnit Test Suite</h1>
+<h2 id="qunit-banner"></h2>
+<div id="qunit-testrunner-toolbar"></div>
+<h2 id="qunit-userAgent"></h2>
+<ol id="qunit-tests"></ol>
+
+
+<br/><br/><br/>
+<div id="container" style="width:600px;height:400px;"></div>
+
+
+<script type="text/javascript" src="../../lib/qunit.js"></script>
+<script type="text/javascript" src="../../lib/jquery-1.4.2.min.js"></script>
+<script type="text/javascript" src="../../lib/jquery-ui-1.7.2.custom.min.js"></script>
+<script type="text/javascript" src="../../lib/jquery.event.drag.custom.js"></script>
+<script type="text/javascript" src="../../lib/jquery.rule-1.0.1.1-min.js"></script>
+<script type="text/javascript" src="../../lib/jquery.simulate.js"></script>
+<script type="text/javascript" src="../../lib/qunit.js"></script>
+<script type="text/javascript" src="../../slick.model.js"></script>
+<script type="text/javascript" src="../../slick.grid.js"></script>
+
+
+
+<script type="text/javascript" src="grid.js"></script>
+
+
+
+
+</body>
+</html> \ No newline at end of file
diff --git a/tests/index.html b/tests/index.html
index 5a8775f..7649948 100644
--- a/tests/index.html
+++ b/tests/index.html
@@ -4,20 +4,34 @@
<head>
<title></title>
<link rel="stylesheet" href="../lib/qunit.css" type="text/css"/>
+ <style type="text/css">
+ ul { font-family: 'trebuchet ms', verdana, arial; }
+ h2, ul {font-size: 10pt; }
+ h2 {
+ background-color:#EEEEEE;
+ color:black;
+ font-size:small;
+ font-weight:normal;
+ margin:0;
+ padding:10px;
+ }
+ </style>
</head>
<body>
-<h1 id="qunit-header">QUnit Test Suite</h1>
+
+<h1 id="qunit-header">jQuery UI Unit Tests</h1>
<h2 id="qunit-banner"></h2>
-<div id="qunit-testrunner-toolbar"></div>
-<h2 id="qunit-userAgent"></h2>
-<ol id="qunit-tests"></ol>
+<h2>Slick.Data</h2>
+<ul>
+ <li><a href="dataview/index.html">DataView</a></li>
+</ul>
-<script type="text/javascript" src="../lib/qunit.js"></script>
-<script type="text/javascript" src="../lib/jquery-1.4.2.min.js"></script>
-<script type="text/javascript" src="../slick.model.js"></script>
+<h2>Slick</h2>
+<ul>
+ <li><a href="grid/index.html">Grid</a></li>
+</ul>
-<script type="text/javascript" src="dataview.js"></script>
</body>
</html> \ No newline at end of file