· 5 years ago · Dec 03, 2020, 02:48 PM
1import * as slickDOM from './lib/dom.js';
2import { extend, isCallable, isObject, isArray, byString, isEmpty } from './lib/common.js';
3
4/**
5 * DataTable
6 * @package Slick
7 * @version 1.5
8 */
9export default function slickDataTable(el, args)
10{
11 const Instance = this;
12
13
14 const table_id = el.charAt( 0 ) == '#' ? el.substr(1) : el;
15 const Table = document.getElementById(table_id);
16
17 const defaults = {
18 url: null,
19 queryargs: null,
20 args: null, // Initial values..
21 loading: null,
22 append: null,
23 prepend: null,
24 filter:null,
25 onLoaded: null,
26 pagination: {
27 enable: false,
28 max_per_page: 10,
29 perpage: true,
30 perpagelabel: 'Per page:', //remove
31 text: '%s%-%e% of %results%', //remove
32 keys: {
33 pages: 'meta.pages',
34 per_page: 'meta.perpage',
35 current_page: 'meta.current',
36 total: 'meta.total',
37 },
38 callback: function() {}
39 },
40 orderby: 'id',
41 sort: 'asc',
42 per_page: 25,
43 timeout_delete: 400,
44 // Localize strings
45 localize: {
46 loading: 'Loading data...',
47 perpage: 'Per page',
48 results: '%s%-%e% of %results%',
49 noresults: 'No results.',
50 resetbutton: 'Reset table'
51 },
52 classes: {
53 delete_row: 'delete-row',
54 table_sort: 'table-sort',
55 selected_row: 'selected-row',
56 active_column: 'is-active-column',
57 pagination: 'ui-pagination',
58 page: 'page-item',
59 active_page: 'current-page',
60 next_page: 'next',
61 prev_page: 'prev',
62 loader: 'loader',
63 footer: 'table-footer-container',
64 resetbutton: 'ui-button white'
65 },
66 cbhandler: false,
67 onCheck: null,
68 savestates: true,
69 resetbutton: true,
70 autodraw: true,
71 renderOnMobile: null
72 };
73
74 let firstPaint = false;
75
76 // Public variables
77 this.tableID = table_id;
78 this.checkedRows = 0;
79 this.tableRows = null;
80 this.lastSelectedRow = null;
81 this.tabledata = [];
82
83 /**
84 * Constructor
85 */
86 let __construct = function()
87 {
88 // Set options
89 Instance.options = extend(true, defaults, args);
90
91 Instance.current_page = 1;
92 Instance.url = Instance.options.url;
93 Instance.pagination = Instance.options.pagination;
94 Instance.order = Instance.options.orderby;
95 Instance.sort = Instance.options.sort;
96 Instance.per_page = Instance.options.per_page;
97 Instance.current_url = Instance.url;
98 Instance.args = Instance.options.args || {};
99
100 // Create the table-structure
101 createTable();
102
103 if( Instance.options.autodraw === true && firstPaint === false ) {
104
105 let loadUrl = generateUrl(Instance.args,null, '__construct'); // Generate the URL
106 load_table_data( loadUrl, function(res) {
107
108 check_localstorage(); // ?
109 tableDraw(res, false);
110
111 // Add pagination only on first draw
112 if( Instance.options.pagination.enable === true ) {
113 generate_pagination( parseInt( get_state('current_page', Instance.current_page) ), 1 );
114 if( Instance.options.pagination.perpage === true ) {
115 generate_perpage_selection();
116 }
117 }
118 // After created
119 let onCreateCallback = Instance.options.onCreate || window[Instance.options.onCreate];
120 if( typeof onCreateCallback === 'function' ) {
121 onCreateCallback(Table, Instance);
122 }
123 // Attach events
124 attachTableEvents();
125 firstPaint = true;
126 });
127 }
128
129 return Instance;
130 };
131
132 let StoreVars = function(k,v)
133 {
134 Instance.args[k] = v
135 };
136
137 let LoadVars = function()
138 {
139 return Instance.args;
140 }
141
142 /**
143 * Create the table structure. Expects Table to be defined
144 * @checked YES!
145 */
146 let createTable = function()
147 {
148 if( typeof Table === 'undefined' || Table == null ) {
149 throw "Table #"+Instance.tableID+" does not exist";
150 }
151
152 const opt = Instance.options;
153 let th_columns = [];
154 let columns = opt.columns;
155 let filters = opt.thead || [];
156 let prepend = opt.prepend;
157 let append = opt.append;
158
159
160 // Prepend columns
161 if( typeof prepend !== 'undefined' && prepend !== null && prepend.length > 0 ) {
162 for(let x=0; x<prepend.length; x++) {
163 let col = prepend[x];
164 let attr = typeof col === 'object' ? apply_attributes(col.attributes) : '';
165 let text = typeof col === 'object' ? col.label : col;
166 th_columns.push(`<th${attr}>${text}</th>`);
167 }
168 }
169 // Add columns
170 if( columns.length > 0 ) {
171 for( let x=0; x<columns.length; x++ ) {
172 let col = columns[x];
173 let attr = typeof filters[col] !== 'undefined' ? apply_attributes(filters[col].attributes) : '';
174 let text = table_sort( col );
175 th_columns.push(`<th${attr}>${text}</th>`);
176 }
177 }
178 // Append columns
179 if( typeof append !== 'undefined' && append !== null && append.length > 0 ) {
180 for(let x=0; x<append.length; x++) {
181 let col = append[x];
182 let attr = typeof col === 'object' ? apply_attributes(col.attributes) : '';
183 let text = typeof col === 'object' ? col.label : col;
184 th_columns.push(`<th${attr}>${text}</th>`);
185 }
186 }
187
188 slickDOM.append(Table, `<thead><tr>${th_columns.join('\n')}</tr></thead>`);
189 let sysl = Instance.options.loading;
190 let loading = typeof sysl !== 'undefined' && sysl != null ? sysl : '<span class="'+opt.classes.loader+'">'+opt.localize.loading+'</span>';
191 slickDOM.append(Table, `<tbody><tr><td colspan="${th_columns.length}">${loading}</td></tr></thead>`);
192
193 const cols_start = opt.resetbutton === true ? th_columns.length-1 : th_columns.length;
194 let reset_button_col = `<td style="text-align:right">
195 <button id="${Instance.tableID}_reset-table" class="${opt.classes.resetbutton}">${opt.localize.resetbutton}</button>
196 </td>`;
197 let pgn = `<div class="pgr">
198 <span id="display_current_results"></span>
199 </div><ul class="${opt.classes.pagination}"></ul>`;
200 let pagination_footer_row;
201
202 if( opt.pagination.enable !== false ) {
203 let last_column = opt.resetbutton === true ? reset_button_col : '';
204 pagination_footer_row = `<tr>
205 <td colspan="${cols_start}">
206 <div class="${opt.classes.footer}">
207 ${pgn}
208 </div>
209 </td>
210 ${last_column}
211 </tr>`;
212 } else {
213 if( opt.resetbutton === true ) {
214 pagination_footer_row = `<tr>
215 <td colspan="${th_columns.length-1}"> </td>
216 ${reset_button_col}
217 </tr>`;
218 }
219 }
220
221 slickDOM.append(Table, `<tfoot>
222 ${pagination_footer_row}
223 </tfoot>`);
224
225 }
226
227 let generate_tablefooter = function(footer,data)
228 {
229 let columns = [];
230 // Prepend
231 if( footer.length > 0 ) {
232 for(let x=0;x<footer.length;x++) {
233 if( typeof footer[x] === 'object' ) {
234 let attr = apply_attributes(footer[x].attributes);
235 let contents;
236 if( isCallable(footer[x].content) ) {
237 let fn = footer[x].content;
238 contents = fn(Instance,data);
239 } else {
240 contents = typeof footer[x].content !== 'undefined' ? footer[x].content : '';
241 }
242 columns.push(`<td${attr}>${contents}</td>`)
243 } else {
244 columns.push(`<td>${footer[x]}</td>`);
245 }
246 }
247 }
248 return `<tr id="${Instance.tableID}_table-footer">${columns.join('\n')}</tr>`;
249 };
250
251 let createDropdown = function(elements,id)
252 {
253
254 let subitems = '';
255 let btnlist = [];
256
257 if( isArray(elements) ) {
258
259 for( var x=0; x<elements.length; x++) {
260 let _btn = elements[x];
261 let _btnID = 'dd_btn_'+id+'_'+x;
262 let _lbl = typeof _btn.icon !== 'undefined' ? '<div><i class="material-icons">'+_btn.icon+'</i>'+_btn.label+'</div>' : _btn.label;
263 subitems += `<li><a href="#" id="${_btnID}">${_lbl}</a></li>`;
264
265 btnlist.push({id:_btnID,trigger:_btn.callback});
266 }
267
268 }
269
270 let classes_btn = ['ui-button silent dropdown-trigger'];
271 let classes_ul = ['dropdown-menu pin-right'];
272 let btn_id = 'dd_trigger_'+id;
273
274 let template = `<div class="list-view_after"><div class="dropdown">
275 <button id="${btn_id}" class="${classes_btn.join(' ')}">
276 <i class="material-icons">more_vert</i>
277 </button>
278 <ul class="${classes_ul.join(' ')}">
279 ${subitems}
280 </ul>
281 </div></div>`;
282
283
284 return template;
285
286 };
287
288 this.mobileRow = function(args) {
289
290 if( typeof args.id === 'undefined' || args.id === '' ) {
291 console.warn(`Row indetifier is missing!`);
292 return '<div class="list-view"><p class="text-primary"><code>List item ID is missing!</code></p></div>';
293 }
294
295 const textPrimary = typeof args.textPrimary !== 'undefined' && args.textPrimary !== '' ? args.textPrimary : 'Primary text required!';
296 const textSecondary = typeof args.textSecondary !== 'undefined' && args.textSecondary !== '' ? `<p class="secondary-text">${args.textSecondary}</p>` : '';
297
298 let avatar = '';
299 let iconRight = '';
300
301 if( typeof args.avatar !== 'undefined' && args.avatar !== '' && args.avatar !== null ) {
302 let isHttp = args.avatar.substr(0,7);
303 if(isHttp.toLowerCase() === 'https:/' || isHttp.toLowerCase() === 'http://') {
304 avatar = `<img src="${args.avatar}" title="${slickDOM.removeHTML(textPrimary)}" />`;
305 } else {
306 if( args.avatar.length > 3 ) {
307 const _sn = args.avatar.split(' ');
308 const _f = _sn.length > 1 ? _sn[0].charAt(0)+_sn[1].charAt(0) : _sn[0].charAt(0);
309 avatar = `<em>${_f}</em>`;
310 } else {
311 avatar = `<em>${args.avatar}</em>`;
312 }
313 }
314
315 avatar = `<div class="list-view_before">
316 <figure class="list-icon list-item-select">
317 ${avatar}
318 <div class="is-checked"><i class="material-icons">check</i></div>
319 </figure></div>`;
320
321 }
322
323 if( typeof args.more !== 'undefined' && args.more !== '' && args.more !== null ) {
324
325
326 if( isArray(args.more) ) {
327 // Dropdown
328 //iconRight = `<div class="list-item_right"><i class="material-icons">more_vert</i></div>`;
329
330 iconRight = createDropdown(args.more, args.id);
331
332 } else if( isObject(args.more) ) {
333 // Single icon
334 iconRight = `<div class="list-view_after"><i class="material-icons">${args.more.icon}</i></div>`;
335 } else {
336 // Text
337 iconRight = `<div class="list-view_after"><span class="text-small">${args.more}</span></div>`;
338 }
339
340
341 }
342
343
344
345
346 let template = `<div class="list-view" data-id="${args.id}" id="list_item-${args.id}">
347 ${avatar}
348 <a class="list-view_content" href="${args.link}">
349 <p class="primary-text">${textPrimary}</p>
350 ${textSecondary}
351 </a>
352 ${iconRight}
353 </div>`;
354
355
356 return template;
357 };
358
359
360 /**
361 * Draw to table (rows, pagination etc..) after data is fetched
362 * @checked YES!
363 */
364 let tableDraw = function( data = [], trigger_events = true )
365 {
366 let opt = Instance.options;
367 let tbody = Table.getElementsByTagName('tbody')[0];
368 let thead = Table.getElementsByTagName('thead')[0];
369 slickDOM.removeChildren(tbody); // Clear
370
371 Instance.checkedRows = 0;
372 Instance.lastSelectedRow = null;
373 toggleCbAllOff(); // <- check this
374
375 Instance.tabledata = data;
376
377 if( data.length < 1 ) {
378 let n = thead.rows[0].cells.length;
379 slickDOM.prepend(tbody, `<tr><td colspan="${n}"><p>${opt.localize.noresults}</p></td></tr>`);
380
381 } else {
382
383 data.forEach(function(r) {
384 let tb_columns = [];
385 // Prepend
386 if( typeof opt.prepend !== 'undefined' && opt.prepend !== null && opt.prepend.length > 0 ) {
387 for(let x=0;x<opt.prepend.length;x++) {
388 let col = opt.prepend[x];
389 let attr = typeof col === 'object' ? apply_attributes(col.attributes) : '';
390 let text = typeof col === 'object' ? col.content : col;
391 let inner = filter_row(null, text, r);
392 tb_columns.push(`<td${attr}>${inner}</td>`)
393 }
394 }
395 // Cols
396 for( let k=0; k<opt.columns.length; k++ ) {
397 let key = opt.columns[k]; // == ID
398
399 // Check for replacement
400
401 if( r.hasOwnProperty(key) ) {
402 tb_columns.push( filter_row(key, r[key], r) );
403 }
404 }
405 // Append
406 if( typeof opt.append !== 'undefined' && opt.append !== null && opt.append.length > 0 ) {
407 for(let x=0;x<opt.append.length;x++) {
408 let col = opt.append[x];
409 let attr = typeof col === 'object' ? apply_attributes(col.attributes) : '';
410 let text = typeof col === 'object' ? col.content : col;
411 let ident = typeof col === 'object' ? col : null;
412 let inner = filter_row(ident, text, r);
413 tb_columns.push(`<td${attr}>${inner}</td>`)
414 }
415 }
416
417 // Mobile render
418 if( typeof Instance.options.renderOnMobile !== 'undefined' && isCallable(Instance.options.renderOnMobile) && null !== Instance.options.renderOnMobile ) {
419 tb_columns.push(`<td class="td-mobile-view"><div class="ui-list-item clearfix">${Instance.options.renderOnMobile(r,Instance)}</div></td>`);
420 }
421
422 // Filter after...
423
424
425 // Push row to table
426 //console.log('Push to table: ', tb_columns);
427 slickDOM.append(tbody, `<tr>${tb_columns.join('\n')}</tr>`);
428 });
429 }
430
431 // We add custom footer on draw
432 if(typeof Instance.options.tfoot !== 'undefined' && Instance.options.tfoot !== null ) {
433 // Remove if exists
434 let exists = Table.querySelector('#'+Instance.tableID+'_table-footer');
435 if( exists ) {
436 slickDOM.remove(exists);
437 }
438 // Draw
439 let tablefooter = generate_tablefooter(Instance.options.tfoot,data);
440 slickDOM.prepend(Table.querySelector('tfoot'), tablefooter);
441
442
443 }
444
445
446
447 if( trigger_events === true ) { // delete??
448 //attachTableEvents();
449 //table_actions(); // Should not be nessacery
450 }
451
452 };
453
454 /**
455 * Attach events to table
456 * @checked N/A
457 */
458 let attachTableEvents = function()
459 {
460 const table_rows = Table.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
461 Instance.tableRows = table_rows;
462 // Clicking rows
463 document.addEventListener('click', function(e) {
464
465 if(!event.target.closest('#'+Instance.tableID)) return;
466
467 if( event.target.closest('tbody td') ) {
468 // Only on tbodys TD (need some adujstments..? )
469 onRowClick( e.target.closest('tr'), e );
470 }
471 });
472
473 // Toggle Checkboxes
474 let cbopt = Instance.options.cbhandler;
475 if( typeof cbopt !== 'undefined' && typeof cbopt === 'object' && cbopt !== false ) {
476
477 // Hide checkboxes
478 if( cbopt.hidden === true ) {
479 let items = document.querySelectorAll(cbopt.items);
480 for (let i = 0; i < items.length; i++) {
481 if( items[i].type == 'checkbox' ) {
482 items[i].style.display = 'none';
483 }
484 }
485 }
486
487 document.addEventListener('click', function(event) {
488 if(!event.target.closest(cbopt.el)) return;
489
490 var handler = event.target.closest(cbopt.el);
491 if( handler.type == 'checkbox' ) {
492
493 //event.preventDefault();
494 Instance.toggleCheckboxes( !handler.checked );
495 if( handler.checked === true ) {
496
497 toggleRowsBetweenIndexes([1, table_rows.length]);
498 } else {
499 clearAllRows();
500 }
501 }
502 });
503 }
504
505 if( Instance.options.resetbutton === true ) {
506 let clearBtn = Table.querySelector('#'+Instance.tableID+'_reset-table');
507 clearBtn.addEventListener('click', function(e) {
508 clearAllRows();
509 Instance.clear_filter();
510 });
511 }
512 // Sorting results
513 document.addEventListener('click', function(e) {
514 if (!e.target.closest("."+Instance.options.classes.table_sort)) return;
515
516 e.preventDefault();
517
518 let col = e.target;
519 let column = col.getAttribute('data-column');
520 let sort = col.getAttribute('data-sort');
521
522 // Remove existing active column class
523 let a = document.querySelectorAll("."+Instance.options.classes.active_column);
524 if( a.length > 0 ) {
525 for( let i=0; i < a.length; i++) {
526 if( column != a[i].getAttribute('data-column') ) {
527 a[i].classList.remove(Instance.options.classes.active_column);
528 }
529 }
530 }
531 // Set current
532 col.setAttribute( 'data-sort', (sort == 'asc' ? 'desc' : 'asc') );
533 col.classList.add(Instance.options.classes.active_column);
534
535 // Set states
536 Instance.sort = sort;
537 Instance.order = column;
538 set_state('sort', sort);
539 set_state('order', column);
540
541 clearAllRows();
542 // Redraw table
543 let theUrl = generateUrl(Instance.args, null, 'attachTableEvents_Sorting_results' );
544 load_table_data( theUrl, function(res) {
545 tableDraw(res);
546 countChecked();
547 // Callback
548 var sortCallback = Instance.options.onSort || window[Instance.options.onSort];
549 if( typeof sortCallback === 'function' ) {
550 sortCallback( e, Instance );
551 }
552 });
553 });
554
555 // Pagination
556 if( Instance.options.pagination.enable === true ) {
557 document.addEventListener('click', function(e) {
558
559 if (!e.target.closest("[data-page]")) return;
560
561 e.preventDefault();
562
563 const classes = Instance.options.classes;
564 let elm = e.target.closest("[data-page]");
565 let _li = e.target.parentNode;
566 let _ul = _li.parentNode;
567
568 let page_number = parseInt(elm.getAttribute('data-page')); // Current clicked page-number to go to
569 let tmpcurrentpage = Instance.current_page;
570
571 Instance.current_page = page_number; // Set the new page-number
572 set_state('current_page', page_number);
573 // Remove existing
574 let a = _ul.querySelectorAll("."+classes.active_page);
575 if( a.length > 0 ) {
576 for( let i=0; i < a.length; i++) {
577 if( page_number != a[i].childNodes[0].getAttribute('data-page') ) {
578 a[i].classList.remove(classes.active_page);
579 }
580 }
581 }
582 // Check if we are over max_pages.. if so, regenerate the pagination
583 let fpa = _ul.querySelectorAll('.'+classes.page);
584 let f = parseInt(fpa[0].childNodes[0].getAttribute('data-page') );
585 let l = parseInt( fpa[fpa.length-1].childNodes[0].getAttribute('data-page') );
586
587 if( page_number > l || page_number < f && ( page_number > 0 && page_number <= parseInt(Instance.num_results) ) ) {
588 generate_pagination( page_number, tmpcurrentpage);
589 }
590 // Add active to the correct page
591 let thePageNumber = _ul.querySelector('.'+classes.page+' > a[data-page="'+page_number+'"]');
592 if( typeof thePageNumber.parentNode !== 'undefined' ) {
593 thePageNumber.parentNode.classList.add(classes.active_page);
594 }
595 // Fix next and prev links
596 let max_page = parseInt(Instance.num_pages);
597 let setNextPage = (parseInt(page_number) + 1) <= max_page ? parseInt(page_number) + 1 : max_page;
598 let setPreviousPage = (parseInt(page_number) - 1) > 0 ? parseInt(page_number) - 1 : 1;
599 _ul.querySelector("."+classes.next_page+" > a").setAttribute('data-page', setNextPage);
600 _ul.querySelector("."+classes.prev_page+" > a").setAttribute('data-page', setPreviousPage);
601
602 // Callback (remove?)
603 var paginationCallback = Instance.options.pagination.callback || window[Instance.options.pagination.callback];
604 if( typeof paginationCallback === 'function' ) {
605 paginationCallback( page_number, e, Instance );
606 }
607 // Redraw table (if nessasery )
608 if( parseInt(tmpcurrentpage) !== parseInt( page_number ) ) {
609 // Fetch stored params
610 //let existingParams = get_state('queryArgs', null);
611 //let theUrl = generateUrl(existingParams,null,'attachTableEvents_Pagination_click');
612 clearAllRows();
613 let theUrl = generateUrl(Instance.args);
614 load_table_data(theUrl, function(res) {
615 tableDraw(res);
616 });
617 }
618 });
619
620 // Filter the num_results text
621 let restext = Instance.options.localize.results;
622
623 if( typeof restext !== 'undefined' && restext !== '' && restext !== null && restext !== false ) {
624 //alert(restext);
625 let pp = parseInt(Instance.per_page); // 10
626 let cp = parseInt(Instance.current_page); // 3
627 let nr = parseInt(table_rows.length);
628 let x, y;
629
630 if( Instance.current_page === Instance.num_pages ) {
631 x = Instance.num_results;
632 y = parseInt(Instance.num_results) - nr + 1;
633 } else {
634 x = cp * pp;
635 y = (x - pp) + 1; // 20 - 10 + 1 = 21
636 y = y < 1 ? 1 : y;
637 }
638 //console.log(Instance.num_results, nr);
639 restext = restext.replace('%s%', y );
640 restext = restext.replace('%e%', x );
641 restext = restext.replace('%rows%', nr );
642 restext = restext.replace('%results%', Instance.num_results);
643 restext = restext.replace('%pages%', Instance.num_pages);
644 restext = restext.replace('%current_page%', Instance.current_page);
645
646 document.getElementById("display_current_results").innerHTML = restext;
647 }
648
649 // Change per page.
650 let set_per_page = Table.querySelector('#per_page');
651 if(set_per_page ) {
652 set_per_page.value = Instance.get('per_page');
653
654 set_per_page.addEventListener('change', function(e) {
655 let pp = e.target.value;
656 clearAllRows();
657 Instance.setPerPage(pp);
658 });
659 }
660 } /* eof pagination */
661
662 // Mobile render
663 var mobileRenderClass = '.list-view';
664 var mobileSelector = '.list-item-select';
665 let pressTimer;
666 document.addEventListener('click', function(event) {
667
668
669
670 if( !event.target.closest(mobileSelector) ) {
671 return;
672 }
673
674
675 let el = event.target.closest(mobileRenderClass);
676 //let par = el.closest(mobileRenderClass);
677 let chk = el.querySelector('input[type="checkbox"]');
678
679
680 toggleRow(event.target.closest('tr'));
681
682
683 //event.preventDefault();
684
685
686
687 });
688
689
690 }
691
692 /**
693 * Generate the pagination
694 * @param {int} page_number
695 * @param {int} last
696 * @param {boolean} force
697 */
698 let generate_pagination = function(page_number, last, force)
699 {
700 force = typeof force === 'undefined' ? false : force;
701 let i = 0,
702 last_visited_page = parseInt(last),
703 current_page = parseInt( page_number ),
704 last_page = parseInt( get_state('num_pages', Instance.num_pages) ),
705 per_view = parseInt( Instance.options.pagination.max_per_page ),
706 pages = makePaginationIndex(last_page),
707 pages_chunk = makePaginationChunk(pages,per_view),
708 chunk_index = getPaginationChunk(current_page,pages_chunk),
709 current_chunk = pages_chunk[chunk_index],
710 list = [],
711 classes = Instance.options.classes,
712 elm = document.querySelector('ul.'+classes.pagination);
713
714 // Check if redraw is necessary
715
716 if( force === false && current_page !== last_visited_page && ( current_page > last_page || current_page <= 1 ) )
717 return;
718
719 // Make shure we dont go over or under ´1´ and last_page
720 let prev = Math.min( Math.max(parseInt(page_number-1), 1), last_page);
721 let next = Math.min( Math.max(parseInt(page_number+1), 1), last_page);
722
723 // Previous link
724 list.push('<li class="'+Instance.options.classes.prev_page+'"><a href="#'+prev+'" data-page="'+prev+'"><</a></li>');
725
726 // Pages links
727 for( i=0; i<current_chunk.length; i++) {
728 let x = current_chunk[i]+1;
729 let cc = current_page == x ? classes.page+' '+classes.active_page : classes.page;
730 list.push('<li class="'+cc+'"><a href="#'+x+'" data-page="'+x+'">'+x+'</a></li>');
731 }
732 // Next link
733 list.push('<li class="'+Instance.options.classes.next_page+'"><a href="#'+next+'" data-page="'+next+'">></a></li>');
734
735 // Clear
736 slickDOM.removeChildren( elm );
737 // Draw pagination
738 elm.insertAdjacentHTML('afterbegin', list.join('\n'));
739
740 }
741
742 function getPaginationChunk(n,chunk)
743 {
744 let res = 0;
745 for(let i = 0; i < chunk.length; i++ ) {
746 if( chunk[i].indexOf(n-1) >= 0 ) {
747 res = i;
748 }
749 }
750 return res;
751 }
752 function makePaginationIndex(n) {
753 let pages = [];
754 for(let i = 0; i < n; i++) {
755 pages.push(i);
756 }
757 return pages;
758 }
759 function makePaginationChunk(pages,v) {
760 let chunks = [];
761 let i,j,chunk = v;
762 for( i=0, j = pages.length; i < j; i+=chunk) {
763 chunks.push( pages.slice(i,i+chunk) );
764 }
765 return chunks;
766 }
767
768 /** Genereate drowpdown */
769 let generate_perpage_selection = function()
770 {
771 let opt = Instance.options.pagination;
772 let perpagelabel = typeof opt.perpagelabel !== 'undefined' && opt.perpagelabel != '' ? '<label for="per_page">'+opt.perpagelabel+'</label>' : '';
773 let elm = `<div class="perpage">${perpagelabel}<select size="1" name="per_page" id="per_page">
774 <option value="5">5</option>'+
775 '<option value="10">10</option>'+
776 '<option value="15">15</option>'+
777 '<option value="25">25</option>'+
778 '<option value="50">50</option>'+
779 '<option value="100">100</option></select></div>`;
780
781 //Table.querySelector('#display_current_results').innerText = Instance.options.localize.perpage;
782 let footer = Table.querySelector("tfoot div.pgr");
783 footer.insertAdjacentHTML('afterbegin', elm);
784 };
785
786 var table_sort = function( id )
787 {
788 let filters = Instance.options.thead || [];
789 let order = get_state('order', Instance.order);
790 let sort = get_state('sort', Instance.sort);
791 let classes = Instance.options.classes;
792
793 let setclass = classes.table_sort;
794 let columnID = id;
795 if( typeof filters[id] !== 'undefined') {
796 if( typeof filters[id].orderby !== 'undefined' && filters[id].orderby !== null ) {
797 columnID = filters[id].orderby;
798 }
799 }
800
801 if( order == columnID ) {
802 sort = sort == 'desc' ? 'asc' : 'desc';
803 setclass += ' '+classes.active_column;
804 }
805
806 if( typeof filters[id] !== 'undefined') {
807 let filter = filters[id];
808 if( typeof filter.sortable !== 'undefined' && filter.sortable === false ) {
809 return setColumnLabel(id);
810 }
811 }
812
813 return '<span class="'+setclass+'" data-column="'+columnID+'" data-sort="'+sort+'">'+setColumnLabel(id)+'</span>';
814 }
815
816 let toggleCbAllOff = function()
817 {
818 let cbopt = Instance.options.cbhandler;
819 if( typeof cbopt !== 'undefined' && typeof cbopt === 'object' && cbopt !== false ) {
820 let handler = document.querySelector(cbopt.el);
821 if (handler.type == 'checkbox' && handler.checked === true ) {
822 handler.checked = false;
823 }
824 }
825 };
826
827 /**
828 * Check if row is selected
829 * @param {object} r Row
830 */
831 let isSelectedRow = function(r)
832 {
833 if( r.hasAttribute("data-selected-row") )
834 return true;
835
836 return false;
837 };
838
839 let isCbItem = function(el)
840 {
841 let cbh = Instance.options.cbhandler;
842 if( typeof cbh === 'undefined')
843 return false;
844
845 if( cbh.items !== 'undefined' ) {
846 var cname = cbh.items.substr(1);
847 return el.classList.contains(cname) ? true : false;
848 }
849 return false;
850 }
851 /**
852 * Handles clicking the row (Checked!)
853 * @param {object} current_row
854 * @param {event} event
855 */
856 let onRowClick = function( current_row, event )
857 {
858 if( event.target.type === 'checkbox' && isCbItem(event.target) ) {
859
860 let ac = document.querySelectorAll('[data-selected-row="true"]');
861
862 if( event.ctrlKey || event.metaKey) {
863 toggleRow(current_row);
864 }
865
866 if (event.button === 0) {
867 if(!(event.ctrlKey || event.metaKey) && !event.shiftKey) {
868 if( !isSelectedRow(current_row) || ac.length > 1 ) {
869 clearAllRows();
870 }
871 toggleRow(current_row);
872 }
873 if(event.shiftKey) {
874 toggleRowsBetweenIndexes([Instance.lastSelectedRow.rowIndex, current_row.rowIndex]);
875 }
876 }
877 toggleCbAllOff();
878 }
879 };
880
881 let toggleRow = function(row)
882 {
883 if( isSelectedRow(row) ) {
884 onDeselectRow( row );
885 } else {
886 onSelectRow( row );
887 }
888 Instance.lastSelectedRow = row;
889 };
890
891 let clearAllRows = function() {
892 let rows = Instance.tableRows;
893 for (var i = 0; i < rows.length; i++) {
894 if( isSelectedRow( rows[i] ) )
895 onDeselectRow( rows[i] );
896 }
897 document.body.classList.remove('select-mode-active');
898 };
899
900 this.clearSelectedRows = function() {
901 clearAllRows();
902 };
903
904 let onSelectRow = function(row)
905 {
906 let onSelectCallback = Instance.options.onSelect || window[Instance.options.onSelect];
907 row.classList.add(Instance.options.classes.selected_row);
908 row.setAttribute('data-selected-row','true');
909
910 // Checking checboxes
911 let cbopt = Instance.options.cbhandler;
912 if( typeof cbopt !== 'undefined' && typeof cbopt === 'object' && cbopt !== false ) {
913
914 //if( cbopt.selectOnRow === true ) {
915 let items = row.querySelectorAll(cbopt.items);
916 for (var i = 0; i < items.length; i++) {
917 if (items[i].type == 'checkbox' && items[i].disabled !== true) {
918 items[i].checked = true;
919 }
920 }
921 countChecked();
922 //}
923 }
924 let rows = Table.querySelectorAll("[data-selected-row]");
925 let numrows = rows.length;
926
927 if( parseInt(numrows) >= 1 ) {
928 document.body.classList.add('select-mode-active');
929 } else {
930 document.body.classList.remove('select-mode-active');
931 }
932
933 let onclearevent = new CustomEvent('sdt-selectrow', {
934 detail: {
935 numrows: numrows,
936 rows: rows
937 }
938 });
939 document.dispatchEvent(onclearevent);
940 if( typeof onSelectCallback === 'function' ) {
941 onSelectCallback( window.event, row, Instance );
942 }
943 };
944
945 let onDeselectRow = function(row)
946 {
947 row.classList.remove(Instance.options.classes.selected_row);
948 row.removeAttribute('data-selected-row');
949
950
951 // Checking checboxes
952 let cbopt = Instance.options.cbhandler;
953 if( typeof cbopt !== 'undefined' && typeof cbopt === 'object' && cbopt !== false ) {
954 //if( cbopt.selectOnRow === true ) {
955 let items = row.querySelectorAll(cbopt.items);
956 for (var i = 0; i < items.length; i++) {
957 if (items[i].type == 'checkbox' && items[i].disabled !== true) {
958 items[i].checked = false;
959 }
960 }
961 countChecked();
962 //}
963 }
964 let rows = Table.querySelectorAll("[data-selected-row]");
965 let numrows = rows.length;
966
967 if( parseInt(numrows) >= 1 ) {
968 document.body.classList.add('select-mode-active');
969 } else {
970 document.body.classList.remove('select-mode-active');
971 }
972
973 let onclearevent = new CustomEvent('sdt-selectrow', {
974 detail: {
975 numrows: numrows,
976 rows: rows,
977 row: row
978 }
979 });
980 document.dispatchEvent(onclearevent);
981 };
982
983 let toggleRowsBetweenIndexes = function(indexes)
984 {
985 // lowest to highest index
986 indexes.sort(function(a, b) {
987 return a - b;
988 });
989
990 let rows = Instance.tableRows;
991
992 clearAllRows();
993 for (var i = indexes[0]; i <= indexes[1]; i++) {
994 onSelectRow(rows[i-1]);
995 }
996 };
997
998
999 /**
1000 * Generate the url, allways appends default arguments
1001 * if setUrl is passed, thats the url that is beeing used, else go for the one in options
1002 * @param {*} args
1003 * @param {*} setUrl
1004 */
1005 let generateUrl = function( args, setUrl, cb )
1006 {
1007
1008
1009 let geturl = Instance.options.url;
1010 let params = Instance.options.queryargs;
1011 let _args = LoadVars();
1012 let url = typeof setUrl !== 'undefined' && setUrl !== null ? setUrl : geturl + params;
1013 let _default_keys = {
1014 order: get_state('order', Instance.order),
1015 sort: get_state('sort', Instance.sort),
1016 perpage: get_state('per_page',Instance.per_page),
1017 page: get_state('current_page', Instance.current_page)
1018 };
1019 let qa = typeof args !== 'undefined' && args !== null ? Object.assign(_default_keys, args) : _default_keys;
1020
1021
1022 // Replace within the url before split
1023 for(let i=0; i< Object.keys(qa).length; i++) {
1024 var k = Object.keys(qa)[i];
1025 url = replaceAll(url, '{{'+k+'}}', qa[k]);
1026 }
1027 // Split the URL to seperate query arguments
1028 let parts = url.split("?", 2);
1029 let base_url = parts[0];
1030 let base_qa = queryStringToObject( parts[1] );
1031
1032 if(!(Object.keys(base_qa).length === 0 && base_qa.constructor === Object)) {
1033 let uqa = Object.assign(base_qa, qa);
1034 return base_url + '?' + objectToQueryString(uqa);
1035 } else {
1036 return base_url;
1037 }
1038
1039 };
1040
1041
1042
1043 let queryStringToObject = function(str)
1044 {
1045 if( typeof str === 'undefined' || str === null || str === '')
1046 return {};
1047
1048 let params = JSON.parse('{"' + str.replace(/&/g, '","').replace(/=/g,'":"') + '"}', function(key, value) { return key===""?value:decodeURIComponent(value) });
1049 return params;
1050 };
1051
1052 let objectToQueryString = function(obj)
1053 {
1054 let pairs = [];
1055 for (var prop in obj) {
1056 if (obj.hasOwnProperty(prop)) {
1057 var k = encodeURIComponent(prop),
1058 v = encodeURIComponent(obj[prop]);
1059 pairs.push( k + "=" + v);
1060 }
1061 }
1062 return pairs.join('&');
1063 };
1064
1065 /**
1066 * Load table-data from url (not checked)
1067 * @param {string} url
1068 * @param {object} params
1069 * @param {function} callback
1070 */
1071 let load_table_data = function( url, callback )
1072 {
1073 Instance.current_url = url;
1074
1075 let wasNumPages = get_state('num_results');
1076
1077 if( typeof url !== 'undefined' ) {
1078 fetch(url, { headers: { "Content-Type": "application/json; charset=utf-8" }})
1079 .then(res => res.json()) // parse response as JSON (can be res.text() for plain response)
1080 .then(response => {
1081
1082 var _data = typeof Instance.options.results !== 'undefined' && Instance.options.results !== null ? response[Instance.options.results] : response.data;
1083 // Meta and pagination
1084
1085 if( typeof Instance.pagination !== 'undefined' && typeof Instance.pagination === 'object' && Instance.pagination.enable === true ) {
1086 let _keys = Instance.pagination.keys;
1087
1088 Instance.current_page = byString(response, _keys.current_page );
1089 Instance.num_pages = byString(response, _keys.pages );
1090 Instance.per_page = byString(response, _keys.per_page );
1091 Instance.num_results = byString(response, _keys.total );
1092
1093 set_state('current_page', Instance.current_page);
1094 set_state('num_pages', Instance.num_pages);
1095 set_state('per_page', Instance.per_page);
1096 set_state('num_results', Instance.num_results);
1097
1098 if( wasNumPages !== Instance.num_results) {
1099 generate_pagination(Instance.current_page,0,true);
1100 }
1101 }
1102
1103 var cbFunc = callback || window[callback];
1104 if( typeof cbFunc === 'function' ) {
1105 cbFunc(_data);
1106 }
1107 })
1108 .catch(err => {
1109 console.error(err);
1110 });
1111 }
1112 };
1113
1114
1115 /**
1116 * Apply filter to row
1117 * @param {string} i Column identifier
1118 * @param {string} v Column value
1119 * @param {object} r Row data
1120 * @checked YES
1121 */
1122 let filter_row = function( i, v, r )
1123 {
1124 let filters = Instance.options.filter;
1125 let content = typeof v == 'undefined' || v == '' || v == null ? " " : v.toString();
1126 let td = i !== null || i !== '' ? '<td' : '';
1127 let attr = '';
1128 let columnID = isObject(i) ? i.id : i;
1129 let columnValue = content;
1130
1131
1132 if( isObject(i) ) {
1133 columnValue = isCallable(v) ? v(r) : content;
1134 attr = apply_attributes(i.attributes);
1135 } else {
1136 if( filters !== null && typeof filters[columnID] !== 'undefined' ) {
1137
1138 if( typeof filters[i] === 'object' ) {
1139 attr = apply_attributes(filters[i].attributes);
1140 columnValue = isCallable(filters[i].content) ? filters[i].content(v,r) : content;
1141 } else {
1142 columnValue = typeof filters[i] !== 'undefined' ? filters[i] : v;
1143 }
1144 }
1145 }
1146
1147 // Replace keywords ( %key% %value% )
1148 content = typeof columnValue == 'undefined' || columnValue == null ? '' : columnValue.toString();
1149
1150 content = content.replace(/%key%/g, columnID);
1151 content = content.replace(/%value%/g, v);
1152
1153 for(var x=0; x< Object.keys(r).length; x++) {
1154 var k = Object.keys(r)[x];
1155 content = replaceAll(content, '{{r.'+k+'}}', r[k]);
1156 td = replaceAll(td, '{{r.'+k+'}}', r[k]);
1157 }
1158
1159
1160 return `<td${attr}>${content}</td>`;
1161
1162 };
1163
1164 /**
1165 * Creates attributes list for use in html elements
1166 * @param {object} attr Attributes (key:value)
1167 * @checked YES!
1168 */
1169 let apply_attributes = function( attr )
1170 {
1171 let attrlist = [],i,x;
1172
1173 if( typeof attr !== 'undefined' && typeof attr === 'object' ) {
1174 for(i=0; i < Object.keys(attr).length; i++ ) {
1175 let a = Object.keys(attr)[i];
1176 if( a === 'data') {
1177 for( x=0; x < Object.keys(attr[a]).length; x++ ) {
1178 let b = Object.keys(attr[a])[x];
1179 attrlist.push('data-'+b+'="'+attr[a][b]+'"');
1180 }
1181 } else {
1182 attrlist.push(a+'="'+attr[a]+'"');
1183 }
1184 }
1185 return " "+attrlist.join(" ");
1186 }
1187 return "";
1188 }
1189
1190
1191
1192 /**
1193 * Set the column label by checking filters and callback functions
1194 * @checked YES!
1195 * @param {string} id ID of column
1196 */
1197 var setColumnLabel = function( id )
1198 {
1199 if( Instance.options.columns !== null ) {
1200 var cols = Instance.options.columns;
1201 var filters = Instance.options.thead || [];
1202
1203 if( typeof filters[id] !== 'undefined' && typeof filters[id].label !== 'undefined') {
1204 var filter = filters[id];
1205 if( typeof filter.label === 'function' || window[filter.label] === 'function' ) {
1206 var lfunc = filter.label || window[filter.label];
1207 return lfunc( id, {
1208 url:Instance.url,
1209 order: get_state('order', Instance.order),
1210 sort:get_state('sort', Instance.order),
1211 per_page:get_state('per_page', Instance.per_page),
1212 total: Instance.num_results,
1213 current_page: get_state('current_page', Instance.current_page)
1214 } );
1215 } else {
1216 return filter.label;
1217 }
1218 }
1219 }
1220 return id;
1221 };
1222
1223 /**
1224 * Replace all ocurances of string in text
1225 * @param {string} originalString Text to search whitin
1226 * @param {string} find String to find
1227 * @param {string} replace Strin to replace-with
1228 */
1229 var replaceAll = function(originalString, find, replace)
1230 {
1231 if( typeof originalString !== 'undefined' && originalString !== null)
1232 return originalString.replace(new RegExp(find, 'g'), replace);
1233
1234 return originalString;
1235 };
1236
1237
1238
1239 /**
1240 * Save state to local storage
1241 * @param {string} key Key to set
1242 * @param {mixed} val Value to store
1243 */
1244 var set_state = function( key, val )
1245 {
1246 let id = 'slickDataTable-'+Instance.tableID+'_'+key;
1247 if( Object.prototype.toString.call(val) === '[object Object]' ) {
1248 val = JSON.stringify(val);
1249 }
1250 localStorage.setItem(id, val );
1251 };
1252
1253 /**
1254 * Get state from local storage
1255 * @param {string} key Key to locate
1256 * @param {mixed} defaults Default return value (NULL)
1257 */
1258 var get_state = function(key,defaults)
1259 {
1260 let id = 'slickDataTable-'+Instance.tableID+'_'+key;
1261 let val = localStorage.getItem(id);
1262 val = getObjFromStorage(val);
1263 defaults = typeof defaults !== 'undefined' ? defaults : null;
1264 let _ret = typeof val !== 'undefined' && val !== null ? val : defaults;
1265
1266 if(Instance.options.savestates === false ) {
1267 delete_state(key);
1268 }
1269 return _ret;
1270 };
1271
1272 /**
1273 * Remove state from local storage
1274 * @param {string} key
1275 */
1276 let delete_state = function(key)
1277 {
1278 let id = 'slickDataTable-'+Instance.tableID+'_'+key;
1279 localStorage.removeItem(id);
1280 }
1281
1282 /**
1283 * Clear all states
1284 * @param {boolean} reload reload table
1285 */
1286 let clear_state = function( reload = true )
1287 {
1288 localStorage.clear();
1289 Instance.updateTableSort(Instance.options.orderby, Instance.options.sort);
1290
1291 let onclearevent = new CustomEvent('sdt-clearstate', {
1292 detail: {
1293 queryargs: Instance.args
1294 }
1295 });
1296 document.dispatchEvent(onclearevent);
1297 if(true === reload)
1298 Instance.reload();
1299 }
1300
1301 /**
1302 * Check if value is object
1303 * @rev needed???
1304 * @param {mixed} val
1305 * @return object or value
1306 */
1307 let getObjFromStorage = function( val )
1308 {
1309 try {
1310 JSON.parse(val);
1311 } catch (e) {
1312 return val;
1313 }
1314 return JSON.parse(val);
1315 };
1316
1317 /**
1318 * Count number of checked rows
1319 */
1320 let countChecked = function()
1321 {
1322 let cbopt = Instance.options.cbhandler; // Get the class/id/name of checkbox
1323
1324 if( typeof cbopt !== 'undefined' && typeof cbopt === 'object' && cbopt !== false ) {
1325 let items = document.querySelectorAll(cbopt.items);
1326 let count = 0;
1327 for (var i = 0; i < items.length; i++) {
1328 if( items[i].type == 'checkbox' && items[i].checked === true && items[i].disabled !== true ) {
1329 count++;
1330 }
1331 }
1332 Instance.checkedRows = count;
1333 }
1334 // Create custom event, when checkbox is checked, this will fire..
1335 var oncheckevent = new CustomEvent('sdt-oncheck', {
1336 detail: {
1337 checked: Instance.checkedRows,
1338 rows: Instance.getSelectedRows()
1339 }
1340 });
1341 document.dispatchEvent(oncheckevent);
1342 let onCheckCallback = Instance.options.onCheck || window[Instance.options.onCheck];
1343 if( typeof onCheckCallback === 'function' ) {
1344 onCheckCallback(Instance.checkedRows, Instance);
1345 }
1346 }
1347
1348 /**
1349 * Create serialized string from object
1350 * @param {object} obj Object of query arguments
1351 * @param {string} prefix Prefix
1352 * @access Private
1353 */
1354 var serialize_url = function(obj, prefix) {
1355 let str = [],
1356 p;
1357
1358 for (p in obj) {
1359 if (obj.hasOwnProperty(p)) {
1360 var k = prefix ? prefix + "[" + p + "]" : p,
1361 v = obj[p];
1362 str.push((v !== null && typeof v === "object") ? serialize(v, k) : encodeURIComponent(k) + "=" + encodeURIComponent(v));
1363 }
1364 }
1365 return str.join("&");
1366 };
1367
1368 // ---------------------------------------------------------------------
1369 // Public methods
1370 // ---------------------------------------------------------------------
1371
1372
1373 this.preventSelect = function()
1374 {
1375 let event = window.event;
1376 let row = event.target.closest('tr');
1377 if( typeof row !== 'undefined' ) {
1378 Instance.dontTouch = row;
1379 }
1380 }
1381 /**
1382 * Get key from state or options
1383 */
1384 this.get = function(key)
1385 {
1386 let state = get_state( key, false);
1387 let opt = Instance[key];
1388 if( state !== false ) {
1389 return state;
1390 }
1391 return typeof opt !== 'undefined' ? opt : false;
1392 };
1393
1394 this.updateTableSort = function(order,sort)
1395 {
1396
1397 let current = document.querySelector('.table-sort[data-column="'+order+'"]');
1398
1399 if( typeof current === 'undefined' || current == null)
1400 return;
1401
1402 // Remove existing
1403 let i = 0;
1404 let a = document.querySelectorAll(".is-active-column");
1405 if( a.length > 0 ) {
1406 for( i=0; i < a.length; i++) {
1407 if( order != a[i].getAttribute('data-column') ) {
1408 a[i].classList.remove("is-active-column");
1409 }
1410 }
1411 }
1412
1413 // Set current
1414 current.setAttribute( 'data-sort', (sort == 'asc' ? 'desc' : 'asc') );
1415 current.classList.add("is-active-column");
1416
1417 // Set state
1418 Instance.sort = sort;
1419 Instance.order = order;
1420 set_state('sort', sort);
1421 set_state('order', order);
1422 }
1423
1424
1425
1426
1427
1428 /**
1429 * Set number of rows per page
1430 * @param {int} n Number of rows per page
1431 * @access Public
1432 * @return void
1433 */
1434 this.setPerPage = function(n)
1435 {
1436 set_state('per_page', parseInt(n) );
1437 set_state('current_page', 1 ); // Reset to page one
1438 Instance.reload(null, function(res) {
1439 generate_pagination( 1, get_state('current_page', 1 ) );
1440 });
1441 };
1442
1443 /**
1444 * Check if something has changed and localstorage needs to be reset
1445 */
1446 var check_localstorage = function()
1447 {
1448 let pp = parseInt(get_state('per_page'));
1449 let nr = parseInt(get_state('num_results'));
1450
1451 if( typeof pp !== 'undefined' && Instance.options.pagination.enable === true ) {
1452 if( pp !== Instance.per_page && nr !== Instance.num_results ) {
1453 localStorage.clear();
1454 }
1455 }
1456 }
1457
1458 /**
1459 * Clear out any stored states of the table
1460 * @access Public
1461 */
1462 this.clear_filter = function( reload = true )
1463 {
1464 clear_state( reload );
1465 };
1466
1467 /**
1468 * Reset table and clear any arguments
1469 * @access Public
1470 */
1471 this.reset = function()
1472 {
1473 let url = generateUrl(null,null,'reset_table');
1474 if( Instance.current_url !== url ) {
1475 Instance.reload();
1476 }
1477 };
1478
1479
1480
1481 this.setArgument = function(k,v)
1482 {
1483 StoreVars(k,v);
1484 }
1485
1486 this.setArgs = function(args, append=true) {
1487
1488 // console.log(Instance.options.args, Object.keys(Instance.options.args));
1489 if( typeof args !== 'undefined' && !isEmpty(Instance.options.args) ) {
1490 Instance.options.args = append === true ? Object.assign(Instance.options.args,args) : args;
1491 set_state('queryArgs', Instance.options.args);
1492 } else {
1493 Instance.options.args = args;
1494 set_state('queryArgs', Instance.options.args);
1495 }
1496 }
1497
1498 /**
1499 * Draw the table
1500 */
1501 this.draw = function()
1502 {
1503 if( firstPaint === false ) {
1504
1505 let loadUrl = generateUrl(Instance.args,null,'this_draw()'); // Generate the URL
1506
1507 load_table_data( loadUrl, function(res) {
1508
1509 check_localstorage(); // ?
1510 tableDraw(res, false);
1511
1512 // Add pagination only on first draw
1513 if( Instance.options.pagination.enable === true ) {
1514 generate_pagination( parseInt( get_state('current_page', Instance.current_page) ), 1 );
1515 if( Instance.options.pagination.perpage === true ) {
1516 generate_perpage_selection();
1517 }
1518 }
1519 // After created
1520 let onCreateCallback = Instance.options.onCreate || window[Instance.options.onCreate];
1521 if( typeof onCreateCallback === 'function' ) {
1522 onCreateCallback(Table, Instance);
1523 }
1524 // Attach events
1525 attachTableEvents();
1526 firstPaint = true;
1527 });
1528 }
1529 };
1530 /**
1531 * Reload the table
1532 * @param {object} params Parameters to prepend to the base url
1533 * @param {function} cb Callback function
1534 * @access Public
1535 */
1536 this.reload = function( params, cb )
1537 {
1538 params = typeof params === 'undefined' ? get_state('queryArgs', null) : params;
1539 let theUrl = generateUrl(params,null,'this.reload()');
1540 load_table_data( theUrl, function(res) {
1541
1542 //draw_table(res);
1543 tableDraw(res);
1544 var cbFunc = cb || window[cb];
1545 if( typeof cbFunc === 'function' ) {
1546 cbFunc(res);
1547 }
1548
1549
1550 if( params !== null && Object.keys(params).length > 0 ) {
1551 let o = params.order;
1552 let s = params.sort;
1553 Instance.updateTableSort(o,s);
1554 }
1555 // generate_pagination( 1, 1 );
1556
1557 });
1558 };
1559
1560 this.getdata = function()
1561 {
1562 return Instance.tabledata;
1563 }
1564
1565 this.getNode = function()
1566 {
1567 return Table;
1568 }
1569
1570 /**
1571 * Removes row from table
1572 * @param {object/int} row Either row-index or object
1573 * @access Public
1574 */
1575 this.removeRow = function(row) {
1576 row = typeof row === 'object' ? row : Instance.getNode().rows[row];
1577 row.classList.add(Instance.options.classes.delete_row);
1578
1579 setTimeout(function () {
1580 Instance.getNode().deleteRow(row.rowIndex);
1581 }, Instance.options.timeout_delete );
1582 };
1583
1584 this.updateRow = function(row,data) {
1585 row = typeof row === 'object' ? row : Instance.getNode().rows[row];
1586
1587 if( data.constructor === Array ) {
1588 let cols = [];
1589 let max = row.cells.length;
1590 for( let x=0; x < data.length; x++ ) {
1591 if( x <= max )
1592 row.cells[x].innerHTML = data[x];
1593 }
1594 } else {
1595 while (row.lastChild) {
1596 row.removeChild(row.lastChild);
1597 }
1598 row.innerHTML = data;
1599 }
1600
1601 //table_actions();
1602 };
1603
1604 this.updateColumn = function(row,col,data) {
1605 row = typeof row === 'object' ? row : Instance.getNode().rows[row];
1606 let elm = row.cells[col];
1607 elm.innerHTML = data;
1608 };
1609
1610 this.toggleCheckboxes = function(state)
1611 {
1612 let cbopt = Instance.options.cbhandler;
1613 if( typeof cbopt !== 'undefined' && typeof cbopt === 'object' && cbopt !== false ) {
1614 let items = document.querySelectorAll(cbopt.items);
1615 for (var i = 0; i < items.length; i++) {
1616 if (items[i].type == 'checkbox' && items[i].disabled !== true) {
1617 //let state = items[i].checked;
1618 items[i].checked === true ? false : true;
1619 }
1620 }
1621 countChecked();
1622 }
1623 }
1624
1625 this.getSelectedRows = function()
1626 {
1627 let cbopt = Instance.options.cbhandler;
1628 if( typeof cbopt !== 'undefined' && typeof cbopt === 'object' && cbopt !== false ) {
1629 let items = document.querySelectorAll(cbopt.items);
1630 let data = [];
1631 for (var i = 0; i < items.length; i++) {
1632 if (items[i].type == 'checkbox') {
1633 //let state = items[i].checked;
1634 if(items[i].checked === true)
1635 data.push(items[i].value);
1636 }
1637 }
1638 return data;
1639 }
1640 return [];
1641 }
1642
1643 this.numCheckedRows = function()
1644 {
1645 return Instance.checkedRows;
1646 }
1647
1648
1649 return __construct();
1650}