rails_slickgrid 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,294 @@
1
+ /***
2
+ * A simple observer pattern implementation.
3
+ */
4
+ function EventHelper() {
5
+ this.handlers = [];
6
+
7
+ this.subscribe = function(fn) {
8
+ this.handlers.push(fn);
9
+ };
10
+
11
+ this.notify = function(args) {
12
+ for (var i = 0; i < this.handlers.length; i++) {
13
+ this.handlers[i].call(this, args);
14
+ }
15
+ };
16
+
17
+ return this;
18
+ }
19
+
20
+
21
+ (function($) {
22
+ /***
23
+ * A sample Model implementation.
24
+ * Provides a filtered view of the underlying data.
25
+ *
26
+ * Relies on the data item having an "id" property uniquely identifying it.
27
+ */
28
+ function DataView() {
29
+ var self = this;
30
+
31
+ // private
32
+ var idProperty = "id"; // property holding a unique row id
33
+ var items = []; // data by index
34
+ var rows = []; // data by row
35
+ var idxById = {}; // indexes by id
36
+ var rowsById = null; // rows by id; lazy-calculated
37
+ var filter = null; // filter function
38
+ var updated = null; // updated item ids
39
+ var suspend = false; // suspends the recalculation
40
+ var sortAsc = true;
41
+ var sortComparer = null;
42
+ var fastSortField = null;
43
+
44
+ var pagesize = 0;
45
+ var pagenum = 0;
46
+ var totalRows = 0;
47
+
48
+ // events
49
+ var onRowCountChanged = new EventHelper();
50
+ var onRowsChanged = new EventHelper();
51
+ var onPagingInfoChanged = new EventHelper();
52
+
53
+
54
+ function beginUpdate() {
55
+ suspend = true;
56
+ }
57
+
58
+ function endUpdate() {
59
+ suspend = false;
60
+ refresh();
61
+ }
62
+
63
+ function refreshIdxById() {
64
+ idxById = {};
65
+ for (var i = 0,l = items.length; i < l; i++) {
66
+ var id = items[i][idProperty];
67
+ if (id == undefined || idxById[id] != undefined)
68
+ throw "Each data element must implement a unique 'id' property";
69
+ idxById[id] = i;
70
+ }
71
+ }
72
+
73
+ function getItems() {
74
+ return items;
75
+ }
76
+
77
+ function setItems(data, objectIdProperty) {
78
+ if (objectIdProperty !== undefined) idProperty = objectIdProperty;
79
+ items = data;
80
+ refreshIdxById();
81
+ refresh();
82
+ }
83
+
84
+ function setPagingOptions(args) {
85
+ if (args.pageSize != undefined)
86
+ pagesize = args.pageSize;
87
+
88
+ if (args.pageNum != undefined)
89
+ pagenum = Math.min(args.pageNum, Math.ceil(totalRows / pagesize));
90
+
91
+ onPagingInfoChanged.notify(getPagingInfo());
92
+
93
+ refresh();
94
+ }
95
+
96
+ function getPagingInfo() {
97
+ return {pageSize:pagesize, pageNum:pagenum, totalRows:totalRows};
98
+ }
99
+
100
+ function sort(comparer, ascending) {
101
+ sortAsc = ascending;
102
+ sortComparer = comparer;
103
+ fastSortField = null;
104
+ if (ascending === false) items.reverse();
105
+ items.sort(comparer);
106
+ if (ascending === false) items.reverse();
107
+ refreshIdxById();
108
+ refresh();
109
+ }
110
+
111
+ /***
112
+ * Provides a workaround for the extremely slow sorting in IE.
113
+ * Does a [lexicographic] sort on a give column by temporarily overriding Object.prototype.toString
114
+ * to return the value of that field and then doing a native Array.sort().
115
+ */
116
+ function fastSort(field, ascending) {
117
+ sortAsc = ascending;
118
+ fastSortField = field;
119
+ sortComparer = null;
120
+ var oldToString = Object.prototype.toString;
121
+ Object.prototype.toString = (typeof field == "function")?field:function() { return this[field] };
122
+ // an extra reversal for descending sort keeps the sort stable
123
+ // (assuming a stable native sort implementation, which isn't true in some cases)
124
+ if (ascending === false) items.reverse();
125
+ items.sort();
126
+ Object.prototype.toString = oldToString;
127
+ if (ascending === false) items.reverse();
128
+ refreshIdxById();
129
+ refresh();
130
+ }
131
+
132
+ function reSort() {
133
+ if (sortComparer)
134
+ sort(sortComparer,sortAsc);
135
+ else if (fastSortField)
136
+ fastSort(fastSortField,sortAsc);
137
+ }
138
+
139
+ function setFilter(filterFn) {
140
+ filter = filterFn;
141
+ refresh();
142
+ }
143
+
144
+ function getItemByIdx(i) {
145
+ return items[i];
146
+ }
147
+
148
+ function getIdxById(id) {
149
+ return idxById[id];
150
+ }
151
+
152
+ // calculate the lookup table on first call
153
+ function getRowById(id) {
154
+ if (!rowsById) {
155
+ rowsById = {};
156
+ for (var i = 0, l = rows.length; i < l; ++i) {
157
+ rowsById[rows[i][idProperty]] = i;
158
+ }
159
+ }
160
+
161
+ return rowsById[id];
162
+ }
163
+
164
+ function getItemById(id) {
165
+ return items[idxById[id]];
166
+ }
167
+
168
+ function updateItem(id, item) {
169
+ if (idxById[id] === undefined || id !== item[idProperty])
170
+ throw "Invalid or non-matching id";
171
+ items[idxById[id]] = item;
172
+ if (!updated) updated = {};
173
+ updated[id] = true;
174
+ refresh();
175
+ }
176
+
177
+ function insertItem(insertBefore, item) {
178
+ items.splice(insertBefore, 0, item);
179
+ refreshIdxById(); // TODO: optimize
180
+ refresh();
181
+ }
182
+
183
+ function addItem(item) {
184
+ items.push(item);
185
+ refreshIdxById(); // TODO: optimize
186
+ refresh();
187
+ }
188
+
189
+ function deleteItem(id) {
190
+ if (idxById[id] === undefined)
191
+ throw "Invalid id";
192
+ items.splice(idxById[id], 1);
193
+ refreshIdxById(); // TODO: optimize
194
+ refresh();
195
+ }
196
+
197
+ function recalc(_items, _rows, _filter, _updated) {
198
+ var diff = [];
199
+ var items = _items, rows = _rows, filter = _filter, updated = _updated; // cache as local vars
200
+
201
+ rowsById = null;
202
+
203
+ // go over all items remapping them to rows on the fly
204
+ // while keeping track of the differences and updating indexes
205
+ var rl = rows.length;
206
+ var currentRowIndex = 0;
207
+ var currentPageIndex = 0;
208
+ var item,id;
209
+
210
+ for (var i = 0, il = items.length; i < il; ++i) {
211
+ item = items[i];
212
+
213
+ if (!filter || filter(item)) {
214
+ id = item[idProperty];
215
+
216
+ if (!pagesize || (currentRowIndex >= pagesize * pagenum && currentRowIndex < pagesize * (pagenum + 1))) {
217
+ if (currentPageIndex >= rl || id != rows[currentPageIndex][idProperty] || (updated && updated[id])) {
218
+ diff[diff.length] = currentPageIndex;
219
+ rows[currentPageIndex] = item;
220
+ }
221
+
222
+ currentPageIndex++;
223
+ }
224
+
225
+ currentRowIndex++;
226
+ }
227
+ }
228
+
229
+ if (rl > currentPageIndex)
230
+ rows.splice(currentPageIndex, rl - currentPageIndex);
231
+
232
+ totalRows = currentRowIndex;
233
+
234
+ return diff;
235
+ }
236
+
237
+ function refresh() {
238
+ if (suspend) return;
239
+
240
+ var countBefore = rows.length;
241
+ var totalRowsBefore = totalRows;
242
+
243
+ var diff = recalc(items, rows, filter, updated); // pass as direct refs to avoid closure perf hit
244
+
245
+ // if the current page is no longer valid, go to last page and recalc
246
+ // we suffer a performance penalty here, but the main loop (recalc) remains highly optimized
247
+ if (pagesize && totalRows < pagenum * pagesize) {
248
+ pagenum = Math.floor(totalRows / pagesize);
249
+ diff = recalc(items, rows, filter, updated);
250
+ }
251
+
252
+ updated = null;
253
+
254
+ if (totalRowsBefore != totalRows) onPagingInfoChanged.notify(getPagingInfo());
255
+ if (countBefore != rows.length) onRowCountChanged.notify({previous:countBefore, current:rows.length});
256
+ if (diff.length > 0) onRowsChanged.notify(diff);
257
+ }
258
+
259
+
260
+ return {
261
+ // properties
262
+ "rows": rows, // note: neither the array or the data in it should be modified directly
263
+
264
+ // methods
265
+ "beginUpdate": beginUpdate,
266
+ "endUpdate": endUpdate,
267
+ "setPagingOptions": setPagingOptions,
268
+ "getPagingInfo": getPagingInfo,
269
+ "getItems": getItems,
270
+ "setItems": setItems,
271
+ "setFilter": setFilter,
272
+ "sort": sort,
273
+ "fastSort": fastSort,
274
+ "reSort": reSort,
275
+ "getIdxById": getIdxById,
276
+ "getRowById": getRowById,
277
+ "getItemById": getItemById,
278
+ "getItemByIdx": getItemByIdx,
279
+ "refresh": refresh,
280
+ "updateItem": updateItem,
281
+ "insertItem": insertItem,
282
+ "addItem": addItem,
283
+ "deleteItem": deleteItem,
284
+
285
+ // events
286
+ "onRowCountChanged": onRowCountChanged,
287
+ "onRowsChanged": onRowsChanged,
288
+ "onPagingInfoChanged": onPagingInfoChanged
289
+ };
290
+ }
291
+
292
+ // Slick.Data.DataView
293
+ $.extend(true, window, { Slick: { Data: { DataView: DataView }}});
294
+ })(jQuery);
@@ -0,0 +1,146 @@
1
+ (function($) {
2
+ function SlickGridPager(dataView, grid, $container)
3
+ {
4
+ var $status, $contextMenu;
5
+
6
+ function init()
7
+ {
8
+ dataView.onPagingInfoChanged.subscribe(function(pagingInfo) {
9
+ updatePager(pagingInfo);
10
+ });
11
+
12
+ constructPagerUI();
13
+ updatePager(dataView.getPagingInfo());
14
+ }
15
+
16
+ function getNavState()
17
+ {
18
+ var cannotLeaveEditMode = !Slick.GlobalEditorLock.commitCurrentEdit();
19
+ var pagingInfo = dataView.getPagingInfo();
20
+ var lastPage = Math.floor(pagingInfo.totalRows/pagingInfo.pageSize);
21
+
22
+ return {
23
+ canGotoFirst: !cannotLeaveEditMode && pagingInfo.pageSize != 0 && pagingInfo.pageNum > 0,
24
+ canGotoLast: !cannotLeaveEditMode && pagingInfo.pageSize != 0 && pagingInfo.pageNum != lastPage,
25
+ canGotoPrev: !cannotLeaveEditMode && pagingInfo.pageSize != 0 && pagingInfo.pageNum > 0,
26
+ canGotoNext: !cannotLeaveEditMode && pagingInfo.pageSize != 0 && pagingInfo.pageNum < lastPage,
27
+ pagingInfo: pagingInfo,
28
+ lastPage: lastPage
29
+ }
30
+ }
31
+
32
+ function setPageSize(n)
33
+ {
34
+ dataView.setPagingOptions({pageSize:n});
35
+ }
36
+
37
+ function gotoFirst()
38
+ {
39
+ if (getNavState().canGotoFirst)
40
+ dataView.setPagingOptions({pageNum: 0});
41
+ }
42
+
43
+ function gotoLast()
44
+ {
45
+ var state = getNavState();
46
+ if (state.canGotoLast)
47
+ dataView.setPagingOptions({pageNum: state.lastPage});
48
+ }
49
+
50
+ function gotoPrev()
51
+ {
52
+ var state = getNavState();
53
+ if (state.canGotoPrev)
54
+ dataView.setPagingOptions({pageNum: state.pagingInfo.pageNum-1});
55
+ }
56
+
57
+ function gotoNext()
58
+ {
59
+ var state = getNavState();
60
+ if (state.canGotoNext)
61
+ dataView.setPagingOptions({pageNum: state.pagingInfo.pageNum+1});
62
+ }
63
+
64
+ function constructPagerUI()
65
+ {
66
+ $container.empty();
67
+
68
+ $status = $("<span class='slick-pager-status' />").appendTo($container);
69
+
70
+ var $nav = $("<span class='slick-pager-nav' />").appendTo($container);
71
+ var $settings = $("<span class='slick-pager-settings' />").appendTo($container);
72
+
73
+ $settings
74
+ .append("<span class='slick-pager-settings-expanded' style='display:none'>Show: <a data=0>All</a><a data='-1'>Auto</a><a data=25>25</a><a data=50>50</a><a data=100>100</a></span>");
75
+
76
+ $settings.find("a[data]").click(function(e) {
77
+ var pagesize = $(e.target).attr("data");
78
+ if (pagesize != undefined)
79
+ {
80
+ if (pagesize == -1)
81
+ {
82
+ var vp = grid.getViewport();
83
+ setPageSize(vp.bottom-vp.top);
84
+ }
85
+ else
86
+ setPageSize(parseInt(pagesize));
87
+ }
88
+ });
89
+
90
+ var icon_prefix = "<span class='ui-state-default ui-corner-all ui-icon-container'><span class='ui-icon ";
91
+ var icon_suffix = "' /></span>";
92
+
93
+ $(icon_prefix + "ui-icon-lightbulb" + icon_suffix)
94
+ .click(function() { $(".slick-pager-settings-expanded").toggle() })
95
+ .appendTo($settings);
96
+
97
+ $(icon_prefix + "ui-icon-seek-first" + icon_suffix)
98
+ .click(gotoFirst)
99
+ .appendTo($nav);
100
+
101
+ $(icon_prefix + "ui-icon-seek-prev" + icon_suffix)
102
+ .click(gotoPrev)
103
+ .appendTo($nav);
104
+
105
+ $(icon_prefix + "ui-icon-seek-next" + icon_suffix)
106
+ .click(gotoNext)
107
+ .appendTo($nav);
108
+
109
+ $(icon_prefix + "ui-icon-seek-end" + icon_suffix)
110
+ .click(gotoLast)
111
+ .appendTo($nav);
112
+
113
+ $container.find(".ui-icon-container")
114
+ .hover(function() {
115
+ $(this).toggleClass("ui-state-hover");
116
+ });
117
+
118
+ $container.children().wrapAll("<div class='slick-pager' />");
119
+ }
120
+
121
+
122
+ function updatePager(pagingInfo)
123
+ {
124
+ var state = getNavState();
125
+
126
+ $container.find(".slick-pager-nav span").removeClass("ui-state-disabled");
127
+ if (!state.canGotoFirst) $container.find(".ui-icon-seek-first").addClass("ui-state-disabled");
128
+ if (!state.canGotoLast) $container.find(".ui-icon-seek-end").addClass("ui-state-disabled");
129
+ if (!state.canGotoNext) $container.find(".ui-icon-seek-next").addClass("ui-state-disabled");
130
+ if (!state.canGotoPrev) $container.find(".ui-icon-seek-prev").addClass("ui-state-disabled");
131
+
132
+
133
+ if (pagingInfo.pageSize == 0)
134
+ $status.text("Showing all " + pagingInfo.totalRows + " rows");
135
+ else
136
+ $status.text("Showing page " + (pagingInfo.pageNum+1) + " of " + (Math.floor(pagingInfo.totalRows/pagingInfo.pageSize)+1));
137
+ }
138
+
139
+
140
+
141
+ init();
142
+ }
143
+
144
+ // Slick.Controls.Pager
145
+ $.extend(true, window, { Slick: { Controls: { Pager: SlickGridPager }}});
146
+ })(jQuery);
@@ -0,0 +1,30 @@
1
+ .slick-columnpicker {
2
+ border: 1px solid #718BB7;
3
+ background: #f0f0f0;
4
+ padding: 6px;
5
+ -moz-box-shadow: 2px 2px 2px silver;
6
+ -webkit-box-shadow: 2px 2px 2px silver;
7
+ min-width: 100px;
8
+ cursor: default;
9
+ }
10
+
11
+ .slick-columnpicker li {
12
+ list-style: none;
13
+ margin: 0;
14
+ padding: 0;
15
+ background: none;
16
+ }
17
+
18
+ .slick-columnpicker input {
19
+ margin: 4px;
20
+ }
21
+
22
+ .slick-columnpicker li a {
23
+ display: block;
24
+ padding: 4px;
25
+ font-weight: bold;
26
+ }
27
+
28
+ .slick-columnpicker li a:hover {
29
+ background: white;
30
+ }