watable-rails 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.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2013 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ = WatableRails
2
+
3
+ This project rocks and uses MIT-LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'WatableRails'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+
27
+
28
+ Bundler::GemHelper.install_tasks
29
+
30
+ require 'rake/testtask'
31
+
32
+ Rake::TestTask.new(:test) do |t|
33
+ t.libs << 'lib'
34
+ t.libs << 'test'
35
+ t.pattern = 'test/**/*_test.rb'
36
+ t.verbose = false
37
+ end
38
+
39
+
40
+ task :default => :test
@@ -0,0 +1,1213 @@
1
+ /*
2
+ WATable 1.07
3
+ Copyright (c) 2012 Andreas Petersson(apesv03@gmail.com)
4
+ http://wootapa-watable.appspot.com/
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining
7
+ a copy of this software and associated documentation files (the
8
+ "Software"), to deal in the Software without restriction, including
9
+ without limitation the rights to use, copy, modify, merge, publish,
10
+ distribute, sublicense, and/or sell copies of the Software, and to
11
+ permit persons to whom the Software is furnished to do so, subject to
12
+ the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ */
25
+ (function ($, undefined) {
26
+
27
+ var WATable = function () {
28
+
29
+ var priv = {}; //private api
30
+ var publ = {}; //public api
31
+
32
+ priv.options = {};
33
+ var defaults = {
34
+ url: '', //webservice url
35
+ urlData: '', //webservice params
36
+ urlPost: false, //use POST instead of GET
37
+ debug: false, //prints debug info to console
38
+ filter: false, //show filter row
39
+ columnPicker: false, //show columnpicker
40
+ checkboxes: false, //show body checkboxes
41
+ actions: '', //holds action links
42
+ pageSize: 10, //current pagesize
43
+ pageSizes: [10, 20, 30, 40, 50, 'All'], //available pagesizes
44
+ hidePagerOnEmpty: false, //removes pager if no rows
45
+ preFill: false, //prefill table with empty rows
46
+ pagingTemplate: '<p>Rows {0}-{1} of {2}</p>',
47
+ types: { //type specific options
48
+ string: {},
49
+ number: {},
50
+ bool: {},
51
+ date: {}
52
+ }
53
+ };
54
+
55
+ /* bundled scripts */
56
+ priv.ext = {};
57
+ priv.ext.XDate = /* xdate 0.7 */ function(a,b,c,d){function e(){var b=this instanceof e?this:new e,c=arguments,d=c.length,h;typeof c[d-1]=="boolean"&&(h=c[--d],c=y(c,0,d));if(d)if(d==1)if(d=c[0],d instanceof a||typeof d=="number")b[0]=new a(+d);else if(d instanceof e){var c=b,i=new a(+d[0]);if(f(d))i.toString=G;c[0]=i}else{if(typeof d=="string"){b[0]=new a(0);a:{for(var c=d,d=h||!1,i=e.parsers,j=0,k;j<i.length;j++)if(k=i[j](c,d,b)){b=k;break a}b[0]=new a(c)}}}else b[0]=new a(F.apply(a,c)),h||(b[0]=u(b[0]));else b[0]=new a;typeof h=="boolean"&&g(b,h);return b}function f(a){return a[0].toString===G}function g(b,c,d){if(c){if(!f(b))d&&(b[0]=new a(F(b[0].getFullYear(),b[0].getMonth(),b[0].getDate(),b[0].getHours(),b[0].getMinutes(),b[0].getSeconds(),b[0].getMilliseconds()))),b[0].toString=G}else f(b)&&(b[0]=d?u(b[0]):new a(+b[0]));return b}function h(a,b,c,d,e){var f=x(s,a[0],e),a=x(t,a[0],e),e=b==1?c%12:f(1),g=!1;d.length==2&&typeof d[1]=="boolean"&&(g=d[1],d=[c]);a(b,d);g&&f(1)!=e&&(a(1,[f(1)-1]),a(2,[v(f(0),f(1))]))}function i(a,c,d,e){var d=Number(d),f=b.floor(d);a["set"+B[c]](a["get"+B[c]]()+f,e||!1);f!=d&&c<6&&i(a,c+1,(d-f)*D[c],e)}function j(a,c,d){var a=a.clone().setUTCMode(!0,!0),c=e(c).setUTCMode(!0,!0),f=0;if(d==0||d==1){for(var g=6;g>=d;g--)f/=D[g],f+=s(c,!1,g)-s(a,!1,g);d==1&&(f+=(c.getFullYear()-a.getFullYear())*12)}else d==2?(d=a.toDate().setUTCHours(0,0,0,0),f=c.toDate().setUTCHours(0,0,0,0),f=b.round((f-d)/864e5)+(c-f-(a-d))/864e5):f=(c-a)/[36e5,6e4,1e3,1][d-3];return f}function k(c){var d=c(0),e=c(1),c=c(2),e=new a(F(d,e,c)),f=l(d),c=f;e<f?c=l(d-1):(d=l(d+1),e>=d&&(c=d));return b.floor(b.round((e-c)/864e5)/7)+1}function l(b){b=new a(F(b,0,4));b.setUTCDate(b.getUTCDate()-(b.getUTCDay()+6)%7);return b}function m(a,b,c,e){var f=x(s,a,e),g=x(t,a,e),c=l(c===d?f(0):c);e||(c=u(c));a.setTime(+c);g(2,[f(2)+(b-1)*7])}function n(a,b,c,d,f){var g=e.locales,h=g[e.defaultLocale]||{},i=x(s,a,f),c=(typeof c=="string"?g[c]:c)||{};return o(a,b,function(a){if(d)for(var b=(a==7?2:a)-1;b>=0;b--)d.push(i(b));return i(a)},function(a){return c[a]||h[a]},f)}function o(a,b,c,e,f){for(var g,h,i="";g=b.match(E);){i+=b.substr(0,g.index);if(g[1]){h=i;for(var i=a,j=g[1],k=c,l=e,m=f,n=j.length,q=void 0,r="";n>0;)q=p(i,j.substr(0,n),k,l,m),q!==d?(r+=q,j=j.substr(n),n=j.length):n--;i=h+(r+j)}else g[3]?(h=o(a,g[4],c,e,f),parseInt(h.replace(/\D/g,""),10)&&(i+=h)):i+=g[7]||"'";b=b.substr(g.index+g[0].length)}return i+b}function p(a,c,d,f,g){var h=e.formatters[c];if(typeof h=="string")return o(a,h,d,f,g);else if(typeof h=="function")return h(a,g||!1,f);switch(c){case"fff":return A(d(6),3);case"s":return d(5);case"ss":return A(d(5));case"m":return d(4);case"mm":return A(d(4));case"h":return d(3)%12||12;case"hh":return A(d(3)%12||12);case"H":return d(3);case"HH":return A(d(3));case"d":return d(2);case"dd":return A(d(2));case"ddd":return f("dayNamesShort")[d(7)]||"";case"dddd":return f("dayNames")[d(7)]||"";case"M":return d(1)+1;case"MM":return A(d(1)+1);case"MMM":return f("monthNamesShort")[d(1)]||"";case"MMMM":return f("monthNames")[d(1)]||"";case"yy":return(d(0)+"").substring(2);case"yyyy":return d(0);case"t":return q(d,f).substr(0,1).toLowerCase();case"tt":return q(d,f).toLowerCase();case"T":return q(d,f).substr(0,1);case"TT":return q(d,f);case"z":case"zz":case"zzz":return g?c="Z":(f=a.getTimezoneOffset(),a=f<0?"+":"-",d=b.floor(b.abs(f)/60),f=b.abs(f)%60,g=d,c=="zz"?g=A(d):c=="zzz"&&(g=A(d)+":"+A(f)),c=a+g),c;case"w":return k(d);case"ww":return A(k(d));case"S":return c=d(2),c>10&&c<20?"th":["st","nd","rd"][c%10-1]||"th"}}function q(a,b){return a(3)<12?b("amDesignator"):b("pmDesignator")}function r(a){return!isNaN(+a[0])}function s(a,b,c){return a["get"+(b?"UTC":"")+B[c]]()}function t(a,b,c,d){a["set"+(b?"UTC":"")+B[c]].apply(a,d)}function u(b){return new a(b.getUTCFullYear(),b.getUTCMonth(),b.getUTCDate(),b.getUTCHours(),b.getUTCMinutes(),b.getUTCSeconds(),b.getUTCMilliseconds())}function v(b,c){return 32-(new a(F(b,c,32))).getUTCDate()}function w(a){return function(){return a.apply(d,[this].concat(y(arguments)))}}function x(a){var b=y(arguments,1);return function(){return a.apply(d,b.concat(y(arguments)))}}function y(a,b,e){return c.prototype.slice.call(a,b||0,e===d?a.length:e)}function z(a,b){for(var c=0;c<a.length;c++)b(a[c],c)}function A(a,b){b=b||2;for(a+="";a.length<b;)a="0"+a;return a}var B="FullYear,Month,Date,Hours,Minutes,Seconds,Milliseconds,Day,Year".split(","),C=["Years","Months","Days"],D=[12,31,24,60,60,1e3,1],E=/(([a-zA-Z])\2*)|(\((('.*?'|\(.*?\)|.)*?)\))|('(.*?)')/,F=a.UTC,G=a.prototype.toUTCString,H=e.prototype;H.length=1;H.splice=c.prototype.splice;H.getUTCMode=w(f);H.setUTCMode=w(g);H.getTimezoneOffset=function(){return f(this)?0:this[0].getTimezoneOffset()};z(B,function(a,b){H["get"+a]=function(){return s(this[0],f(this),b)};b!=8&&(H["getUTC"+a]=function(){return s(this[0],!0,b)});b!=7&&(H["set"+a]=function(a){h(this,b,a,arguments,f(this));return this},b!=8&&(H["setUTC"+a]=function(a){h(this,b,a,arguments,!0);return this},H["add"+(C[b]||a)]=function(a,c){i(this,b,a,c);return this},H["diff"+(C[b]||a)]=function(a){return j(this,a,b)}))});H.getWeek=function(){return k(x(s,this,!1))};H.getUTCWeek=function(){return k(x(s,this,!0))};H.setWeek=function(a,b){m(this,a,b,!1);return this};H.setUTCWeek=function(a,b){m(this,a,b,!0);return this};H.addWeeks=function(a){return this.addDays(Number(a)*7)};H.diffWeeks=function(a){return j(this,a,2)/7};e.parsers=[function(b,c,d){if(b=b.match(/^(\d{4})(-(\d{2})(-(\d{2})([T ](\d{2}):(\d{2})(:(\d{2})(\.(\d+))?)?(Z|(([-+])(\d{2})(:?(\d{2}))?))?)?)?)?$/)){var e=new a(F(b[1],b[3]?b[3]-1:0,b[5]||1,b[7]||0,b[8]||0,b[10]||0,b[12]?Number("0."+b[12])*1e3:0));b[13]?b[14]&&e.setUTCMinutes(e.getUTCMinutes()+(b[15]=="-"?1:-1)*(Number(b[16])*60+(b[18]?Number(b[18]):0))):c||(e=u(e));return d.setTime(+e)}}];e.parse=function(a){return+e(""+a)};H.toString=function(a,b,c){return a===d||!r(this)?this[0].toString():n(this,a,b,c,f(this))};H.toUTCString=H.toGMTString=function(a,b,c){return a===d||!r(this)?this[0].toUTCString():n(this,a,b,c,!0)};H.toISOString=function(){return this.toUTCString("yyyy-MM-dd'T'HH:mm:ss(.fff)zzz")};e.defaultLocale="";e.locales={"":{monthNames:"January,February,March,April,May,June,July,August,September,October,November,December".split(","),monthNamesShort:"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(","),dayNames:"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(","),dayNamesShort:"Sun,Mon,Tue,Wed,Thu,Fri,Sat".split(","),amDesignator:"AM",pmDesignator:"PM"}};e.formatters={i:"yyyy-MM-dd'T'HH:mm:ss(.fff)",u:"yyyy-MM-dd'T'HH:mm:ss(.fff)zzz"};z("getTime,valueOf,toDateString,toTimeString,toLocaleString,toLocaleDateString,toLocaleTimeString,toJSON".split(","),function(a){H[a]=function(){return this[0][a]()}});H.setTime=function(a){this[0].setTime(a);return this};H.valid=w(r);H.clone=function(){return new e(this)};H.clearTime=function(){return this.setHours(0,0,0,0)};H.toDate=function(){return new a(+this[0])};e.now=function(){return+(new a)};e.today=function(){return(new e).clearTime()};e.UTC=F;e.getDaysInMonth=v;if(typeof module!=="undefined"&&module.exports)module.exports=e;return e}(Date,Math,Array);
58
+
59
+ //these holds the actual dom table objects, and is used to identify what parts of the table that needs to be created.
60
+ var _cont; //container holding table
61
+ var _table; //the table
62
+ var _head; //table header
63
+ var _headSort; //table header sorting row
64
+ var _headFilter; //table header filter row
65
+ var _body; //table body
66
+ var _foot; //table footer
67
+
68
+ var _data; //columns and rows
69
+ var _currPage = 1; //current page
70
+ var _pageSize; //current pagesize
71
+ var _totalPages; //total pages
72
+ var _currSortCol; //current sorting column
73
+ var _currSortFlip = false; //current sorting direction
74
+ var _currDpOp; //clicked datepicker operator
75
+ var _filterCols = {}; //array with current filters
76
+ var _filterTimeout; //timer for delayed filtering
77
+ var _uniqueCol; //reference to column with the unique property
78
+ var _uniqueCols = {}; //array with checked rows
79
+ var _checkToggleChecked = false; //check-all toggle state
80
+
81
+ /*
82
+ initialize the plugin.
83
+ */
84
+ priv.init = function () {
85
+ _cont = priv.options.id;
86
+ priv.options.types.string = ((priv.options.types || {}).string || {});
87
+ priv.options.types.number = ((priv.options.types || {}).number || {});
88
+ priv.options.types.bool = ((priv.options.types || {}).bool || {});
89
+ priv.options.types.date = ((priv.options.types || {}).date || {});
90
+
91
+ //fill the table with empty cells
92
+ if (priv.options.preFill) {
93
+ var data = {
94
+ cols: {
95
+ dummy: {
96
+ index: 1,
97
+ friendly: "&nbsp;",
98
+ type: "string"
99
+ }
100
+ },
101
+ rows: []
102
+ };
103
+ for (var i = 0; i < priv.options.pageSize; i++)
104
+ data.rows.push({dummy: "&nbsp;"});
105
+ priv.setData(data);
106
+ }
107
+ //try call webservice for data
108
+ priv.update();
109
+ };
110
+
111
+ /*
112
+ creates the table with all its parts and handlers.
113
+ note that only the parts we need is created.
114
+ (yeah, the function is huge)
115
+ */
116
+ priv.createTable = function () {
117
+ var start = new priv.ext.XDate();
118
+
119
+ //create table itself
120
+ if (!_table) {
121
+ _head = _body = _foot = undefined;
122
+ _table = $('<table class="watable table table-striped table-hover table-bordered table-condensed"></table>').appendTo(_cont);
123
+ }
124
+
125
+ //create the header which will later hold both sorting and filtering
126
+ if (!_head) {
127
+ _table.find('thead').remove();
128
+ _headSort = _headFilter = undefined;
129
+ _head = $('<thead></thead>').prependTo(_table);
130
+ }
131
+
132
+ //sort the columns in index order
133
+ var colsSorted = Object.keys(_data.cols).sort(function (a, b) {
134
+ return _data.cols[a].index - _data.cols[b].index;
135
+ });
136
+
137
+ //create the header sorting row
138
+ if (!_headSort) {
139
+ _head.find('.sort i').tooltip('hide');
140
+ _head.find(".sort").remove();
141
+ _headSort = $('<tr class="sort"></tr>').prependTo(_head);
142
+
143
+ //create the checkall toggle
144
+ if (_uniqueCol && priv.options.checkboxes) {
145
+ var checked = _checkToggleChecked ? 'checked' : '';
146
+ var headCell = $('<th></th>').appendTo(_headSort);
147
+ var elem = $('<input {0} class="checkToggle" type="checkbox" />'.f(checked)).appendTo(headCell);
148
+ elem.on('change', priv.checkToggleChanged);
149
+ }
150
+
151
+ //create the sortable headers
152
+ for (var i = 0; i < colsSorted.length; i++) {
153
+ var column = colsSorted[i];
154
+ var props = _data.cols[column];
155
+
156
+ if (!props.hidden) {
157
+ var headCell = $('<th></th>').appendTo(_headSort);
158
+ var link = $('<a class="pull-left" href="#">{0}</a>'.f(props.friendly || column));
159
+ link.on('click', {column: column}, priv.columnClicked).appendTo(headCell);
160
+
161
+ if (props.tooltip) {
162
+ $('<i class="icon-info-sign"></i>').tooltip({
163
+ title: props.tooltip.trim(),
164
+ html: true,
165
+ container: 'body',
166
+ placement: "top",
167
+ delay: {
168
+ show: 500,
169
+ hide: 100
170
+ }
171
+ }).appendTo(link);
172
+ }
173
+
174
+ //Add sort arrow
175
+ if (column == _currSortCol) {
176
+ if (_currSortFlip) $('<i class="icon-chevron-down pull-right"></i>').appendTo(headCell);
177
+ else $('<i class="icon-chevron-up pull-right"></i>').appendTo(headCell);
178
+ }
179
+ }
180
+ }
181
+ }
182
+
183
+ //create the header filtering row
184
+ if (!_headFilter && priv.options.filter) {
185
+ _head.find(".filter").remove();
186
+ _headFilter = $('<tr class="filter"></tr>').appendTo(_head);
187
+ var headCell;
188
+ var elem;
189
+ var placeHolder = '';
190
+ var tooltip = '';
191
+
192
+ //create the filter checkbox
193
+ if (_uniqueCol && priv.options.checkboxes) {
194
+ tooltip = priv.options.types.bool.filterTooltip || 'Toggle between:<br/>indeterminate,<br/>checked,<br/>unchecked';
195
+ headCell = $('<th></th>').appendTo(_headFilter);
196
+ elem = $('<input class="filter indeterminate" checked type="checkbox" />').appendTo(headCell);
197
+ $.map(_filterCols, function (colProps, col) {
198
+ if (col == "unique") {
199
+ if (colProps.filter) elem.prop('checked', true).removeClass('indeterminate');
200
+ else if (!colProps.filter) elem.prop('checked', false).removeClass('indeterminate');
201
+ else if (colProps.filter == '') elem.addClass('indeterminate');
202
+ }
203
+ });
204
+
205
+ if (tooltip) {
206
+ elem.tooltip({
207
+ title: tooltip.trim(),
208
+ html: true,
209
+ container: 'body',
210
+ trigger: 'hover',
211
+ placement: 'top',
212
+ delay: {
213
+ show: 500,
214
+ hide: 100
215
+ }
216
+ });
217
+ }
218
+ elem.on('click', {column: "unique"}, priv.filterChanged);
219
+ }
220
+
221
+ //create the column filters
222
+ for (var i = 0; i < colsSorted.length; i++) {
223
+ var column = colsSorted[i];
224
+ var props = _data.cols[column];
225
+ tooltip = props.filterTooltip === true ? undefined : props.filterTooltip === false ? '' : props.filterTooltip;
226
+ placeHolder = props.placeHolder === true ? undefined : props.placeHolder === false ? '' : props.placeHolder;
227
+
228
+ if (!props.hidden) {
229
+ headCell = $('<th></th>').appendTo(_headFilter);
230
+
231
+ switch (props.type || 'string') {
232
+ case "number":
233
+ if (placeHolder == undefined) placeHolder = priv.options.types.number.placeHolder;
234
+ placeHolder = (placeHolder === true || placeHolder == undefined) ? '10..20 =50' : placeHolder === false ? '' : placeHolder;
235
+ if (tooltip == undefined) tooltip = priv.options.types.number.filterTooltip;
236
+ tooltip = (tooltip === true || tooltip == undefined) ? 'Values 10 to 20:<br/>10..20<br/>Values exactly 50:<br/>=50' : tooltip === false ? '' : tooltip;
237
+ elem = $('<input placeholder="{0}" class="filter" type="text" />'.f(placeHolder));
238
+ elem.on('keyup', {column: column}, priv.filterChanged);
239
+ break;
240
+ case "date":
241
+ if (placeHolder == undefined) placeHolder = priv.options.types.date.placeHolder;
242
+ placeHolder = (placeHolder === true || placeHolder == undefined) ? '-7..0' : placeHolder === false ? '' : placeHolder;
243
+ if (tooltip == undefined) tooltip = priv.options.types.date.filterTooltip;
244
+ tooltip = (tooltip === true || tooltip == undefined) ? 'Today:<br/>0..1<br/>A week today excluded:<br/>-7..0' : tooltip === false ? '' : tooltip;
245
+ elem = $('<div><input placeholder="{0}" class="filter" type="text" /></div>'.f(placeHolder));
246
+
247
+ if (priv.options.types.date.datePicker === true || priv.options.types.date.datePicker == undefined)
248
+ {
249
+ if ($().datepicker)
250
+ {
251
+ elem.addClass('date-wrap');
252
+ var today = new priv.ext.XDate(false).setHours(0, 0, 0, 0).toString('yyyy-MM-dd');
253
+ var dp = $('<div style="float:right" class="date" data-date="{0}" data-date-format="{1}" />'.f(today, 'yyyy-mm-dd')).appendTo(elem);
254
+ $('<input style="display:none" type="text" />').appendTo(dp);
255
+ $('<span class="add-on"><i class="icon-chevron-right"></i></span>').on('click', {op: "l"}, priv.dpOpChanged).appendTo(dp);
256
+ $('<span class="add-on"><i class="icon-chevron-left"></i></span>').on('click', {op: "r"}, priv.dpOpChanged).appendTo(dp);
257
+ dp.datepicker({weekStart:1});
258
+ dp.on('changeDate', {column: column, input: $('input.filter', elem)}, priv.dpClicked);
259
+ }
260
+ else
261
+ if (priv.options.debug) console.log('datepicker plugin not found');
262
+ }
263
+ elem.on('keyup', 'input.filter', {column: column}, priv.filterChanged);
264
+ break;
265
+ case "bool":
266
+ if (tooltip == undefined) tooltip = priv.options.types.bool.filterTooltip;
267
+ tooltip = (tooltip === true || tooltip == undefined) ? 'Toggle between:<br/>indeterminate,<br/>checked,<br/>unchecked' : tooltip === false ? '' : tooltip;
268
+ elem = $('<input class="filter indeterminate" checked type="checkbox" />');
269
+ elem.on('click', {column: column}, priv.filterChanged);
270
+ break;
271
+ case "string":
272
+ if (placeHolder == undefined) placeHolder = priv.options.types.string.placeHolder;
273
+ placeHolder = (placeHolder === true || placeHolder == undefined) ? 'John Doe' : placeHolder === false ? '' : placeHolder;
274
+ if (tooltip == undefined) tooltip = priv.options.types.string.filterTooltip;
275
+ tooltip = (tooltip === true || tooltip == undefined) ? 'Find John Doe:<br/>John Doe<br/>Find all but John Doe:<br/>!John Doe' : tooltip === false ? '' : tooltip;
276
+ elem = $('<input placeholder="{0}" class="filter" type="text" />'.f(placeHolder));
277
+ elem.on('keyup', {column: column}, priv.filterChanged);
278
+ break;
279
+ case "none":
280
+ elem = $('<div>&nbsp;</div>');
281
+ break;
282
+ }
283
+
284
+ if (tooltip) {
285
+ elem.tooltip({
286
+ title: tooltip.trim(),
287
+ html: true,
288
+ container: 'body',
289
+ trigger: 'hover',
290
+ placement: 'top',
291
+ delay: {
292
+ show: 500,
293
+ hide: 100
294
+ }
295
+ });
296
+ }
297
+
298
+ if (elem && props.filter) {
299
+ $.map(_filterCols, function (colProps, col) {
300
+ if (col == column) {
301
+ if (colProps.col.type == 'bool') {
302
+ if (colProps.filter) elem.prop('checked', true).removeClass('indeterminate');
303
+ else if (!colProps.filter) elem.prop('checked', false).removeClass('indeterminate');
304
+ else if (colProps.filter == '') elem.addClass('indeterminate');
305
+ }
306
+ else elem.val(colProps.filter);
307
+ }
308
+ });
309
+ elem.appendTo(headCell);
310
+ }
311
+ }
312
+ }
313
+ }
314
+
315
+ //create the body
316
+ if (!_body) {
317
+ _table.find('tbody').remove();
318
+ _body = $('<tbody></tbody>').insertAfter(_head);
319
+ _body.on('change', '.unique', priv.rowChecked);
320
+ _body.on('click', 'td', priv.rowClicked);
321
+
322
+ //find out what rows to show next...
323
+ var rowsAdded = 0;
324
+ _data.toRow = _data.fromRow + priv.options.pageSize;
325
+ if (_data.toRow > _data.rows.length)
326
+ _data.toRow = _data.rows.length;
327
+
328
+ //slice out the chunk of data we need and create rows
329
+ $.each(_data.rows.slice(_data.fromRow, _data.toRow), function (index, props) {
330
+ var row = $('<tr></tr>').appendTo(_body);
331
+
332
+ //create checkbox
333
+ if (_uniqueCol && priv.options.checkboxes) {
334
+ var check = _uniqueCols[props[_uniqueCol]] != undefined ? 'checked' : '';
335
+ var checkable = props['checkable'] === false ? 'disabled' : '';
336
+ var cell = $('<td></td>').appendTo(row);
337
+ $('<input class="unique" {0} {1} type="checkbox" />'.f(check, checkable)).appendTo(cell);
338
+ }
339
+
340
+ //create cells
341
+ for (var i = 0; i < colsSorted.length; i++) {
342
+ var key = colsSorted[i];
343
+ var val = props[key];
344
+ if (!_data.cols[key]) return;
345
+ if (_data.cols[key].unique) row.data('unique', val);
346
+
347
+ if (!_data.cols[key].hidden) {
348
+ var cell = $('<td></td>').appendTo(row);
349
+ cell.data('column', key);
350
+ if (val === undefined) continue;
351
+
352
+ var format = props[key + 'Format'] || _data.cols[key].format || '{0}';
353
+
354
+ switch (_data.cols[key].type) {
355
+ case "string":
356
+ cell.html(format.f(val));
357
+ break;
358
+ case "number":
359
+ val = Number(val);
360
+ var forceDecimals = !isNaN(_data.cols[key].decimals);
361
+ if (forceDecimals) cell.html(format.f(val.toFixed(_data.cols[key].decimals)));
362
+ else {
363
+ (val || 0) % 1 === 0
364
+ ? cell.html(format.f(val))
365
+ : cell.html(format.f(val.toFixed(priv.options.types.number.decimals || 2)));
366
+ }
367
+ break;
368
+ case "date":
369
+ val = new priv.ext.XDate(val, priv.options.types.date.utc === true).toString(priv.options.types.date.format || 'yyyy-MM-dd HH:mm:ss');
370
+ cell.html(format.f(val));
371
+ break;
372
+ case "bool":
373
+ $('<input type="checkbox" {0} disabled />'.f(val ? "checked" : "")).appendTo(cell);
374
+ break;
375
+ }
376
+ }
377
+ }
378
+ rowsAdded++;
379
+
380
+ //enough rows created?
381
+ if (rowsAdded >= priv.options.pageSize) {
382
+ _data.toRow = _data.fromRow + rowsAdded;
383
+ return false;
384
+ }
385
+ });
386
+
387
+ if (_currPage == 1) {
388
+ _pageSize = rowsAdded;
389
+ _totalPages = Math.round(Math.ceil(_data.rows.length / _pageSize));
390
+ }
391
+
392
+ //pad with empty rows if we're at last page.
393
+ if (_currPage == _totalPages) {
394
+ while (rowsAdded < _pageSize) {
395
+ var row = $('<tr></tr>').appendTo(_body);
396
+
397
+ if (_uniqueCol && priv.options.checkboxes) {
398
+ var cell = $('<td></td>').appendTo(row);
399
+ $('<input disabled type="checkbox" />').appendTo(cell);
400
+ }
401
+
402
+ $.each(_data.cols, function (column, props) {
403
+ if (!props.hidden) $('<td>&nbsp;</td>').appendTo(row);
404
+ });
405
+ rowsAdded++;
406
+ }
407
+ }
408
+ }
409
+
410
+ //create the footer
411
+ if (!_foot) {
412
+ _table.find('tfoot').remove();
413
+ _foot = $('<tfoot></tfoot>').insertAfter(_body);
414
+
415
+ var footRow = $('<tr></tr>').appendTo(_foot);
416
+ var footCell = $('<td colspan="999"></td>').appendTo(footRow);
417
+
418
+ //create summary
419
+ if (_data.rows.length > 0)
420
+ $(priv.options.pagingTemplate.f(_data.fromRow + 1, Math.min(_data.toRow, _data.rows.length), _data.rows.length)).appendTo(footCell);
421
+ else {
422
+ $('<p>No results</p>').appendTo(footCell);
423
+ _totalPages = 0;
424
+ }
425
+
426
+ //create the pager.
427
+ var lowerPage = _currPage - 2;
428
+ var upperPage = _currPage + 2;
429
+ if (upperPage > _totalPages) {
430
+ var diff = upperPage - _totalPages;
431
+ upperPage = _totalPages;
432
+ lowerPage -= diff;
433
+ }
434
+ if (lowerPage < 1) lowerPage = 1;
435
+ if (upperPage < 5) upperPage = 5;
436
+
437
+ var footToolbar = $('<div class="btn-toolbar"></div>').appendTo(footCell);
438
+ var footDiv = $('<div class="btn-group"></div>').appendTo(footToolbar);
439
+ var footPagerDiv = $('<div class="pagination"></div>').appendTo(footDiv);
440
+ var footPagerUl = $('<ul></ul>').appendTo(footPagerDiv);
441
+
442
+ $('<li class="{0}"><a href="#">«</a></li>'.f(_currPage == 1 ? 'disabled' : ''))
443
+ .on('click', {pageIndex: _currPage - 1}, priv.pageChanged).appendTo(footPagerUl);
444
+
445
+ for (var i = lowerPage; i <= upperPage; i++) {
446
+ var link;
447
+ if (i != _currPage) link = $('<li class="{1}"><a href="#">{0}</a></li>'.f(i, i > _totalPages ? 'disabled' : ''));
448
+ if (i == _currPage) link = $('<li class="active"><a href="#">{0}</a></li>'.f(i));
449
+
450
+ if (link) {
451
+ link.on('click', {pageIndex: i}, priv.pageChanged);
452
+ link.appendTo(footPagerUl);
453
+ }
454
+ }
455
+ $('<li class="{0}"><a href="#">»</a></li>'.f(_currPage == _totalPages ? 'disabled' : ''))
456
+ .on('click', {pageIndex: _currPage + 1}, priv.pageChanged).appendTo(footPagerUl);
457
+
458
+ //create pagesize dropdown
459
+ if (priv.options.pageSizes.length) {
460
+ var div = $('<div class="btn-group dropup pagesize"></div>').appendTo(footToolbar);
461
+ var btn = $('<button class="btn dropdown-toggle" data-toggle="dropdown" href="#">Rows&nbsp;</button>').appendTo(div);
462
+ var span = $('<span class="caret"></span>').appendTo(btn);
463
+ var ul = $('<ul class="dropdown-menu">').appendTo(div);
464
+
465
+ $.each(priv.options.pageSizes, function (index, val) {
466
+ var li = $('<li></li>').appendTo(ul);
467
+ $('<a href="#">{0}</a>'.f(val)).appendTo(li);
468
+ });
469
+ div.on('click', 'a', priv.pageSizeChanged);
470
+ }
471
+
472
+ //create columnpicker dropdown
473
+ if (priv.options.columnPicker) {
474
+ var div = $('<div class="btn-group dropup columnpicker"></div>').appendTo(footToolbar);
475
+ var btn = $('<button class="btn dropdown-toggle" data-toggle="dropdown" href="#">Columns&nbsp;</button>').appendTo(div);
476
+ var span = $('<span class="caret"></span>').appendTo(btn);
477
+ var ul = $('<ul class="dropdown-menu">').appendTo(div);
478
+
479
+ $.each(_data.cols, function (col, props) {
480
+ if (props.type != "unique") {
481
+ var li = $('<li></li>').appendTo(ul);
482
+ $('<input {0} type="checkbox" title="{1}" value="{1}" >&nbsp;{2}</input>'.f(props.hidden ? '' : 'checked', col, props.friendly || col)).appendTo(li);
483
+ }
484
+ });
485
+ div.on('click', 'input', priv.columnPickerClicked);
486
+ }
487
+
488
+ //create actions dropdown
489
+ if (priv.options.actions) {
490
+ var div = $('<div class="btn-group dropup actions"></div>').appendTo(footToolbar);
491
+ var btn = $('<button class="btn dropdown-toggle" data-toggle="dropdown" href="#"><i class="icon-list"></i>&nbsp;</button>').appendTo(div);
492
+ var span = $('<span class="caret"></span>').appendTo(btn);
493
+ var ul = $('<ul class="dropdown-menu">').appendTo(div);
494
+
495
+ if (priv.options.actions.filter) {
496
+ var li = $('<li></li>').appendTo(ul);
497
+ $('<input {0} type="checkbox" >&nbsp;Filter</input>'.f(priv.options.filter ? 'checked' : '')).appendTo(li);
498
+ li.on('click', 'input', function (e) {
499
+ priv.options.filter = !priv.options.filter;
500
+ _head = undefined;
501
+ priv.createTable();
502
+ });
503
+ }
504
+ if (priv.options.actions.columnPicker) {
505
+ var li = $('<li></li>').appendTo(ul);
506
+ $('<input {0} type="checkbox" >&nbsp;ColumnPicker</input>'.f(priv.options.columnPicker ? 'checked' : '')).appendTo(li);
507
+ li.on('click', 'input', function (e) {
508
+ priv.options.columnPicker = !priv.options.columnPicker;
509
+ _foot = undefined;
510
+ priv.createTable();
511
+ });
512
+ }
513
+ if (priv.options.actions.custom) {
514
+ $.each(priv.options.actions.custom, function (index, val) {
515
+ var li = $('<li></li>').appendTo(ul);
516
+ $(val).appendTo(li);
517
+ });
518
+ }
519
+ }
520
+ }
521
+
522
+ if (_data.rows.length == 0 && priv.options.hidePagerOnEmpty)
523
+ $('.btn-toolbar', _foot).remove();
524
+ if (priv.options.debug)
525
+ console.log('table created in {0} ms.'.f(new priv.ext.XDate() - start));
526
+ if (typeof priv.options.tableCreated == 'function')
527
+ priv.options.tableCreated.call(_table.get(0), {table: _table.get(0)});
528
+
529
+ };
530
+
531
+ /*
532
+ calls the webservice(if defined).
533
+ */
534
+ priv.update = function (callback, skipCols, resetChecked) {
535
+ if (!priv.options.url) {
536
+ if (priv.options.debug) console.log('no url found');
537
+ return;
538
+ }
539
+
540
+ if (priv.options.debug) console.log('requesting data from url:{0} data:{1}'.f(priv.options.url, JSON.stringify(priv.options.urlData) || ''));
541
+ var start = new priv.ext.XDate();
542
+
543
+ $.ajax({
544
+ url: priv.options.url,
545
+ type: priv.options.urlPost ? 'POST' : 'GET',
546
+ dataType: 'json',
547
+ contentType: "application/json; charset=utf-8",
548
+ data: priv.options.urlData,
549
+ async: true,
550
+ success: function (data) {
551
+ if (priv.options.debug) console.log('request finished in {0} ms.'.f(new priv.ext.XDate() - start));
552
+
553
+ //assign the new data
554
+ if (data.d && data.d.cols)
555
+ priv.setData(data.d, skipCols, resetChecked);
556
+ else
557
+ priv.setData(data, skipCols, resetChecked);
558
+ if (typeof callback == "function")
559
+ callback.call(this);
560
+ },
561
+ error: function (err) {
562
+ console.log('request error: '.f(err));
563
+ }
564
+ });
565
+ };
566
+
567
+ /*
568
+ assigns the new data.
569
+ */
570
+ priv.setData = function (pData, skipCols, resetChecked) {
571
+ var data = $.extend(true, {}, pData);
572
+ data.fromRow = _data && _data.fromRow || 0;
573
+ data.toRow = _data && _data.toRow || 0;
574
+
575
+ //use previous column definitions?
576
+ skipCols = skipCols || false;
577
+ if (skipCols) data.cols = _data.cols;
578
+ else _filterCols = {};
579
+
580
+ _data = data;
581
+ _data.rowsOrg = _data.rows;
582
+
583
+ //we might have more/less data now. Recalculate stuff.
584
+ if (_currPage > 1) {
585
+ _data.toRow = Math.min(_data.rows.length, _data.toRow);
586
+ _data.fromRow = _data.toRow - _pageSize;
587
+ _data.fromRow = Math.max(_data.fromRow, 0);
588
+ _currPage = Math.ceil(_data.fromRow / _pageSize) + 1;
589
+ _totalPages = Math.ceil(_data.rows.length / _pageSize);
590
+ } else {
591
+ _data.fromRow = 0;
592
+ if (priv.options.pageSize != -1)
593
+ _data.toRow = _data.fromRow + priv.options.pageSize;
594
+ _data.toRow = Math.max(_data.toRow, _data.rows.length);
595
+ }
596
+
597
+ //wash the new data a bit
598
+ _uniqueCol = "";
599
+ $.each(_data.cols, function (col, props) {
600
+ //set sorting
601
+ if (!_currSortCol && props.sortOrder) {
602
+ _currSortCol = col;
603
+ _currSortFlip = props.sortOrder != "asc";
604
+ }
605
+
606
+ //default to string type if missing
607
+ if (!props.type) _data.cols[col].type = "string";
608
+
609
+ //if several unique columns is defined, use the first.
610
+ if (props.unique) {
611
+ if (!_uniqueCol) _uniqueCol = col;
612
+ else props.unique = false;
613
+ }
614
+
615
+ //if index property is missing, create one
616
+ if (!props.index) _data.cols[col].index = new priv.ext.XDate();
617
+ props.column = col;
618
+
619
+ //set any initial filter
620
+ if (!skipCols) {
621
+ if (props.filter == undefined) props.filter = true;
622
+ if (props.filter && typeof props.type != "bool" && typeof props.filter != "boolean") {
623
+ _filterCols[col] = _filterCols[col] || {
624
+ filter: String(props.filter),
625
+ col: props
626
+ };
627
+ }
628
+ }
629
+ });
630
+
631
+ //keep any previously checked rows around?
632
+ if (resetChecked === true || resetChecked === undefined)
633
+ _uniqueCols = {};
634
+ else {
635
+ for (var key in _uniqueCols)
636
+ _uniqueCols[key] = priv.getRow(key);
637
+ }
638
+
639
+ if (_uniqueCol) {
640
+ //create a unique column definition
641
+ _data.cols["unique"] = {
642
+ column: "unique",
643
+ type: "unique",
644
+ index: -1,
645
+ hidden: true
646
+ };
647
+
648
+ //add rows that needs to be pre-checked
649
+ $.each(_data.rows, function (index, row) {
650
+ if (row["checked"] === true)
651
+ _uniqueCols[row[_uniqueCol]] = row;
652
+ });
653
+ }
654
+
655
+ _head = undefined;
656
+ _body = undefined;
657
+ _foot = undefined;
658
+ priv.filter();
659
+ priv.sort();
660
+ priv.createTable();
661
+ };
662
+
663
+ /*
664
+ filters the data.
665
+ */
666
+ priv.filter = function () {
667
+ if (!priv.options.filter) return;
668
+ if (Object.keys(_filterCols).length == 0) return;
669
+
670
+ //get a fresh copy of the data
671
+ _data.rows = $.extend(true, {}, _data.rowsOrg);
672
+ var start = new priv.ext.XDate();
673
+
674
+ //for every column with a filter, run through the rows and return the matching rows
675
+ $.each(_filterCols, function (col, colProps) {
676
+ if (priv.options.debug) console.log('filtering on text:{0} col:{1} type:{2} '.f(colProps.filter, colProps.col.column, colProps.col.type));
677
+
678
+ switch (colProps.col.type) {
679
+ case "string":
680
+ _data.rows = $.map(_data.rows, function (row) {
681
+ var val = String(row[col]);
682
+ var filter = colProps.filter.toLowerCase();
683
+ var ne = filter.charAt(0) == '!';
684
+ if (ne) filter = filter.substring(1);
685
+ var pos = val.toLowerCase().indexOf(filter);
686
+
687
+ if ((pos == -1 && ne) || colProps.filter === '') return row;
688
+ else if (row[col] != undefined && pos >= 0 && !ne) {
689
+ if (!row[col + 'Format'] && !colProps.col.format) {
690
+ var pre = val.substring(0, pos);
691
+ var match = val.substring(pos, pos + colProps.filter.length);
692
+ var post = val.substring(pos + colProps.filter.length, row[col].length);
693
+ row[col + 'Format'] = '<span>{0}<span class="filter">{1}</span>{2}</span>'.f(pre, match, post);
694
+ }
695
+ return row;
696
+ }
697
+ });
698
+ break;
699
+ case "number":
700
+ case "date":
701
+ _data.rows = $.map(_data.rows, function (row) {
702
+ var exp = colProps.filter.replace(/\s+/gi, ' ').split(' ');
703
+ exp = $(exp).filter(function(){return this});
704
+ var opArray = [">=", "<=", "..", ">", "<", "="];
705
+ var matches = 0;
706
+ var illegal = true;
707
+
708
+ $.each(exp, function (index, expr) {
709
+
710
+ for (var i = 0; i < opArray.length; i++) {
711
+
712
+ var op = opArray[i];
713
+ var pos = expr.indexOf(op);
714
+ var lval = expr.substring(0, pos);
715
+ var rval = expr.substring(pos + op.length);
716
+
717
+ if (pos == -1) continue;
718
+
719
+ illegal = ((lval.length + rval.length) == 0);
720
+ lval = parseFloat(lval);
721
+ rval = parseFloat(rval);
722
+ if (isNaN(lval)) lval = Number.NEGATIVE_INFINITY;
723
+ if (isNaN(rval)) rval = Number.MAX_VALUE;
724
+
725
+ if (colProps.col.type == "date") {
726
+ var today = new priv.ext.XDate(priv.options.types.date.utc === true).setHours(0, 0, 0, 0);
727
+ lval = today - (lval * -1) * (60 * 60 * 24 * 1000);
728
+ rval = today - (rval * -1) * (60 * 60 * 24 * 1000);
729
+ }
730
+
731
+ switch (op) {
732
+ case ">":
733
+ if (row[col] > rval) matches++;
734
+ break;
735
+ case ">=":
736
+ if (row[col] >= rval) matches++;
737
+ break;
738
+ case "<":
739
+ if (row[col] < rval) matches++;
740
+ break;
741
+ case "<=":
742
+ if (row[col] <= rval) matches++;
743
+ break;
744
+ case "=":
745
+ if (row[col] == rval) matches++;
746
+ break;
747
+ case "..":
748
+ if (colProps.col.type == "date") {
749
+ if (row[col] >= lval && row[col] < rval) matches++;
750
+ }
751
+ else {
752
+ if (row[col] >= lval && row[col] <= rval) matches++;
753
+ }
754
+ break;
755
+ default:
756
+ illegal = true;
757
+ }
758
+ break;
759
+ }
760
+ });
761
+ if ((exp.length == 1 && illegal) ||
762
+ (matches > 0 && illegal) ||
763
+ matches == exp.length ||
764
+ colProps.filter == '') return row;
765
+ });
766
+ break;
767
+ case "bool":
768
+ _data.rows = $.map(_data.rows, function (row) {
769
+ if (colProps.filter === '') return row;
770
+ if (row[col] != undefined && ((Boolean(row[col]) && colProps.filter) || (!Boolean(row[col]) && !colProps.filter))) return row;
771
+ });
772
+ break;
773
+ case "unique":
774
+ _data.rows = $.map(_data.rows, function (row) {
775
+ if (colProps.filter === '') return row;
776
+ var a = row[_uniqueCol];
777
+ var b = _uniqueCols[a] ? _uniqueCols[a][_uniqueCol] : '';
778
+ if ((colProps.filter && a === b) || (!colProps.filter && b === '')) return row;
779
+ });
780
+ break;
781
+ }
782
+ if (colProps.filter === '') delete _filterCols[colProps.col.column];
783
+ });
784
+ if (priv.options.debug) console.log('filtering finished in {0} ms.'.f(new priv.ext.XDate() - start));
785
+
786
+ _currPage = 1;
787
+ _data.fromRow = 0;
788
+ _body = undefined;
789
+ _foot = undefined;
790
+ };
791
+
792
+ /*
793
+ sorts the data on the current sorting column
794
+ */
795
+ priv.sort = function () {
796
+ if (!_data.cols[_currSortCol]) _currSortCol = "";
797
+ if (!_currSortCol) return;
798
+
799
+ var start = new priv.ext.XDate();
800
+ if (priv.options.debug) console.log('sorting on col:{0} order:{1}'.f(_currSortCol, _currSortFlip ? "desc" : "asc"));
801
+
802
+ var isString = (_data.cols[_currSortCol].type == "string");
803
+ _data.rows = _data.rows.sort(function (a, b) {
804
+
805
+ var valA = a[_currSortCol];
806
+ var valB = b[_currSortCol];
807
+
808
+ if (isString) {
809
+ if (valA == undefined) valA = '';
810
+ if (valB == undefined) valB = '';
811
+
812
+ if (String(valA).toLowerCase() == String(valB).toLowerCase()) return 0;
813
+ if (String(valA).toLowerCase() > String(valB).toLowerCase()) return _currSortFlip ? -1 : 1;
814
+ else return _currSortFlip ? 1 : -1;
815
+ } else {
816
+ if (valA == undefined) valA = Number.NEGATIVE_INFINITY;
817
+ if (valB == undefined) valB = Number.NEGATIVE_INFINITY;
818
+
819
+ if (valA == valB) return 0;
820
+ if (valA > valB) return _currSortFlip ? -1 : 1;
821
+ else return _currSortFlip ? 1 : -1;
822
+ }
823
+ });
824
+ if (priv.options.debug) console.log('sorting finished in {0} ms.'.f(new priv.ext.XDate() - start));
825
+ };
826
+
827
+ /*
828
+ helper that returns the underlying data by the unique value
829
+ */
830
+ priv.getRow = function (unique) {
831
+ var start = new priv.ext.XDate();
832
+ var row;
833
+ $.each(_data.rowsOrg, function (i, r) {
834
+ if (r[_uniqueCol] == unique) {
835
+ row = r;
836
+ return false;
837
+ }
838
+ });
839
+ if (priv.options.debug) console.log('row lookup finished in {0} ms.'.f(new priv.ext.XDate() - start));
840
+ return row;
841
+ };
842
+
843
+
844
+ /* Event Handlers
845
+ *************************************************************************/
846
+
847
+ /*
848
+ when: typing a filter
849
+ what: triggers filtering on the value
850
+ */
851
+ priv.filterChanged = function (e) {
852
+ //clear old timer if we're typing fast enough
853
+ if (_filterTimeout) {
854
+ clearTimeout(_filterTimeout);
855
+ if (priv.options.debug) console.log('filtering cancelled');
856
+ }
857
+
858
+ var filter = this.value;
859
+ var col = _data.cols[e.data.column];
860
+ var timeout = 200;
861
+
862
+ //boolean filters needs some special care
863
+ if (col.type == "bool" || col.type == "unique") {
864
+ timeout = 0;
865
+ var elem = $(this);
866
+ var cssClass = 'indeterminate';
867
+ if (elem.hasClass(cssClass)) {
868
+ e.preventDefault();
869
+ elem.removeClass(cssClass);
870
+ filter = true;
871
+ } else {
872
+ if (!elem.is(':checked')) {
873
+ filter = false;
874
+ } else {
875
+ elem.addClass(cssClass);
876
+ filter = '';
877
+ }
878
+ }
879
+ }
880
+
881
+ //add the filter to the filter array
882
+ _filterCols[col.column] = {
883
+ filter: filter,
884
+ col: col
885
+ };
886
+
887
+ //wait a few deciseconds before filtering
888
+ _filterTimeout = setTimeout(function () {
889
+ _filterTimeout = undefined;
890
+ priv.filter();
891
+ priv.sort();
892
+ priv.createTable();
893
+ }, timeout);
894
+ };
895
+
896
+ /*
897
+ when: changing page in pager
898
+ what: triggers table to be created with new page
899
+ */
900
+ priv.pageChanged = function (e) {
901
+ e.preventDefault();
902
+ if (e.data.pageIndex < 1 || e.data.pageIndex > _totalPages) return;
903
+
904
+ //set the new page
905
+ _currPage = e.data.pageIndex;
906
+ if (priv.options.debug) console.log('paging to index:{0}'.f(_currPage));
907
+
908
+ //find out what rows to create
909
+ _data.fromRow = ((_currPage - 1) * _pageSize);
910
+ _data.toRow = _data.fromRow + _pageSize;
911
+ if (_data.toRow > _data.rows.length) _data.toRow = _data.rows.length;
912
+
913
+ //trigger callback
914
+ if (typeof priv.options.pageChanged == 'function') {
915
+ priv.options.pageChanged.call(e.target, {
916
+ event: e,
917
+ page: _currPage
918
+ });
919
+ }
920
+
921
+ _body = undefined;
922
+ _foot = undefined;
923
+ priv.createTable();
924
+ };
925
+
926
+ /*
927
+ when: changing pagesize in pagesize dropdown
928
+ what: triggers table to be created with new pagesize
929
+ */
930
+ priv.pageSizeChanged = function (e) {
931
+ e.preventDefault();
932
+ var val = $(this).text().toLowerCase();
933
+ if (priv.options.debug) console.log('pagesize changed to:{0}'.f(val));
934
+
935
+ //set the new pagesize
936
+ if (val == "all") priv.options.pageSize = _data.rows.length;
937
+ else priv.options.pageSize = parseInt(val);
938
+
939
+ //revert to first page, as its gets messy otherwise.
940
+ _currPage = 1;
941
+ _data.fromRow = 0;
942
+ _data.toRow = _data.fromRow + priv.options.pageSize;
943
+ if (_data.toRow > _data.rows.length) _data.toRow = _data.rows.length;
944
+
945
+ //trigger callback
946
+ if (typeof priv.options.pageSizeChanged == 'function') {
947
+ priv.options.pageSizeChanged.call(e.target, {
948
+ event: e,
949
+ pageSize: priv.options.pageSize
950
+ });
951
+ }
952
+
953
+ _body = undefined;
954
+ _foot = undefined;
955
+ priv.createTable();
956
+ };
957
+
958
+ /*
959
+ when: clicking a column
960
+ what: triggers table to be sorted by the column
961
+ */
962
+ priv.columnClicked = function (e) {
963
+ e.preventDefault();
964
+ if (priv.options.debug) console.log('col:{0} clicked'.f(e.data.column));
965
+
966
+ //set the new sorting column
967
+ if (_currSortCol == e.data.column) _currSortFlip = !_currSortFlip;
968
+ _currSortCol = e.data.column;
969
+
970
+ //trigger callback
971
+ if (typeof priv.options.columnClicked == 'function') {
972
+ priv.options.columnClicked.call(e.target, {
973
+ event: e,
974
+ column: _data.cols[_currSortCol],
975
+ descending: _currSortFlip
976
+ });
977
+ }
978
+
979
+ _headSort = undefined;
980
+ _body = undefined;
981
+ priv.sort();
982
+ priv.createTable();
983
+ };
984
+
985
+ /*
986
+ when: clicking a column in columnpicker
987
+ what: triggers table to show/hide the column
988
+ */
989
+ priv.columnPickerClicked = function (e) {
990
+ e.stopPropagation();
991
+
992
+ var elem = $(this);
993
+ var col = elem.val();
994
+ if (priv.options.debug) console.log('col:{0} {1}'.f(col, elem.is(':checked') ? 'checked' : 'unchecked'));
995
+
996
+ //toggle column visibility
997
+ _data.cols[col].hidden = !_data.cols[col].hidden;
998
+
999
+ _data.cols[col].index = new priv.ext.XDate();
1000
+ _head = undefined;
1001
+ _body = undefined;
1002
+ priv.createTable();
1003
+ };
1004
+
1005
+ /*
1006
+ when: clicking the check-all checkbox
1007
+ what: toggles checked state on all rows, and adds/removes them from checked array
1008
+ */
1009
+ priv.checkToggleChanged = function (e) {
1010
+ var elem = $(this);
1011
+
1012
+ if (elem.is(':checked')) {
1013
+ var start = new priv.ext.XDate();
1014
+ //for every row(except non checkables), add it to the checked array
1015
+ $.each(_data.rows, function (index, props) {
1016
+ var row = _data.rows[index];
1017
+ if (row.checkable === false) return;
1018
+ _uniqueCols[props[_uniqueCol]] = row;
1019
+ });
1020
+ if (priv.options.debug) console.log('{0} rows checked in {1} ms.'.f(_data.rows.length, new priv.ext.XDate() - start));
1021
+ _checkToggleChecked = true;
1022
+ }
1023
+ else {
1024
+ var start = new priv.ext.XDate();
1025
+ //for every checked row(except non checkables), remove it from checked array
1026
+ for (var key in _uniqueCols) {
1027
+ var row = _uniqueCols[key];
1028
+ if (row.checkable === false)
1029
+ continue;
1030
+ else
1031
+ delete _uniqueCols[key];
1032
+ }
1033
+ if (priv.options.debug) console.log('{0} rows unchecked in {1} ms.'.f(_data.rows.length, new priv.ext.XDate() - start));
1034
+ _checkToggleChecked = false;
1035
+ }
1036
+ _body = undefined;
1037
+ priv.createTable();
1038
+ };
1039
+
1040
+ /*
1041
+ when: clicking a row checkbox
1042
+ what: toggles checked state on row, and add/removes it from checked array
1043
+ */
1044
+ priv.rowChecked = function (e) {
1045
+ var elem = $(this);
1046
+
1047
+ //get the row's unique value
1048
+ var unique = elem.closest('tr').data('unique');
1049
+ if (priv.options.debug) console.log('row({0}) {1}'.f(unique, elem.is(':checked') ? 'checked' : 'unchecked'));
1050
+
1051
+ //store the row in checked array
1052
+ if (elem.is(':checked')) _uniqueCols[unique] = priv.getRow(unique);
1053
+ else delete _uniqueCols[unique];
1054
+ };
1055
+
1056
+ /*
1057
+ when: clicking anywhere on a row
1058
+ what: row data and other info is returned to caller
1059
+ */
1060
+ priv.rowClicked = function (e) {
1061
+ if (!_uniqueCol) {
1062
+ if (priv.options.debug) console.log('no unique column specified');
1063
+ return;
1064
+ }
1065
+
1066
+ //gather callback data
1067
+ var elem = $(this);
1068
+ var column = _data.cols[elem.data('column')];
1069
+ var unique = elem.closest('tr').data('unique');
1070
+ var row = priv.getRow(unique);
1071
+ var isChecked = elem.closest('tr').find('.unique').is(':checked');
1072
+
1073
+ //trigger callback
1074
+ if (typeof priv.options.rowClicked == 'function') {
1075
+ priv.options.rowClicked.call(e.target, {
1076
+ event: e,
1077
+ row: row,
1078
+ column: column,
1079
+ checked: isChecked
1080
+ });
1081
+ }
1082
+
1083
+ };
1084
+
1085
+ /*
1086
+ when: clicking a datepicker operator
1087
+ what: sets the datepicker operator before a datepicker date is chosen.
1088
+ */
1089
+ priv.dpOpChanged = function(e) {
1090
+ if (priv.options.debug) console.log('dp oper:{0} clicked'.f(e.data.op));
1091
+ e.preventDefault();
1092
+ _currDpOp = e.data.op;
1093
+ };
1094
+
1095
+ /*
1096
+ when: clicking a datepicker date
1097
+ what: triggers filtering on the date
1098
+ */
1099
+ priv.dpClicked = function (e) {
1100
+ if (priv.options.debug) console.log('dp date:{0} clicked'.f(new priv.ext.XDate(e.date, priv.options.types.date.utc === true).toString('yyyy-MM-dd')));
1101
+
1102
+ e.preventDefault();
1103
+ input = $(this).prev('input.filter').get(0);
1104
+ Placeholders.disable(input); //Remove date placeholders for IE
1105
+
1106
+ var today = new priv.ext.XDate(false).setHours(0, 0, 0, 0);
1107
+ var daysDiff = Math.floor(e.date / (60 * 60 * 24 * 1000)) - Math.floor(today / (60 * 60 * 24 * 1000));
1108
+
1109
+ var filter = $(e.data.input);
1110
+ var op = "..";
1111
+ var pos = filter.val().indexOf(op);
1112
+ var lval = filter.val().substring(0, pos);
1113
+ var rval = filter.val().substring(pos + op.length);
1114
+
1115
+ if (_currDpOp == "l") lval = daysDiff;
1116
+ if (_currDpOp == "r") rval = daysDiff;
1117
+
1118
+ filter.val("{0}{1}{2}".f(lval, op, rval));
1119
+ Placeholders.enable(input);
1120
+ $(this).datepicker('hide');
1121
+ filter.trigger('keyup');
1122
+ };
1123
+
1124
+
1125
+ /* Public API
1126
+ *************************************************************************/
1127
+
1128
+ publ.init = function (options) {
1129
+ if (priv.options.debug) console.log('watable initialization...');
1130
+ //merge supplied options with defaults
1131
+ $.extend(priv.options, defaults, options);
1132
+ priv.init();
1133
+ return publ;
1134
+ };
1135
+
1136
+ publ.update = function (callback, skipCols, resetChecked) {
1137
+ if (priv.options.debug) console.log('publ.update called');
1138
+ priv.update(callback, skipCols, resetChecked);
1139
+ return publ;
1140
+ };
1141
+
1142
+ publ.getData = function (checked, filtered) {
1143
+ if (priv.options.debug) console.log('publ.getData called');
1144
+ checked = checked || false;
1145
+ filtered = filtered || false;
1146
+
1147
+ var data = $.extend(true, {}, _data);
1148
+ delete data.cols["unique"];
1149
+
1150
+ $.each(data.cols, function(col) {
1151
+ if (_filterCols[col]) data.cols[col].filter = _filterCols[col].filter;
1152
+ });
1153
+
1154
+ if (!filtered) {
1155
+ delete data.rows;
1156
+ data.rows = data.rowsOrg;
1157
+ }
1158
+ delete data.rowsOrg;
1159
+ delete data.fromRow;
1160
+ delete data.toRow;
1161
+
1162
+ if (checked) {
1163
+ delete data.rows;
1164
+ data.rows = $.map(_uniqueCols, function (val, index) {
1165
+ return val;
1166
+ });
1167
+ }
1168
+ return data;
1169
+ };
1170
+
1171
+ publ.setData = function (data, skipCols, resetChecked) {
1172
+ if (priv.options.debug) console.log('publ.setData called');
1173
+ priv.setData(data, skipCols, resetChecked);
1174
+ return publ;
1175
+ };
1176
+
1177
+ publ.option = function (option, val) {
1178
+ if (priv.options.debug) console.log('publ.option called');
1179
+ if (val == undefined) return priv.options[option];
1180
+ priv.options[option] = val;
1181
+ _head = undefined;
1182
+ _body = undefined;
1183
+ _foot = undefined;
1184
+ priv.createTable();
1185
+ return publ;
1186
+ };
1187
+
1188
+ return publ;
1189
+ };
1190
+
1191
+ $.fn.WATable = function (options) {
1192
+ options = options || {};
1193
+ return this.each(function () {
1194
+ options.id = this;
1195
+ $(this).data('WATable', new WATable().init(options));
1196
+ });
1197
+ };
1198
+
1199
+ String.prototype.format = String.prototype.f = function () {
1200
+ var s = this;
1201
+ i = arguments.length;
1202
+ while (i--) s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), arguments[i]);
1203
+ return s;
1204
+ };
1205
+
1206
+ //IE Polyfills
1207
+ /* placeholders.js */ (function(t){"use strict";function e(t,e,r){return t.addEventListener?t.addEventListener(e,r,!1):t.attachEvent?t.attachEvent("on"+e,r):void 0}function r(t,e){var r,n;for(r=0,n=t.length;n>r;r++)if(t[r]===e)return!0;return!1}function n(t,e){var r;t.createTextRange?(r=t.createTextRange(),r.move("character",e),r.select()):t.selectionStart&&(t.focus(),t.setSelectionRange(e,e))}function a(t,e){try{return t.type=e,!0}catch(r){return!1}}t.Placeholders={Utils:{addEventListener:e,inArray:r,moveCaret:n,changeType:a}}})(this),function(t){"use strict";function e(t){var e;return t.value===t.getAttribute(S)&&"true"===t.getAttribute(I)?(t.setAttribute(I,"false"),t.value="",t.className=t.className.replace(R,""),e=t.getAttribute(P),e&&(t.type=e),!0):!1}function r(t){var e,r=t.getAttribute(S);return""===t.value&&r?(t.setAttribute(I,"true"),t.value=r,t.className+=" "+k,e=t.getAttribute(P),e?t.type="text":"password"===t.type&&H.changeType(t,"text")&&t.setAttribute(P,"password"),!0):!1}function n(t,e){var r,n,a,u,i;if(t&&t.getAttribute(S))e(t);else for(r=t?t.getElementsByTagName("input"):v,n=t?t.getElementsByTagName("textarea"):b,i=0,u=r.length+n.length;u>i;i++)a=r.length>i?r[i]:n[i-r.length],e(a)}function a(t){n(t,e)}function u(t){n(t,r)}function i(t){return function(){f&&t.value===t.getAttribute(S)&&"true"===t.getAttribute(I)?H.moveCaret(t,0):e(t)}}function l(t){return function(){r(t)}}function c(t){return function(e){return p=t.value,"true"===t.getAttribute(I)?!(p===t.getAttribute(S)&&H.inArray(C,e.keyCode)):void 0}}function o(t){return function(){var e;"true"===t.getAttribute(I)&&t.value!==p&&(t.className=t.className.replace(R,""),t.value=t.value.replace(t.getAttribute(S),""),t.setAttribute(I,!1),e=t.getAttribute(P),e&&(t.type=e)),""===t.value&&(t.blur(),H.moveCaret(t,0))}}function s(t){return function(){t===document.activeElement&&t.value===t.getAttribute(S)&&"true"===t.getAttribute(I)&&H.moveCaret(t,0)}}function d(t){return function(){a(t)}}function g(t){t.form&&(x=t.form,x.getAttribute(U)||(H.addEventListener(x,"submit",d(x)),x.setAttribute(U,"true"))),H.addEventListener(t,"focus",i(t)),H.addEventListener(t,"blur",l(t)),f&&(H.addEventListener(t,"keydown",c(t)),H.addEventListener(t,"keyup",o(t)),H.addEventListener(t,"click",s(t))),t.setAttribute(j,"true"),t.setAttribute(S,y),r(t)}var v,b,f,h,p,m,A,y,E,x,T,N,L,w=["text","search","url","tel","email","password","number","textarea"],C=[27,33,34,35,36,37,38,39,40,8,46],B="#ccc",k="placeholdersjs",R=RegExp("\\b"+k+"\\b"),S="data-placeholder-value",I="data-placeholder-active",P="data-placeholder-type",U="data-placeholder-submit",j="data-placeholder-bound",V="data-placeholder-focus",q="data-placeholder-live",z=document.createElement("input"),D=document.getElementsByTagName("head")[0],F=document.documentElement,G=t.Placeholders,H=G.Utils;if(void 0===z.placeholder){for(v=document.getElementsByTagName("input"),b=document.getElementsByTagName("textarea"),f="false"===F.getAttribute(V),h="false"!==F.getAttribute(q),m=document.createElement("style"),m.type="text/css",A=document.createTextNode("."+k+" { color:"+B+"; }"),m.styleSheet?m.styleSheet.cssText=A.nodeValue:m.appendChild(A),D.insertBefore(m,D.firstChild),L=0,N=v.length+b.length;N>L;L++)T=v.length>L?v[L]:b[L-v.length],y=T.getAttribute("placeholder"),y&&H.inArray(w,T.type)&&g(T);E=setInterval(function(){for(L=0,N=v.length+b.length;N>L;L++)T=v.length>L?v[L]:b[L-v.length],y=T.getAttribute("placeholder"),y&&H.inArray(w,T.type)&&(T.getAttribute(j)||g(T),(y!==T.getAttribute(S)||"password"===T.type&&!T.getAttribute(P))&&("password"===T.type&&!T.getAttribute(P)&&H.changeType(T,"text")&&T.setAttribute(P,"password"),T.value===T.getAttribute(S)&&(T.value=y),T.setAttribute(S,y)));h||clearInterval(E)},100)}G.disable=a,G.enable=u}(this);
1208
+ Object.keys = Object.keys || function(o) { var result = []; for(var name in o) { if (o.hasOwnProperty(name)) result.push(name); } return result; };
1209
+ String.prototype.trim = String.prototype.trim || function () { return this.replace(/^\s+|\s+$/g,''); };
1210
+ Date.now = Date.now || function() { return +new Date; };
1211
+ console = window.console || { log:function(){} };
1212
+
1213
+ })(jQuery);