Merge remote-tracking branch 'origin/new/bug_7367'
[koha.git] / koha-tmpl / intranet-tmpl / prog / en / js / datatables.js
1 // These default options are for translation but can be used
2 // for any other datatables settings
3 // MSG_DT_* variables comes from datatables-strings.inc
4 // To use it, write:
5 //  $("#table_id").dataTable($.extend(true, {}, dataTableDefaults, {
6 //      // other settings
7 //  } ) );
8 var dataTablesDefaults = {
9     "oLanguage": {
10         "oPaginate": {
11             "sFirst"    : window.MSG_DT_FIRST || "First",
12             "sLast"     : window.MSG_DT_LAST || "Last",
13             "sNext"     : window.MSG_DT_NEXT || "Next",
14             "sPrevious" : window.MSG_DT_PREVIOUS || "Previous"
15         },
16         "sEmptyTable"       : window.MSG_DT_EMPTY_TABLE || "No data available in table",
17         "sInfo"             : window.MSG_DT_INFO || "Showing _START_ to _END_ of _TOTAL_ entries",
18         "sInfoEmpty"        : window.MSG_DT_INFO_EMPTY || "No entries to show",
19         "sInfoFiltered"     : window.MSG_DT_INFO_FILTERED || "(filtered from _MAX_ total entries)",
20         "sLengthMenu"       : window.MSG_DT_LENGTH_MENU || "Show _MENU_ entries",
21         "sLoadingRecords"   : window.MSG_DT_LOADING_RECORDS || "Loading...",
22         "sProcessing"       : window.MSG_DT_PROCESSING || "Processing...",
23         "sSearch"           : window.MSG_DT_SEARCH || "Search:",
24         "sZeroRecords"      : window.MSG_DT_ZERO_RECORDS || "No matching records found"
25     },
26     "sDom": '<"top pager"ilpf>t<"bottom pager"ip>'
27 };
28
29
30 // Return an array of string containing the values of a particular column
31 $.fn.dataTableExt.oApi.fnGetColumnData = function ( oSettings, iColumn, bUnique, bFiltered, bIgnoreEmpty ) {
32     // check that we have a column id
33     if ( typeof iColumn == "undefined" ) return new Array();
34     // by default we only wany unique data
35     if ( typeof bUnique == "undefined" ) bUnique = true;
36     // by default we do want to only look at filtered data
37     if ( typeof bFiltered == "undefined" ) bFiltered = true;
38     // by default we do not wany to include empty values
39     if ( typeof bIgnoreEmpty == "undefined" ) bIgnoreEmpty = true;
40     // list of rows which we're going to loop through
41     var aiRows;
42     // use only filtered rows
43     if (bFiltered == true) aiRows = oSettings.aiDisplay;
44     // use all rows
45     else aiRows = oSettings.aiDisplayMaster; // all row numbers
46
47     // set up data array
48     var asResultData = new Array();
49     for (var i=0,c=aiRows.length; i<c; i++) {
50         iRow = aiRows[i];
51         var aData = this.fnGetData(iRow);
52         var sValue = aData[iColumn];
53         // ignore empty values?
54         if (bIgnoreEmpty == true && sValue.length == 0) continue;
55         // ignore unique values?
56         else if (bUnique == true && jQuery.inArray(sValue, asResultData) > -1) continue;
57         // else push the value onto the result data array
58         else asResultData.push(sValue);
59     }
60     return asResultData;
61 }
62
63 // List of unbind keys (Ctrl, Alt, Direction keys, etc.)
64 // These keys must not launch filtering
65 var blacklist_keys = new Array(0, 16, 17, 18, 37, 38, 39, 40);
66
67 // Set a filtering delay for global search field
68 jQuery.fn.dataTableExt.oApi.fnSetFilteringDelay = function ( oSettings, iDelay ) {
69     /*
70      * Inputs:      object:oSettings - dataTables settings object - automatically given
71      *              integer:iDelay - delay in milliseconds
72      * Usage:       $('#example').dataTable().fnSetFilteringDelay(250);
73      * Author:      Zygimantas Berziunas (www.zygimantas.com) and Allan Jardine
74      * License:     GPL v2 or BSD 3 point style
75      * Contact:     zygimantas.berziunas /AT\ hotmail.com
76      */
77     var
78         _that = this,
79         iDelay = (typeof iDelay == 'undefined') ? 250 : iDelay;
80
81     this.each( function ( i ) {
82         $.fn.dataTableExt.iApiIndex = i;
83         var
84             $this = this,
85             oTimerId = null,
86             sPreviousSearch = null,
87             anControl = $( 'input', _that.fnSettings().aanFeatures.f );
88
89         anControl.unbind( 'keyup.DT' ).bind( 'keyup.DT', function(event) {
90             var $$this = $this;
91             if (blacklist_keys.indexOf(event.keyCode) != -1) {
92                 return this;
93             }else if ( event.keyCode == '13' ) {
94                 $.fn.dataTableExt.iApiIndex = i;
95                 _that.fnFilter( $(this).val() );
96             } else {
97                 if (sPreviousSearch === null || sPreviousSearch != anControl.val()) {
98                     window.clearTimeout(oTimerId);
99                     sPreviousSearch = anControl.val();
100                     oTimerId = window.setTimeout(function() {
101                         $.fn.dataTableExt.iApiIndex = i;
102                         _that.fnFilter( anControl.val() );
103                     }, iDelay);
104                 }
105             }
106         });
107
108         return this;
109     } );
110     return this;
111 }
112
113 // Add a filtering delay on general search and on all input (with a class 'filter')
114 jQuery.fn.dataTableExt.oApi.fnAddFilters = function ( oSettings, sClass, iDelay ) {
115     var table = this;
116     this.fnSetFilteringDelay(iDelay);
117     var filterTimerId = null;
118     $("input."+sClass).keyup(function(event) {
119       if (blacklist_keys.indexOf(event.keyCode) != -1) {
120         return this;
121       }else if ( event.keyCode == '13' ) {
122         table.fnFilter( $(this).val(), $(this).attr('data-column_num') );
123       } else {
124         window.clearTimeout(filterTimerId);
125         var input = this;
126         filterTimerId = window.setTimeout(function() {
127           table.fnFilter($(input).val(), $(input).attr('data-column_num'));
128         }, iDelay);
129       }
130     });
131 }
132
133 // Useful if you want to filter on dates with 2 inputs (start date and end date)
134 // You have to include calendar.inc to use it
135 function dt_add_rangedate_filter(begindate_id, enddate_id, dateCol) {
136     $.fn.dataTableExt.afnFiltering.push(
137         function( oSettings, aData, iDataIndex ) {
138
139             var beginDate = Date_from_syspref($("#"+begindate_id).val()).getTime();
140             var endDate   = Date_from_syspref($("#"+enddate_id).val()).getTime();
141
142             var data = Date_from_syspref(aData[dateCol]).getTime();
143
144             if ( !parseInt(beginDate) && ! parseInt(endDate) ) {
145                 return true;
146             }
147             else if ( beginDate <= data && !parseInt(endDate) ) {
148                 return true;
149             }
150             else if ( data <= endDate && !parseInt(beginDate) ) {
151                 return true;
152             }
153             else if ( beginDate <= data && data <= endDate) {
154                 return true;
155             }
156             return false;
157         }
158     );
159 }
160
161 //Sorting for dates (uk format)
162 function dt_add_type_uk_date() {
163   jQuery.fn.dataTableExt.aTypes.unshift(
164     function ( sData )
165     {
166       if (sData.match(/(0[1-9]|[12][0-9]|3[01])\/(0[1-9]|1[012])\/(19|20|21)\d\d/))
167       {
168         return 'uk_date';
169       }
170       return null;
171     }
172   );
173
174   jQuery.fn.dataTableExt.oSort['uk_date-asc']  = function(a,b) {
175     var re = /(\d{2}\/\d{2}\/\d{4})/;
176     a.match(re);
177     var ukDatea = RegExp.$1.split("/");
178     b.match(re);
179     var ukDateb = RegExp.$1.split("/");
180
181     var x = (ukDatea[2] + ukDatea[1] + ukDatea[0]) * 1;
182     var y = (ukDateb[2] + ukDateb[1] + ukDateb[0]) * 1;
183
184     return ((x < y) ? -1 : ((x > y) ?  1 : 0));
185   };
186
187   jQuery.fn.dataTableExt.oSort['uk_date-desc'] = function(a,b) {
188     var re = /(\d{2}\/\d{2}\/\d{4})/;
189     a.match(re);
190     var ukDatea = RegExp.$1.split("/");
191     b.match(re);
192     var ukDateb = RegExp.$1.split("/");
193
194     var x = (ukDatea[2] + ukDatea[1] + ukDatea[0]) * 1;
195     var y = (ukDateb[2] + ukDateb[1] + ukDateb[0]) * 1;
196
197     return ((x < y) ? 1 : ((x > y) ?  -1 : 0));
198   };
199 }
200
201 // Sorting on html contains
202 // <a href="foo.pl">bar</a> sort on 'bar'
203 function dt_overwrite_html_sorting_localeCompare() {
204     jQuery.fn.dataTableExt.oSort['html-asc']  = function(a,b) {
205         a = a.replace(/<.*?>/g, "").replace(/\s+/g, " ");
206         b = b.replace(/<.*?>/g, "").replace(/\s+/g, " ");
207         if (typeof(a.localeCompare == "function")) {
208            return a.localeCompare(b);
209         } else {
210            return (a > b) ? 1 : ((a < b) ? -1 : 0);
211         }
212     };
213
214     jQuery.fn.dataTableExt.oSort['html-desc'] = function(a,b) {
215         a = a.replace(/<.*?>/g, "").replace(/\s+/g, " ");
216         b = b.replace(/<.*?>/g, "").replace(/\s+/g, " ");
217         if(typeof(b.localeCompare == "function")) {
218             return b.localeCompare(a);
219         } else {
220             return (b > a) ? 1 : ((b < a) ? -1 : 0);
221         }
222     };
223 }
224
225 // Sorting on string without accentued characters
226 function dt_overwrite_string_sorting_localeCompare() {
227     jQuery.fn.dataTableExt.oSort['string-asc']  = function(a,b) {
228         a = a.replace(/<.*?>/g, "").replace(/\s+/g, " ");
229         b = b.replace(/<.*?>/g, "").replace(/\s+/g, " ");
230         if (typeof(a.localeCompare == "function")) {
231            return a.localeCompare(b);
232         } else {
233            return (a > b) ? 1 : ((a < b) ? -1 : 0);
234         }
235     };
236
237     jQuery.fn.dataTableExt.oSort['string-desc'] = function(a,b) {
238         a = a.replace(/<.*?>/g, "").replace(/\s+/g, " ");
239         b = b.replace(/<.*?>/g, "").replace(/\s+/g, " ");
240         if(typeof(b.localeCompare == "function")) {
241             return b.localeCompare(a);
242         } else {
243             return (b > a) ? 1 : ((b < a) ? -1 : 0);
244         }
245     };
246 }
247
248 // Replace a node with a html and js contain.
249 function replace_html( original_node, type ) {
250     switch ( $(original_node).attr('data-type') ) {
251         case "range_dates":
252             var id = $(original_node).attr("data-id");
253             var format = $(original_node).attr("data-format");
254             replace_html_date( original_node, id, format );
255             break;
256         default:
257             alert("_(This node can't be replaced)");
258     }
259 }
260
261 // Replace a node with a "From [date] To [date]" element
262 // Used on tfoot > td
263 function replace_html_date( original_node, id, format ) {
264     var node = $('<span style="white-space:nowrap">' + _("From") + '<input type="text" id="' + id + 'from" readonly="readonly" placeholder=\'' + _("Pick date") + '\' size="7" /><a title="Delete this filter" style="cursor:pointer" onclick=\'$("#' + id + 'from").val("").change();\' >&times;</a></span><br/><span style="white-space:nowrap">' + _("To") + '<input type="text" id="' + id + 'to" readonly="readonly" placeholder=\'' + _("Pick date") + '\' size="7" /><a title="Delete this filter" style="cursor:pointer" onclick=\'$("#' + id + 'to").val("").change();\' >&times;</a></span>');
265     $(original_node).replaceWith(node);
266     var script = document.createElement( 'script' );
267     script.type = 'text/javascript';
268     var script_content = "Calendar.setup({";
269     script_content += "    inputField: \"" + id + "from\",";
270     script_content += "    ifFormat: \"" + format + "\",";
271     script_content += "    button: \"" + id + "from\",";
272     script_content += "    onClose: function(){ $(\"#" + id + "from\").change(); this.hide();}";
273     script_content += "  });";
274     script_content += "  Calendar.setup({";
275     script_content += "    inputField: \"" + id + "to\",";
276     script_content += "    ifFormat: \"" + format + "\",";
277     script_content += "    button: \"" + id + "to\",";
278     script_content += "    onClose: function(){ $(\"#" + id + "to\").change(); this.hide();}";
279     script_content += "  });";
280     script.text = script_content;
281     $(original_node).append( script );
282 }
283
284 $.fn.dataTableExt.oPagination.four_button = {
285     /*
286      * Function: oPagination.four_button.fnInit
287      * Purpose:  Initalise dom elements required for pagination with a list of the pages
288      * Returns:  -
289      * Inputs:   object:oSettings - dataTables settings object
290      *           node:nPaging - the DIV which contains this pagination control
291      *           function:fnCallbackDraw - draw function which must be called on update
292      */
293     "fnInit": function ( oSettings, nPaging, fnCallbackDraw )
294     {
295         nFirst = document.createElement( 'span' );
296         nPrevious = document.createElement( 'span' );
297         nNext = document.createElement( 'span' );
298         nLast = document.createElement( 'span' );
299
300 /*        nFirst.appendChild( document.createTextNode( oSettings.oLanguage.oPaginate.sFirst ) );
301         nPrevious.appendChild( document.createTextNode( oSettings.oLanguage.oPaginate.sPrevious ) );
302         nNext.appendChild( document.createTextNode( oSettings.oLanguage.oPaginate.sNext ) );
303         nLast.appendChild( document.createTextNode( oSettings.oLanguage.oPaginate.sLast ) );*/
304
305         nFirst.className = "paginate_button first";
306         nPrevious.className = "paginate_button previous";
307         nNext.className="paginate_button next";
308         nLast.className = "paginate_button last";
309
310         nPaging.appendChild( nFirst );
311         nPaging.appendChild( nPrevious );
312         nPaging.appendChild( nNext );
313         nPaging.appendChild( nLast );
314
315         $(nFirst).click( function () {
316             oSettings.oApi._fnPageChange( oSettings, "first" );
317             fnCallbackDraw( oSettings );
318         } );
319
320         $(nPrevious).click( function() {
321             oSettings.oApi._fnPageChange( oSettings, "previous" );
322             fnCallbackDraw( oSettings );
323         } );
324
325         $(nNext).click( function() {
326             oSettings.oApi._fnPageChange( oSettings, "next" );
327             fnCallbackDraw( oSettings );
328         } );
329
330         $(nLast).click( function() {
331             oSettings.oApi._fnPageChange( oSettings, "last" );
332             fnCallbackDraw( oSettings );
333         } );
334
335         /* Disallow text selection */
336         $(nFirst).bind( 'selectstart', function () { return false; } );
337         $(nPrevious).bind( 'selectstart', function () { return false; } );
338         $(nNext).bind( 'selectstart', function () { return false; } );
339         $(nLast).bind( 'selectstart', function () { return false; } );
340     },
341
342     /*
343      * Function: oPagination.four_button.fnUpdate
344      * Purpose:  Update the list of page buttons shows
345      * Returns:  -
346      * Inputs:   object:oSettings - dataTables settings object
347      *           function:fnCallbackDraw - draw function which must be called on update
348      */
349     "fnUpdate": function ( oSettings, fnCallbackDraw )
350     {
351         if ( !oSettings.aanFeatures.p )
352         {
353             return;
354         }
355
356         /* Loop over each instance of the pager */
357         var an = oSettings.aanFeatures.p;
358         for ( var i=0, iLen=an.length ; i<iLen ; i++ )
359         {
360             var buttons = an[i].getElementsByTagName('span');
361             if ( oSettings._iDisplayStart === 0 )
362             {
363                 buttons[0].className = "paginate_disabled_first";
364                 buttons[1].className = "paginate_disabled_previous";
365             }
366             else
367             {
368                 buttons[0].className = "paginate_enabled_first";
369                 buttons[1].className = "paginate_enabled_previous";
370             }
371
372             if ( oSettings.fnDisplayEnd() == oSettings.fnRecordsDisplay() )
373             {
374                 buttons[2].className = "paginate_disabled_next";
375                 buttons[3].className = "paginate_disabled_last";
376             }
377             else
378             {
379                 buttons[2].className = "paginate_enabled_next";
380                 buttons[3].className = "paginate_enabled_last";
381             }
382         }
383     }
384 };