jquery-jtable-rails 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (127) hide show
  1. checksums.yaml +7 -0
  2. data/app/assets/javascripts/jtable/extensions/jquery.jtable.aspnetpagemethods.js +150 -0
  3. data/app/assets/javascripts/jtable/extensions/jquery.jtable.aspnetpagemethods.min.js +27 -0
  4. data/app/assets/javascripts/jtable/external/json2.js +486 -0
  5. data/app/assets/javascripts/jtable/external/json2.min.js +8 -0
  6. data/app/assets/javascripts/jtable/jquery.jtable.js +4791 -0
  7. data/app/assets/javascripts/jtable/localization/jquery.jtable.ca.js +30 -0
  8. data/app/assets/javascripts/jtable/localization/jquery.jtable.de.js +30 -0
  9. data/app/assets/javascripts/jtable/localization/jquery.jtable.es.js +30 -0
  10. data/app/assets/javascripts/jtable/localization/jquery.jtable.fa.js +30 -0
  11. data/app/assets/javascripts/jtable/localization/jquery.jtable.fr.js +30 -0
  12. data/app/assets/javascripts/jtable/localization/jquery.jtable.hr.js +30 -0
  13. data/app/assets/javascripts/jtable/localization/jquery.jtable.hu.js +30 -0
  14. data/app/assets/javascripts/jtable/localization/jquery.jtable.id.js +30 -0
  15. data/app/assets/javascripts/jtable/localization/jquery.jtable.it.js +30 -0
  16. data/app/assets/javascripts/jtable/localization/jquery.jtable.lt.js +30 -0
  17. data/app/assets/javascripts/jtable/localization/jquery.jtable.nl-NL.js +30 -0
  18. data/app/assets/javascripts/jtable/localization/jquery.jtable.pl.js +30 -0
  19. data/app/assets/javascripts/jtable/localization/jquery.jtable.pt-BR.js +30 -0
  20. data/app/assets/javascripts/jtable/localization/jquery.jtable.pt-PT.js +29 -0
  21. data/app/assets/javascripts/jtable/localization/jquery.jtable.ro.js +30 -0
  22. data/app/assets/javascripts/jtable/localization/jquery.jtable.ru.js +28 -0
  23. data/app/assets/javascripts/jtable/localization/jquery.jtable.se.js +30 -0
  24. data/app/assets/javascripts/jtable/localization/jquery.jtable.tr.js +30 -0
  25. data/app/assets/javascripts/jtable/localization/jquery.jtable.vi.js +28 -0
  26. data/app/assets/javascripts/jtable/localization/jquery.jtable.zh-CN.js +30 -0
  27. data/app/assets/stylesheets/jtable/themes/basic/close.png +0 -0
  28. data/app/assets/stylesheets/jtable/themes/basic/column-asc.png +0 -0
  29. data/app/assets/stylesheets/jtable/themes/basic/column-desc.png +0 -0
  30. data/app/assets/stylesheets/jtable/themes/basic/column-sortable.png +0 -0
  31. data/app/assets/stylesheets/jtable/themes/basic/delete.png +0 -0
  32. data/app/assets/stylesheets/jtable/themes/basic/edit.png +0 -0
  33. data/app/assets/stylesheets/jtable/themes/basic/jtable_basic.css +282 -0
  34. data/app/assets/stylesheets/jtable/themes/basic/jtable_basic.min.css +1 -0
  35. data/app/assets/stylesheets/jtable/themes/jqueryui/add.png +0 -0
  36. data/app/assets/stylesheets/jtable/themes/jqueryui/bg-thead.png +0 -0
  37. data/app/assets/stylesheets/jtable/themes/jqueryui/close.png +0 -0
  38. data/app/assets/stylesheets/jtable/themes/jqueryui/column-asc.png +0 -0
  39. data/app/assets/stylesheets/jtable/themes/jqueryui/column-desc.png +0 -0
  40. data/app/assets/stylesheets/jtable/themes/jqueryui/column-sortable.png +0 -0
  41. data/app/assets/stylesheets/jtable/themes/jqueryui/delete.png +0 -0
  42. data/app/assets/stylesheets/jtable/themes/jqueryui/edit.png +0 -0
  43. data/app/assets/stylesheets/jtable/themes/jqueryui/jtable_jqueryui.css +398 -0
  44. data/app/assets/stylesheets/jtable/themes/jqueryui/jtable_jqueryui.min.css +1 -0
  45. data/app/assets/stylesheets/jtable/themes/jqueryui/loading.gif +0 -0
  46. data/app/assets/stylesheets/jtable/themes/lightcolor/add.png +0 -0
  47. data/app/assets/stylesheets/jtable/themes/lightcolor/bg-thead.png +0 -0
  48. data/app/assets/stylesheets/jtable/themes/lightcolor/blue/jtable.css +521 -0
  49. data/app/assets/stylesheets/jtable/themes/lightcolor/blue/jtable.less +90 -0
  50. data/app/assets/stylesheets/jtable/themes/lightcolor/blue/jtable.min.css +1 -0
  51. data/app/assets/stylesheets/jtable/themes/lightcolor/blue/loading.gif +0 -0
  52. data/app/assets/stylesheets/jtable/themes/lightcolor/close.png +0 -0
  53. data/app/assets/stylesheets/jtable/themes/lightcolor/column-asc.png +0 -0
  54. data/app/assets/stylesheets/jtable/themes/lightcolor/column-desc.png +0 -0
  55. data/app/assets/stylesheets/jtable/themes/lightcolor/column-sortable.png +0 -0
  56. data/app/assets/stylesheets/jtable/themes/lightcolor/delete.png +0 -0
  57. data/app/assets/stylesheets/jtable/themes/lightcolor/edit.png +0 -0
  58. data/app/assets/stylesheets/jtable/themes/lightcolor/gray/jtable.css +521 -0
  59. data/app/assets/stylesheets/jtable/themes/lightcolor/gray/jtable.less +90 -0
  60. data/app/assets/stylesheets/jtable/themes/lightcolor/gray/jtable.min.css +1 -0
  61. data/app/assets/stylesheets/jtable/themes/lightcolor/gray/loading.gif +0 -0
  62. data/app/assets/stylesheets/jtable/themes/lightcolor/green/jtable.css +521 -0
  63. data/app/assets/stylesheets/jtable/themes/lightcolor/green/jtable.less +90 -0
  64. data/app/assets/stylesheets/jtable/themes/lightcolor/green/jtable.min.css +1 -0
  65. data/app/assets/stylesheets/jtable/themes/lightcolor/green/loading.gif +0 -0
  66. data/app/assets/stylesheets/jtable/themes/lightcolor/jtable_lightcolor_base.less +329 -0
  67. data/app/assets/stylesheets/jtable/themes/lightcolor/orange/jtable.css +521 -0
  68. data/app/assets/stylesheets/jtable/themes/lightcolor/orange/jtable.less +90 -0
  69. data/app/assets/stylesheets/jtable/themes/lightcolor/orange/jtable.min.css +1 -0
  70. data/app/assets/stylesheets/jtable/themes/lightcolor/orange/loading.gif +0 -0
  71. data/app/assets/stylesheets/jtable/themes/lightcolor/red/jtable.css +521 -0
  72. data/app/assets/stylesheets/jtable/themes/lightcolor/red/jtable.less +90 -0
  73. data/app/assets/stylesheets/jtable/themes/lightcolor/red/jtable.min.css +1 -0
  74. data/app/assets/stylesheets/jtable/themes/lightcolor/red/loading.gif +0 -0
  75. data/app/assets/stylesheets/jtable/themes/metro/add.png +0 -0
  76. data/app/assets/stylesheets/jtable/themes/metro/blue/jtable.css +495 -0
  77. data/app/assets/stylesheets/jtable/themes/metro/blue/jtable.less +11 -0
  78. data/app/assets/stylesheets/jtable/themes/metro/blue/jtable.min.css +1 -0
  79. data/app/assets/stylesheets/jtable/themes/metro/blue/loading.gif +0 -0
  80. data/app/assets/stylesheets/jtable/themes/metro/brown/jtable.css +495 -0
  81. data/app/assets/stylesheets/jtable/themes/metro/brown/jtable.less +11 -0
  82. data/app/assets/stylesheets/jtable/themes/metro/brown/jtable.min.css +1 -0
  83. data/app/assets/stylesheets/jtable/themes/metro/brown/loading.gif +0 -0
  84. data/app/assets/stylesheets/jtable/themes/metro/close.png +0 -0
  85. data/app/assets/stylesheets/jtable/themes/metro/column-asc.png +0 -0
  86. data/app/assets/stylesheets/jtable/themes/metro/column-desc.png +0 -0
  87. data/app/assets/stylesheets/jtable/themes/metro/column-sortable.png +0 -0
  88. data/app/assets/stylesheets/jtable/themes/metro/crimson/jtable.css +495 -0
  89. data/app/assets/stylesheets/jtable/themes/metro/crimson/jtable.less +11 -0
  90. data/app/assets/stylesheets/jtable/themes/metro/crimson/jtable.min.css +1 -0
  91. data/app/assets/stylesheets/jtable/themes/metro/crimson/loading.gif +0 -0
  92. data/app/assets/stylesheets/jtable/themes/metro/darkgray/jtable.css +495 -0
  93. data/app/assets/stylesheets/jtable/themes/metro/darkgray/jtable.less +11 -0
  94. data/app/assets/stylesheets/jtable/themes/metro/darkgray/jtable.min.css +1 -0
  95. data/app/assets/stylesheets/jtable/themes/metro/darkgray/loading.gif +0 -0
  96. data/app/assets/stylesheets/jtable/themes/metro/darkorange/jtable.css +495 -0
  97. data/app/assets/stylesheets/jtable/themes/metro/darkorange/jtable.less +11 -0
  98. data/app/assets/stylesheets/jtable/themes/metro/darkorange/jtable.min.css +1 -0
  99. data/app/assets/stylesheets/jtable/themes/metro/darkorange/loading.gif +0 -0
  100. data/app/assets/stylesheets/jtable/themes/metro/delete.png +0 -0
  101. data/app/assets/stylesheets/jtable/themes/metro/edit.png +0 -0
  102. data/app/assets/stylesheets/jtable/themes/metro/green/jtable.css +495 -0
  103. data/app/assets/stylesheets/jtable/themes/metro/green/jtable.less +11 -0
  104. data/app/assets/stylesheets/jtable/themes/metro/green/jtable.min.css +1 -0
  105. data/app/assets/stylesheets/jtable/themes/metro/green/loading.gif +0 -0
  106. data/app/assets/stylesheets/jtable/themes/metro/jtable_metro_base.css +48 -0
  107. data/app/assets/stylesheets/jtable/themes/metro/jtable_metro_base.min.css +1 -0
  108. data/app/assets/stylesheets/jtable/themes/metro/lightgray/jtable.css +495 -0
  109. data/app/assets/stylesheets/jtable/themes/metro/lightgray/jtable.less +11 -0
  110. data/app/assets/stylesheets/jtable/themes/metro/lightgray/jtable.min.css +1 -0
  111. data/app/assets/stylesheets/jtable/themes/metro/lightgray/loading.gif +0 -0
  112. data/app/assets/stylesheets/jtable/themes/metro/pink/jtable.css +495 -0
  113. data/app/assets/stylesheets/jtable/themes/metro/pink/jtable.less +11 -0
  114. data/app/assets/stylesheets/jtable/themes/metro/pink/jtable.min.css +1 -0
  115. data/app/assets/stylesheets/jtable/themes/metro/pink/loading.gif +0 -0
  116. data/app/assets/stylesheets/jtable/themes/metro/purple/jtable.css +495 -0
  117. data/app/assets/stylesheets/jtable/themes/metro/purple/jtable.less +11 -0
  118. data/app/assets/stylesheets/jtable/themes/metro/purple/jtable.min.css +1 -0
  119. data/app/assets/stylesheets/jtable/themes/metro/purple/loading.gif +0 -0
  120. data/app/assets/stylesheets/jtable/themes/metro/red/jtable.css +495 -0
  121. data/app/assets/stylesheets/jtable/themes/metro/red/jtable.less +11 -0
  122. data/app/assets/stylesheets/jtable/themes/metro/red/jtable.min.css +1 -0
  123. data/app/assets/stylesheets/jtable/themes/metro/red/loading.gif +0 -0
  124. data/lib/jquery-jtable-rails.rb +2 -0
  125. data/lib/jquery/jtable/rails/engine.rb +8 -0
  126. data/lib/jquery/jtable/rails/version.rb +7 -0
  127. metadata +182 -0
@@ -0,0 +1,8 @@
1
+ /* http://www.JSON.org */
2
+ "object"!==typeof JSON&&(JSON={});
3
+ (function(){function l(a){return 10>a?"0"+a:a}function q(a){r.lastIndex=0;return r.test(a)?'"'+a.replace(r,function(a){var c=t[a];return"string"===typeof c?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function n(a,k){var c,d,h,p,g=e,f,b=k[a];b&&("object"===typeof b&&"function"===typeof b.toJSON)&&(b=b.toJSON(a));"function"===typeof j&&(b=j.call(k,a,b));switch(typeof b){case "string":return q(b);case "number":return isFinite(b)?String(b):"null";case "boolean":case "null":return String(b);
4
+ case "object":if(!b)return"null";e+=m;f=[];if("[object Array]"===Object.prototype.toString.apply(b)){p=b.length;for(c=0;c<p;c+=1)f[c]=n(c,b)||"null";h=0===f.length?"[]":e?"[\n"+e+f.join(",\n"+e)+"\n"+g+"]":"["+f.join(",")+"]";e=g;return h}if(j&&"object"===typeof j){p=j.length;for(c=0;c<p;c+=1)"string"===typeof j[c]&&(d=j[c],(h=n(d,b))&&f.push(q(d)+(e?": ":":")+h))}else for(d in b)Object.prototype.hasOwnProperty.call(b,d)&&(h=n(d,b))&&f.push(q(d)+(e?": ":":")+h);h=0===f.length?"{}":e?"{\n"+e+f.join(",\n"+
5
+ e)+"\n"+g+"}":"{"+f.join(",")+"}";e=g;return h}}"function"!==typeof Date.prototype.toJSON&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+l(this.getUTCMonth()+1)+"-"+l(this.getUTCDate())+"T"+l(this.getUTCHours())+":"+l(this.getUTCMinutes())+":"+l(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()});var s=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
6
+ r=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,e,m,t={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},j;"function"!==typeof JSON.stringify&&(JSON.stringify=function(a,k,c){var d;m=e="";if("number"===typeof c)for(d=0;d<c;d+=1)m+=" ";else"string"===typeof c&&(m=c);if((j=k)&&"function"!==typeof k&&("object"!==typeof k||"number"!==typeof k.length))throw Error("JSON.stringify");return n("",{"":a})});
7
+ "function"!==typeof JSON.parse&&(JSON.parse=function(a,e){function c(a,d){var g,f,b=a[d];if(b&&"object"===typeof b)for(g in b)Object.prototype.hasOwnProperty.call(b,g)&&(f=c(b,g),void 0!==f?b[g]=f:delete b[g]);return e.call(a,d,b)}var d;a=String(a);s.lastIndex=0;s.test(a)&&(a=a.replace(s,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)}));if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
8
+ "]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return d=eval("("+a+")"),"function"===typeof e?c({"":d},""):d;throw new SyntaxError("JSON.parse");})})();
@@ -0,0 +1,4791 @@
1
+ /*
2
+
3
+ jTable 2.3.1
4
+ http://www.jtable.org
5
+
6
+ ---------------------------------------------------------------------------
7
+
8
+ Copyright (C) 2011-2013 by Halil İbrahim Kalkan (http://www.halilibrahimkalkan.com)
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in
18
+ all copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26
+ THE SOFTWARE.
27
+
28
+ */
29
+
30
+ /************************************************************************
31
+ * CORE jTable module *
32
+ *************************************************************************/
33
+ (function ($) {
34
+
35
+ var unloadingPage;
36
+
37
+ $(window).on('beforeunload', function () {
38
+ unloadingPage = true;
39
+ });
40
+ $(window).on('unload', function () {
41
+ unloadingPage = false;
42
+ });
43
+
44
+ $.widget("hik.jtable", {
45
+
46
+ /************************************************************************
47
+ * DEFAULT OPTIONS / EVENTS *
48
+ *************************************************************************/
49
+ options: {
50
+
51
+ //Options
52
+ actions: {},
53
+ fields: {},
54
+ animationsEnabled: true,
55
+ defaultDateFormat: 'yy-mm-dd',
56
+ dialogShowEffect: 'fade',
57
+ dialogHideEffect: 'fade',
58
+ showCloseButton: false,
59
+ loadingAnimationDelay: 500,
60
+ saveUserPreferences: true,
61
+ jqueryuiTheme: false,
62
+
63
+ ajaxSettings: {
64
+ type: 'POST',
65
+ dataType: 'json'
66
+ },
67
+
68
+ toolbar: {
69
+ hoverAnimation: true,
70
+ hoverAnimationDuration: 60,
71
+ hoverAnimationEasing: undefined,
72
+ items: []
73
+ },
74
+
75
+ //Events
76
+ closeRequested: function (event, data) { },
77
+ formCreated: function (event, data) { },
78
+ formSubmitting: function (event, data) { },
79
+ formClosed: function (event, data) { },
80
+ loadingRecords: function (event, data) { },
81
+ recordsLoaded: function (event, data) { },
82
+ rowInserted: function (event, data) { },
83
+ rowsRemoved: function (event, data) { },
84
+
85
+ //Localization
86
+ messages: {
87
+ serverCommunicationError: 'An error occured while communicating to the server.',
88
+ loadingMessage: 'Loading records...',
89
+ noDataAvailable: 'No data available!',
90
+ areYouSure: 'Are you sure?',
91
+ save: 'Save',
92
+ saving: 'Saving',
93
+ cancel: 'Cancel',
94
+ error: 'Error',
95
+ close: 'Close',
96
+ cannotLoadOptionsFor: 'Can not load options for field {0}'
97
+ }
98
+ },
99
+
100
+ /************************************************************************
101
+ * PRIVATE FIELDS *
102
+ *************************************************************************/
103
+
104
+ _$mainContainer: null, //Reference to the main container of all elements that are created by this plug-in (jQuery object)
105
+
106
+ _$titleDiv: null, //Reference to the title div (jQuery object)
107
+ _$toolbarDiv: null, //Reference to the toolbar div (jQuery object)
108
+
109
+ _$table: null, //Reference to the main <table> (jQuery object)
110
+ _$tableBody: null, //Reference to <body> in the table (jQuery object)
111
+ _$tableRows: null, //Array of all <tr> in the table (except "no data" row) (jQuery object array)
112
+
113
+ _$busyDiv: null, //Reference to the div that is used to block UI while busy (jQuery object)
114
+ _$busyMessageDiv: null, //Reference to the div that is used to show some message when UI is blocked (jQuery object)
115
+ _$errorDialogDiv: null, //Reference to the error dialog div (jQuery object)
116
+
117
+ _columnList: null, //Name of all data columns in the table (select column and command columns are not included) (string array)
118
+ _fieldList: null, //Name of all fields of a record (defined in fields option) (string array)
119
+ _keyField: null, //Name of the key field of a record (that is defined as 'key: true' in the fields option) (string)
120
+
121
+ _firstDataColumnOffset: 0, //Start index of first record field in table columns (some columns can be placed before first data column, such as select checkbox column) (integer)
122
+ _lastPostData: null, //Last posted data on load method (object)
123
+
124
+ _cache: null, //General purpose cache dictionary (object)
125
+
126
+ /************************************************************************
127
+ * CONSTRUCTOR AND INITIALIZATION METHODS *
128
+ *************************************************************************/
129
+
130
+ /* Contructor.
131
+ *************************************************************************/
132
+ _create: function () {
133
+
134
+ //Initialization
135
+ this._normalizeFieldsOptions();
136
+ this._initializeFields();
137
+ this._createFieldAndColumnList();
138
+
139
+ //Creating DOM elements
140
+ this._createMainContainer();
141
+ this._createTableTitle();
142
+ this._createToolBar();
143
+ this._createTable();
144
+ this._createBusyPanel();
145
+ this._createErrorDialogDiv();
146
+ this._addNoDataRow();
147
+
148
+ this._cookieKeyPrefix = this._generateCookieKeyPrefix();
149
+ },
150
+
151
+ /* Normalizes some options for all fields (sets default values).
152
+ *************************************************************************/
153
+ _normalizeFieldsOptions: function () {
154
+ var self = this;
155
+ $.each(self.options.fields, function (fieldName, props) {
156
+ self._normalizeFieldOptions(fieldName, props);
157
+ });
158
+ },
159
+
160
+ /* Normalizes some options for a field (sets default values).
161
+ *************************************************************************/
162
+ _normalizeFieldOptions: function (fieldName, props) {
163
+ if (props.listClass == undefined) {
164
+ props.listClass = '';
165
+ }
166
+ if (props.inputClass == undefined) {
167
+ props.inputClass = '';
168
+ }
169
+
170
+ //Convert dependsOn to array if it's a comma seperated lists
171
+ if (props.dependsOn && $.type(props.dependsOn) === 'string') {
172
+ var dependsOnArray = props.dependsOn.split(',');
173
+ props.dependsOn = [];
174
+ for (var i = 0; i < dependsOnArray.length; i++) {
175
+ props.dependsOn.push($.trim(dependsOnArray[i]));
176
+ }
177
+ }
178
+ },
179
+
180
+ /* Intializes some private variables.
181
+ *************************************************************************/
182
+ _initializeFields: function () {
183
+ this._lastPostData = {};
184
+ this._$tableRows = [];
185
+ this._columnList = [];
186
+ this._fieldList = [];
187
+ this._cache = [];
188
+ },
189
+
190
+ /* Fills _fieldList, _columnList arrays and sets _keyField variable.
191
+ *************************************************************************/
192
+ _createFieldAndColumnList: function () {
193
+ var self = this;
194
+
195
+ $.each(self.options.fields, function (name, props) {
196
+
197
+ //Add field to the field list
198
+ self._fieldList.push(name);
199
+
200
+ //Check if this field is the key field
201
+ if (props.key == true) {
202
+ self._keyField = name;
203
+ }
204
+
205
+ //Add field to column list if it is shown in the table
206
+ if (props.list != false && props.type != 'hidden') {
207
+ self._columnList.push(name);
208
+ }
209
+ });
210
+ },
211
+
212
+ /* Creates the main container div.
213
+ *************************************************************************/
214
+ _createMainContainer: function () {
215
+ this._$mainContainer = $('<div />')
216
+ .addClass('jtable-main-container')
217
+ .appendTo(this.element);
218
+
219
+ this._jqueryuiThemeAddClass(this._$mainContainer, 'ui-widget');
220
+ },
221
+
222
+ /* Creates title of the table if a title supplied in options.
223
+ *************************************************************************/
224
+ _createTableTitle: function () {
225
+ var self = this;
226
+
227
+ if (!self.options.title) {
228
+ return;
229
+ }
230
+
231
+ var $titleDiv = $('<div />')
232
+ .addClass('jtable-title')
233
+ .appendTo(self._$mainContainer);
234
+
235
+ self._jqueryuiThemeAddClass($titleDiv, 'ui-widget-header');
236
+
237
+ $('<div />')
238
+ .addClass('jtable-title-text')
239
+ .appendTo($titleDiv)
240
+ .append(self.options.title);
241
+
242
+ if (self.options.showCloseButton) {
243
+
244
+ var $textSpan = $('<span />')
245
+ .html(self.options.messages.close);
246
+
247
+ $('<button></button>')
248
+ .addClass('jtable-command-button jtable-close-button')
249
+ .attr('title', self.options.messages.close)
250
+ .append($textSpan)
251
+ .appendTo($titleDiv)
252
+ .click(function (e) {
253
+ e.preventDefault();
254
+ e.stopPropagation();
255
+ self._onCloseRequested();
256
+ });
257
+ }
258
+
259
+ self._$titleDiv = $titleDiv;
260
+ },
261
+
262
+ /* Creates the table.
263
+ *************************************************************************/
264
+ _createTable: function () {
265
+ this._$table = $('<table></table>')
266
+ .addClass('jtable')
267
+ .appendTo(this._$mainContainer);
268
+
269
+ if (this.options.tableId) {
270
+ this._$table.attr('id', this.options.tableId);
271
+ }
272
+
273
+ this._jqueryuiThemeAddClass(this._$table, 'ui-widget-content');
274
+
275
+ this._createTableHead();
276
+ this._createTableBody();
277
+ },
278
+
279
+ /* Creates header (all column headers) of the table.
280
+ *************************************************************************/
281
+ _createTableHead: function () {
282
+ var $thead = $('<thead></thead>')
283
+ .appendTo(this._$table);
284
+
285
+ this._addRowToTableHead($thead);
286
+ },
287
+
288
+ /* Adds tr element to given thead element
289
+ *************************************************************************/
290
+ _addRowToTableHead: function ($thead) {
291
+ var $tr = $('<tr></tr>')
292
+ .appendTo($thead);
293
+
294
+ this._addColumnsToHeaderRow($tr);
295
+ },
296
+
297
+ /* Adds column header cells to given tr element.
298
+ *************************************************************************/
299
+ _addColumnsToHeaderRow: function ($tr) {
300
+ for (var i = 0; i < this._columnList.length; i++) {
301
+ var fieldName = this._columnList[i];
302
+ var $headerCell = this._createHeaderCellForField(fieldName, this.options.fields[fieldName]);
303
+ $headerCell.appendTo($tr);
304
+ }
305
+ },
306
+
307
+ /* Creates a header cell for given field.
308
+ * Returns th jQuery object.
309
+ *************************************************************************/
310
+ _createHeaderCellForField: function (fieldName, field) {
311
+ field.width = field.width || '10%'; //default column width: 10%.
312
+
313
+ var $headerTextSpan = $('<span />')
314
+ .addClass('jtable-column-header-text')
315
+ .html(field.title);
316
+
317
+ var $headerContainerDiv = $('<div />')
318
+ .addClass('jtable-column-header-container')
319
+ .append($headerTextSpan);
320
+
321
+ var $th = $('<th></th>')
322
+ .addClass('jtable-column-header')
323
+ .addClass(field.listClass)
324
+ .css('width', field.width)
325
+ .data('fieldName', fieldName)
326
+ .append($headerContainerDiv);
327
+
328
+ this._jqueryuiThemeAddClass($th, 'ui-state-default');
329
+
330
+ return $th;
331
+ },
332
+
333
+ /* Creates an empty header cell that can be used as command column headers.
334
+ *************************************************************************/
335
+ _createEmptyCommandHeader: function () {
336
+ var $th = $('<th></th>')
337
+ .addClass('jtable-command-column-header')
338
+ .css('width', '1%');
339
+
340
+ this._jqueryuiThemeAddClass($th, 'ui-state-default');
341
+
342
+ return $th;
343
+ },
344
+
345
+ /* Creates tbody tag and adds to the table.
346
+ *************************************************************************/
347
+ _createTableBody: function () {
348
+ this._$tableBody = $('<tbody></tbody>').appendTo(this._$table);
349
+ },
350
+
351
+ /* Creates a div to block UI while jTable is busy.
352
+ *************************************************************************/
353
+ _createBusyPanel: function () {
354
+ this._$busyMessageDiv = $('<div />').addClass('jtable-busy-message').prependTo(this._$mainContainer);
355
+ this._$busyDiv = $('<div />').addClass('jtable-busy-panel-background').prependTo(this._$mainContainer);
356
+ this._jqueryuiThemeAddClass(this._$busyMessageDiv, 'ui-widget-header');
357
+ this._hideBusy();
358
+ },
359
+
360
+ /* Creates and prepares error dialog div.
361
+ *************************************************************************/
362
+ _createErrorDialogDiv: function () {
363
+ var self = this;
364
+
365
+ self._$errorDialogDiv = $('<div></div>').appendTo(self._$mainContainer);
366
+ self._$errorDialogDiv.dialog({
367
+ autoOpen: false,
368
+ show: self.options.dialogShowEffect,
369
+ hide: self.options.dialogHideEffect,
370
+ modal: true,
371
+ title: self.options.messages.error,
372
+ buttons: [{
373
+ text: self.options.messages.close,
374
+ click: function () {
375
+ self._$errorDialogDiv.dialog('close');
376
+ }
377
+ }]
378
+ });
379
+ },
380
+
381
+ /************************************************************************
382
+ * PUBLIC METHODS *
383
+ *************************************************************************/
384
+
385
+ /* Loads data using AJAX call, clears table and fills with new data.
386
+ *************************************************************************/
387
+ load: function (postData, completeCallback) {
388
+ this._lastPostData = postData;
389
+ this._reloadTable(completeCallback);
390
+ },
391
+
392
+ /* Refreshes (re-loads) table data with last postData.
393
+ *************************************************************************/
394
+ reload: function (completeCallback) {
395
+ this._reloadTable(completeCallback);
396
+ },
397
+
398
+ /* Gets a jQuery row object according to given record key
399
+ *************************************************************************/
400
+ getRowByKey: function (key) {
401
+ for (var i = 0; i < this._$tableRows.length; i++) {
402
+ if (key == this._getKeyValueOfRecord(this._$tableRows[i].data('record'))) {
403
+ return this._$tableRows[i];
404
+ }
405
+ }
406
+
407
+ return null;
408
+ },
409
+
410
+ /* Completely removes the table from it's container.
411
+ *************************************************************************/
412
+ destroy: function () {
413
+ this.element.empty();
414
+ $.Widget.prototype.destroy.call(this);
415
+ },
416
+
417
+ /************************************************************************
418
+ * PRIVATE METHODS *
419
+ *************************************************************************/
420
+
421
+ /* Used to change options dynamically after initialization.
422
+ *************************************************************************/
423
+ _setOption: function (key, value) {
424
+
425
+ },
426
+
427
+ /* LOADING RECORDS *****************************************************/
428
+
429
+ /* Performs an AJAX call to reload data of the table.
430
+ *************************************************************************/
431
+ _reloadTable: function (completeCallback) {
432
+ var self = this;
433
+
434
+ //Disable table since it's busy
435
+ self._showBusy(self.options.messages.loadingMessage, self.options.loadingAnimationDelay);
436
+
437
+ //Generate URL (with query string parameters) to load records
438
+ var loadUrl = self._createRecordLoadUrl();
439
+
440
+ //Load data from server
441
+ self._onLoadingRecords();
442
+ self._ajax({
443
+ url: loadUrl,
444
+ data: self._lastPostData,
445
+ success: function (data) {
446
+ self._hideBusy();
447
+
448
+ //Show the error message if server returns error
449
+ if (data.Result != 'OK') {
450
+ self._showError(data.Message);
451
+ return;
452
+ }
453
+
454
+ //Re-generate table rows
455
+ self._removeAllRows('reloading');
456
+ self._addRecordsToTable(data.Records);
457
+
458
+ self._onRecordsLoaded(data);
459
+
460
+ //Call complete callback
461
+ if (completeCallback) {
462
+ completeCallback();
463
+ }
464
+ },
465
+ error: function () {
466
+ self._hideBusy();
467
+ self._showError(self.options.messages.serverCommunicationError);
468
+ }
469
+ });
470
+ },
471
+
472
+ /* Creates URL to load records.
473
+ *************************************************************************/
474
+ _createRecordLoadUrl: function () {
475
+ return this.options.actions.listAction;
476
+ },
477
+
478
+ /* TABLE MANIPULATION METHODS *******************************************/
479
+
480
+ /* Creates a row from given record
481
+ *************************************************************************/
482
+ _createRowFromRecord: function (record) {
483
+ var $tr = $('<tr></tr>')
484
+ .addClass('jtable-data-row')
485
+ .attr('data-record-key', this._getKeyValueOfRecord(record))
486
+ .data('record', record);
487
+
488
+ this._addCellsToRowUsingRecord($tr);
489
+ return $tr;
490
+ },
491
+
492
+ /* Adds all cells to given row.
493
+ *************************************************************************/
494
+ _addCellsToRowUsingRecord: function ($row) {
495
+ var record = $row.data('record');
496
+ for (var i = 0; i < this._columnList.length; i++) {
497
+ this._createCellForRecordField(record, this._columnList[i])
498
+ .appendTo($row);
499
+ }
500
+ },
501
+
502
+ /* Create a cell for given field.
503
+ *************************************************************************/
504
+ _createCellForRecordField: function (record, fieldName) {
505
+ return $('<td></td>')
506
+ .addClass(this.options.fields[fieldName].listClass)
507
+ .append((this._getDisplayTextForRecordField(record, fieldName)));
508
+ },
509
+
510
+ /* Adds a list of records to the table.
511
+ *************************************************************************/
512
+ _addRecordsToTable: function (records) {
513
+ var self = this;
514
+
515
+ $.each(records, function (index, record) {
516
+ self._addRow(self._createRowFromRecord(record));
517
+ });
518
+
519
+ self._refreshRowStyles();
520
+ },
521
+
522
+ /* Adds a single row to the table.
523
+ * NOTE: THIS METHOD IS DEPRECATED AND WILL BE REMOVED FROM FEATURE RELEASES.
524
+ * USE _addRow METHOD.
525
+ *************************************************************************/
526
+ _addRowToTable: function ($tableRow, index, isNewRow, animationsEnabled) {
527
+ var options = {
528
+ index: this._normalizeNumber(index, 0, this._$tableRows.length, this._$tableRows.length)
529
+ };
530
+
531
+ if (isNewRow == true) {
532
+ options.isNewRow = true;
533
+ }
534
+
535
+ if (animationsEnabled == false) {
536
+ options.animationsEnabled = false;
537
+ }
538
+
539
+ this._addRow($tableRow, options);
540
+ },
541
+
542
+ /* Adds a single row to the table.
543
+ *************************************************************************/
544
+ _addRow: function ($row, options) {
545
+ //Set defaults
546
+ options = $.extend({
547
+ index: this._$tableRows.length,
548
+ isNewRow: false,
549
+ animationsEnabled: true
550
+ }, options);
551
+
552
+ //Remove 'no data' row if this is first row
553
+ if (this._$tableRows.length <= 0) {
554
+ this._removeNoDataRow();
555
+ }
556
+
557
+ //Add new row to the table according to it's index
558
+ options.index = this._normalizeNumber(options.index, 0, this._$tableRows.length, this._$tableRows.length);
559
+ if (options.index == this._$tableRows.length) {
560
+ //add as last row
561
+ this._$tableBody.append($row);
562
+ this._$tableRows.push($row);
563
+ } else if (options.index == 0) {
564
+ //add as first row
565
+ this._$tableBody.prepend($row);
566
+ this._$tableRows.unshift($row);
567
+ } else {
568
+ //insert to specified index
569
+ this._$tableRows[options.index - 1].after($row);
570
+ this._$tableRows.splice(options.index, 0, $row);
571
+ }
572
+
573
+ this._onRowInserted($row, options.isNewRow);
574
+
575
+ //Show animation if needed
576
+ if (options.isNewRow) {
577
+ this._refreshRowStyles();
578
+ if (this.options.animationsEnabled && options.animationsEnabled) {
579
+ this._showNewRowAnimation($row);
580
+ }
581
+ }
582
+ },
583
+
584
+ /* Shows created animation for a table row
585
+ * TODO: Make this animation cofigurable and changable
586
+ *************************************************************************/
587
+ _showNewRowAnimation: function ($tableRow) {
588
+ var className = 'jtable-row-created';
589
+ if (this.options.jqueryuiTheme) {
590
+ className = className + ' ui-state-highlight';
591
+ }
592
+
593
+ $tableRow.addClass(className, 'slow', '', function () {
594
+ $tableRow.removeClass(className, 5000);
595
+ });
596
+ },
597
+
598
+ /* Removes a row or rows (jQuery selection) from table.
599
+ *************************************************************************/
600
+ _removeRowsFromTable: function ($rows, reason) {
601
+ var self = this;
602
+
603
+ //Check if any row specified
604
+ if ($rows.length <= 0) {
605
+ return;
606
+ }
607
+
608
+ //remove from DOM
609
+ $rows.addClass('jtable-row-removed').remove();
610
+
611
+ //remove from _$tableRows array
612
+ $rows.each(function () {
613
+ var index = self._findRowIndex($(this));
614
+ if (index >= 0) {
615
+ self._$tableRows.splice(index, 1);
616
+ }
617
+ });
618
+
619
+ self._onRowsRemoved($rows, reason);
620
+
621
+ //Add 'no data' row if all rows removed from table
622
+ if (self._$tableRows.length == 0) {
623
+ self._addNoDataRow();
624
+ }
625
+
626
+ self._refreshRowStyles();
627
+ },
628
+
629
+ /* Finds index of a row in table.
630
+ *************************************************************************/
631
+ _findRowIndex: function ($row) {
632
+ return this._findIndexInArray($row, this._$tableRows, function ($row1, $row2) {
633
+ return $row1.data('record') == $row2.data('record');
634
+ });
635
+ },
636
+
637
+ /* Removes all rows in the table and adds 'no data' row.
638
+ *************************************************************************/
639
+ _removeAllRows: function (reason) {
640
+ //If no rows does exists, do nothing
641
+ if (this._$tableRows.length <= 0) {
642
+ return;
643
+ }
644
+
645
+ //Select all rows (to pass it on raising _onRowsRemoved event)
646
+ var $rows = this._$tableBody.find('tr.jtable-data-row');
647
+
648
+ //Remove all rows from DOM and the _$tableRows array
649
+ this._$tableBody.empty();
650
+ this._$tableRows = [];
651
+
652
+ this._onRowsRemoved($rows, reason);
653
+
654
+ //Add 'no data' row since we removed all rows
655
+ this._addNoDataRow();
656
+ },
657
+
658
+ /* Adds "no data available" row to the table.
659
+ *************************************************************************/
660
+ _addNoDataRow: function () {
661
+ if (this._$tableBody.find('>tr.jtable-no-data-row').length > 0) {
662
+ return;
663
+ }
664
+
665
+ var $tr = $('<tr></tr>')
666
+ .addClass('jtable-no-data-row')
667
+ .appendTo(this._$tableBody);
668
+
669
+ var totalColumnCount = this._$table.find('thead th').length;
670
+ $('<td></td>')
671
+ .attr('colspan', totalColumnCount)
672
+ .html(this.options.messages.noDataAvailable)
673
+ .appendTo($tr);
674
+ },
675
+
676
+ /* Removes "no data available" row from the table.
677
+ *************************************************************************/
678
+ _removeNoDataRow: function () {
679
+ this._$tableBody.find('.jtable-no-data-row').remove();
680
+ },
681
+
682
+ /* Refreshes styles of all rows in the table
683
+ *************************************************************************/
684
+ _refreshRowStyles: function () {
685
+ for (var i = 0; i < this._$tableRows.length; i++) {
686
+ if (i % 2 == 0) {
687
+ this._$tableRows[i].addClass('jtable-row-even');
688
+ } else {
689
+ this._$tableRows[i].removeClass('jtable-row-even');
690
+ }
691
+ }
692
+ },
693
+
694
+ /* RENDERING FIELD VALUES ***********************************************/
695
+
696
+ /* Gets text for a field of a record according to it's type.
697
+ *************************************************************************/
698
+ _getDisplayTextForRecordField: function (record, fieldName) {
699
+ var field = this.options.fields[fieldName];
700
+ var fieldValue = record[fieldName];
701
+
702
+ //if this is a custom field, call display function
703
+ if (field.display) {
704
+ return field.display({ record: record });
705
+ }
706
+
707
+ if (field.type == 'date') {
708
+ return this._getDisplayTextForDateRecordField(field, fieldValue);
709
+ } else if (field.type == 'checkbox') {
710
+ return this._getCheckBoxTextForFieldByValue(fieldName, fieldValue);
711
+ } else if (field.options) { //combobox or radio button list since there are options.
712
+ var options = this._getOptionsForField(fieldName, {
713
+ record: record,
714
+ value: fieldValue,
715
+ source: 'list',
716
+ dependedValues: this._createDependedValuesUsingRecord(record, field.dependsOn)
717
+ });
718
+ return this._findOptionByValue(options, fieldValue).DisplayText;
719
+ } else { //other types
720
+ return fieldValue;
721
+ }
722
+ },
723
+
724
+ /* Creates and returns an object that's properties are depended values of a record.
725
+ *************************************************************************/
726
+ _createDependedValuesUsingRecord: function (record, dependsOn) {
727
+ if (!dependsOn) {
728
+ return {};
729
+ }
730
+
731
+ var dependedValues = {};
732
+ for (var i = 0; i < dependsOn.length; i++) {
733
+ dependedValues[dependsOn[i]] = record[dependsOn[i]];
734
+ }
735
+
736
+ return dependedValues;
737
+ },
738
+
739
+ /* Finds an option object by given value.
740
+ *************************************************************************/
741
+ _findOptionByValue: function (options, value) {
742
+ for (var i = 0; i < options.length; i++) {
743
+ if (options[i].Value == value) {
744
+ return options[i];
745
+ }
746
+ }
747
+
748
+ return {}; //no option found
749
+ },
750
+
751
+ /* Gets text for a date field.
752
+ *************************************************************************/
753
+ _getDisplayTextForDateRecordField: function (field, fieldValue) {
754
+ if (!fieldValue) {
755
+ return '';
756
+ }
757
+
758
+ var displayFormat = field.displayFormat || this.options.defaultDateFormat;
759
+ var date = this._parseDate(fieldValue);
760
+ return $.datepicker.formatDate(displayFormat, date);
761
+ },
762
+
763
+ /* Gets options for a field according to user preferences.
764
+ *************************************************************************/
765
+ _getOptionsForField: function (fieldName, funcParams) {
766
+ var field = this.options.fields[fieldName];
767
+ var optionsSource = field.options;
768
+
769
+ if ($.isFunction(optionsSource)) {
770
+ //prepare parameter to the function
771
+ funcParams = $.extend(true, {
772
+ _cacheCleared: false,
773
+ dependedValues: {},
774
+ clearCache: function () {
775
+ this._cacheCleared = true;
776
+ }
777
+ }, funcParams);
778
+
779
+ //call function and get actual options source
780
+ optionsSource = optionsSource(funcParams);
781
+ }
782
+
783
+ var options;
784
+
785
+ //Build options according to it's source type
786
+ if (typeof optionsSource == 'string') { //It is an Url to download options
787
+ var cacheKey = 'options_' + fieldName + '_' + optionsSource; //create a unique cache key
788
+ if (funcParams._cacheCleared || (!this._cache[cacheKey])) {
789
+ //if user calls clearCache() or options are not found in the cache, download options
790
+ this._cache[cacheKey] = this._buildOptionsFromArray(this._downloadOptions(fieldName, optionsSource));
791
+ this._sortFieldOptions(this._cache[cacheKey], field.optionsSorting);
792
+ } else {
793
+ //found on cache..
794
+ //if this method (_getOptionsForField) is called to get option for a specific value (on funcParams.source == 'list')
795
+ //and this value is not in cached options, we need to re-download options to get the unfound (probably new) option.
796
+ if (funcParams.value != undefined) {
797
+ var optionForValue = this._findOptionByValue(this._cache[cacheKey], funcParams.value);
798
+ if (optionForValue.DisplayText == undefined) { //this value is not in cached options...
799
+ this._cache[cacheKey] = this._buildOptionsFromArray(this._downloadOptions(fieldName, optionsSource));
800
+ this._sortFieldOptions(this._cache[cacheKey], field.optionsSorting);
801
+ }
802
+ }
803
+ }
804
+
805
+ options = this._cache[cacheKey];
806
+ } else if (jQuery.isArray(optionsSource)) { //It is an array of options
807
+ options = this._buildOptionsFromArray(optionsSource);
808
+ this._sortFieldOptions(options, field.optionsSorting);
809
+ } else { //It is an object that it's properties are options
810
+ options = this._buildOptionsArrayFromObject(optionsSource);
811
+ this._sortFieldOptions(options, field.optionsSorting);
812
+ }
813
+
814
+ return options;
815
+ },
816
+
817
+ /* Download options for a field from server.
818
+ *************************************************************************/
819
+ _downloadOptions: function (fieldName, url) {
820
+ var self = this;
821
+ var options = [];
822
+
823
+ self._ajax({
824
+ url: url,
825
+ async: false,
826
+ success: function (data) {
827
+ if (data.Result != 'OK') {
828
+ self._showError(data.Message);
829
+ return;
830
+ }
831
+
832
+ options = data.Options;
833
+ },
834
+ error: function () {
835
+ var errMessage = self._formatString(self.options.messages.cannotLoadOptionsFor, fieldName);
836
+ self._showError(errMessage);
837
+ }
838
+ });
839
+
840
+ return options;
841
+ },
842
+
843
+ /* Sorts given options according to sorting parameter.
844
+ * sorting can be: 'value', 'value-desc', 'text' or 'text-desc'.
845
+ *************************************************************************/
846
+ _sortFieldOptions: function (options, sorting) {
847
+
848
+ if ((!options) || (!options.length) || (!sorting)) {
849
+ return;
850
+ }
851
+
852
+ //Determine using value of text
853
+ var dataSelector;
854
+ if (sorting.indexOf('value') == 0) {
855
+ dataSelector = function (option) {
856
+ return option.Value;
857
+ };
858
+ } else { //assume as text
859
+ dataSelector = function (option) {
860
+ return option.DisplayText;
861
+ };
862
+ }
863
+
864
+ var compareFunc;
865
+ if ($.type(dataSelector(options[0])) == 'string') {
866
+ compareFunc = function (option1, option2) {
867
+ return dataSelector(option1).localeCompare(dataSelector(option2));
868
+ };
869
+ } else { //asuume as numeric
870
+ compareFunc = function (option1, option2) {
871
+ return dataSelector(option1) - dataSelector(option2);
872
+ };
873
+ }
874
+
875
+ if (sorting.indexOf('desc') > 0) {
876
+ options.sort(function (a, b) {
877
+ return compareFunc(b, a);
878
+ });
879
+ } else { //assume as asc
880
+ options.sort(function (a, b) {
881
+ return compareFunc(a, b);
882
+ });
883
+ }
884
+ },
885
+
886
+ /* Creates an array of options from given object.
887
+ *************************************************************************/
888
+ _buildOptionsArrayFromObject: function (options) {
889
+ var list = [];
890
+
891
+ $.each(options, function (propName, propValue) {
892
+ list.push({
893
+ Value: propName,
894
+ DisplayText: propValue
895
+ });
896
+ });
897
+
898
+ return list;
899
+ },
900
+
901
+ /* Creates array of options from giving options array.
902
+ *************************************************************************/
903
+ _buildOptionsFromArray: function (optionsArray) {
904
+ var list = [];
905
+
906
+ for (var i = 0; i < optionsArray.length; i++) {
907
+ if ($.isPlainObject(optionsArray[i])) {
908
+ list.push(optionsArray[i]);
909
+ } else { //assumed as primitive type (int, string...)
910
+ list.push({
911
+ Value: optionsArray[i],
912
+ DisplayText: optionsArray[i]
913
+ });
914
+ }
915
+ }
916
+
917
+ return list;
918
+ },
919
+
920
+ /* Parses given date string to a javascript Date object.
921
+ * Given string must be formatted one of the samples shown below:
922
+ * /Date(1320259705710)/
923
+ * 2011-01-01 20:32:42 (YYYY-MM-DD HH:MM:SS)
924
+ * 2011-01-01 (YYYY-MM-DD)
925
+ *************************************************************************/
926
+ _parseDate: function (dateString) {
927
+ if (dateString.indexOf('Date') >= 0) { //Format: /Date(1320259705710)/
928
+ return new Date(
929
+ parseInt(dateString.substr(6), 10)
930
+ );
931
+ } else if (dateString.length == 10) { //Format: 2011-01-01
932
+ return new Date(
933
+ parseInt(dateString.substr(0, 4), 10),
934
+ parseInt(dateString.substr(5, 2), 10) - 1,
935
+ parseInt(dateString.substr(8, 2), 10)
936
+ );
937
+ } else if (dateString.length == 19) { //Format: 2011-01-01 20:32:42
938
+ return new Date(
939
+ parseInt(dateString.substr(0, 4), 10),
940
+ parseInt(dateString.substr(5, 2), 10) - 1,
941
+ parseInt(dateString.substr(8, 2, 10)),
942
+ parseInt(dateString.substr(11, 2), 10),
943
+ parseInt(dateString.substr(14, 2), 10),
944
+ parseInt(dateString.substr(17, 2), 10)
945
+ );
946
+ } else {
947
+ this._logWarn('Given date is not properly formatted: ' + dateString);
948
+ return 'format error!';
949
+ }
950
+ },
951
+
952
+ /* TOOL BAR *************************************************************/
953
+
954
+ /* Creates the toolbar.
955
+ *************************************************************************/
956
+ _createToolBar: function () {
957
+ this._$toolbarDiv = $('<div />')
958
+ .addClass('jtable-toolbar')
959
+ .appendTo(this._$titleDiv);
960
+
961
+ for (var i = 0; i < this.options.toolbar.items.length; i++) {
962
+ this._addToolBarItem(this.options.toolbar.items[i]);
963
+ }
964
+ },
965
+
966
+ /* Adds a new item to the toolbar.
967
+ *************************************************************************/
968
+ _addToolBarItem: function (item) {
969
+
970
+ //Check if item is valid
971
+ if ((item == undefined) || (item.text == undefined && item.icon == undefined)) {
972
+ this._logWarn('Can not add tool bar item since it is not valid!');
973
+ this._logWarn(item);
974
+ return null;
975
+ }
976
+
977
+ var $toolBarItem = $('<span></span>')
978
+ .addClass('jtable-toolbar-item')
979
+ .appendTo(this._$toolbarDiv);
980
+
981
+ this._jqueryuiThemeAddClass($toolBarItem, 'ui-widget ui-state-default ui-corner-all', 'ui-state-hover');
982
+
983
+ //cssClass property
984
+ if (item.cssClass) {
985
+ $toolBarItem
986
+ .addClass(item.cssClass);
987
+ }
988
+
989
+ //tooltip property
990
+ if (item.tooltip) {
991
+ $toolBarItem
992
+ .attr('title', item.tooltip);
993
+ }
994
+
995
+ //icon property
996
+ if (item.icon) {
997
+ var $icon = $('<span class="jtable-toolbar-item-icon"></span>').appendTo($toolBarItem);
998
+ if (item.icon === true) {
999
+ //do nothing
1000
+ } else if ($.type(item.icon === 'string')) {
1001
+ $icon.css('background', 'url("' + item.icon + '")');
1002
+ }
1003
+ }
1004
+
1005
+ //text property
1006
+ if (item.text) {
1007
+ $('<span class=""></span>')
1008
+ .html(item.text)
1009
+ .addClass('jtable-toolbar-item-text').appendTo($toolBarItem);
1010
+ }
1011
+
1012
+ //click event
1013
+ if (item.click) {
1014
+ $toolBarItem.click(function () {
1015
+ item.click();
1016
+ });
1017
+ }
1018
+
1019
+ //set hover animation parameters
1020
+ var hoverAnimationDuration = undefined;
1021
+ var hoverAnimationEasing = undefined;
1022
+ if (this.options.toolbar.hoverAnimation) {
1023
+ hoverAnimationDuration = this.options.toolbar.hoverAnimationDuration;
1024
+ hoverAnimationEasing = this.options.toolbar.hoverAnimationEasing;
1025
+ }
1026
+
1027
+ //change class on hover
1028
+ $toolBarItem.hover(function () {
1029
+ $toolBarItem.addClass('jtable-toolbar-item-hover', hoverAnimationDuration, hoverAnimationEasing);
1030
+ }, function () {
1031
+ $toolBarItem.removeClass('jtable-toolbar-item-hover', hoverAnimationDuration, hoverAnimationEasing);
1032
+ });
1033
+
1034
+ return $toolBarItem;
1035
+ },
1036
+
1037
+ /* ERROR DIALOG *********************************************************/
1038
+
1039
+ /* Shows error message dialog with given message.
1040
+ *************************************************************************/
1041
+ _showError: function (message) {
1042
+ this._$errorDialogDiv.html(message).dialog('open');
1043
+ },
1044
+
1045
+ /* BUSY PANEL ***********************************************************/
1046
+
1047
+ /* Shows busy indicator and blocks table UI.
1048
+ * TODO: Make this cofigurable and changable
1049
+ *************************************************************************/
1050
+ _setBusyTimer: null,
1051
+ _showBusy: function (message, delay) {
1052
+ var self = this; //
1053
+
1054
+ //Show a transparent overlay to prevent clicking to the table
1055
+ self._$busyDiv
1056
+ .width(self._$mainContainer.width())
1057
+ .height(self._$mainContainer.height())
1058
+ .addClass('jtable-busy-panel-background-invisible')
1059
+ .show();
1060
+
1061
+ var makeVisible = function () {
1062
+ self._$busyDiv.removeClass('jtable-busy-panel-background-invisible');
1063
+ self._$busyMessageDiv.html(message).show();
1064
+ };
1065
+
1066
+ if (delay) {
1067
+ if (self._setBusyTimer) {
1068
+ return;
1069
+ }
1070
+
1071
+ self._setBusyTimer = setTimeout(makeVisible, delay);
1072
+ } else {
1073
+ makeVisible();
1074
+ }
1075
+ },
1076
+
1077
+ /* Hides busy indicator and unblocks table UI.
1078
+ *************************************************************************/
1079
+ _hideBusy: function () {
1080
+ clearTimeout(this._setBusyTimer);
1081
+ this._setBusyTimer = null;
1082
+ this._$busyDiv.hide();
1083
+ this._$busyMessageDiv.html('').hide();
1084
+ },
1085
+
1086
+ /* Returns true if jTable is busy.
1087
+ *************************************************************************/
1088
+ _isBusy: function () {
1089
+ return this._$busyMessageDiv.is(':visible');
1090
+ },
1091
+
1092
+ /* Adds jQueryUI class to an item.
1093
+ *************************************************************************/
1094
+ _jqueryuiThemeAddClass: function ($elm, className, hoverClassName) {
1095
+ if (!this.options.jqueryuiTheme) {
1096
+ return;
1097
+ }
1098
+
1099
+ $elm.addClass(className);
1100
+
1101
+ if (hoverClassName) {
1102
+ $elm.hover(function () {
1103
+ $elm.addClass(hoverClassName);
1104
+ }, function () {
1105
+ $elm.removeClass(hoverClassName);
1106
+ });
1107
+ }
1108
+ },
1109
+
1110
+ /* COMMON METHODS *******************************************************/
1111
+
1112
+ /* Performs an AJAX call to specified URL.
1113
+ * THIS METHOD IS DEPRECATED AND WILL BE REMOVED FROM FEATURE RELEASES.
1114
+ * USE _ajax METHOD.
1115
+ *************************************************************************/
1116
+ _performAjaxCall: function (url, postData, async, success, error) {
1117
+ this._ajax({
1118
+ url: url,
1119
+ data: postData,
1120
+ async: async,
1121
+ success: success,
1122
+ error: error
1123
+ });
1124
+ },
1125
+
1126
+ /* This method is used to perform AJAX calls in jTable instead of direct
1127
+ * usage of jQuery.ajax method.
1128
+ *************************************************************************/
1129
+ _ajax: function (options) {
1130
+ var opts = $.extend({}, this.options.ajaxSettings, options);
1131
+
1132
+ //Override success
1133
+ opts.success = function (data) {
1134
+ if (options.success) {
1135
+ options.success(data);
1136
+ }
1137
+ };
1138
+
1139
+ //Override error
1140
+ opts.error = function (jqXHR, textStatus, errorThrown) {
1141
+ if (unloadingPage) {
1142
+ jqXHR.abort();
1143
+ return;
1144
+ }
1145
+
1146
+ if (options.error) {
1147
+ options.error(arguments);
1148
+ }
1149
+ };
1150
+
1151
+ //Override complete
1152
+ opts.complete = function () {
1153
+ if (options.complete) {
1154
+ options.complete();
1155
+ }
1156
+ };
1157
+
1158
+ $.ajax(opts);
1159
+ },
1160
+
1161
+ /* Gets value of key field of a record.
1162
+ *************************************************************************/
1163
+ _getKeyValueOfRecord: function (record) {
1164
+ return record[this._keyField];
1165
+ },
1166
+
1167
+ /************************************************************************
1168
+ * COOKIE *
1169
+ *************************************************************************/
1170
+
1171
+ /* Sets a cookie with given key.
1172
+ *************************************************************************/
1173
+ _setCookie: function (key, value) {
1174
+ key = this._cookieKeyPrefix + key;
1175
+
1176
+ var expireDate = new Date();
1177
+ expireDate.setDate(expireDate.getDate() + 30);
1178
+ document.cookie = encodeURIComponent(key) + '=' + encodeURIComponent(value) + "; expires=" + expireDate.toUTCString();
1179
+ },
1180
+
1181
+ /* Gets a cookie with given key.
1182
+ *************************************************************************/
1183
+ _getCookie: function (key) {
1184
+ key = this._cookieKeyPrefix + key;
1185
+
1186
+ var equalities = document.cookie.split('; ');
1187
+ for (var i = 0; i < equalities.length; i++) {
1188
+ if (!equalities[i]) {
1189
+ continue;
1190
+ }
1191
+
1192
+ var splitted = equalities[i].split('=');
1193
+ if (splitted.length != 2) {
1194
+ continue;
1195
+ }
1196
+
1197
+ if (decodeURIComponent(splitted[0]) === key) {
1198
+ return decodeURIComponent(splitted[1] || '');
1199
+ }
1200
+ }
1201
+
1202
+ return null;
1203
+ },
1204
+
1205
+ /* Generates a hash key to be prefix for all cookies for this jtable instance.
1206
+ *************************************************************************/
1207
+ _generateCookieKeyPrefix: function () {
1208
+
1209
+ var simpleHash = function (value) {
1210
+ var hash = 0;
1211
+ if (value.length == 0) {
1212
+ return hash;
1213
+ }
1214
+
1215
+ for (var i = 0; i < value.length; i++) {
1216
+ var ch = value.charCodeAt(i);
1217
+ hash = ((hash << 5) - hash) + ch;
1218
+ hash = hash & hash;
1219
+ }
1220
+
1221
+ return hash;
1222
+ };
1223
+
1224
+ var strToHash = '';
1225
+ if (this.options.tableId) {
1226
+ strToHash = strToHash + this.options.tableId + '#';
1227
+ }
1228
+
1229
+ strToHash = strToHash + this._columnList.join('$') + '#c' + this._$table.find('thead th').length;
1230
+ return 'jtable#' + simpleHash(strToHash);
1231
+ },
1232
+
1233
+ /************************************************************************
1234
+ * EVENT RAISING METHODS *
1235
+ *************************************************************************/
1236
+
1237
+ _onLoadingRecords: function () {
1238
+ this._trigger("loadingRecords", null, {});
1239
+ },
1240
+
1241
+ _onRecordsLoaded: function (data) {
1242
+ this._trigger("recordsLoaded", null, { records: data.Records, serverResponse: data });
1243
+ },
1244
+
1245
+ _onRowInserted: function ($row, isNewRow) {
1246
+ this._trigger("rowInserted", null, { row: $row, record: $row.data('record'), isNewRow: isNewRow });
1247
+ },
1248
+
1249
+ _onRowsRemoved: function ($rows, reason) {
1250
+ this._trigger("rowsRemoved", null, { rows: $rows, reason: reason });
1251
+ },
1252
+
1253
+ _onCloseRequested: function () {
1254
+ this._trigger("closeRequested", null, {});
1255
+ }
1256
+
1257
+ });
1258
+
1259
+ }(jQuery));
1260
+
1261
+
1262
+ /************************************************************************
1263
+ * Some UTULITY methods used by jTable *
1264
+ *************************************************************************/
1265
+ (function ($) {
1266
+
1267
+ $.extend(true, $.hik.jtable.prototype, {
1268
+
1269
+ /* Gets property value of an object recursively.
1270
+ *************************************************************************/
1271
+ _getPropertyOfObject: function (obj, propName) {
1272
+ if (propName.indexOf('.') < 0) {
1273
+ return obj[propName];
1274
+ } else {
1275
+ var preDot = propName.substring(0, propName.indexOf('.'));
1276
+ var postDot = propName.substring(propName.indexOf('.') + 1);
1277
+ return this._getPropertyOfObject(obj[preDot], postDot);
1278
+ }
1279
+ },
1280
+
1281
+ /* Sets property value of an object recursively.
1282
+ *************************************************************************/
1283
+ _setPropertyOfObject: function (obj, propName, value) {
1284
+ if (propName.indexOf('.') < 0) {
1285
+ obj[propName] = value;
1286
+ } else {
1287
+ var preDot = propName.substring(0, propName.indexOf('.'));
1288
+ var postDot = propName.substring(propName.indexOf('.') + 1);
1289
+ this._setPropertyOfObject(obj[preDot], postDot, value);
1290
+ }
1291
+ },
1292
+
1293
+ /* Inserts a value to an array if it does not exists in the array.
1294
+ *************************************************************************/
1295
+ _insertToArrayIfDoesNotExists: function (array, value) {
1296
+ if ($.inArray(value, array) < 0) {
1297
+ array.push(value);
1298
+ }
1299
+ },
1300
+
1301
+ /* Finds index of an element in an array according to given comparision function
1302
+ *************************************************************************/
1303
+ _findIndexInArray: function (value, array, compareFunc) {
1304
+
1305
+ //If not defined, use default comparision
1306
+ if (!compareFunc) {
1307
+ compareFunc = function (a, b) {
1308
+ return a == b;
1309
+ };
1310
+ }
1311
+
1312
+ for (var i = 0; i < array.length; i++) {
1313
+ if (compareFunc(value, array[i])) {
1314
+ return i;
1315
+ }
1316
+ }
1317
+
1318
+ return -1;
1319
+ },
1320
+
1321
+ /* Normalizes a number between given bounds or sets to a defaultValue
1322
+ * if it is undefined
1323
+ *************************************************************************/
1324
+ _normalizeNumber: function (number, min, max, defaultValue) {
1325
+ if (number == undefined || number == null || isNaN(number)) {
1326
+ return defaultValue;
1327
+ }
1328
+
1329
+ if (number < min) {
1330
+ return min;
1331
+ }
1332
+
1333
+ if (number > max) {
1334
+ return max;
1335
+ }
1336
+
1337
+ return number;
1338
+ },
1339
+
1340
+ /* Formats a string just like string.format in c#.
1341
+ * Example:
1342
+ * _formatString('Hello {0}','Halil') = 'Hello Halil'
1343
+ *************************************************************************/
1344
+ _formatString: function () {
1345
+ if (arguments.length == 0) {
1346
+ return null;
1347
+ }
1348
+
1349
+ var str = arguments[0];
1350
+ for (var i = 1; i < arguments.length; i++) {
1351
+ var placeHolder = '{' + (i - 1) + '}';
1352
+ str = str.replace(placeHolder, arguments[i]);
1353
+ }
1354
+
1355
+ return str;
1356
+ },
1357
+
1358
+ //Logging methods ////////////////////////////////////////////////////////
1359
+
1360
+ _logDebug: function (text) {
1361
+ if (!window.console) {
1362
+ return;
1363
+ }
1364
+
1365
+ console.log('jTable DEBUG: ' + text);
1366
+ },
1367
+
1368
+ _logInfo: function (text) {
1369
+ if (!window.console) {
1370
+ return;
1371
+ }
1372
+
1373
+ console.log('jTable INFO: ' + text);
1374
+ },
1375
+
1376
+ _logWarn: function (text) {
1377
+ if (!window.console) {
1378
+ return;
1379
+ }
1380
+
1381
+ console.log('jTable WARNING: ' + text);
1382
+ },
1383
+
1384
+ _logError: function (text) {
1385
+ if (!window.console) {
1386
+ return;
1387
+ }
1388
+
1389
+ console.log('jTable ERROR: ' + text);
1390
+ }
1391
+
1392
+ });
1393
+
1394
+ /* Fix for array.indexOf method in IE7.
1395
+ * This code is taken from http://www.tutorialspoint.com/javascript/array_indexof.htm */
1396
+ if (!Array.prototype.indexOf) {
1397
+ Array.prototype.indexOf = function (elt) {
1398
+ var len = this.length;
1399
+ var from = Number(arguments[1]) || 0;
1400
+ from = (from < 0)
1401
+ ? Math.ceil(from)
1402
+ : Math.floor(from);
1403
+ if (from < 0)
1404
+ from += len;
1405
+ for (; from < len; from++) {
1406
+ if (from in this &&
1407
+ this[from] === elt)
1408
+ return from;
1409
+ }
1410
+ return -1;
1411
+ };
1412
+ }
1413
+
1414
+ })(jQuery);
1415
+
1416
+
1417
+ /************************************************************************
1418
+ * FORMS extension for jTable (base for edit/create forms) *
1419
+ *************************************************************************/
1420
+ (function ($) {
1421
+
1422
+ $.extend(true, $.hik.jtable.prototype, {
1423
+
1424
+ /************************************************************************
1425
+ * PRIVATE METHODS *
1426
+ *************************************************************************/
1427
+
1428
+ /* Submits a form asynchronously using AJAX.
1429
+ * This method is needed, since form submitting logic can be overrided
1430
+ * by extensions.
1431
+ *************************************************************************/
1432
+ _submitFormUsingAjax: function (url, formData, success, error) {
1433
+ this._ajax({
1434
+ url: url,
1435
+ data: formData,
1436
+ success: success,
1437
+ error: error
1438
+ });
1439
+ },
1440
+
1441
+ /* Creates label for an input element.
1442
+ *************************************************************************/
1443
+ _createInputLabelForRecordField: function (fieldName) {
1444
+ //TODO: May create label tag instead of a div.
1445
+ return $('<div />')
1446
+ .addClass('jtable-input-label')
1447
+ .html(this.options.fields[fieldName].inputTitle || this.options.fields[fieldName].title);
1448
+ },
1449
+
1450
+ /* Creates an input element according to field type.
1451
+ *************************************************************************/
1452
+ _createInputForRecordField: function (funcParams) {
1453
+ var fieldName = funcParams.fieldName,
1454
+ value = funcParams.value,
1455
+ record = funcParams.record,
1456
+ formType = funcParams.formType,
1457
+ form = funcParams.form;
1458
+
1459
+ //Get the field
1460
+ var field = this.options.fields[fieldName];
1461
+
1462
+ //If value if not supplied, use defaultValue of the field
1463
+ if (value == undefined || value == null) {
1464
+ value = field.defaultValue;
1465
+ }
1466
+
1467
+ //Use custom function if supplied
1468
+ if (field.input) {
1469
+ var $input = $(field.input({
1470
+ value: value,
1471
+ record: record,
1472
+ formType: formType,
1473
+ form: form
1474
+ }));
1475
+
1476
+ //Add id attribute if does not exists
1477
+ if (!$input.attr('id')) {
1478
+ $input.attr('id', 'Edit-' + fieldName);
1479
+ }
1480
+
1481
+ //Wrap input element with div
1482
+ return $('<div />')
1483
+ .addClass('jtable-input jtable-custom-input')
1484
+ .append($input);
1485
+ }
1486
+
1487
+ //Create input according to field type
1488
+ if (field.type == 'date') {
1489
+ return this._createDateInputForField(field, fieldName, value);
1490
+ } else if (field.type == 'textarea') {
1491
+ return this._createTextAreaForField(field, fieldName, value);
1492
+ } else if (field.type == 'password') {
1493
+ return this._createPasswordInputForField(field, fieldName, value);
1494
+ } else if (field.type == 'checkbox') {
1495
+ return this._createCheckboxForField(field, fieldName, value);
1496
+ } else if (field.options) {
1497
+ if (field.type == 'radiobutton') {
1498
+ return this._createRadioButtonListForField(field, fieldName, value, record, formType);
1499
+ } else {
1500
+ return this._createDropDownListForField(field, fieldName, value, record, formType, form);
1501
+ }
1502
+ } else {
1503
+ return this._createTextInputForField(field, fieldName, value);
1504
+ }
1505
+ },
1506
+
1507
+ //Creates a hidden input element with given name and value.
1508
+ _createInputForHidden: function (fieldName, value) {
1509
+ if (value == undefined) {
1510
+ value = "";
1511
+ }
1512
+
1513
+ return $('<input type="hidden" name="' + fieldName + '" id="Edit-' + fieldName + '"></input>')
1514
+ .val(value);
1515
+ },
1516
+
1517
+ /* Creates a date input for a field.
1518
+ *************************************************************************/
1519
+ _createDateInputForField: function (field, fieldName, value) {
1520
+ var $input = $('<input class="' + field.inputClass + '" id="Edit-' + fieldName + '" type="text" name="' + fieldName + '"></input>');
1521
+ if(value != undefined) {
1522
+ $input.val(value);
1523
+ }
1524
+
1525
+ var displayFormat = field.displayFormat || this.options.defaultDateFormat;
1526
+ $input.datepicker({ dateFormat: displayFormat });
1527
+ return $('<div />')
1528
+ .addClass('jtable-input jtable-date-input')
1529
+ .append($input);
1530
+ },
1531
+
1532
+ /* Creates a textarea element for a field.
1533
+ *************************************************************************/
1534
+ _createTextAreaForField: function (field, fieldName, value) {
1535
+ var $textArea = $('<textarea class="' + field.inputClass + '" id="Edit-' + fieldName + '" name="' + fieldName + '"></textarea>');
1536
+ if (value != undefined) {
1537
+ $textArea.val(value);
1538
+ }
1539
+
1540
+ return $('<div />')
1541
+ .addClass('jtable-input jtable-textarea-input')
1542
+ .append($textArea);
1543
+ },
1544
+
1545
+ /* Creates a standart textbox for a field.
1546
+ *************************************************************************/
1547
+ _createTextInputForField: function (field, fieldName, value) {
1548
+ var $input = $('<input class="' + field.inputClass + '" id="Edit-' + fieldName + '" type="text" name="' + fieldName + '"></input>');
1549
+ if (value != undefined) {
1550
+ $input.val(value);
1551
+ }
1552
+
1553
+ return $('<div />')
1554
+ .addClass('jtable-input jtable-text-input')
1555
+ .append($input);
1556
+ },
1557
+
1558
+ /* Creates a password input for a field.
1559
+ *************************************************************************/
1560
+ _createPasswordInputForField: function (field, fieldName, value) {
1561
+ var $input = $('<input class="' + field.inputClass + '" id="Edit-' + fieldName + '" type="password" name="' + fieldName + '"></input>');
1562
+ if (value != undefined) {
1563
+ $input.val(value);
1564
+ }
1565
+
1566
+ return $('<div />')
1567
+ .addClass('jtable-input jtable-password-input')
1568
+ .append($input);
1569
+ },
1570
+
1571
+ /* Creates a checkboxfor a field.
1572
+ *************************************************************************/
1573
+ _createCheckboxForField: function (field, fieldName, value) {
1574
+ var self = this;
1575
+
1576
+ //If value is undefined, get unchecked state's value
1577
+ if (value == undefined) {
1578
+ value = self._getCheckBoxPropertiesForFieldByState(fieldName, false).Value;
1579
+ }
1580
+
1581
+ //Create a container div
1582
+ var $containerDiv = $('<div />')
1583
+ .addClass('jtable-input jtable-checkbox-input');
1584
+
1585
+ //Create checkbox and check if needed
1586
+ var $checkBox = $('<input class="' + field.inputClass + '" id="Edit-' + fieldName + '" type="checkbox" name="' + fieldName + '" />')
1587
+ .appendTo($containerDiv);
1588
+ if (value != undefined) {
1589
+ $checkBox.val(value);
1590
+ }
1591
+
1592
+ //Create display text of checkbox for current state
1593
+ var $textSpan = $('<span>' + (field.formText || self._getCheckBoxTextForFieldByValue(fieldName, value)) + '</span>')
1594
+ .appendTo($containerDiv);
1595
+
1596
+ //Check the checkbox if it's value is checked-value
1597
+ if (self._getIsCheckBoxSelectedForFieldByValue(fieldName, value)) {
1598
+ $checkBox.attr('checked', 'checked');
1599
+ }
1600
+
1601
+ //This method sets checkbox's value and text according to state of the checkbox
1602
+ var refreshCheckBoxValueAndText = function () {
1603
+ var checkboxProps = self._getCheckBoxPropertiesForFieldByState(fieldName, $checkBox.is(':checked'));
1604
+ $checkBox.attr('value', checkboxProps.Value);
1605
+ $textSpan.html(field.formText || checkboxProps.DisplayText);
1606
+ };
1607
+
1608
+ //Register to click event to change display text when state of checkbox is changed.
1609
+ $checkBox.click(function () {
1610
+ refreshCheckBoxValueAndText();
1611
+ });
1612
+
1613
+ //Change checkbox state when clicked to text
1614
+ if (field.setOnTextClick != false) {
1615
+ $textSpan
1616
+ .addClass('jtable-option-text-clickable')
1617
+ .click(function () {
1618
+ if ($checkBox.is(':checked')) {
1619
+ $checkBox.attr('checked', false);
1620
+ } else {
1621
+ $checkBox.attr('checked', true);
1622
+ }
1623
+
1624
+ refreshCheckBoxValueAndText();
1625
+ });
1626
+ }
1627
+
1628
+ return $containerDiv;
1629
+ },
1630
+
1631
+ /* Creates a drop down list (combobox) input element for a field.
1632
+ *************************************************************************/
1633
+ _createDropDownListForField: function (field, fieldName, value, record, source, form) {
1634
+
1635
+ //Create a container div
1636
+ var $containerDiv = $('<div />')
1637
+ .addClass('jtable-input jtable-dropdown-input');
1638
+
1639
+ //Create select element
1640
+ var $select = $('<select class="' + field.inputClass + '" id="Edit-' + fieldName + '" name="' + fieldName + '"></select>')
1641
+ .appendTo($containerDiv);
1642
+
1643
+ //add options
1644
+ var options = this._getOptionsForField(fieldName, {
1645
+ record: record,
1646
+ source: source,
1647
+ form: form,
1648
+ dependedValues: this._createDependedValuesUsingForm(form, field.dependsOn)
1649
+ });
1650
+
1651
+ this._fillDropDownListWithOptions($select, options, value);
1652
+
1653
+ return $containerDiv;
1654
+ },
1655
+
1656
+ /* Fills a dropdown list with given options.
1657
+ *************************************************************************/
1658
+ _fillDropDownListWithOptions: function ($select, options, value) {
1659
+ $select.empty();
1660
+ for (var i = 0; i < options.length; i++) {
1661
+ $('<option' + (options[i].Value == value ? ' selected="selected"' : '') + '>' + options[i].DisplayText + '</option>')
1662
+ .val(options[i].Value)
1663
+ .appendTo($select);
1664
+ }
1665
+ },
1666
+
1667
+ /* Creates depended values object from given form.
1668
+ *************************************************************************/
1669
+ _createDependedValuesUsingForm: function ($form, dependsOn) {
1670
+ if (!dependsOn) {
1671
+ return {};
1672
+ }
1673
+
1674
+ var dependedValues = {};
1675
+
1676
+ for (var i = 0; i < dependsOn.length; i++) {
1677
+ var dependedField = dependsOn[i];
1678
+
1679
+ var $dependsOn = $form.find('select[name=' + dependedField + ']');
1680
+ if ($dependsOn.length <= 0) {
1681
+ continue;
1682
+ }
1683
+
1684
+ dependedValues[dependedField] = $dependsOn.val();
1685
+ }
1686
+
1687
+
1688
+ return dependedValues;
1689
+ },
1690
+
1691
+ /* Creates a radio button list for a field.
1692
+ *************************************************************************/
1693
+ _createRadioButtonListForField: function (field, fieldName, value, record, source) {
1694
+ var $containerDiv = $('<div />')
1695
+ .addClass('jtable-input jtable-radiobuttonlist-input');
1696
+
1697
+ var options = this._getOptionsForField(fieldName, {
1698
+ record: record,
1699
+ source: source
1700
+ });
1701
+
1702
+ $.each(options, function(i, option) {
1703
+ var $radioButtonDiv = $('<div class=""></div>')
1704
+ .addClass('jtable-radio-input')
1705
+ .appendTo($containerDiv);
1706
+
1707
+ var $radioButton = $('<input type="radio" id="Edit-' + fieldName + '-' + i + '" class="' + field.inputClass + '" name="' + fieldName + '"' + ((option.Value == (value + '')) ? ' checked="true"' : '') + ' />')
1708
+ .val(option.Value)
1709
+ .appendTo($radioButtonDiv);
1710
+
1711
+ var $textSpan = $('<span></span>')
1712
+ .html(option.DisplayText)
1713
+ .appendTo($radioButtonDiv);
1714
+
1715
+ if (field.setOnTextClick != false) {
1716
+ $textSpan
1717
+ .addClass('jtable-option-text-clickable')
1718
+ .click(function () {
1719
+ if (!$radioButton.is(':checked')) {
1720
+ $radioButton.attr('checked', true);
1721
+ }
1722
+ });
1723
+ }
1724
+ });
1725
+
1726
+ return $containerDiv;
1727
+ },
1728
+
1729
+ /* Gets display text for a checkbox field.
1730
+ *************************************************************************/
1731
+ _getCheckBoxTextForFieldByValue: function (fieldName, value) {
1732
+ return this.options.fields[fieldName].values[value];
1733
+ },
1734
+
1735
+ /* Returns true if given field's value must be checked state.
1736
+ *************************************************************************/
1737
+ _getIsCheckBoxSelectedForFieldByValue: function (fieldName, value) {
1738
+ return (this._createCheckBoxStateArrayForFieldWithCaching(fieldName)[1].Value.toString() == value.toString());
1739
+ },
1740
+
1741
+ /* Gets an object for a checkbox field that has Value and DisplayText
1742
+ * properties.
1743
+ *************************************************************************/
1744
+ _getCheckBoxPropertiesForFieldByState: function (fieldName, checked) {
1745
+ return this._createCheckBoxStateArrayForFieldWithCaching(fieldName)[(checked ? 1 : 0)];
1746
+ },
1747
+
1748
+ /* Calls _createCheckBoxStateArrayForField with caching.
1749
+ *************************************************************************/
1750
+ _createCheckBoxStateArrayForFieldWithCaching: function (fieldName) {
1751
+ var cacheKey = 'checkbox_' + fieldName;
1752
+ if (!this._cache[cacheKey]) {
1753
+
1754
+ this._cache[cacheKey] = this._createCheckBoxStateArrayForField(fieldName);
1755
+ }
1756
+
1757
+ return this._cache[cacheKey];
1758
+ },
1759
+
1760
+ /* Creates a two element array of objects for states of a checkbox field.
1761
+ * First element for unchecked state, second for checked state.
1762
+ * Each object has two properties: Value and DisplayText
1763
+ *************************************************************************/
1764
+ _createCheckBoxStateArrayForField: function (fieldName) {
1765
+ var stateArray = [];
1766
+ var currentIndex = 0;
1767
+ $.each(this.options.fields[fieldName].values, function (propName, propValue) {
1768
+ if (currentIndex++ < 2) {
1769
+ stateArray.push({ 'Value': propName, 'DisplayText': propValue });
1770
+ }
1771
+ });
1772
+
1773
+ return stateArray;
1774
+ },
1775
+
1776
+ /* Searches a form for dependend dropdowns and makes them cascaded.
1777
+ */
1778
+ _makeCascadeDropDowns: function ($form, record, source) {
1779
+ var self = this;
1780
+
1781
+ $form.find('select') //for each combobox
1782
+ .each(function () {
1783
+ var $thisDropdown = $(this);
1784
+
1785
+ //get field name
1786
+ var fieldName = $thisDropdown.attr('name');
1787
+ if (!fieldName) {
1788
+ return;
1789
+ }
1790
+
1791
+ var field = self.options.fields[fieldName];
1792
+
1793
+ //check if this combobox depends on others
1794
+ if (!field.dependsOn) {
1795
+ return;
1796
+ }
1797
+
1798
+ //for each dependency
1799
+ $.each(field.dependsOn, function (index, dependsOnField) {
1800
+ //find the depended combobox
1801
+ var $dependsOnDropdown = $form.find('select[name=' + dependsOnField + ']');
1802
+ //when depended combobox changes
1803
+ $dependsOnDropdown.change(function () {
1804
+
1805
+ //Refresh options
1806
+ var funcParams = {
1807
+ record: record,
1808
+ source: source,
1809
+ form: $form,
1810
+ dependedValues: {}
1811
+ };
1812
+ funcParams.dependedValues = self._createDependedValuesUsingForm($form, field.dependsOn);
1813
+ var options = self._getOptionsForField(fieldName, funcParams);
1814
+
1815
+ //Fill combobox with new options
1816
+ self._fillDropDownListWithOptions($thisDropdown, options, undefined);
1817
+
1818
+ //Thigger change event to refresh multi cascade dropdowns.
1819
+ $thisDropdown.change();
1820
+ });
1821
+ });
1822
+ });
1823
+ },
1824
+
1825
+ /* Updates values of a record from given form
1826
+ *************************************************************************/
1827
+ _updateRecordValuesFromForm: function (record, $form) {
1828
+ for (var i = 0; i < this._fieldList.length; i++) {
1829
+ var fieldName = this._fieldList[i];
1830
+ var field = this.options.fields[fieldName];
1831
+
1832
+ //Do not update non-editable fields
1833
+ if (field.edit == false) {
1834
+ continue;
1835
+ }
1836
+
1837
+ //Get field name and the input element of this field in the form
1838
+ var $inputElement = $form.find('[name="' + fieldName + '"]');
1839
+ if ($inputElement.length <= 0) {
1840
+ continue;
1841
+ }
1842
+
1843
+ //Update field in record according to it's type
1844
+ if (field.type == 'date') {
1845
+ var dateVal = $inputElement.val();
1846
+ if (dateVal) {
1847
+ var displayFormat = field.displayFormat || this.options.defaultDateFormat;
1848
+ try {
1849
+ var date = $.datepicker.parseDate(displayFormat, dateVal);
1850
+ record[fieldName] = '/Date(' + date.getTime() + ')/';
1851
+ } catch (e) {
1852
+ //TODO: Handle incorrect/different date formats
1853
+ this._logWarn('Date format is incorrect for field ' + fieldName + ': ' + dateVal);
1854
+ record[fieldName] = undefined;
1855
+ }
1856
+ } else {
1857
+ this._logDebug('Date is empty for ' + fieldName);
1858
+ record[fieldName] = undefined; //TODO: undefined, null or empty string?
1859
+ }
1860
+ } else if (field.options && field.type == 'radiobutton') {
1861
+ var $checkedElement = $inputElement.filter(':checked');
1862
+ if ($checkedElement.length) {
1863
+ record[fieldName] = $checkedElement.val();
1864
+ } else {
1865
+ record[fieldName] = undefined;
1866
+ }
1867
+ } else {
1868
+ record[fieldName] = $inputElement.val();
1869
+ }
1870
+ }
1871
+ },
1872
+
1873
+ /* Sets enabled/disabled state of a dialog button.
1874
+ *************************************************************************/
1875
+ _setEnabledOfDialogButton: function ($button, enabled, buttonText) {
1876
+ if (!$button) {
1877
+ return;
1878
+ }
1879
+
1880
+ if (enabled != false) {
1881
+ $button
1882
+ .removeAttr('disabled')
1883
+ .removeClass('ui-state-disabled');
1884
+ } else {
1885
+ $button
1886
+ .attr('disabled', 'disabled')
1887
+ .addClass('ui-state-disabled');
1888
+ }
1889
+
1890
+ if (buttonText) {
1891
+ $button
1892
+ .find('span')
1893
+ .text(buttonText);
1894
+ }
1895
+ }
1896
+
1897
+ });
1898
+
1899
+ })(jQuery);
1900
+
1901
+
1902
+ /************************************************************************
1903
+ * CREATE RECORD extension for jTable *
1904
+ *************************************************************************/
1905
+ (function ($) {
1906
+
1907
+ //Reference to base object members
1908
+ var base = {
1909
+ _create: $.hik.jtable.prototype._create
1910
+ };
1911
+
1912
+ //extension members
1913
+ $.extend(true, $.hik.jtable.prototype, {
1914
+
1915
+ /************************************************************************
1916
+ * DEFAULT OPTIONS / EVENTS *
1917
+ *************************************************************************/
1918
+ options: {
1919
+
1920
+ //Events
1921
+ recordAdded: function (event, data) { },
1922
+
1923
+ //Localization
1924
+ messages: {
1925
+ addNewRecord: 'Add new record'
1926
+ }
1927
+ },
1928
+
1929
+ /************************************************************************
1930
+ * PRIVATE FIELDS *
1931
+ *************************************************************************/
1932
+
1933
+ _$addRecordDiv: null, //Reference to the adding new record dialog div (jQuery object)
1934
+
1935
+ /************************************************************************
1936
+ * CONSTRUCTOR *
1937
+ *************************************************************************/
1938
+
1939
+ /* Overrides base method to do create-specific constructions.
1940
+ *************************************************************************/
1941
+ _create: function () {
1942
+ base._create.apply(this, arguments);
1943
+
1944
+ if (!this.options.actions.createAction) {
1945
+ return;
1946
+ }
1947
+
1948
+ this._createAddRecordDialogDiv();
1949
+ },
1950
+
1951
+ /* Creates and prepares add new record dialog div
1952
+ *************************************************************************/
1953
+ _createAddRecordDialogDiv: function () {
1954
+ var self = this;
1955
+
1956
+ //Create a div for dialog and add to container element
1957
+ self._$addRecordDiv = $('<div />')
1958
+ .appendTo(self._$mainContainer);
1959
+
1960
+ //Prepare dialog
1961
+ self._$addRecordDiv.dialog({
1962
+ autoOpen: false,
1963
+ show: self.options.dialogShowEffect,
1964
+ hide: self.options.dialogHideEffect,
1965
+ width: 'auto',
1966
+ minWidth: '300',
1967
+ modal: true,
1968
+ title: self.options.messages.addNewRecord,
1969
+ buttons:
1970
+ [{ //Cancel button
1971
+ text: self.options.messages.cancel,
1972
+ click: function () {
1973
+ self._$addRecordDiv.dialog('close');
1974
+ }
1975
+ }, { //Save button
1976
+ id: 'AddRecordDialogSaveButton',
1977
+ text: self.options.messages.save,
1978
+ click: function () {
1979
+ self._onSaveClickedOnCreateForm();
1980
+ }
1981
+ }],
1982
+ close: function () {
1983
+ var $addRecordForm = self._$addRecordDiv.find('form').first();
1984
+ var $saveButton = $('#AddRecordDialogSaveButton');
1985
+ self._trigger("formClosed", null, { form: $addRecordForm, formType: 'create' });
1986
+ self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save);
1987
+ $addRecordForm.remove();
1988
+ }
1989
+ });
1990
+
1991
+ if (self.options.addRecordButton) {
1992
+ //If user supplied a button, bind the click event to show dialog form
1993
+ self.options.addRecordButton.click(function (e) {
1994
+ e.preventDefault();
1995
+ self._showAddRecordForm();
1996
+ });
1997
+ } else {
1998
+ //If user did not supplied a button, create a 'add record button' toolbar item.
1999
+ self._addToolBarItem({
2000
+ icon: true,
2001
+ cssClass: 'jtable-toolbar-item-add-record',
2002
+ text: self.options.messages.addNewRecord,
2003
+ click: function () {
2004
+ self._showAddRecordForm();
2005
+ }
2006
+ });
2007
+ }
2008
+ },
2009
+
2010
+ _onSaveClickedOnCreateForm: function () {
2011
+ var self = this;
2012
+
2013
+ var $saveButton = $('#AddRecordDialogSaveButton');
2014
+ var $addRecordForm = self._$addRecordDiv.find('form');
2015
+
2016
+ if (self._trigger("formSubmitting", null, { form: $addRecordForm, formType: 'create' }) != false) {
2017
+ self._setEnabledOfDialogButton($saveButton, false, self.options.messages.saving);
2018
+ self._saveAddRecordForm($addRecordForm, $saveButton);
2019
+ }
2020
+ },
2021
+
2022
+ /************************************************************************
2023
+ * PUBLIC METHODS *
2024
+ *************************************************************************/
2025
+
2026
+ /* Shows add new record dialog form.
2027
+ *************************************************************************/
2028
+ showCreateForm: function () {
2029
+ this._showAddRecordForm();
2030
+ },
2031
+
2032
+ /* Adds a new record to the table (optionally to the server also)
2033
+ *************************************************************************/
2034
+ addRecord: function (options) {
2035
+ var self = this;
2036
+ options = $.extend({
2037
+ clientOnly: false,
2038
+ animationsEnabled: self.options.animationsEnabled,
2039
+ url: self.options.actions.createAction,
2040
+ success: function () { },
2041
+ error: function () { }
2042
+ }, options);
2043
+
2044
+ if (!options.record) {
2045
+ self._logWarn('options parameter in addRecord method must contain a record property.');
2046
+ return;
2047
+ }
2048
+
2049
+ if (options.clientOnly) {
2050
+ self._addRow(
2051
+ self._createRowFromRecord(options.record), {
2052
+ isNewRow: true,
2053
+ animationsEnabled: options.animationsEnabled
2054
+ });
2055
+
2056
+ options.success();
2057
+ return;
2058
+ }
2059
+
2060
+ self._submitFormUsingAjax(
2061
+ options.url,
2062
+ $.param(options.record),
2063
+ function (data) {
2064
+ if (data.Result != 'OK') {
2065
+ self._showError(data.Message);
2066
+ options.error(data);
2067
+ return;
2068
+ }
2069
+
2070
+ if(!data.Record) {
2071
+ self._logError('Server must return the created Record object.');
2072
+ options.error(data);
2073
+ return;
2074
+ }
2075
+
2076
+ self._onRecordAdded(data);
2077
+
2078
+ self._addRow(
2079
+ self._createRowFromRecord(data.Record), {
2080
+ isNewRow: true,
2081
+ animationsEnabled: options.animationsEnabled
2082
+ });
2083
+
2084
+ options.success(data);
2085
+ },
2086
+ function () {
2087
+ self._showError(self.options.messages.serverCommunicationError);
2088
+ options.error();
2089
+ });
2090
+ },
2091
+
2092
+ /************************************************************************
2093
+ * PRIVATE METHODS *
2094
+ *************************************************************************/
2095
+
2096
+ /* Shows add new record dialog form.
2097
+ *************************************************************************/
2098
+ _showAddRecordForm: function () {
2099
+ var self = this;
2100
+
2101
+ //Create add new record form
2102
+ var $addRecordForm = $('<form id="jtable-create-form" class="jtable-dialog-form jtable-create-form"></form>');
2103
+
2104
+ //Create input elements
2105
+ for (var i = 0; i < self._fieldList.length; i++) {
2106
+
2107
+ var fieldName = self._fieldList[i];
2108
+ var field = self.options.fields[fieldName];
2109
+
2110
+ //Do not create input for fields that is key and not specially marked as creatable
2111
+ if (field.key == true && field.create != true) {
2112
+ continue;
2113
+ }
2114
+
2115
+ //Do not create input for fields that are not creatable
2116
+ if (field.create == false) {
2117
+ continue;
2118
+ }
2119
+
2120
+ if (field.type == 'hidden') {
2121
+ $addRecordForm.append(self._createInputForHidden(fieldName, field.defaultValue));
2122
+ continue;
2123
+ }
2124
+
2125
+ //Create a container div for this input field and add to form
2126
+ var $fieldContainer = $('<div />')
2127
+ .addClass('jtable-input-field-container')
2128
+ .appendTo($addRecordForm);
2129
+
2130
+ //Create a label for input
2131
+ $fieldContainer.append(self._createInputLabelForRecordField(fieldName));
2132
+
2133
+ //Create input element
2134
+ $fieldContainer.append(
2135
+ self._createInputForRecordField({
2136
+ fieldName: fieldName,
2137
+ formType: 'create',
2138
+ form: $addRecordForm
2139
+ }));
2140
+ }
2141
+
2142
+ self._makeCascadeDropDowns($addRecordForm, undefined, 'create');
2143
+
2144
+ $addRecordForm.submit(function () {
2145
+ self._onSaveClickedOnCreateForm();
2146
+ return false;
2147
+ });
2148
+
2149
+ //Open the form
2150
+ self._$addRecordDiv.append($addRecordForm).dialog('open');
2151
+ self._trigger("formCreated", null, { form: $addRecordForm, formType: 'create' });
2152
+ },
2153
+
2154
+ /* Saves new added record to the server and updates table.
2155
+ *************************************************************************/
2156
+ _saveAddRecordForm: function ($addRecordForm, $saveButton) {
2157
+ var self = this;
2158
+
2159
+ //Make an Ajax call to update record
2160
+ $addRecordForm.data('submitting', true);
2161
+
2162
+ self._submitFormUsingAjax(
2163
+ self.options.actions.createAction,
2164
+ $addRecordForm.serialize(),
2165
+ function (data) {
2166
+
2167
+ if (data.Result != 'OK') {
2168
+ self._showError(data.Message);
2169
+ self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save);
2170
+ return;
2171
+ }
2172
+
2173
+ if (!data.Record) {
2174
+ self._logError('Server must return the created Record object.');
2175
+ self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save);
2176
+ return;
2177
+ }
2178
+
2179
+ self._onRecordAdded(data);
2180
+ self._addRow(
2181
+ self._createRowFromRecord(data.Record), {
2182
+ isNewRow: true
2183
+ });
2184
+ self._$addRecordDiv.dialog("close");
2185
+ },
2186
+ function () {
2187
+ self._showError(self.options.messages.serverCommunicationError);
2188
+ self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save);
2189
+ });
2190
+ },
2191
+
2192
+ _onRecordAdded: function (data) {
2193
+ this._trigger("recordAdded", null, { record: data.Record, serverResponse: data });
2194
+ }
2195
+
2196
+ });
2197
+
2198
+ })(jQuery);
2199
+
2200
+
2201
+ /************************************************************************
2202
+ * EDIT RECORD extension for jTable *
2203
+ *************************************************************************/
2204
+ (function ($) {
2205
+
2206
+ //Reference to base object members
2207
+ var base = {
2208
+ _create: $.hik.jtable.prototype._create,
2209
+ _addColumnsToHeaderRow: $.hik.jtable.prototype._addColumnsToHeaderRow,
2210
+ _addCellsToRowUsingRecord: $.hik.jtable.prototype._addCellsToRowUsingRecord
2211
+ };
2212
+
2213
+ //extension members
2214
+ $.extend(true, $.hik.jtable.prototype, {
2215
+
2216
+ /************************************************************************
2217
+ * DEFAULT OPTIONS / EVENTS *
2218
+ *************************************************************************/
2219
+ options: {
2220
+
2221
+ //Events
2222
+ recordUpdated: function (event, data) { },
2223
+ rowUpdated: function (event, data) { },
2224
+
2225
+ //Localization
2226
+ messages: {
2227
+ editRecord: 'Edit Record'
2228
+ }
2229
+ },
2230
+
2231
+ /************************************************************************
2232
+ * PRIVATE FIELDS *
2233
+ *************************************************************************/
2234
+
2235
+ _$editDiv: null, //Reference to the editing dialog div (jQuery object)
2236
+ _$editingRow: null, //Reference to currently editing row (jQuery object)
2237
+
2238
+ /************************************************************************
2239
+ * CONSTRUCTOR AND INITIALIZATION METHODS *
2240
+ *************************************************************************/
2241
+
2242
+ /* Overrides base method to do editing-specific constructions.
2243
+ *************************************************************************/
2244
+ _create: function () {
2245
+ base._create.apply(this, arguments);
2246
+
2247
+ if (!this.options.actions.updateAction) {
2248
+ return;
2249
+ }
2250
+
2251
+ this._createEditDialogDiv();
2252
+ },
2253
+
2254
+ /* Creates and prepares edit dialog div
2255
+ *************************************************************************/
2256
+ _createEditDialogDiv: function () {
2257
+ var self = this;
2258
+
2259
+ //Create a div for dialog and add to container element
2260
+ self._$editDiv = $('<div></div>')
2261
+ .appendTo(self._$mainContainer);
2262
+
2263
+ //Prepare dialog
2264
+ self._$editDiv.dialog({
2265
+ autoOpen: false,
2266
+ show: self.options.dialogShowEffect,
2267
+ hide: self.options.dialogHideEffect,
2268
+ width: 'auto',
2269
+ minWidth: '300',
2270
+ modal: true,
2271
+ title: self.options.messages.editRecord,
2272
+ buttons:
2273
+ [{ //cancel button
2274
+ text: self.options.messages.cancel,
2275
+ click: function () {
2276
+ self._$editDiv.dialog('close');
2277
+ }
2278
+ }, { //save button
2279
+ id: 'EditDialogSaveButton',
2280
+ text: self.options.messages.save,
2281
+ click: function () {
2282
+ self._onSaveClickedOnEditForm();
2283
+ }
2284
+ }],
2285
+ close: function () {
2286
+ var $editForm = self._$editDiv.find('form:first');
2287
+ var $saveButton = $('#EditDialogSaveButton');
2288
+ self._trigger("formClosed", null, { form: $editForm, formType: 'edit', row: self._$editingRow });
2289
+ self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save);
2290
+ $editForm.remove();
2291
+ }
2292
+ });
2293
+ },
2294
+
2295
+ /* Saves editing form to server.
2296
+ *************************************************************************/
2297
+ _onSaveClickedOnEditForm: function () {
2298
+ var self = this;
2299
+
2300
+ //row maybe removed by another source, if so, do nothing
2301
+ if (self._$editingRow.hasClass('jtable-row-removed')) {
2302
+ self._$editDiv.dialog('close');
2303
+ return;
2304
+ }
2305
+
2306
+ var $saveButton = $('#EditDialogSaveButton');
2307
+ var $editForm = self._$editDiv.find('form');
2308
+ if (self._trigger("formSubmitting", null, { form: $editForm, formType: 'edit', row: self._$editingRow }) != false) {
2309
+ self._setEnabledOfDialogButton($saveButton, false, self.options.messages.saving);
2310
+ self._saveEditForm($editForm, $saveButton);
2311
+ }
2312
+ },
2313
+
2314
+ /************************************************************************
2315
+ * PUBLIC METHODS *
2316
+ *************************************************************************/
2317
+
2318
+ /* Updates a record on the table (optionally on the server also)
2319
+ *************************************************************************/
2320
+ updateRecord: function (options) {
2321
+ var self = this;
2322
+ options = $.extend({
2323
+ clientOnly: false,
2324
+ animationsEnabled: self.options.animationsEnabled,
2325
+ url: self.options.actions.updateAction,
2326
+ success: function () { },
2327
+ error: function () { }
2328
+ }, options);
2329
+
2330
+ if (!options.record) {
2331
+ self._logWarn('options parameter in updateRecord method must contain a record property.');
2332
+ return;
2333
+ }
2334
+
2335
+ var key = self._getKeyValueOfRecord(options.record);
2336
+ if (key == undefined || key == null) {
2337
+ self._logWarn('options parameter in updateRecord method must contain a record that contains the key field property.');
2338
+ return;
2339
+ }
2340
+
2341
+ var $updatingRow = self.getRowByKey(key);
2342
+ if ($updatingRow == null) {
2343
+ self._logWarn('Can not found any row by key: ' + key);
2344
+ return;
2345
+ }
2346
+
2347
+ if (options.clientOnly) {
2348
+ $.extend($updatingRow.data('record'), options.record);
2349
+ self._updateRowTexts($updatingRow);
2350
+ self._onRecordUpdated($updatingRow, null);
2351
+ if (options.animationsEnabled) {
2352
+ self._showUpdateAnimationForRow($updatingRow);
2353
+ }
2354
+
2355
+ options.success();
2356
+ return;
2357
+ }
2358
+
2359
+ self._submitFormUsingAjax(
2360
+ options.url,
2361
+ $.param(options.record),
2362
+ function (data) {
2363
+ if (data.Result != 'OK') {
2364
+ self._showError(data.Message);
2365
+ options.error(data);
2366
+ return;
2367
+ }
2368
+
2369
+ $.extend($updatingRow.data('record'), options.record);
2370
+ self._updateRecordValuesFromServerResponse($updatingRow.data('record'), data);
2371
+
2372
+ self._updateRowTexts($updatingRow);
2373
+ self._onRecordUpdated($updatingRow, data);
2374
+ if (options.animationsEnabled) {
2375
+ self._showUpdateAnimationForRow($updatingRow);
2376
+ }
2377
+
2378
+ options.success(data);
2379
+ },
2380
+ function () {
2381
+ self._showError(self.options.messages.serverCommunicationError);
2382
+ options.error();
2383
+ });
2384
+ },
2385
+
2386
+ /************************************************************************
2387
+ * OVERRIDED METHODS *
2388
+ *************************************************************************/
2389
+
2390
+ /* Overrides base method to add a 'editing column cell' to header row.
2391
+ *************************************************************************/
2392
+ _addColumnsToHeaderRow: function ($tr) {
2393
+ base._addColumnsToHeaderRow.apply(this, arguments);
2394
+ if (this.options.actions.updateAction != undefined) {
2395
+ $tr.append(this._createEmptyCommandHeader());
2396
+ }
2397
+ },
2398
+
2399
+ /* Overrides base method to add a 'edit command cell' to a row.
2400
+ *************************************************************************/
2401
+ _addCellsToRowUsingRecord: function ($row) {
2402
+ var self = this;
2403
+ base._addCellsToRowUsingRecord.apply(this, arguments);
2404
+
2405
+ if (self.options.actions.updateAction != undefined) {
2406
+ var $span = $('<span></span>').html(self.options.messages.editRecord);
2407
+ var $button = $('<button title="' + self.options.messages.editRecord + '"></button>')
2408
+ .addClass('jtable-command-button jtable-edit-command-button')
2409
+ .append($span)
2410
+ .click(function (e) {
2411
+ e.preventDefault();
2412
+ e.stopPropagation();
2413
+ self._showEditForm($row);
2414
+ });
2415
+ $('<td></td>')
2416
+ .addClass('jtable-command-column')
2417
+ .append($button)
2418
+ .appendTo($row);
2419
+ }
2420
+ },
2421
+
2422
+ /************************************************************************
2423
+ * PRIVATE METHODS *
2424
+ *************************************************************************/
2425
+
2426
+ /* Shows edit form for a row.
2427
+ *************************************************************************/
2428
+ _showEditForm: function ($tableRow) {
2429
+ var self = this;
2430
+ var record = $tableRow.data('record');
2431
+
2432
+ //Create edit form
2433
+ var $editForm = $('<form id="jtable-edit-form" class="jtable-dialog-form jtable-edit-form"></form>');
2434
+
2435
+ //Create input fields
2436
+ for (var i = 0; i < self._fieldList.length; i++) {
2437
+
2438
+ var fieldName = self._fieldList[i];
2439
+ var field = self.options.fields[fieldName];
2440
+ var fieldValue = record[fieldName];
2441
+
2442
+ if (field.key == true) {
2443
+ if (field.edit != true) {
2444
+ //Create hidden field for key
2445
+ $editForm.append(self._createInputForHidden(fieldName, fieldValue));
2446
+ continue;
2447
+ } else {
2448
+ //Create a special hidden field for key (since key is be editable)
2449
+ $editForm.append(self._createInputForHidden('jtRecordKey', fieldValue));
2450
+ }
2451
+ }
2452
+
2453
+ //Do not create element for non-editable fields
2454
+ if (field.edit == false) {
2455
+ continue;
2456
+ }
2457
+
2458
+ //Hidden field
2459
+ if (field.type == 'hidden') {
2460
+ $editForm.append(self._createInputForHidden(fieldName, fieldValue));
2461
+ continue;
2462
+ }
2463
+
2464
+ //Create a container div for this input field and add to form
2465
+ var $fieldContainer = $('<div class="jtable-input-field-container"></div>').appendTo($editForm);
2466
+
2467
+ //Create a label for input
2468
+ $fieldContainer.append(self._createInputLabelForRecordField(fieldName));
2469
+
2470
+ //Create input element with it's current value
2471
+ var currentValue = self._getValueForRecordField(record, fieldName);
2472
+ $fieldContainer.append(
2473
+ self._createInputForRecordField({
2474
+ fieldName: fieldName,
2475
+ value: currentValue,
2476
+ record: record,
2477
+ formType: 'edit',
2478
+ form: $editForm
2479
+ }));
2480
+ }
2481
+
2482
+ self._makeCascadeDropDowns($editForm, record, 'edit');
2483
+
2484
+ $editForm.submit(function () {
2485
+ self._onSaveClickedOnEditForm();
2486
+ return false;
2487
+ });
2488
+
2489
+ //Open dialog
2490
+ self._$editingRow = $tableRow;
2491
+ self._$editDiv.append($editForm).dialog('open');
2492
+ self._trigger("formCreated", null, { form: $editForm, formType: 'edit', record: record, row: $tableRow });
2493
+ },
2494
+
2495
+ /* Saves editing form to the server and updates the record on the table.
2496
+ *************************************************************************/
2497
+ _saveEditForm: function ($editForm, $saveButton) {
2498
+ var self = this;
2499
+ self._submitFormUsingAjax(
2500
+ self.options.actions.updateAction,
2501
+ $editForm.serialize(),
2502
+ function (data) {
2503
+ //Check for errors
2504
+ if (data.Result != 'OK') {
2505
+ self._showError(data.Message);
2506
+ self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save);
2507
+ return;
2508
+ }
2509
+
2510
+ var record = self._$editingRow.data('record');
2511
+
2512
+ self._updateRecordValuesFromForm(record, $editForm);
2513
+ self._updateRecordValuesFromServerResponse(record, data);
2514
+ self._updateRowTexts(self._$editingRow);
2515
+
2516
+ self._$editingRow.attr('data-record-key', self._getKeyValueOfRecord(record));
2517
+
2518
+ self._onRecordUpdated(self._$editingRow, data);
2519
+
2520
+ if (self.options.animationsEnabled) {
2521
+ self._showUpdateAnimationForRow(self._$editingRow);
2522
+ }
2523
+
2524
+ self._$editDiv.dialog("close");
2525
+ },
2526
+ function () {
2527
+ self._showError(self.options.messages.serverCommunicationError);
2528
+ self._setEnabledOfDialogButton($saveButton, true, self.options.messages.save);
2529
+ });
2530
+ },
2531
+
2532
+ /* This method ensures updating of current record with server response,
2533
+ * if server sends a Record object as response to updateAction.
2534
+ *************************************************************************/
2535
+ _updateRecordValuesFromServerResponse: function (record, serverResponse) {
2536
+ if (!serverResponse || !serverResponse.Record) {
2537
+ return;
2538
+ }
2539
+
2540
+ $.extend(true, record, serverResponse.Record);
2541
+ },
2542
+
2543
+ /* Gets text for a field of a record according to it's type.
2544
+ *************************************************************************/
2545
+ _getValueForRecordField: function (record, fieldName) {
2546
+ var field = this.options.fields[fieldName];
2547
+ var fieldValue = record[fieldName];
2548
+ if (field.type == 'date') {
2549
+ return this._getDisplayTextForDateRecordField(field, fieldValue);
2550
+ } else {
2551
+ return fieldValue;
2552
+ }
2553
+ },
2554
+
2555
+ /* Updates cells of a table row's text values from row's record values.
2556
+ *************************************************************************/
2557
+ _updateRowTexts: function ($tableRow) {
2558
+ var record = $tableRow.data('record');
2559
+ var $columns = $tableRow.find('td');
2560
+ for (var i = 0; i < this._columnList.length; i++) {
2561
+ var displayItem = this._getDisplayTextForRecordField(record, this._columnList[i]);
2562
+ if (displayItem == 0) displayItem = "0";
2563
+ $columns.eq(this._firstDataColumnOffset + i).html(displayItem || '');
2564
+ }
2565
+
2566
+ this._onRowUpdated($tableRow);
2567
+ },
2568
+
2569
+ /* Shows 'updated' animation for a table row.
2570
+ *************************************************************************/
2571
+ _showUpdateAnimationForRow: function ($tableRow) {
2572
+ var className = 'jtable-row-updated';
2573
+ if (this.options.jqueryuiTheme) {
2574
+ className = className + ' ui-state-highlight';
2575
+ }
2576
+
2577
+ $tableRow.stop(true, true).addClass(className, 'slow', '', function () {
2578
+ $tableRow.removeClass(className, 5000);
2579
+ });
2580
+ },
2581
+
2582
+ /************************************************************************
2583
+ * EVENT RAISING METHODS *
2584
+ *************************************************************************/
2585
+
2586
+ _onRowUpdated: function ($row) {
2587
+ this._trigger("rowUpdated", null, { row: $row, record: $row.data('record') });
2588
+ },
2589
+
2590
+ _onRecordUpdated: function ($row, data) {
2591
+ this._trigger("recordUpdated", null, { record: $row.data('record'), row: $row, serverResponse: data });
2592
+ }
2593
+
2594
+ });
2595
+
2596
+ })(jQuery);
2597
+
2598
+
2599
+ /************************************************************************
2600
+ * DELETION extension for jTable *
2601
+ *************************************************************************/
2602
+ (function ($) {
2603
+
2604
+ //Reference to base object members
2605
+ var base = {
2606
+ _create: $.hik.jtable.prototype._create,
2607
+ _addColumnsToHeaderRow: $.hik.jtable.prototype._addColumnsToHeaderRow,
2608
+ _addCellsToRowUsingRecord: $.hik.jtable.prototype._addCellsToRowUsingRecord
2609
+ };
2610
+
2611
+ //extension members
2612
+ $.extend(true, $.hik.jtable.prototype, {
2613
+
2614
+ /************************************************************************
2615
+ * DEFAULT OPTIONS / EVENTS *
2616
+ *************************************************************************/
2617
+ options: {
2618
+
2619
+ //Options
2620
+ deleteConfirmation: true,
2621
+
2622
+ //Events
2623
+ recordDeleted: function (event, data) { },
2624
+
2625
+ //Localization
2626
+ messages: {
2627
+ deleteConfirmation: 'This record will be deleted. Are you sure?',
2628
+ deleteText: 'Delete',
2629
+ deleting: 'Deleting',
2630
+ canNotDeletedRecords: 'Can not delete {0} of {1} records!',
2631
+ deleteProggress: 'Deleting {0} of {1} records, processing...'
2632
+ }
2633
+ },
2634
+
2635
+ /************************************************************************
2636
+ * PRIVATE FIELDS *
2637
+ *************************************************************************/
2638
+
2639
+ _$deleteRecordDiv: null, //Reference to the adding new record dialog div (jQuery object)
2640
+ _$deletingRow: null, //Reference to currently deleting row (jQuery object)
2641
+
2642
+ /************************************************************************
2643
+ * CONSTRUCTOR *
2644
+ *************************************************************************/
2645
+
2646
+ /* Overrides base method to do deletion-specific constructions.
2647
+ *************************************************************************/
2648
+ _create: function () {
2649
+ base._create.apply(this, arguments);
2650
+ this._createDeleteDialogDiv();
2651
+ },
2652
+
2653
+ /* Creates and prepares delete record confirmation dialog div.
2654
+ *************************************************************************/
2655
+ _createDeleteDialogDiv: function () {
2656
+ var self = this;
2657
+
2658
+ //Check if deleteAction is supplied
2659
+ if (!self.options.actions.deleteAction) {
2660
+ return;
2661
+ }
2662
+
2663
+ //Create div element for delete confirmation dialog
2664
+ self._$deleteRecordDiv = $('<div><p><span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 20px 0;"></span><span class="jtable-delete-confirm-message"></span></p></div>').appendTo(self._$mainContainer);
2665
+
2666
+ //Prepare dialog
2667
+ self._$deleteRecordDiv.dialog({
2668
+ autoOpen: false,
2669
+ show: self.options.dialogShowEffect,
2670
+ hide: self.options.dialogHideEffect,
2671
+ modal: true,
2672
+ title: self.options.messages.areYouSure,
2673
+ buttons:
2674
+ [{ //cancel button
2675
+ text: self.options.messages.cancel,
2676
+ click: function () {
2677
+ self._$deleteRecordDiv.dialog("close");
2678
+ }
2679
+ }, {//delete button
2680
+ id: 'DeleteDialogButton',
2681
+ text: self.options.messages.deleteText,
2682
+ click: function () {
2683
+
2684
+ //row maybe removed by another source, if so, do nothing
2685
+ if (self._$deletingRow.hasClass('jtable-row-removed')) {
2686
+ self._$deleteRecordDiv.dialog('close');
2687
+ return;
2688
+ }
2689
+
2690
+ var $deleteButton = $('#DeleteDialogButton');
2691
+ self._setEnabledOfDialogButton($deleteButton, false, self.options.messages.deleting);
2692
+ self._deleteRecordFromServer(
2693
+ self._$deletingRow,
2694
+ function () {
2695
+ self._removeRowsFromTableWithAnimation(self._$deletingRow);
2696
+ self._$deleteRecordDiv.dialog('close');
2697
+ },
2698
+ function (message) { //error
2699
+ self._showError(message);
2700
+ self._setEnabledOfDialogButton($deleteButton, true, self.options.messages.deleteText);
2701
+ }
2702
+ );
2703
+ }
2704
+ }],
2705
+ close: function () {
2706
+ var $deleteButton = $('#DeleteDialogButton');
2707
+ self._setEnabledOfDialogButton($deleteButton, true, self.options.messages.deleteText);
2708
+ }
2709
+ });
2710
+ },
2711
+
2712
+ /************************************************************************
2713
+ * PUBLIC METHODS *
2714
+ *************************************************************************/
2715
+
2716
+ /* This method is used to delete one or more rows from server and the table.
2717
+ *************************************************************************/
2718
+ deleteRows: function ($rows) {
2719
+ var self = this;
2720
+
2721
+ if ($rows.length <= 0) {
2722
+ self._logWarn('No rows specified to jTable deleteRows method.');
2723
+ return;
2724
+ }
2725
+
2726
+ if (self._isBusy()) {
2727
+ self._logWarn('Can not delete rows since jTable is busy!');
2728
+ return;
2729
+ }
2730
+
2731
+ //Deleting just one row
2732
+ if ($rows.length == 1) {
2733
+ self._deleteRecordFromServer(
2734
+ $rows,
2735
+ function () { //success
2736
+ self._removeRowsFromTableWithAnimation($rows);
2737
+ },
2738
+ function (message) { //error
2739
+ self._showError(message);
2740
+ }
2741
+ );
2742
+
2743
+ return;
2744
+ }
2745
+
2746
+ //Deleting multiple rows
2747
+ self._showBusy(self._formatString(self.options.messages.deleteProggress, 0, $rows.length));
2748
+
2749
+ //This method checks if deleting of all records is completed
2750
+ var completedCount = 0;
2751
+ var isCompleted = function () {
2752
+ return (completedCount >= $rows.length);
2753
+ };
2754
+
2755
+ //This method is called when deleting of all records completed
2756
+ var completed = function () {
2757
+ var $deletedRows = $rows.filter('.jtable-row-ready-to-remove');
2758
+ if ($deletedRows.length < $rows.length) {
2759
+ self._showError(self._formatString(self.options.messages.canNotDeletedRecords, $rows.length - $deletedRows.length, $rows.length));
2760
+ }
2761
+
2762
+ if ($deletedRows.length > 0) {
2763
+ self._removeRowsFromTableWithAnimation($deletedRows);
2764
+ }
2765
+
2766
+ self._hideBusy();
2767
+ };
2768
+
2769
+ //Delete all rows
2770
+ var deletedCount = 0;
2771
+ $rows.each(function () {
2772
+ var $row = $(this);
2773
+ self._deleteRecordFromServer(
2774
+ $row,
2775
+ function () { //success
2776
+ ++deletedCount; ++completedCount;
2777
+ $row.addClass('jtable-row-ready-to-remove');
2778
+ self._showBusy(self._formatString(self.options.messages.deleteProggress, deletedCount, $rows.length));
2779
+ if (isCompleted()) {
2780
+ completed();
2781
+ }
2782
+ },
2783
+ function () { //error
2784
+ ++completedCount;
2785
+ if (isCompleted()) {
2786
+ completed();
2787
+ }
2788
+ }
2789
+ );
2790
+ });
2791
+ },
2792
+
2793
+ /* Deletes a record from the table (optionally from the server also).
2794
+ *************************************************************************/
2795
+ deleteRecord: function (options) {
2796
+ var self = this;
2797
+ options = $.extend({
2798
+ clientOnly: false,
2799
+ animationsEnabled: self.options.animationsEnabled,
2800
+ url: self.options.actions.deleteAction,
2801
+ success: function () { },
2802
+ error: function () { }
2803
+ }, options);
2804
+
2805
+ if (options.key == undefined) {
2806
+ self._logWarn('options parameter in deleteRecord method must contain a key property.');
2807
+ return;
2808
+ }
2809
+
2810
+ var $deletingRow = self.getRowByKey(options.key);
2811
+ if ($deletingRow == null) {
2812
+ self._logWarn('Can not found any row by key: ' + options.key);
2813
+ return;
2814
+ }
2815
+
2816
+ if (options.clientOnly) {
2817
+ self._removeRowsFromTableWithAnimation($deletingRow, options.animationsEnabled);
2818
+ options.success();
2819
+ return;
2820
+ }
2821
+
2822
+ self._deleteRecordFromServer(
2823
+ $deletingRow,
2824
+ function (data) { //success
2825
+ self._removeRowsFromTableWithAnimation($deletingRow, options.animationsEnabled);
2826
+ options.success(data);
2827
+ },
2828
+ function (message) { //error
2829
+ self._showError(message);
2830
+ options.error(message);
2831
+ },
2832
+ options.url
2833
+ );
2834
+ },
2835
+
2836
+ /************************************************************************
2837
+ * OVERRIDED METHODS *
2838
+ *************************************************************************/
2839
+
2840
+ /* Overrides base method to add a 'deletion column cell' to header row.
2841
+ *************************************************************************/
2842
+ _addColumnsToHeaderRow: function ($tr) {
2843
+ base._addColumnsToHeaderRow.apply(this, arguments);
2844
+ if (this.options.actions.deleteAction != undefined) {
2845
+ $tr.append(this._createEmptyCommandHeader());
2846
+ }
2847
+ },
2848
+
2849
+ /* Overrides base method to add a 'delete command cell' to a row.
2850
+ *************************************************************************/
2851
+ _addCellsToRowUsingRecord: function ($row) {
2852
+ base._addCellsToRowUsingRecord.apply(this, arguments);
2853
+
2854
+ var self = this;
2855
+ if (self.options.actions.deleteAction != undefined) {
2856
+ var $span = $('<span></span>').html(self.options.messages.deleteText);
2857
+ var $button = $('<button title="' + self.options.messages.deleteText + '"></button>')
2858
+ .addClass('jtable-command-button jtable-delete-command-button')
2859
+ .append($span)
2860
+ .click(function (e) {
2861
+ e.preventDefault();
2862
+ e.stopPropagation();
2863
+ self._deleteButtonClickedForRow($row);
2864
+ });
2865
+ $('<td></td>')
2866
+ .addClass('jtable-command-column')
2867
+ .append($button)
2868
+ .appendTo($row);
2869
+ }
2870
+ },
2871
+
2872
+ /************************************************************************
2873
+ * PRIVATE METHODS *
2874
+ *************************************************************************/
2875
+
2876
+ /* This method is called when user clicks delete button on a row.
2877
+ *************************************************************************/
2878
+ _deleteButtonClickedForRow: function ($row) {
2879
+ var self = this;
2880
+
2881
+ var deleteConfirm;
2882
+ var deleteConfirmMessage = self.options.messages.deleteConfirmation;
2883
+
2884
+ //If options.deleteConfirmation is function then call it
2885
+ if ($.isFunction(self.options.deleteConfirmation)) {
2886
+ var data = { row: $row, record: $row.data('record'), deleteConfirm: true, deleteConfirmMessage: deleteConfirmMessage, cancel: false, cancelMessage: null };
2887
+ self.options.deleteConfirmation(data);
2888
+
2889
+ //If delete progress is cancelled
2890
+ if (data.cancel) {
2891
+
2892
+ //If a canlellation reason is specified
2893
+ if (data.cancelMessage) {
2894
+ self._showError(data.cancelMessage); //TODO: show warning/stop message instead of error (also show warning/error ui icon)!
2895
+ }
2896
+
2897
+ return;
2898
+ }
2899
+
2900
+ deleteConfirmMessage = data.deleteConfirmMessage;
2901
+ deleteConfirm = data.deleteConfirm;
2902
+ } else {
2903
+ deleteConfirm = self.options.deleteConfirmation;
2904
+ }
2905
+
2906
+ if (deleteConfirm != false) {
2907
+ //Confirmation
2908
+ self._$deleteRecordDiv.find('.jtable-delete-confirm-message').html(deleteConfirmMessage);
2909
+ self._showDeleteDialog($row);
2910
+ } else {
2911
+ //No confirmation
2912
+ self._deleteRecordFromServer(
2913
+ $row,
2914
+ function () { //success
2915
+ self._removeRowsFromTableWithAnimation($row);
2916
+ },
2917
+ function (message) { //error
2918
+ self._showError(message);
2919
+ }
2920
+ );
2921
+ }
2922
+ },
2923
+
2924
+ /* Shows delete comfirmation dialog.
2925
+ *************************************************************************/
2926
+ _showDeleteDialog: function ($row) {
2927
+ this._$deletingRow = $row;
2928
+ this._$deleteRecordDiv.dialog('open');
2929
+ },
2930
+
2931
+ /* Performs an ajax call to server to delete record
2932
+ * and removesd row of record from table if ajax call success.
2933
+ *************************************************************************/
2934
+ _deleteRecordFromServer: function ($row, success, error, url) {
2935
+ var self = this;
2936
+
2937
+ //Check if it is already being deleted right now
2938
+ if ($row.data('deleting') == true) {
2939
+ return;
2940
+ }
2941
+
2942
+ $row.data('deleting', true);
2943
+
2944
+ var postData = {};
2945
+ postData[self._keyField] = self._getKeyValueOfRecord($row.data('record'));
2946
+
2947
+ this._ajax({
2948
+ url: (url || self.options.actions.deleteAction),
2949
+ data: postData,
2950
+ success: function (data) {
2951
+
2952
+ if (data.Result != 'OK') {
2953
+ $row.data('deleting', false);
2954
+ if (error) {
2955
+ error(data.Message);
2956
+ }
2957
+
2958
+ return;
2959
+ }
2960
+
2961
+ self._trigger("recordDeleted", null, { record: $row.data('record'), row: $row, serverResponse: data });
2962
+
2963
+ if (success) {
2964
+ success(data);
2965
+ }
2966
+ },
2967
+ error: function () {
2968
+ $row.data('deleting', false);
2969
+ if (error) {
2970
+ error(self.options.messages.serverCommunicationError);
2971
+ }
2972
+ }
2973
+ });
2974
+ },
2975
+
2976
+ /* Removes a row from table after a 'deleting' animation.
2977
+ *************************************************************************/
2978
+ _removeRowsFromTableWithAnimation: function ($rows, animationsEnabled) {
2979
+ var self = this;
2980
+
2981
+ if (animationsEnabled == undefined) {
2982
+ animationsEnabled = self.options.animationsEnabled;
2983
+ }
2984
+
2985
+ if (animationsEnabled) {
2986
+ var className = 'jtable-row-deleting';
2987
+ if (this.options.jqueryuiTheme) {
2988
+ className = className + ' ui-state-disabled';
2989
+ }
2990
+
2991
+ //Stop current animation (if does exists) and begin 'deleting' animation.
2992
+ $rows.stop(true, true).addClass(className, 'slow', '').promise().done(function () {
2993
+ self._removeRowsFromTable($rows, 'deleted');
2994
+ });
2995
+ } else {
2996
+ self._removeRowsFromTable($rows, 'deleted');
2997
+ }
2998
+ }
2999
+
3000
+ });
3001
+
3002
+ })(jQuery);
3003
+
3004
+
3005
+ /************************************************************************
3006
+ * SELECTING extension for jTable *
3007
+ *************************************************************************/
3008
+ (function ($) {
3009
+
3010
+ //Reference to base object members
3011
+ var base = {
3012
+ _create: $.hik.jtable.prototype._create,
3013
+ _addColumnsToHeaderRow: $.hik.jtable.prototype._addColumnsToHeaderRow,
3014
+ _addCellsToRowUsingRecord: $.hik.jtable.prototype._addCellsToRowUsingRecord,
3015
+ _onLoadingRecords: $.hik.jtable.prototype._onLoadingRecords,
3016
+ _onRecordsLoaded: $.hik.jtable.prototype._onRecordsLoaded,
3017
+ _onRowsRemoved: $.hik.jtable.prototype._onRowsRemoved
3018
+ };
3019
+
3020
+ //extension members
3021
+ $.extend(true, $.hik.jtable.prototype, {
3022
+
3023
+ /************************************************************************
3024
+ * DEFAULT OPTIONS / EVENTS *
3025
+ *************************************************************************/
3026
+ options: {
3027
+
3028
+ //Options
3029
+ selecting: false,
3030
+ multiselect: false,
3031
+ selectingCheckboxes: false,
3032
+ selectOnRowClick: true,
3033
+
3034
+ //Events
3035
+ selectionChanged: function (event, data) { }
3036
+ },
3037
+
3038
+ /************************************************************************
3039
+ * PRIVATE FIELDS *
3040
+ *************************************************************************/
3041
+
3042
+ _selectedRecordIdsBeforeLoad: null, //This array is used to store selected row Id's to restore them after a page refresh (string array).
3043
+ _$selectAllCheckbox: null, //Reference to the 'select/deselect all' checkbox (jQuery object)
3044
+ _shiftKeyDown: false, //True, if shift key is currently down.
3045
+
3046
+ /************************************************************************
3047
+ * CONSTRUCTOR *
3048
+ *************************************************************************/
3049
+
3050
+ /* Overrides base method to do selecting-specific constructions.
3051
+ *************************************************************************/
3052
+ _create: function () {
3053
+ if (this.options.selecting && this.options.selectingCheckboxes) {
3054
+ ++this._firstDataColumnOffset;
3055
+ this._bindKeyboardEvents();
3056
+ }
3057
+
3058
+ //Call base method
3059
+ base._create.apply(this, arguments);
3060
+ },
3061
+
3062
+ /* Registers to keyboard events those are needed for selection
3063
+ *************************************************************************/
3064
+ _bindKeyboardEvents: function () {
3065
+ var self = this;
3066
+ //Register to events to set _shiftKeyDown value
3067
+ $(document)
3068
+ .keydown(function (event) {
3069
+ switch (event.which) {
3070
+ case 16:
3071
+ self._shiftKeyDown = true;
3072
+ break;
3073
+ }
3074
+ })
3075
+ .keyup(function (event) {
3076
+ switch (event.which) {
3077
+ case 16:
3078
+ self._shiftKeyDown = false;
3079
+ break;
3080
+ }
3081
+ });
3082
+ },
3083
+
3084
+ /************************************************************************
3085
+ * PUBLIC METHODS *
3086
+ *************************************************************************/
3087
+
3088
+ /* Gets jQuery selection for currently selected rows.
3089
+ *************************************************************************/
3090
+ selectedRows: function () {
3091
+ return this._getSelectedRows();
3092
+ },
3093
+
3094
+ /* Makes row/rows 'selected'.
3095
+ *************************************************************************/
3096
+ selectRows: function ($rows) {
3097
+ this._selectRows($rows);
3098
+ this._onSelectionChanged(); //TODO: trigger only if selected rows changes?
3099
+ },
3100
+
3101
+ /************************************************************************
3102
+ * OVERRIDED METHODS *
3103
+ *************************************************************************/
3104
+
3105
+ /* Overrides base method to add a 'select column' to header row.
3106
+ *************************************************************************/
3107
+ _addColumnsToHeaderRow: function ($tr) {
3108
+ if (this.options.selecting && this.options.selectingCheckboxes) {
3109
+ if (this.options.multiselect) {
3110
+ $tr.append(this._createSelectAllHeader());
3111
+ } else {
3112
+ $tr.append(this._createEmptyCommandHeader());
3113
+ }
3114
+ }
3115
+
3116
+ base._addColumnsToHeaderRow.apply(this, arguments);
3117
+ },
3118
+
3119
+ /* Overrides base method to add a 'delete command cell' to a row.
3120
+ *************************************************************************/
3121
+ _addCellsToRowUsingRecord: function ($row) {
3122
+ if (this.options.selecting) {
3123
+ this._makeRowSelectable($row);
3124
+ }
3125
+
3126
+ base._addCellsToRowUsingRecord.apply(this, arguments);
3127
+ },
3128
+
3129
+ /* Overrides base event to store selection list
3130
+ *************************************************************************/
3131
+ _onLoadingRecords: function () {
3132
+ if (this.options.selecting) {
3133
+ this._storeSelectionList();
3134
+ }
3135
+
3136
+ base._onLoadingRecords.apply(this, arguments);
3137
+ },
3138
+
3139
+ /* Overrides base event to restore selection list
3140
+ *************************************************************************/
3141
+ _onRecordsLoaded: function () {
3142
+ if (this.options.selecting) {
3143
+ this._restoreSelectionList();
3144
+ }
3145
+
3146
+ base._onRecordsLoaded.apply(this, arguments);
3147
+ },
3148
+
3149
+ /* Overrides base event to check is any selected row is being removed.
3150
+ *************************************************************************/
3151
+ _onRowsRemoved: function ($rows, reason) {
3152
+ if (this.options.selecting && (reason != 'reloading') && ($rows.filter('.jtable-row-selected').length > 0)) {
3153
+ this._onSelectionChanged();
3154
+ }
3155
+
3156
+ base._onRowsRemoved.apply(this, arguments);
3157
+ },
3158
+
3159
+ /************************************************************************
3160
+ * PRIVATE METHODS *
3161
+ *************************************************************************/
3162
+
3163
+ /* Creates a header column to select/deselect all rows.
3164
+ *************************************************************************/
3165
+ _createSelectAllHeader: function () {
3166
+ var self = this;
3167
+
3168
+ var $columnHeader = $('<th class=""></th>')
3169
+ .addClass('jtable-command-column-header jtable-column-header-selecting');
3170
+ this._jqueryuiThemeAddClass($columnHeader, 'ui-state-default');
3171
+
3172
+ var $headerContainer = $('<div />')
3173
+ .addClass('jtable-column-header-container')
3174
+ .appendTo($columnHeader);
3175
+
3176
+ self._$selectAllCheckbox = $('<input type="checkbox" />')
3177
+ .appendTo($headerContainer)
3178
+ .click(function () {
3179
+ if (self._$tableRows.length <= 0) {
3180
+ self._$selectAllCheckbox.attr('checked', false);
3181
+ return;
3182
+ }
3183
+
3184
+ var allRows = self._$tableBody.find('>tr.jtable-data-row');
3185
+ if (self._$selectAllCheckbox.is(':checked')) {
3186
+ self._selectRows(allRows);
3187
+ } else {
3188
+ self._deselectRows(allRows);
3189
+ }
3190
+
3191
+ self._onSelectionChanged();
3192
+ });
3193
+
3194
+ return $columnHeader;
3195
+ },
3196
+
3197
+ /* Stores Id's of currently selected records to _selectedRecordIdsBeforeLoad.
3198
+ *************************************************************************/
3199
+ _storeSelectionList: function () {
3200
+ var self = this;
3201
+
3202
+ if (!self.options.selecting) {
3203
+ return;
3204
+ }
3205
+
3206
+ self._selectedRecordIdsBeforeLoad = [];
3207
+ self._getSelectedRows().each(function () {
3208
+ self._selectedRecordIdsBeforeLoad.push(self._getKeyValueOfRecord($(this).data('record')));
3209
+ });
3210
+ },
3211
+
3212
+ /* Selects rows whose Id is in _selectedRecordIdsBeforeLoad;
3213
+ *************************************************************************/
3214
+ _restoreSelectionList: function () {
3215
+ var self = this;
3216
+
3217
+ if (!self.options.selecting) {
3218
+ return;
3219
+ }
3220
+
3221
+ var selectedRowCount = 0;
3222
+ for (var i = 0; i < self._$tableRows.length; ++i) {
3223
+ var recordId = self._getKeyValueOfRecord(self._$tableRows[i].data('record'));
3224
+ if ($.inArray(recordId, self._selectedRecordIdsBeforeLoad) > -1) {
3225
+ self._selectRows(self._$tableRows[i]);
3226
+ ++selectedRowCount;
3227
+ }
3228
+ }
3229
+
3230
+ if (self._selectedRecordIdsBeforeLoad.length > 0 && self._selectedRecordIdsBeforeLoad.length != selectedRowCount) {
3231
+ self._onSelectionChanged();
3232
+ }
3233
+
3234
+ self._selectedRecordIdsBeforeLoad = [];
3235
+ self._refreshSelectAllCheckboxState();
3236
+ },
3237
+
3238
+ /* Gets all selected rows.
3239
+ *************************************************************************/
3240
+ _getSelectedRows: function () {
3241
+ return this._$tableBody
3242
+ .find('>tr.jtable-row-selected');
3243
+ },
3244
+
3245
+ /* Adds selectable feature to a row.
3246
+ *************************************************************************/
3247
+ _makeRowSelectable: function ($row) {
3248
+ var self = this;
3249
+
3250
+ //Select/deselect on row click
3251
+ if (self.options.selectOnRowClick) {
3252
+ $row.click(function () {
3253
+ self._invertRowSelection($row);
3254
+ });
3255
+ }
3256
+
3257
+ //'select/deselect' checkbox column
3258
+ if (self.options.selectingCheckboxes) {
3259
+ var $cell = $('<td></td>').addClass('jtable-selecting-column');
3260
+ var $selectCheckbox = $('<input type="checkbox" />').appendTo($cell);
3261
+ if (!self.options.selectOnRowClick) {
3262
+ $selectCheckbox.click(function () {
3263
+ self._invertRowSelection($row);
3264
+ });
3265
+ }
3266
+
3267
+ $row.append($cell);
3268
+ }
3269
+ },
3270
+
3271
+ /* Inverts selection state of a single row.
3272
+ *************************************************************************/
3273
+ _invertRowSelection: function ($row) {
3274
+ if ($row.hasClass('jtable-row-selected')) {
3275
+ this._deselectRows($row);
3276
+ } else {
3277
+ //Shift key?
3278
+ if (this._shiftKeyDown) {
3279
+ var rowIndex = this._findRowIndex($row);
3280
+ //try to select row and above rows until first selected row
3281
+ var beforeIndex = this._findFirstSelectedRowIndexBeforeIndex(rowIndex) + 1;
3282
+ if (beforeIndex > 0 && beforeIndex < rowIndex) {
3283
+ this._selectRows(this._$tableBody.find('tr').slice(beforeIndex, rowIndex + 1));
3284
+ } else {
3285
+ //try to select row and below rows until first selected row
3286
+ var afterIndex = this._findFirstSelectedRowIndexAfterIndex(rowIndex) - 1;
3287
+ if (afterIndex > rowIndex) {
3288
+ this._selectRows(this._$tableBody.find('tr').slice(rowIndex, afterIndex + 1));
3289
+ } else {
3290
+ //just select this row
3291
+ this._selectRows($row);
3292
+ }
3293
+ }
3294
+ } else {
3295
+ this._selectRows($row);
3296
+ }
3297
+ }
3298
+
3299
+ this._onSelectionChanged();
3300
+ },
3301
+
3302
+ /* Search for a selected row (that is before given row index) to up and returns it's index
3303
+ *************************************************************************/
3304
+ _findFirstSelectedRowIndexBeforeIndex: function (rowIndex) {
3305
+ for (var i = rowIndex - 1; i >= 0; --i) {
3306
+ if (this._$tableRows[i].hasClass('jtable-row-selected')) {
3307
+ return i;
3308
+ }
3309
+ }
3310
+
3311
+ return -1;
3312
+ },
3313
+
3314
+ /* Search for a selected row (that is after given row index) to down and returns it's index
3315
+ *************************************************************************/
3316
+ _findFirstSelectedRowIndexAfterIndex: function (rowIndex) {
3317
+ for (var i = rowIndex + 1; i < this._$tableRows.length; ++i) {
3318
+ if (this._$tableRows[i].hasClass('jtable-row-selected')) {
3319
+ return i;
3320
+ }
3321
+ }
3322
+
3323
+ return -1;
3324
+ },
3325
+
3326
+ /* Makes row/rows 'selected'.
3327
+ *************************************************************************/
3328
+ _selectRows: function ($rows) {
3329
+ if (!this.options.multiselect) {
3330
+ this._deselectRows(this._getSelectedRows());
3331
+ }
3332
+
3333
+ $rows.addClass('jtable-row-selected');
3334
+ this._jqueryuiThemeAddClass($rows, 'ui-state-highlight');
3335
+
3336
+ if (this.options.selectingCheckboxes) {
3337
+ $rows.find('>td.jtable-selecting-column >input').prop('checked', true);
3338
+ }
3339
+
3340
+ this._refreshSelectAllCheckboxState();
3341
+ },
3342
+
3343
+ /* Makes row/rows 'non selected'.
3344
+ *************************************************************************/
3345
+ _deselectRows: function ($rows) {
3346
+ $rows.removeClass('jtable-row-selected ui-state-highlight');
3347
+ if (this.options.selectingCheckboxes) {
3348
+ $rows.find('>td.jtable-selecting-column >input').prop('checked', false);
3349
+ }
3350
+
3351
+ this._refreshSelectAllCheckboxState();
3352
+ },
3353
+
3354
+ /* Updates state of the 'select/deselect' all checkbox according to count of selected rows.
3355
+ *************************************************************************/
3356
+ _refreshSelectAllCheckboxState: function () {
3357
+ if (!this.options.selectingCheckboxes || !this.options.multiselect) {
3358
+ return;
3359
+ }
3360
+
3361
+ var totalRowCount = this._$tableRows.length;
3362
+ var selectedRowCount = this._getSelectedRows().length;
3363
+
3364
+ if (selectedRowCount == 0) {
3365
+ this._$selectAllCheckbox.prop('indeterminate', false);
3366
+ this._$selectAllCheckbox.attr('checked', false);
3367
+ } else if (selectedRowCount == totalRowCount) {
3368
+ this._$selectAllCheckbox.prop('indeterminate', false);
3369
+ this._$selectAllCheckbox.attr('checked', true);
3370
+ } else {
3371
+ this._$selectAllCheckbox.attr('checked', false);
3372
+ this._$selectAllCheckbox.prop('indeterminate', true);
3373
+ }
3374
+ },
3375
+
3376
+ /************************************************************************
3377
+ * EVENT RAISING METHODS *
3378
+ *************************************************************************/
3379
+
3380
+ _onSelectionChanged: function () {
3381
+ this._trigger("selectionChanged", null, {});
3382
+ }
3383
+
3384
+ });
3385
+
3386
+ })(jQuery);
3387
+
3388
+
3389
+ /************************************************************************
3390
+ * PAGING extension for jTable *
3391
+ *************************************************************************/
3392
+ (function ($) {
3393
+
3394
+ //Reference to base object members
3395
+ var base = {
3396
+ load: $.hik.jtable.prototype.load,
3397
+ _create: $.hik.jtable.prototype._create,
3398
+ _setOption: $.hik.jtable.prototype._setOption,
3399
+ _createRecordLoadUrl: $.hik.jtable.prototype._createRecordLoadUrl,
3400
+ _addRowToTable: $.hik.jtable.prototype._addRowToTable,
3401
+ _addRow: $.hik.jtable.prototype._addRow,
3402
+ _removeRowsFromTable: $.hik.jtable.prototype._removeRowsFromTable,
3403
+ _onRecordsLoaded: $.hik.jtable.prototype._onRecordsLoaded
3404
+ };
3405
+
3406
+ //extension members
3407
+ $.extend(true, $.hik.jtable.prototype, {
3408
+
3409
+ /************************************************************************
3410
+ * DEFAULT OPTIONS / EVENTS *
3411
+ *************************************************************************/
3412
+ options: {
3413
+ paging: false,
3414
+ pageList: 'normal', //possible values: 'minimal', 'normal'
3415
+ pageSize: 10,
3416
+ pageSizes: [10, 25, 50, 100, 250, 500],
3417
+ pageSizeChangeArea: true,
3418
+ gotoPageArea: 'combobox', //possible values: 'textbox', 'combobox', 'none'
3419
+
3420
+ messages: {
3421
+ pagingInfo: 'Showing {0}-{1} of {2}',
3422
+ pageSizeChangeLabel: 'Row count',
3423
+ gotoPageLabel: 'Go to page'
3424
+ }
3425
+ },
3426
+
3427
+ /************************************************************************
3428
+ * PRIVATE FIELDS *
3429
+ *************************************************************************/
3430
+
3431
+ _$bottomPanel: null, //Reference to the panel at the bottom of the table (jQuery object)
3432
+ _$pagingListArea: null, //Reference to the page list area in to bottom panel (jQuery object)
3433
+ _$pageSizeChangeArea: null, //Reference to the page size change area in to bottom panel (jQuery object)
3434
+ _$pageInfoSpan: null, //Reference to the paging info area in to bottom panel (jQuery object)
3435
+ _$gotoPageArea: null, //Reference to 'Go to page' input area in to bottom panel (jQuery object)
3436
+ _$gotoPageInput: null, //Reference to 'Go to page' input in to bottom panel (jQuery object)
3437
+ _totalRecordCount: 0, //Total count of records on all pages
3438
+ _currentPageNo: 1, //Current page number
3439
+
3440
+ /************************************************************************
3441
+ * CONSTRUCTOR AND INITIALIZING METHODS *
3442
+ *************************************************************************/
3443
+
3444
+ /* Overrides base method to do paging-specific constructions.
3445
+ *************************************************************************/
3446
+ _create: function () {
3447
+ base._create.apply(this, arguments);
3448
+ if (this.options.paging) {
3449
+ this._loadPagingSettings();
3450
+ this._createBottomPanel();
3451
+ this._createPageListArea();
3452
+ this._createGotoPageInput();
3453
+ this._createPageSizeSelection();
3454
+ }
3455
+ },
3456
+
3457
+ /* Loads user preferences for paging.
3458
+ *************************************************************************/
3459
+ _loadPagingSettings: function () {
3460
+ if (!this.options.saveUserPreferences) {
3461
+ return;
3462
+ }
3463
+
3464
+ var pageSize = this._getCookie('page-size');
3465
+ if (pageSize) {
3466
+ this.options.pageSize = this._normalizeNumber(pageSize, 1, 1000000, this.options.pageSize);
3467
+ }
3468
+ },
3469
+
3470
+ /* Creates bottom panel and adds to the page.
3471
+ *************************************************************************/
3472
+ _createBottomPanel: function () {
3473
+ this._$bottomPanel = $('<div />')
3474
+ .addClass('jtable-bottom-panel')
3475
+ .insertAfter(this._$table);
3476
+
3477
+ this._jqueryuiThemeAddClass(this._$bottomPanel, 'ui-state-default');
3478
+
3479
+ $('<div />').addClass('jtable-left-area').appendTo(this._$bottomPanel);
3480
+ $('<div />').addClass('jtable-right-area').appendTo(this._$bottomPanel);
3481
+ },
3482
+
3483
+ /* Creates page list area.
3484
+ *************************************************************************/
3485
+ _createPageListArea: function () {
3486
+ this._$pagingListArea = $('<span></span>')
3487
+ .addClass('jtable-page-list')
3488
+ .appendTo(this._$bottomPanel.find('.jtable-left-area'));
3489
+
3490
+ this._$pageInfoSpan = $('<span></span>')
3491
+ .addClass('jtable-page-info')
3492
+ .appendTo(this._$bottomPanel.find('.jtable-right-area'));
3493
+ },
3494
+
3495
+ /* Creates page list change area.
3496
+ *************************************************************************/
3497
+ _createPageSizeSelection: function () {
3498
+ var self = this;
3499
+
3500
+ if (!self.options.pageSizeChangeArea) {
3501
+ return;
3502
+ }
3503
+
3504
+ //Add current page size to page sizes list if not contains it
3505
+ if (self._findIndexInArray(self.options.pageSize, self.options.pageSizes) < 0) {
3506
+ self.options.pageSizes.push(parseInt(self.options.pageSize));
3507
+ self.options.pageSizes.sort(function (a, b) { return a - b; });
3508
+ }
3509
+
3510
+ //Add a span to contain page size change items
3511
+ self._$pageSizeChangeArea = $('<span></span>')
3512
+ .addClass('jtable-page-size-change')
3513
+ .appendTo(self._$bottomPanel.find('.jtable-left-area'));
3514
+
3515
+ //Page size label
3516
+ self._$pageSizeChangeArea.append('<span>' + self.options.messages.pageSizeChangeLabel + ': </span>');
3517
+
3518
+ //Page size change combobox
3519
+ var $pageSizeChangeCombobox = $('<select></select>').appendTo(self._$pageSizeChangeArea);
3520
+
3521
+ //Add page sizes to the combobox
3522
+ for (var i = 0; i < self.options.pageSizes.length; i++) {
3523
+ $pageSizeChangeCombobox.append('<option value="' + self.options.pageSizes[i] + '">' + self.options.pageSizes[i] + '</option>');
3524
+ }
3525
+
3526
+ //Select current page size
3527
+ $pageSizeChangeCombobox.val(self.options.pageSize);
3528
+
3529
+ //Change page size on combobox change
3530
+ $pageSizeChangeCombobox.change(function () {
3531
+ self._changePageSize(parseInt($(this).val()));
3532
+ });
3533
+ },
3534
+
3535
+ /* Creates go to page area.
3536
+ *************************************************************************/
3537
+ _createGotoPageInput: function () {
3538
+ var self = this;
3539
+
3540
+ if (!self.options.gotoPageArea || self.options.gotoPageArea == 'none') {
3541
+ return;
3542
+ }
3543
+
3544
+ //Add a span to contain goto page items
3545
+ this._$gotoPageArea = $('<span></span>')
3546
+ .addClass('jtable-goto-page')
3547
+ .appendTo(self._$bottomPanel.find('.jtable-left-area'));
3548
+
3549
+ //Goto page label
3550
+ this._$gotoPageArea.append('<span>' + self.options.messages.gotoPageLabel + ': </span>');
3551
+
3552
+ //Goto page input
3553
+ if (self.options.gotoPageArea == 'combobox') {
3554
+
3555
+ self._$gotoPageInput = $('<select></select>')
3556
+ .appendTo(this._$gotoPageArea)
3557
+ .data('pageCount', 1)
3558
+ .change(function () {
3559
+ self._changePage(parseInt($(this).val()));
3560
+ });
3561
+ self._$gotoPageInput.append('<option value="1">1</option>');
3562
+
3563
+ } else { //textbox
3564
+
3565
+ self._$gotoPageInput = $('<input type="text" maxlength="10" value="' + self._currentPageNo + '" />')
3566
+ .appendTo(this._$gotoPageArea)
3567
+ .keypress(function (event) {
3568
+ if (event.which == 13) { //enter
3569
+ event.preventDefault();
3570
+ self._changePage(parseInt(self._$gotoPageInput.val()));
3571
+ } else if (event.which == 43) { // +
3572
+ event.preventDefault();
3573
+ self._changePage(parseInt(self._$gotoPageInput.val()) + 1);
3574
+ } else if (event.which == 45) { // -
3575
+ event.preventDefault();
3576
+ self._changePage(parseInt(self._$gotoPageInput.val()) - 1);
3577
+ } else {
3578
+ //Allow only digits
3579
+ var isValid = (
3580
+ (47 < event.keyCode && event.keyCode < 58 && event.shiftKey == false && event.altKey == false)
3581
+ || (event.keyCode == 8)
3582
+ || (event.keyCode == 9)
3583
+ );
3584
+
3585
+ if (!isValid) {
3586
+ event.preventDefault();
3587
+ }
3588
+ }
3589
+ });
3590
+
3591
+ }
3592
+ },
3593
+
3594
+ /* Refreshes the 'go to page' input.
3595
+ *************************************************************************/
3596
+ _refreshGotoPageInput: function () {
3597
+ if (!this.options.gotoPageArea || this.options.gotoPageArea == 'none') {
3598
+ return;
3599
+ }
3600
+
3601
+ if (this._totalRecordCount <= 0) {
3602
+ this._$gotoPageArea.hide();
3603
+ } else {
3604
+ this._$gotoPageArea.show();
3605
+ }
3606
+
3607
+ if (this.options.gotoPageArea == 'combobox') {
3608
+ var oldPageCount = this._$gotoPageInput.data('pageCount');
3609
+ var currentPageCount = this._calculatePageCount();
3610
+ if (oldPageCount != currentPageCount) {
3611
+ this._$gotoPageInput.empty();
3612
+
3613
+ //Skip some pages is there are too many pages
3614
+ var pageStep = 1;
3615
+ if (currentPageCount > 10000) {
3616
+ pageStep = 100;
3617
+ } else if (currentPageCount > 5000) {
3618
+ pageStep = 10;
3619
+ } else if (currentPageCount > 2000) {
3620
+ pageStep = 5;
3621
+ } else if (currentPageCount > 1000) {
3622
+ pageStep = 2;
3623
+ }
3624
+
3625
+ for (var i = pageStep; i <= currentPageCount; i += pageStep) {
3626
+ this._$gotoPageInput.append('<option value="' + i + '">' + i + '</option>');
3627
+ }
3628
+
3629
+ this._$gotoPageInput.data('pageCount', currentPageCount);
3630
+ }
3631
+ }
3632
+
3633
+ //same for 'textbox' and 'combobox'
3634
+ this._$gotoPageInput.val(this._currentPageNo);
3635
+ },
3636
+
3637
+ /************************************************************************
3638
+ * OVERRIDED METHODS *
3639
+ *************************************************************************/
3640
+
3641
+ /* Overrides load method to set current page to 1.
3642
+ *************************************************************************/
3643
+ load: function () {
3644
+ this._currentPageNo = 1;
3645
+
3646
+ base.load.apply(this, arguments);
3647
+ },
3648
+
3649
+ /* Used to change options dynamically after initialization.
3650
+ *************************************************************************/
3651
+ _setOption: function (key, value) {
3652
+ base._setOption.apply(this, arguments);
3653
+
3654
+ if (key == 'pageSize') {
3655
+ this._changePageSize(parseInt(value));
3656
+ }
3657
+ },
3658
+
3659
+ /* Changes current page size with given value.
3660
+ *************************************************************************/
3661
+ _changePageSize: function (pageSize) {
3662
+ if (pageSize == this.options.pageSize) {
3663
+ return;
3664
+ }
3665
+
3666
+ this.options.pageSize = pageSize;
3667
+
3668
+ //Normalize current page
3669
+ var pageCount = this._calculatePageCount();
3670
+ if (this._currentPageNo > pageCount) {
3671
+ this._currentPageNo = pageCount;
3672
+ }
3673
+ if (this._currentPageNo <= 0) {
3674
+ this._currentPageNo = 1;
3675
+ }
3676
+
3677
+ //if user sets one of the options on the combobox, then select it.
3678
+ var $pageSizeChangeCombobox = this._$bottomPanel.find('.jtable-page-size-change select');
3679
+ if ($pageSizeChangeCombobox.length > 0) {
3680
+ if (parseInt($pageSizeChangeCombobox.val()) != pageSize) {
3681
+ var selectedOption = $pageSizeChangeCombobox.find('option[value=' + pageSize + ']');
3682
+ if (selectedOption.length > 0) {
3683
+ $pageSizeChangeCombobox.val(pageSize);
3684
+ }
3685
+ }
3686
+ }
3687
+
3688
+ this._savePagingSettings();
3689
+ this._reloadTable();
3690
+ },
3691
+
3692
+ /* Saves user preferences for paging
3693
+ *************************************************************************/
3694
+ _savePagingSettings: function () {
3695
+ if (!this.options.saveUserPreferences) {
3696
+ return;
3697
+ }
3698
+
3699
+ this._setCookie('page-size', this.options.pageSize);
3700
+ },
3701
+
3702
+ /* Overrides _createRecordLoadUrl method to add paging info to URL.
3703
+ *************************************************************************/
3704
+ _createRecordLoadUrl: function () {
3705
+ var loadUrl = base._createRecordLoadUrl.apply(this, arguments);
3706
+ loadUrl = this._addPagingInfoToUrl(loadUrl, this._currentPageNo);
3707
+ return loadUrl;
3708
+ },
3709
+
3710
+ /* Overrides _addRowToTable method to re-load table when a new row is created.
3711
+ * NOTE: THIS METHOD IS DEPRECATED AND WILL BE REMOVED FROM FEATURE RELEASES.
3712
+ * USE _addRow METHOD.
3713
+ *************************************************************************/
3714
+ _addRowToTable: function ($tableRow, index, isNewRow) {
3715
+ if (isNewRow && this.options.paging) {
3716
+ this._reloadTable();
3717
+ return;
3718
+ }
3719
+
3720
+ base._addRowToTable.apply(this, arguments);
3721
+ },
3722
+
3723
+ /* Overrides _addRow method to re-load table when a new row is created.
3724
+ *************************************************************************/
3725
+ _addRow: function ($row, options) {
3726
+ if (options && options.isNewRow && this.options.paging) {
3727
+ this._reloadTable();
3728
+ return;
3729
+ }
3730
+
3731
+ base._addRow.apply(this, arguments);
3732
+ },
3733
+
3734
+ /* Overrides _removeRowsFromTable method to re-load table when a row is removed from table.
3735
+ *************************************************************************/
3736
+ _removeRowsFromTable: function ($rows, reason) {
3737
+ base._removeRowsFromTable.apply(this, arguments);
3738
+
3739
+ if (this.options.paging) {
3740
+ if (this._$tableRows.length <= 0 && this._currentPageNo > 1) {
3741
+ --this._currentPageNo;
3742
+ }
3743
+
3744
+ this._reloadTable();
3745
+ }
3746
+ },
3747
+
3748
+ /* Overrides _onRecordsLoaded method to to do paging specific tasks.
3749
+ *************************************************************************/
3750
+ _onRecordsLoaded: function (data) {
3751
+ if (this.options.paging) {
3752
+ this._totalRecordCount = data.TotalRecordCount;
3753
+ this._createPagingList();
3754
+ this._createPagingInfo();
3755
+ this._refreshGotoPageInput();
3756
+ }
3757
+
3758
+ base._onRecordsLoaded.apply(this, arguments);
3759
+ },
3760
+
3761
+ /************************************************************************
3762
+ * PRIVATE METHODS *
3763
+ *************************************************************************/
3764
+
3765
+ /* Adds jtStartIndex and jtPageSize parameters to a URL as query string.
3766
+ *************************************************************************/
3767
+ _addPagingInfoToUrl: function (url, pageNumber) {
3768
+ if (!this.options.paging) {
3769
+ return url;
3770
+ }
3771
+
3772
+ var jtStartIndex = (pageNumber - 1) * this.options.pageSize;
3773
+ var jtPageSize = this.options.pageSize;
3774
+
3775
+ return (url + (url.indexOf('?') < 0 ? '?' : '&') + 'jtStartIndex=' + jtStartIndex + '&jtPageSize=' + jtPageSize);
3776
+ },
3777
+
3778
+ /* Creates and shows the page list.
3779
+ *************************************************************************/
3780
+ _createPagingList: function () {
3781
+ if (this.options.pageSize <= 0) {
3782
+ return;
3783
+ }
3784
+
3785
+ this._$pagingListArea.empty();
3786
+ if (this._totalRecordCount <= 0) {
3787
+ return;
3788
+ }
3789
+
3790
+ var pageCount = this._calculatePageCount();
3791
+
3792
+ this._createFirstAndPreviousPageButtons();
3793
+ if (this.options.pageList == 'normal') {
3794
+ this._createPageNumberButtons(this._calculatePageNumbers(pageCount));
3795
+ }
3796
+ this._createLastAndNextPageButtons(pageCount);
3797
+ this._bindClickEventsToPageNumberButtons();
3798
+ },
3799
+
3800
+ /* Creates and shows previous and first page links.
3801
+ *************************************************************************/
3802
+ _createFirstAndPreviousPageButtons: function () {
3803
+ var $first = $('<span></span>')
3804
+ .addClass('jtable-page-number-first')
3805
+ .html('&lt&lt')
3806
+ .data('pageNumber', 1)
3807
+ .appendTo(this._$pagingListArea);
3808
+
3809
+ var $previous = $('<span></span>')
3810
+ .addClass('jtable-page-number-previous')
3811
+ .html('&lt')
3812
+ .data('pageNumber', this._currentPageNo - 1)
3813
+ .appendTo(this._$pagingListArea);
3814
+
3815
+ this._jqueryuiThemeAddClass($first, 'ui-button ui-state-default', 'ui-state-hover');
3816
+ this._jqueryuiThemeAddClass($previous, 'ui-button ui-state-default', 'ui-state-hover');
3817
+
3818
+ if (this._currentPageNo <= 1) {
3819
+ $first.addClass('jtable-page-number-disabled');
3820
+ $previous.addClass('jtable-page-number-disabled');
3821
+ this._jqueryuiThemeAddClass($first, 'ui-state-disabled');
3822
+ this._jqueryuiThemeAddClass($previous, 'ui-state-disabled');
3823
+ }
3824
+ },
3825
+
3826
+ /* Creates and shows next and last page links.
3827
+ *************************************************************************/
3828
+ _createLastAndNextPageButtons: function (pageCount) {
3829
+ var $next = $('<span></span>')
3830
+ .addClass('jtable-page-number-next')
3831
+ .html('&gt')
3832
+ .data('pageNumber', this._currentPageNo + 1)
3833
+ .appendTo(this._$pagingListArea);
3834
+ var $last = $('<span></span>')
3835
+ .addClass('jtable-page-number-last')
3836
+ .html('&gt&gt')
3837
+ .data('pageNumber', pageCount)
3838
+ .appendTo(this._$pagingListArea);
3839
+
3840
+ this._jqueryuiThemeAddClass($next, 'ui-button ui-state-default', 'ui-state-hover');
3841
+ this._jqueryuiThemeAddClass($last, 'ui-button ui-state-default', 'ui-state-hover');
3842
+
3843
+ if (this._currentPageNo >= pageCount) {
3844
+ $next.addClass('jtable-page-number-disabled');
3845
+ $last.addClass('jtable-page-number-disabled');
3846
+ this._jqueryuiThemeAddClass($next, 'ui-state-disabled');
3847
+ this._jqueryuiThemeAddClass($last, 'ui-state-disabled');
3848
+ }
3849
+ },
3850
+
3851
+ /* Creates and shows page number links for given number array.
3852
+ *************************************************************************/
3853
+ _createPageNumberButtons: function (pageNumbers) {
3854
+ var previousNumber = 0;
3855
+ for (var i = 0; i < pageNumbers.length; i++) {
3856
+ //Create "..." between page numbers if needed
3857
+ if ((pageNumbers[i] - previousNumber) > 1) {
3858
+ $('<span></span>')
3859
+ .addClass('jtable-page-number-space')
3860
+ .html('...')
3861
+ .appendTo(this._$pagingListArea);
3862
+ }
3863
+
3864
+ this._createPageNumberButton(pageNumbers[i]);
3865
+ previousNumber = pageNumbers[i];
3866
+ }
3867
+ },
3868
+
3869
+ /* Creates a page number link and adds to paging area.
3870
+ *************************************************************************/
3871
+ _createPageNumberButton: function (pageNumber) {
3872
+ var $pageNumber = $('<span></span>')
3873
+ .addClass('jtable-page-number')
3874
+ .html(pageNumber)
3875
+ .data('pageNumber', pageNumber)
3876
+ .appendTo(this._$pagingListArea);
3877
+
3878
+ this._jqueryuiThemeAddClass($pageNumber, 'ui-button ui-state-default', 'ui-state-hover');
3879
+
3880
+ if (this._currentPageNo == pageNumber) {
3881
+ $pageNumber.addClass('jtable-page-number-active jtable-page-number-disabled');
3882
+ this._jqueryuiThemeAddClass($pageNumber, 'ui-state-active');
3883
+ }
3884
+ },
3885
+
3886
+ /* Calculates total page count according to page size and total record count.
3887
+ *************************************************************************/
3888
+ _calculatePageCount: function () {
3889
+ var pageCount = Math.floor(this._totalRecordCount / this.options.pageSize);
3890
+ if (this._totalRecordCount % this.options.pageSize != 0) {
3891
+ ++pageCount;
3892
+ }
3893
+
3894
+ return pageCount;
3895
+ },
3896
+
3897
+ /* Calculates page numbers and returns an array of these numbers.
3898
+ *************************************************************************/
3899
+ _calculatePageNumbers: function (pageCount) {
3900
+ if (pageCount <= 4) {
3901
+ //Show all pages
3902
+ var pageNumbers = [];
3903
+ for (var i = 1; i <= pageCount; ++i) {
3904
+ pageNumbers.push(i);
3905
+ }
3906
+
3907
+ return pageNumbers;
3908
+ } else {
3909
+ //show first three, last three, current, previous and next page numbers
3910
+ var shownPageNumbers = [1, 2, pageCount - 1, pageCount];
3911
+ var previousPageNo = this._normalizeNumber(this._currentPageNo - 1, 1, pageCount, 1);
3912
+ var nextPageNo = this._normalizeNumber(this._currentPageNo + 1, 1, pageCount, 1);
3913
+
3914
+ this._insertToArrayIfDoesNotExists(shownPageNumbers, previousPageNo);
3915
+ this._insertToArrayIfDoesNotExists(shownPageNumbers, this._currentPageNo);
3916
+ this._insertToArrayIfDoesNotExists(shownPageNumbers, nextPageNo);
3917
+
3918
+ shownPageNumbers.sort(function (a, b) { return a - b; });
3919
+ return shownPageNumbers;
3920
+ }
3921
+ },
3922
+
3923
+ /* Creates and shows paging informations.
3924
+ *************************************************************************/
3925
+ _createPagingInfo: function () {
3926
+ if (this._totalRecordCount <= 0) {
3927
+ this._$pageInfoSpan.empty();
3928
+ return;
3929
+ }
3930
+
3931
+ var startNo = (this._currentPageNo - 1) * this.options.pageSize + 1;
3932
+ var endNo = this._currentPageNo * this.options.pageSize;
3933
+ endNo = this._normalizeNumber(endNo, startNo, this._totalRecordCount, 0);
3934
+
3935
+ if (endNo >= startNo) {
3936
+ var pagingInfoMessage = this._formatString(this.options.messages.pagingInfo, startNo, endNo, this._totalRecordCount);
3937
+ this._$pageInfoSpan.html(pagingInfoMessage);
3938
+ }
3939
+ },
3940
+
3941
+ /* Binds click events of all page links to change the page.
3942
+ *************************************************************************/
3943
+ _bindClickEventsToPageNumberButtons: function () {
3944
+ var self = this;
3945
+ self._$pagingListArea
3946
+ .find('.jtable-page-number,.jtable-page-number-previous,.jtable-page-number-next,.jtable-page-number-first,.jtable-page-number-last')
3947
+ .not('.jtable-page-number-disabled')
3948
+ .click(function (e) {
3949
+ e.preventDefault();
3950
+ self._changePage($(this).data('pageNumber'));
3951
+ });
3952
+ },
3953
+
3954
+ /* Changes current page to given value.
3955
+ *************************************************************************/
3956
+ _changePage: function (pageNo) {
3957
+ pageNo = this._normalizeNumber(pageNo, 1, this._calculatePageCount(), 1);
3958
+ if (pageNo == this._currentPageNo) {
3959
+ this._refreshGotoPageInput();
3960
+ return;
3961
+ }
3962
+
3963
+ this._currentPageNo = pageNo;
3964
+ this._reloadTable();
3965
+ }
3966
+
3967
+ });
3968
+
3969
+ })(jQuery);
3970
+
3971
+
3972
+ /************************************************************************
3973
+ * SORTING extension for jTable *
3974
+ *************************************************************************/
3975
+ (function ($) {
3976
+
3977
+ //Reference to base object members
3978
+ var base = {
3979
+ _initializeFields: $.hik.jtable.prototype._initializeFields,
3980
+ _normalizeFieldOptions: $.hik.jtable.prototype._normalizeFieldOptions,
3981
+ _createHeaderCellForField: $.hik.jtable.prototype._createHeaderCellForField,
3982
+ _createRecordLoadUrl: $.hik.jtable.prototype._createRecordLoadUrl
3983
+ };
3984
+
3985
+ //extension members
3986
+ $.extend(true, $.hik.jtable.prototype, {
3987
+
3988
+ /************************************************************************
3989
+ * DEFAULT OPTIONS / EVENTS *
3990
+ *************************************************************************/
3991
+ options: {
3992
+ sorting: false,
3993
+ multiSorting: false,
3994
+ defaultSorting: ''
3995
+ },
3996
+
3997
+ /************************************************************************
3998
+ * PRIVATE FIELDS *
3999
+ *************************************************************************/
4000
+
4001
+ _lastSorting: null, //Last sorting of the table
4002
+
4003
+ /************************************************************************
4004
+ * OVERRIDED METHODS *
4005
+ *************************************************************************/
4006
+
4007
+ /* Overrides base method to create sorting array.
4008
+ *************************************************************************/
4009
+ _initializeFields: function () {
4010
+ base._initializeFields.apply(this, arguments);
4011
+
4012
+ this._lastSorting = [];
4013
+ if (this.options.sorting) {
4014
+ this._buildDefaultSortingArray();
4015
+ }
4016
+ },
4017
+
4018
+ /* Overrides _normalizeFieldOptions method to normalize sorting option for fields.
4019
+ *************************************************************************/
4020
+ _normalizeFieldOptions: function (fieldName, props) {
4021
+ base._normalizeFieldOptions.apply(this, arguments);
4022
+ props.sorting = (props.sorting != false);
4023
+ },
4024
+
4025
+ /* Overrides _createHeaderCellForField to make columns sortable.
4026
+ *************************************************************************/
4027
+ _createHeaderCellForField: function (fieldName, field) {
4028
+ var $headerCell = base._createHeaderCellForField.apply(this, arguments);
4029
+ if (this.options.sorting && field.sorting) {
4030
+ this._makeColumnSortable($headerCell, fieldName);
4031
+ }
4032
+
4033
+ return $headerCell;
4034
+ },
4035
+
4036
+ /* Overrides _createRecordLoadUrl to add sorting specific info to URL.
4037
+ *************************************************************************/
4038
+ _createRecordLoadUrl: function () {
4039
+ var loadUrl = base._createRecordLoadUrl.apply(this, arguments);
4040
+ loadUrl = this._addSortingInfoToUrl(loadUrl);
4041
+ return loadUrl;
4042
+ },
4043
+
4044
+ /************************************************************************
4045
+ * PRIVATE METHODS *
4046
+ *************************************************************************/
4047
+
4048
+ /* Builds the sorting array according to defaultSorting string
4049
+ *************************************************************************/
4050
+ _buildDefaultSortingArray: function () {
4051
+ var self = this;
4052
+
4053
+ $.each(self.options.defaultSorting.split(","), function (orderIndex, orderValue) {
4054
+ $.each(self.options.fields, function (fieldName, fieldProps) {
4055
+ if (fieldProps.sorting) {
4056
+ var colOffset = orderValue.indexOf(fieldName);
4057
+ if (colOffset > -1) {
4058
+ if (orderValue.toUpperCase().indexOf(' DESC', colOffset) > -1) {
4059
+ self._lastSorting.push({
4060
+ fieldName: fieldName,
4061
+ sortOrder: 'DESC'
4062
+ });
4063
+ } else {
4064
+ self._lastSorting.push({
4065
+ fieldName: fieldName,
4066
+ sortOrder: 'ASC'
4067
+ });
4068
+ }
4069
+ }
4070
+ }
4071
+ });
4072
+ });
4073
+ },
4074
+
4075
+ /* Makes a column sortable.
4076
+ *************************************************************************/
4077
+ _makeColumnSortable: function ($columnHeader, fieldName) {
4078
+ var self = this;
4079
+
4080
+ $columnHeader
4081
+ .addClass('jtable-column-header-sortable')
4082
+ .click(function (e) {
4083
+ e.preventDefault();
4084
+
4085
+ if (!self.options.multiSorting || !e.ctrlKey) {
4086
+ self._lastSorting = []; //clear previous sorting
4087
+ }
4088
+
4089
+ self._sortTableByColumn($columnHeader);
4090
+ });
4091
+
4092
+ //Set default sorting
4093
+ $.each(this._lastSorting, function (sortIndex, sortField) {
4094
+ if (sortField.fieldName == fieldName) {
4095
+ if (sortField.sortOrder == 'DESC') {
4096
+ $columnHeader.addClass('jtable-column-header-sorted-desc');
4097
+ } else {
4098
+ $columnHeader.addClass('jtable-column-header-sorted-asc');
4099
+ }
4100
+ }
4101
+ });
4102
+ },
4103
+
4104
+ /* Sorts table according to a column header.
4105
+ *************************************************************************/
4106
+ _sortTableByColumn: function ($columnHeader) {
4107
+ //Remove sorting styles from all columns except this one
4108
+ if (this._lastSorting.length == 0) {
4109
+ $columnHeader.siblings().removeClass('jtable-column-header-sorted-asc jtable-column-header-sorted-desc');
4110
+ }
4111
+
4112
+ //If current sorting list includes this column, remove it from the list
4113
+ for (var i = 0; i < this._lastSorting.length; i++) {
4114
+ if (this._lastSorting[i].fieldName == $columnHeader.data('fieldName')) {
4115
+ this._lastSorting.splice(i--, 1);
4116
+ }
4117
+ }
4118
+
4119
+ //Sort ASC or DESC according to current sorting state
4120
+ if ($columnHeader.hasClass('jtable-column-header-sorted-asc')) {
4121
+ $columnHeader.removeClass('jtable-column-header-sorted-asc').addClass('jtable-column-header-sorted-desc');
4122
+ this._lastSorting.push({
4123
+ 'fieldName': $columnHeader.data('fieldName'),
4124
+ sortOrder: 'DESC'
4125
+ });
4126
+ } else {
4127
+ $columnHeader.removeClass('jtable-column-header-sorted-desc').addClass('jtable-column-header-sorted-asc');
4128
+ this._lastSorting.push({
4129
+ 'fieldName': $columnHeader.data('fieldName'),
4130
+ sortOrder: 'ASC'
4131
+ });
4132
+ }
4133
+
4134
+ //Load current page again
4135
+ this._reloadTable();
4136
+ },
4137
+
4138
+ /* Adds jtSorting parameter to a URL as query string.
4139
+ *************************************************************************/
4140
+ _addSortingInfoToUrl: function (url) {
4141
+ if (!this.options.sorting || this._lastSorting.length == 0) {
4142
+ return url;
4143
+ }
4144
+
4145
+ var sorting = [];
4146
+ $.each(this._lastSorting, function (idx, value) {
4147
+ sorting.push(value.fieldName + ' ' + value.sortOrder);
4148
+ });
4149
+
4150
+ return (url + (url.indexOf('?') < 0 ? '?' : '&') + 'jtSorting=' + sorting.join(","));
4151
+ }
4152
+
4153
+ });
4154
+
4155
+ })(jQuery);
4156
+
4157
+ /************************************************************************
4158
+ * DYNAMIC COLUMNS extension for jTable *
4159
+ * (Show/hide/resize columns) *
4160
+ *************************************************************************/
4161
+ (function ($) {
4162
+
4163
+ //Reference to base object members
4164
+ var base = {
4165
+ _create: $.hik.jtable.prototype._create,
4166
+ _normalizeFieldOptions: $.hik.jtable.prototype._normalizeFieldOptions,
4167
+ _createHeaderCellForField: $.hik.jtable.prototype._createHeaderCellForField,
4168
+ _createCellForRecordField: $.hik.jtable.prototype._createCellForRecordField
4169
+ };
4170
+
4171
+ //extension members
4172
+ $.extend(true, $.hik.jtable.prototype, {
4173
+
4174
+ /************************************************************************
4175
+ * DEFAULT OPTIONS / EVENTS *
4176
+ *************************************************************************/
4177
+
4178
+ options: {
4179
+ tableId: undefined,
4180
+ columnResizable: true,
4181
+ columnSelectable: true
4182
+ },
4183
+
4184
+ /************************************************************************
4185
+ * PRIVATE FIELDS *
4186
+ *************************************************************************/
4187
+
4188
+ _$columnSelectionDiv: null,
4189
+ _$columnResizeBar: null,
4190
+ _cookieKeyPrefix: null,
4191
+ _currentResizeArgs: null,
4192
+
4193
+ /************************************************************************
4194
+ * OVERRIDED METHODS *
4195
+ *************************************************************************/
4196
+
4197
+ /* Overrides _addRowToTableHead method.
4198
+ *************************************************************************/
4199
+
4200
+ _create: function () {
4201
+ base._create.apply(this, arguments);
4202
+
4203
+ this._createColumnResizeBar();
4204
+ this._createColumnSelection();
4205
+
4206
+ if (this.options.saveUserPreferences) {
4207
+ this._loadColumnSettings();
4208
+ }
4209
+
4210
+ this._normalizeColumnWidths();
4211
+ },
4212
+
4213
+ /* Normalizes some options for a field (sets default values).
4214
+ *************************************************************************/
4215
+ _normalizeFieldOptions: function (fieldName, props) {
4216
+ base._normalizeFieldOptions.apply(this, arguments);
4217
+
4218
+ //columnResizable
4219
+ if (this.options.columnResizable) {
4220
+ props.columnResizable = (props.columnResizable != false);
4221
+ }
4222
+
4223
+ //visibility
4224
+ if (!props.visibility) {
4225
+ props.visibility = 'visible';
4226
+ }
4227
+ },
4228
+
4229
+ /* Overrides _createHeaderCellForField to make columns dynamic.
4230
+ *************************************************************************/
4231
+ _createHeaderCellForField: function (fieldName, field) {
4232
+ var $headerCell = base._createHeaderCellForField.apply(this, arguments);
4233
+
4234
+ //Make data columns resizable except the last one
4235
+ if (this.options.columnResizable && field.columnResizable && (fieldName != this._columnList[this._columnList.length - 1])) {
4236
+ this._makeColumnResizable($headerCell);
4237
+ }
4238
+
4239
+ //Hide column if needed
4240
+ if (field.visibility == 'hidden') {
4241
+ $headerCell.hide();
4242
+ }
4243
+
4244
+ return $headerCell;
4245
+ },
4246
+
4247
+ /* Overrides _createHeaderCellForField to decide show or hide a column.
4248
+ *************************************************************************/
4249
+ _createCellForRecordField: function (record, fieldName) {
4250
+ var $column = base._createCellForRecordField.apply(this, arguments);
4251
+
4252
+ var field = this.options.fields[fieldName];
4253
+ if (field.visibility == 'hidden') {
4254
+ $column.hide();
4255
+ }
4256
+
4257
+ return $column;
4258
+ },
4259
+
4260
+ /************************************************************************
4261
+ * PUBLIC METHODS *
4262
+ *************************************************************************/
4263
+
4264
+ /* Changes visibility of a column.
4265
+ *************************************************************************/
4266
+ changeColumnVisibility: function (columnName, visibility) {
4267
+ this._changeColumnVisibilityInternal(columnName, visibility);
4268
+ this._normalizeColumnWidths();
4269
+ if (this.options.saveUserPreferences) {
4270
+ this._saveColumnSettings();
4271
+ }
4272
+ },
4273
+
4274
+ /************************************************************************
4275
+ * PRIVATE METHODS *
4276
+ *************************************************************************/
4277
+
4278
+ /* Changes visibility of a column.
4279
+ *************************************************************************/
4280
+ _changeColumnVisibilityInternal: function (columnName, visibility) {
4281
+ //Check if there is a column with given name
4282
+ var columnIndex = this._columnList.indexOf(columnName);
4283
+ if (columnIndex < 0) {
4284
+ this._logWarn('Column "' + columnName + '" does not exist in fields!');
4285
+ return;
4286
+ }
4287
+
4288
+ //Check if visibility value is valid
4289
+ if (['visible', 'hidden', 'fixed'].indexOf(visibility) < 0) {
4290
+ this._logWarn('Visibility value is not valid: "' + visibility + '"! Options are: visible, hidden, fixed.');
4291
+ return;
4292
+ }
4293
+
4294
+ //Get the field
4295
+ var field = this.options.fields[columnName];
4296
+ if (field.visibility == visibility) {
4297
+ return; //No action if new value is same as old one.
4298
+ }
4299
+
4300
+ //Hide or show the column if needed
4301
+ var columnIndexInTable = this._firstDataColumnOffset + columnIndex + 1;
4302
+ if (field.visibility != 'hidden' && visibility == 'hidden') {
4303
+ this._$table
4304
+ .find('>thead >tr >th:nth-child(' + columnIndexInTable + '),>tbody >tr >td:nth-child(' + columnIndexInTable + ')')
4305
+ .hide();
4306
+ } else if (field.visibility == 'hidden' && visibility != 'hidden') {
4307
+ this._$table
4308
+ .find('>thead >tr >th:nth-child(' + columnIndexInTable + '),>tbody >tr >td:nth-child(' + columnIndexInTable + ')')
4309
+ .show()
4310
+ .css('display', 'table-cell');
4311
+ }
4312
+
4313
+ field.visibility = visibility;
4314
+ },
4315
+
4316
+ /* Prepares dialog to change settings.
4317
+ *************************************************************************/
4318
+ _createColumnSelection: function () {
4319
+ var self = this;
4320
+
4321
+ //Create a div for dialog and add to container element
4322
+ this._$columnSelectionDiv = $('<div />')
4323
+ .addClass('jtable-column-selection-container')
4324
+ .appendTo(self._$mainContainer);
4325
+
4326
+ this._$table.children('thead').bind('contextmenu', function (e) {
4327
+ if (!self.options.columnSelectable) {
4328
+ return;
4329
+ }
4330
+
4331
+ e.preventDefault();
4332
+
4333
+ //Make an overlay div to disable page clicks
4334
+ $('<div />')
4335
+ .addClass('jtable-contextmenu-overlay')
4336
+ .click(function () {
4337
+ $(this).remove();
4338
+ self._$columnSelectionDiv.hide();
4339
+ })
4340
+ .bind('contextmenu', function () { return false; })
4341
+ .appendTo(document.body);
4342
+
4343
+ self._fillColumnSelection();
4344
+
4345
+ //Calculate position of column selection list and show it
4346
+
4347
+ var containerOffset = self._$mainContainer.offset();
4348
+ var selectionDivTop = e.pageY - containerOffset.top;
4349
+ var selectionDivLeft = e.pageX - containerOffset.left;
4350
+
4351
+ var selectionDivMinWidth = 100; //in pixels
4352
+ var containerWidth = self._$mainContainer.width();
4353
+
4354
+ //If user clicks right area of header of the table, show list at a little left
4355
+ if ((containerWidth > selectionDivMinWidth) && (selectionDivLeft > (containerWidth - selectionDivMinWidth))) {
4356
+ selectionDivLeft = containerWidth - selectionDivMinWidth;
4357
+ }
4358
+
4359
+ self._$columnSelectionDiv.css({
4360
+ left: selectionDivLeft,
4361
+ top: selectionDivTop,
4362
+ 'min-width': selectionDivMinWidth + 'px'
4363
+ }).show();
4364
+ });
4365
+ },
4366
+
4367
+ /* Prepares content of settings dialog.
4368
+ *************************************************************************/
4369
+ _fillColumnSelection: function () {
4370
+ var self = this;
4371
+
4372
+ var $columnsUl = $('<ul></ul>')
4373
+ .addClass('jtable-column-select-list');
4374
+ for (var i = 0; i < this._columnList.length; i++) {
4375
+ var columnName = this._columnList[i];
4376
+ var field = this.options.fields[columnName];
4377
+
4378
+ //Crete li element
4379
+ var $columnLi = $('<li></li>').appendTo($columnsUl);
4380
+
4381
+ //Create label for the checkbox
4382
+ var $label = $('<label for="' + columnName + '"></label>')
4383
+ .append($('<span>' + (field.title || columnName) + '</span>'))
4384
+ .appendTo($columnLi);
4385
+
4386
+ //Create checkbox
4387
+ var $checkbox = $('<input type="checkbox" name="' + columnName + '">')
4388
+ .prependTo($label)
4389
+ .click(function () {
4390
+ var $clickedCheckbox = $(this);
4391
+ var clickedColumnName = $clickedCheckbox.attr('name');
4392
+ var clickedField = self.options.fields[clickedColumnName];
4393
+ if (clickedField.visibility == 'fixed') {
4394
+ return;
4395
+ }
4396
+
4397
+ self.changeColumnVisibility(clickedColumnName, $clickedCheckbox.is(':checked') ? 'visible' : 'hidden');
4398
+ });
4399
+
4400
+ //Check, if column if shown
4401
+ if (field.visibility != 'hidden') {
4402
+ $checkbox.attr('checked', 'checked');
4403
+ }
4404
+
4405
+ //Disable, if column is fixed
4406
+ if (field.visibility == 'fixed') {
4407
+ $checkbox.attr('disabled', 'disabled');
4408
+ }
4409
+ }
4410
+
4411
+ this._$columnSelectionDiv.html($columnsUl);
4412
+ },
4413
+
4414
+ /* creates a vertical bar that is shown while resizing columns.
4415
+ *************************************************************************/
4416
+ _createColumnResizeBar: function () {
4417
+ this._$columnResizeBar = $('<div />')
4418
+ .addClass('jtable-column-resize-bar')
4419
+ .appendTo(this._$mainContainer)
4420
+ .hide();
4421
+ },
4422
+
4423
+ /* Makes a column sortable.
4424
+ *************************************************************************/
4425
+ _makeColumnResizable: function ($columnHeader) {
4426
+ var self = this;
4427
+
4428
+ //Create a handler to handle mouse click event
4429
+ $('<div />')
4430
+ .addClass('jtable-column-resize-handler')
4431
+ .appendTo($columnHeader.find('.jtable-column-header-container')) //Append the handler to the column
4432
+ .mousedown(function (downevent) { //handle mousedown event for the handler
4433
+ downevent.preventDefault();
4434
+ downevent.stopPropagation();
4435
+
4436
+ var mainContainerOffset = self._$mainContainer.offset();
4437
+
4438
+ //Get a reference to the next column
4439
+ var $nextColumnHeader = $columnHeader.nextAll('th.jtable-column-header:visible:first');
4440
+ if (!$nextColumnHeader.length) {
4441
+ return;
4442
+ }
4443
+
4444
+ //Store some information to be used on resizing
4445
+ var minimumColumnWidth = 10; //A column's width can not be smaller than 10 pixel.
4446
+ self._currentResizeArgs = {
4447
+ currentColumnStartWidth: $columnHeader.outerWidth(),
4448
+ minWidth: minimumColumnWidth,
4449
+ maxWidth: $columnHeader.outerWidth() + $nextColumnHeader.outerWidth() - minimumColumnWidth,
4450
+ mouseStartX: downevent.pageX,
4451
+ minResizeX: function () { return this.mouseStartX - (this.currentColumnStartWidth - this.minWidth); },
4452
+ maxResizeX: function () { return this.mouseStartX + (this.maxWidth - this.currentColumnStartWidth); }
4453
+ };
4454
+
4455
+ //Handle mouse move event to move resizing bar
4456
+ var resizeonmousemove = function (moveevent) {
4457
+ if (!self._currentResizeArgs) {
4458
+ return;
4459
+ }
4460
+
4461
+ var resizeBarX = self._normalizeNumber(moveevent.pageX, self._currentResizeArgs.minResizeX(), self._currentResizeArgs.maxResizeX());
4462
+ self._$columnResizeBar.css('left', (resizeBarX - mainContainerOffset.left) + 'px');
4463
+ };
4464
+
4465
+ //Handle mouse up event to finish resizing of the column
4466
+ var resizeonmouseup = function (upevent) {
4467
+ if (!self._currentResizeArgs) {
4468
+ return;
4469
+ }
4470
+
4471
+ $(document).unbind('mousemove', resizeonmousemove);
4472
+ $(document).unbind('mouseup', resizeonmouseup);
4473
+
4474
+ self._$columnResizeBar.hide();
4475
+
4476
+ //Calculate new widths in pixels
4477
+ var mouseChangeX = upevent.pageX - self._currentResizeArgs.mouseStartX;
4478
+ var currentColumnFinalWidth = self._normalizeNumber(self._currentResizeArgs.currentColumnStartWidth + mouseChangeX, self._currentResizeArgs.minWidth, self._currentResizeArgs.maxWidth);
4479
+ var nextColumnFinalWidth = $nextColumnHeader.outerWidth() + (self._currentResizeArgs.currentColumnStartWidth - currentColumnFinalWidth);
4480
+
4481
+ //Calculate widths as percent
4482
+ var pixelToPercentRatio = $columnHeader.data('width-in-percent') / self._currentResizeArgs.currentColumnStartWidth;
4483
+ $columnHeader.data('width-in-percent', currentColumnFinalWidth * pixelToPercentRatio);
4484
+ $nextColumnHeader.data('width-in-percent', nextColumnFinalWidth * pixelToPercentRatio);
4485
+
4486
+ //Set new widths to columns (resize!)
4487
+ $columnHeader.css('width', $columnHeader.data('width-in-percent') + '%');
4488
+ $nextColumnHeader.css('width', $nextColumnHeader.data('width-in-percent') + '%');
4489
+
4490
+ //Normalize all column widths
4491
+ self._normalizeColumnWidths();
4492
+
4493
+ //Finish resizing
4494
+ self._currentResizeArgs = null;
4495
+
4496
+ //Save current preferences
4497
+ if (self.options.saveUserPreferences) {
4498
+ self._saveColumnSettings();
4499
+ }
4500
+ };
4501
+
4502
+ //Show vertical resize bar
4503
+ self._$columnResizeBar
4504
+ .show()
4505
+ .css({
4506
+ top: ($columnHeader.offset().top - mainContainerOffset.top) + 'px',
4507
+ left: (downevent.pageX - mainContainerOffset.left) + 'px',
4508
+ height: (self._$table.outerHeight()) + 'px'
4509
+ });
4510
+
4511
+ //Bind events
4512
+ $(document).bind('mousemove', resizeonmousemove);
4513
+ $(document).bind('mouseup', resizeonmouseup);
4514
+ });
4515
+ },
4516
+
4517
+ /* Normalizes column widths as percent for current view.
4518
+ *************************************************************************/
4519
+ _normalizeColumnWidths: function () {
4520
+
4521
+ //Set command column width
4522
+ var commandColumnHeaders = this._$table
4523
+ .find('>thead th.jtable-command-column-header')
4524
+ .data('width-in-percent', 1)
4525
+ .css('width', '1%');
4526
+
4527
+ //Find data columns
4528
+ var headerCells = this._$table.find('>thead th.jtable-column-header');
4529
+
4530
+ //Calculate total width of data columns
4531
+ var totalWidthInPixel = 0;
4532
+ headerCells.each(function () {
4533
+ var $cell = $(this);
4534
+ if ($cell.is(':visible')) {
4535
+ totalWidthInPixel += $cell.outerWidth();
4536
+ }
4537
+ });
4538
+
4539
+ //Calculate width of each column
4540
+ var columnWidhts = {};
4541
+ var availableWidthInPercent = 100.0 - commandColumnHeaders.length;
4542
+ headerCells.each(function () {
4543
+ var $cell = $(this);
4544
+ if ($cell.is(':visible')) {
4545
+ var fieldName = $cell.data('fieldName');
4546
+ var widthInPercent = $cell.outerWidth() * availableWidthInPercent / totalWidthInPixel;
4547
+ columnWidhts[fieldName] = widthInPercent;
4548
+ }
4549
+ });
4550
+
4551
+ //Set width of each column
4552
+ headerCells.each(function () {
4553
+ var $cell = $(this);
4554
+ if ($cell.is(':visible')) {
4555
+ var fieldName = $cell.data('fieldName');
4556
+ $cell.data('width-in-percent', columnWidhts[fieldName]).css('width', columnWidhts[fieldName] + '%');
4557
+ }
4558
+ });
4559
+ },
4560
+
4561
+ /* Saves field setting to cookie.
4562
+ * Saved setting will be a string like that:
4563
+ * fieldName1=visible;23|fieldName2=hidden;17|...
4564
+ *************************************************************************/
4565
+ _saveColumnSettings: function () {
4566
+ var self = this;
4567
+ var fieldSettings = '';
4568
+ this._$table.find('>thead >tr >th.jtable-column-header').each(function () {
4569
+ var $cell = $(this);
4570
+ var fieldName = $cell.data('fieldName');
4571
+ var columnWidth = $cell.data('width-in-percent');
4572
+ var fieldVisibility = self.options.fields[fieldName].visibility;
4573
+ var fieldSetting = fieldName + "=" + fieldVisibility + ';' + columnWidth;
4574
+ fieldSettings = fieldSettings + fieldSetting + '|';
4575
+ });
4576
+
4577
+ this._setCookie('column-settings', fieldSettings.substr(0, fieldSettings.length - 1));
4578
+ },
4579
+
4580
+ /* Loads field settings from cookie that is saved by _saveFieldSettings method.
4581
+ *************************************************************************/
4582
+ _loadColumnSettings: function () {
4583
+ var self = this;
4584
+ var columnSettingsCookie = this._getCookie('column-settings');
4585
+ if (!columnSettingsCookie) {
4586
+ return;
4587
+ }
4588
+
4589
+ var columnSettings = {};
4590
+ $.each(columnSettingsCookie.split('|'), function (inx, fieldSetting) {
4591
+ var splitted = fieldSetting.split('=');
4592
+ var fieldName = splitted[0];
4593
+ var settings = splitted[1].split(';');
4594
+ columnSettings[fieldName] = {
4595
+ columnVisibility: settings[0],
4596
+ columnWidth: settings[1]
4597
+ };
4598
+ });
4599
+
4600
+ var headerCells = this._$table.find('>thead >tr >th.jtable-column-header');
4601
+ headerCells.each(function () {
4602
+ var $cell = $(this);
4603
+ var fieldName = $cell.data('fieldName');
4604
+ var field = self.options.fields[fieldName];
4605
+ if (columnSettings[fieldName]) {
4606
+ if (field.visibility != 'fixed') {
4607
+ self._changeColumnVisibilityInternal(fieldName, columnSettings[fieldName].columnVisibility);
4608
+ }
4609
+
4610
+ $cell.data('width-in-percent', columnSettings[fieldName].columnWidth).css('width', columnSettings[fieldName].columnWidth + '%');
4611
+ }
4612
+ });
4613
+ }
4614
+
4615
+ });
4616
+
4617
+ })(jQuery);
4618
+
4619
+
4620
+ /************************************************************************
4621
+ * MASTER/CHILD tables extension for jTable *
4622
+ *************************************************************************/
4623
+ (function ($) {
4624
+
4625
+ //Reference to base object members
4626
+ var base = {
4627
+ _removeRowsFromTable: $.hik.jtable.prototype._removeRowsFromTable
4628
+ };
4629
+
4630
+ //extension members
4631
+ $.extend(true, $.hik.jtable.prototype, {
4632
+
4633
+ /************************************************************************
4634
+ * DEFAULT OPTIONS / EVENTS *
4635
+ *************************************************************************/
4636
+ options: {
4637
+ openChildAsAccordion: false
4638
+ },
4639
+
4640
+ /************************************************************************
4641
+ * PUBLIC METHODS *
4642
+ *************************************************************************/
4643
+
4644
+ /* Creates and opens a new child table for given row.
4645
+ *************************************************************************/
4646
+ openChildTable: function ($row, tableOptions, opened) {
4647
+ var self = this;
4648
+
4649
+ //Apply theming as same as parent table unless explicitily set
4650
+ if (tableOptions.jqueryuiTheme == undefined) {
4651
+ tableOptions.jqueryuiTheme = self.options.jqueryuiTheme;
4652
+ }
4653
+
4654
+ //Show close button as default
4655
+ tableOptions.showCloseButton = (tableOptions.showCloseButton != false);
4656
+
4657
+ //Close child table when close button is clicked (default behavior)
4658
+ if (tableOptions.showCloseButton && !tableOptions.closeRequested) {
4659
+ tableOptions.closeRequested = function () {
4660
+ self.closeChildTable($row);
4661
+ };
4662
+ }
4663
+
4664
+ //If accordion style, close open child table (if it does exists)
4665
+ if (self.options.openChildAsAccordion) {
4666
+ $row.siblings('.jtable-data-row').each(function () {
4667
+ self.closeChildTable($(this));
4668
+ });
4669
+ }
4670
+
4671
+ //Close child table for this row and open new one for child table
4672
+ self.closeChildTable($row, function () {
4673
+ var $childRowColumn = self.getChildRow($row).children('td').empty();
4674
+ var $childTableContainer = $('<div />')
4675
+ .addClass('jtable-child-table-container')
4676
+ .appendTo($childRowColumn);
4677
+ $childRowColumn.data('childTable', $childTableContainer);
4678
+ $childTableContainer.jtable(tableOptions);
4679
+ self.openChildRow($row);
4680
+ $childTableContainer.hide().slideDown('fast', function () {
4681
+ if (opened) {
4682
+ opened({
4683
+ childTable: $childTableContainer
4684
+ });
4685
+ }
4686
+ });
4687
+ });
4688
+ },
4689
+
4690
+ /* Closes child table for given row.
4691
+ *************************************************************************/
4692
+ closeChildTable: function ($row, closed) {
4693
+ var self = this;
4694
+
4695
+ var $childRowColumn = this.getChildRow($row).children('td');
4696
+ var $childTable = $childRowColumn.data('childTable');
4697
+ if (!$childTable) {
4698
+ if (closed) {
4699
+ closed();
4700
+ }
4701
+
4702
+ return;
4703
+ }
4704
+
4705
+ $childRowColumn.data('childTable', null);
4706
+ $childTable.slideUp('fast', function () {
4707
+ $childTable.jtable('destroy');
4708
+ $childTable.remove();
4709
+ self.closeChildRow($row);
4710
+ if (closed) {
4711
+ closed();
4712
+ }
4713
+ });
4714
+ },
4715
+
4716
+ /* Returns a boolean value indicates that if a child row is open for given row.
4717
+ *************************************************************************/
4718
+ isChildRowOpen: function ($row) {
4719
+ return (this.getChildRow($row).is(':visible'));
4720
+ },
4721
+
4722
+ /* Gets child row for given row, opens it if it's closed (Creates if needed).
4723
+ *************************************************************************/
4724
+ getChildRow: function ($row) {
4725
+ return $row.data('childRow') || this._createChildRow($row);
4726
+ },
4727
+
4728
+ /* Creates and opens child row for given row.
4729
+ *************************************************************************/
4730
+ openChildRow: function ($row) {
4731
+ var $childRow = this.getChildRow($row);
4732
+ if (!$childRow.is(':visible')) {
4733
+ $childRow.show();
4734
+ }
4735
+
4736
+ return $childRow;
4737
+ },
4738
+
4739
+ /* Closes child row if it's open.
4740
+ *************************************************************************/
4741
+ closeChildRow: function ($row) {
4742
+ var $childRow = this.getChildRow($row);
4743
+ if ($childRow.is(':visible')) {
4744
+ $childRow.hide();
4745
+ }
4746
+ },
4747
+
4748
+ /************************************************************************
4749
+ * OVERRIDED METHODS *
4750
+ *************************************************************************/
4751
+
4752
+ /* Overrides _removeRowsFromTable method to remove child rows of deleted rows.
4753
+ *************************************************************************/
4754
+ _removeRowsFromTable: function ($rows, reason) {
4755
+ //var self = this;
4756
+
4757
+ if (reason == 'deleted') {
4758
+ $rows.each(function () {
4759
+ var $row = $(this);
4760
+ var $childRow = $row.data('childRow');
4761
+ if ($childRow) {
4762
+ //self.closeChildTable($row); //Removed since it causes "Uncaught Error: cannot call methods on jtable prior to initialization; attempted to call method 'destroy'"
4763
+ $childRow.remove();
4764
+ }
4765
+ });
4766
+ }
4767
+
4768
+ base._removeRowsFromTable.apply(this, arguments);
4769
+ },
4770
+
4771
+ /************************************************************************
4772
+ * PRIVATE METHODS *
4773
+ *************************************************************************/
4774
+
4775
+ /* Creates a child row for a row, hides and returns it.
4776
+ *************************************************************************/
4777
+ _createChildRow: function ($row) {
4778
+ var totalColumnCount = this._$table.find('thead th').length;
4779
+ var $childRow = $('<tr></tr>')
4780
+ .addClass('jtable-child-row')
4781
+ .append('<td colspan="' + totalColumnCount + '"></td>');
4782
+ $row.after($childRow);
4783
+ $row.data('childRow', $childRow);
4784
+ $childRow.hide();
4785
+ return $childRow;
4786
+ }
4787
+
4788
+ });
4789
+
4790
+ })(jQuery);
4791
+