www/jqwidgets/jqximport.js

changeset 733
67bf19c50fcc
equal deleted inserted replaced
732:db4de4f37b65 733:67bf19c50fcc
1 
2 /* tslint:disable */
3 /* eslint-disable */
4 (function ($) {
5 class DataAdapter {
6 constructor ( config ) {
7 if ( !config ) {
8 config = {};
9 }
10
11 const that = Object.assign( this, config );
12
13 const generateKey = function () {
14 const S4 = function () {
15 return ( ( ( 1 + Math.random() ) * 0x10000 ) | 0 ).toString( 16 ).substring( 1 );
16 };
17 return S4();
18 };
19
20 that.key = generateKey();
21
22 that.boundSource = [];
23 that.dataItemById = [];
24
25 if ( that.allowAdd === undefined ) {
26 that.allowAdd = true;
27 }
28
29 if ( that.allowRemove === undefined ) {
30 that.allowRemove = true;
31 }
32
33 if ( that.allowUpdate === undefined ) {
34 that.allowUpdate = true;
35 }
36
37 if ( config.observable === undefined ) {
38 that.observable = true;
39 }
40
41
42 if ( !config.dataSource ) {
43 that.dataSource = [];
44 }
45
46 if ( !config.dataFields ) {
47 that.dataFields = [];
48 }
49 else {
50 /* if (config.dataSource && config.dataSource.length > 0) {
51 const keys = Object.keys(config.dataSource[0]);
52
53 // that.dataFields = [];
54
55 for (let i = 0; i < keys.length; i++) {
56
57 }
58 }
59 */
60 }
61
62 if ( !config.dataSourceType ) {
63 that.dataSourceType = 'array';
64 }
65
66 if ( !config.id ) {
67 that.id = null;
68 }
69
70 if ( !config.autoFetch ) {
71 that.autoFetch = true;
72 }
73
74 if ( config.dataFields ) {
75 that.dataFields = config.dataFields;
76 }
77
78 Object.defineProperty( that, 'groupBy', {
79 configurable: false,
80 enumerable: true,
81 get() {
82 if ( !that._groupBy ) {
83 return [];
84 }
85
86 return that._groupBy;
87 },
88 set( value ) {
89 const updateGrouping = () => {
90 that.boundHierarchy = null;
91 that.refreshHierarchy();
92
93 if ( that.onGroup ) {
94 that.onGroup();
95 }
96 }
97
98 that._groupBy = [].concat(value);
99
100 if ( that.isInitialized ) {
101 updateGrouping();
102 }
103 }
104 } );
105
106 if ( !config.groupBy ) {
107 that.groupBy = [];
108 }
109 else {
110 if ( config.groupBy.toArray ) {
111 that.groupBy = config.groupBy.toArray();
112 }
113 else {
114 that.groupBy = config.groupBy;
115 }
116 }
117
118 if ( config && config.autoBind !== false ) {
119 that.dataBind();
120 }
121
122 that.isInitialized = true;
123 }
124
125 get dataFields() {
126 const that = this;
127
128 return that._dataFields;
129 }
130
131 set dataFields( value ) {
132 const that = this;
133
134 that._dataFields = that._getDataFieldObjects( value );
135
136 return that._dataFields;
137 }
138
139 _getDataFieldObjects( dataFields ) {
140 //const that = this;
141
142 let dataFieldObjects = [];
143
144 if ( typeof dataFields === 'number' ) {
145 const charCode = 'A'.charCodeAt( 0 );
146 let prefix = '';
147 let index = 0;
148
149 for ( let i = 0; i < dataFields; i++ ) {
150 const letter = String.fromCharCode( charCode + index );
151
152 index++;
153
154 const label = prefix + letter;
155
156 dataFieldObjects.push( { name: label, dataType: 'string' } )
157
158 if ( index >= 26 ) {
159 index = 0;
160 prefix += 'A';
161 }
162 }
163 }
164 else if ( dataFields.length > 0 ) {
165 for ( let i = 0; i < dataFields.length; i++ ) {
166 const dataField = dataFields[ i ];
167
168 if ( typeof dataField === 'string' ) {
169 const dataFieldParts = dataField.split( ':' );
170 const name = dataFieldParts[ 0 ].trim();
171 const dataType = dataFieldParts.length > 1 ? dataFieldParts[ 1 ].trim() : 'string';
172
173 dataFieldObjects.push( { name: name, dataType: dataType } );
174 }
175 else {
176 dataFieldObjects.push( dataField );
177 }
178 }
179 }
180
181 return dataFieldObjects;
182 }
183
184 get dataSource() {
185 const that = this;
186
187 if ( !that._dataSource ) {
188 that._dataSource = [];
189 }
190
191 return that._dataSource;
192 }
193
194 set dataSource( value ) {
195 const that = this;
196
197 that._dataSource = value;
198
199 if ( that.isInitialized ) {
200 that.boundSource = false === that.observable ? [] : new JQX.ObservableArray();
201 that.dataItemById = [];
202 that.bindingCompleted = false;
203 that.dataBind();
204 }
205 }
206
207 get canNotify() {
208 const that = this;
209
210 if ( that._canNotify === undefined ) {
211 that._canNotify = true;
212 }
213
214 return that._canNotify;
215 }
216
217 set canNotify( value ) {
218 const that = this;
219
220 that._canNotify = value;
221 }
222
223 _notify( changeArgs ) {
224 const that = this;
225
226 if ( !that.canNotify ) {
227 return;
228 }
229
230 if ( that.notifyFn ) {
231 that.notifyFn( changeArgs );
232 }
233 }
234
235 notify( notifyFn ) {
236 const that = this;
237
238 if ( notifyFn ) {
239 that.notifyFn = notifyFn;
240 }
241 }
242
243 toArray() {
244 const that = this;
245
246 return that.boundSource.toArray();
247 }
248
249 dataBind() {
250 const that = this;
251
252 that.clear();
253
254 const completed = () => {
255
256
257 that._onBindingComplete();
258 }
259
260 if ( typeof that.dataSource === 'string' && ( that.dataSource.indexOf( '.json' ) >= 0 ) ) {
261 that.url = that.dataSource;
262 that.dataSourceType = 'json';
263
264 new Ajax( that, ( data/*, status*/ ) => {
265 that.dataSource = data;
266
267 that._bindToJSON();
268 } );
269 }
270 else if ( typeof that.dataSource === 'string' && ( that.dataSource.indexOf( '.xlsx' ) >= 0 ) ) {
271 that.url = that.dataSource;
272 that.dataSourceType = 'xlsx';
273
274 new Ajax( that, ( data/*, status*/ ) => {
275 if ( !data[ 0 ] ) {
276 data = [];
277 that._bindToArray();
278 completed();
279 return;
280 }
281
282 const keys = Object.keys( data[ 0 ] );
283 const dataFieldMap = {};
284 const dataRows = [];
285
286 if ( that.exportHeader !== false ) {
287 let index = 0;
288
289 for ( let key in keys ) {
290 const name = keys[ key ];
291
292 dataFieldMap[ name ] = that.dataFields[ index++ ].name;
293 }
294
295 for ( let i = 1; i < data.length; i++ ) {
296 const row = data[ i ];
297 const dataRow = {};
298
299 for ( let key in keys ) {
300 const name = keys[ key ];
301
302 dataRow[ dataFieldMap[ name ] ] = row[ name ];
303 }
304
305 dataRows.push( dataRow );
306 }
307
308 that.dataSource = dataRows;
309 }
310
311 that._bindToArray();
312 completed();
313 } );
314 }
315 else if ( typeof that.dataSource === 'string' && ( that.dataSource.indexOf( '.csv' ) >= 0 ) ) {
316 that.dataSourceType = 'csv';
317
318 new Ajax( that, (/*data, status*/ ) => {
319 that._bindToArray();
320 } );
321 }
322 else if ( typeof that.dataSource === 'string' && ( that.dataSource.indexOf( '.tsv' ) >= 0 ) ) {
323 that.dataSourceType = 'tsv';
324
325 new Ajax( that, (/*data, status*/ ) => {
326 } );
327 }
328 else if ( that.dataSourceType === 'array' ) {
329 that._bindToArray();
330 completed();
331 }
332 else if ( that.dataSourceType === 'json' ) {
333 that._bindToJSON();
334 completed();
335 }
336 }
337
338 _onBindingComplete() {
339 const that = this;
340
341 that._buildHierarchy();
342
343 if ( that.onBindingComplete ) {
344 that.onBindingComplete( { data: that.boundSource } );
345 }
346
347 if ( that._notify ) {
348 that._notify( { action: 'bindingComplete', data: that.boundSource } );
349 }
350
351 that.bindingCompleted = true;
352 }
353
354 refreshHierarchy() {
355 const that = this;
356
357 that._buildHierarchy();
358 }
359
360 find() {
361 const that = this;
362
363 return that.boundSource.find.apply( that.boundSource, arguments );
364 }
365
366 onVirtualDataSourceRequested( requestCallback, details ) {
367 const that = this;
368
369 let first = details ? details.first : Infinity;
370 let last = details ? details.last : Infinity;
371 let row = details ? details.row : null;
372
373 if ( undefined === first ) {
374 first = Infinity;
375 }
376
377 if ( undefined === last ) {
378 last = Infinity;
379 }
380
381 that.virtualFirstIndex = first;
382 that.virtualLastIndex = last;
383
384 if ( that.virtualDataSource ) {
385 const getDataSource = function ( ExcelAdapterSettings ) {
386 if ( ExcelAdapterSettings.virtualDataSourceLength !== undefined ) {
387 that.virtualDataSourceLength = ExcelAdapterSettings.virtualDataSourceLength;
388 }
389
390 new JQX.ExcelAdapter(
391 {
392 dataSource: ExcelAdapterSettings.dataSource,
393 dataFields: ExcelAdapterSettings.dataFields || that.dataFields,
394 data: details,
395 onBindingComplete( event ) {
396
397 if ( that.virtualDataSourceOnExpand && row ) {
398 if ( event.data && event.data.length > 0 ) {
399 that.add( event.data, row.$.id );
400 }
401 else {
402 row.leaf = true;
403 }
404
405 if ( that.onFilter ) {
406 that.onFilter()
407 }
408
409 requestCallback();
410
411 return;
412 }
413
414 if ( first === Infinity ) {
415 that.add( event.data );
416 }
417 else {
418 let items = [];
419 let indexes = [];
420
421 for ( let i = 0; i < event.data.length; i++ ) {
422 const item = event.data[ i ];
423
424 if ( first + i <= last ) {
425 items.push( item );
426 indexes.push( first + i );
427 }
428 }
429
430 that.update( indexes, items );
431 }
432
433
434 if ( that.onFilter ) {
435 that.onFilter()
436 }
437
438 requestCallback();
439 }
440 } );
441 }
442
443 let hasCache = false;
444
445 const isEmpty = ( obj ) => Object.entries( obj ).length === 0 && ( obj.constructor === Object || obj.constructor === Array );
446 const canCache = isEmpty( details.sorting ) && isEmpty( details.filtering ) && isEmpty( details.grouping ) && !details.row && ( details.action !== 'filter' && details.action !== 'sort' && details.action !== 'group' );
447
448 if ( that.virtualDataSourceCache && first !== Infinity && canCache ) {
449 let cachedCount = 0;
450
451 for ( let i = first; i < last; i++ ) {
452 if ( !that[ i ].$.isEmpty ) {
453 cachedCount++;
454 }
455 }
456
457 if ( cachedCount === last - first ) {
458 hasCache = true;
459 }
460 }
461
462 if ( hasCache ) {
463 requestCallback();
464 }
465 else {
466 if ( details.action === 'expand' ) {
467 that.virtualDataSourceOnExpand( getDataSource, {
468 first: first,
469 last: last,
470 row: details.row,
471 sorting: details.sorting,
472 filtering: details.filtering,
473 grouping: details.grouping,
474 action: details.action
475 } );
476 }
477 else {
478 that.virtualDataSource( getDataSource, {
479 first: first,
480 last: last,
481 sorting: details.sorting,
482 filtering: details.filtering,
483 filterOperator: details.filterOperator || 'and',
484 grouping: details.grouping,
485 action: details.action
486 } );
487 }
488 }
489 }
490 else {
491 requestCallback();
492 }
493 }
494
495 add( item, parentId ) {
496 const that = this;
497
498 if ( !item ) {
499 return;
500 }
501
502 let result = true;
503
504 const addItem = function ( item ) {
505 const itemObject = that._getDataItem( item, that.boundSource.length );
506
507 that[ that.boundSource.length ] = itemObject;
508 that.dataItemById[ itemObject.$.id ] = itemObject;
509
510 const pushResult = that.boundSource.push( itemObject );
511
512 if ( parentId !== undefined ) {
513 itemObject.$.parentId = parentId;
514 }
515
516 if ( !pushResult ) {
517 result = false;
518 }
519
520 return itemObject;
521 }
522
523 if ( item.length ) {
524 let itemObjects = [];
525
526 for ( let i = 0; i < item.length; i++ ) {
527 const itemObject = addItem( item[ i ] );
528
529 itemObjects.push( itemObject );
530 }
531
532 that._notify( { action: 'add', data: itemObjects } );
533 }
534 else {
535 const itemObject = addItem( item );
536
537 that._notify( { action: 'add', data: itemObject } );
538 }
539
540 that.refreshHierarchy();
541
542 return result;
543 }
544
545 refreshIndexes() {
546 const that = this;
547
548 for (let i = 0; i < that.boundSource.length; i++) {
549 that[i] = that.boundSource[i];
550 that[i].$.index = i;
551 that.dataItemById[that[i].$.id] = that[i];
552 }
553
554 let i = that.boundSource.length;
555
556 while (that[i]) {
557 delete that[i];
558 i++;
559 }
560 }
561
562 removeLast() {
563 const that = this;
564
565 delete that[that.boundSource.length - 1];
566 const result = that.boundSource.pop();
567 delete that.dataItemById[result.$.id];
568
569 that._notify({ action: 'removeLast', data: result });
570
571 that.refreshHierarchy();
572
573 return result;
574 }
575
576 removeAt(index) {
577 const that = this;
578
579 const item = that.boundSource[index];
580
581 if (!item) {
582 throw new Error('Invalid Item Index');
583 }
584
585 that.boundSource.splice(index, 1);
586 delete that.dataItemById[item.$.id];
587 that.refreshIndexes();
588
589 that._notify({ action: 'remove', index: index, data: item });
590
591 that.refreshHierarchy();
592 }
593
594 update( index, dataSourceItem ) {
595 const that = this;
596
597 if ( JQX.Utilities.Types.isArray( index ) && JQX.Utilities.Types.isArray( dataSourceItem ) ) {
598 if ( index.length === 0 && dataSourceItem.length === 0 ) {
599 that.refreshHierarchy();
600 return;
601 }
602 }
603
604 if ( dataSourceItem.length && index.length ) {
605 let itemObjects = [];
606
607 for ( let i = 0; i < index.length; i++ ) {
608 const itemObject = that._getDataItem( dataSourceItem[ i ], index[ i ] );
609 const currentIndex = index[ i ];
610
611 itemObjects.push( itemObject );
612
613 that.boundSource[ currentIndex ] = itemObject;
614 that[ currentIndex ] = that.boundSource[ currentIndex ];
615 that.dataItemById[ itemObject.$.id ] = that[ currentIndex ];
616 }
617
618 that._notify( { action: 'update', index: index, data: itemObjects } );
619
620 that.refreshHierarchy();
621
622 return;
623 }
624
625 const itemObject = that._getDataItem( dataSourceItem, index );
626
627 that.boundSource[ index ] = itemObject;
628 that[ index ] = that.boundSource[ index ];
629 that.dataItemById[ itemObject.$.id ] = that[ index ];
630
631 that._notify( { action: 'update', index: index, data: itemObject } );
632
633 that.refreshHierarchy();
634
635 return itemObject;
636 }
637
638 insert( index, item ) {
639 const that = this;
640
641 item = that._getDataItem( item, index );
642
643 const result = that.boundSource.splice( index, 0, item );
644
645 that.refreshIndexes();
646
647 that._notify( { action: 'insert', index: index, data: item } );
648
649 that.refreshHierarchy();
650
651 return result;
652 }
653
654 move( from, to ) {
655 if ( to > from && to - from === 1 || from === to ) {
656 return;
657 }
658
659 const that = this,
660 recordToMove = that.boundSource.splice( from, 1 )[ 0 ];
661
662 if ( to > from ) {
663 to--;
664 that.boundSource.splice( to, 0, recordToMove );
665 }
666 else {
667 that.boundSource.splice( to, 0, recordToMove );
668 }
669
670 that.refreshIndexes();
671
672 that._notify( { action: 'move', index: to, data: that.boundSource[ to ] } );
673
674 that.refreshHierarchy();
675 }
676
677 indexOf( item ) {
678 const that = this;
679 const index = that.boundSource.indexOf( item );
680
681 return index;
682 }
683
684 get length() {
685 const that = this;
686
687 if ( that.virtualDataSourceLength !== undefined ) {
688 return that.virtualDataSourceLength;
689 }
690
691 if ( that.dataSourceLength ) {
692 return that.dataSourceLength;
693 }
694
695 if ( typeof ( that.dataSource ) === 'number' ) {
696 return that.dataSource;
697 }
698
699 if ( that.bindingCompleted ) {
700 return that.boundSource.length;
701 }
702
703 if ( that.dataSource && typeof that.dataSource !== 'string' && that.dataSource.length ) {
704 return that.dataSource.length;
705 }
706
707 return that.boundSource.length;
708 }
709
710 clear() {
711 const that = this;
712
713 if ( !that.isInitialized ) {
714 that._cachedValues = [];
715 that.dataItemById = [];
716 return;
717 }
718
719 for ( let i = 0; i < that.boundSource.length; i++ ) {
720 delete that[ i ];
721 }
722
723 that._cachedValues = [];
724 that.boundSource = that.observable ? new JQX.ObservableArray() : [];
725 that.dataItemById = [];
726 that.refreshHierarchy();
727 }
728
729 _getId( id, item, index ) {
730 if ( id !== null && id.name !== undefined ) {
731 if ( id.name && item.getAttribute ) {
732 let result = item.getAttribute( id.name );
733 if ( result !== null && result.toString().length > 0 ) {
734 return result;
735 }
736 else if ( id.map ) {
737 try {
738 let result = item.getAttribute( id.map );
739 if ( result !== null && result.toString().length > 0 ) {
740 return result;
741 }
742 }
743 catch ( error ) {
744 return index;
745 }
746 }
747 return;
748 }
749 }
750
751 if ( id ) {
752 if ( id.toString().length > 0 && item.getAttribute ) {
753 let result = item.getAttribute( id );
754 if ( result !== null && result.toString().length > 0 ) {
755 return result.trim().split( ' ' ).join( '' ).replace( /([ #;?%&,.+*~\':'!^$[\]()=>|\/@])/g, '' );
756 }
757 else {
758 let splitMap = id.split( this.mapChar );
759 if ( splitMap.length > 1 ) {
760 let d = item;
761 for ( let p = 0; p < splitMap.length; p++ ) {
762 if ( d !== undefined ) {
763 d = d[ splitMap[ p ] ];
764 }
765 }
766 if ( d !== undefined ) {
767 return d;
768 }
769 }
770 else {
771 if ( item[ id ] !== undefined ) {
772 return item[ id ];
773 }
774 }
775 }
776 }
777 }
778
779 return index;
780 }
781
782 _buildHierarchy() {
783 const that = this;
784
785 if ( !that.reservedNames ) {
786 that.reservedNames = {
787 leaf: 'leaf',
788 parent: 'parent',
789 expanded: 'expanded',
790 checked: 'checked',
791 selected: 'selected',
792 level: 'level',
793 icon: 'icon',
794 data: 'data'
795 }
796 }
797 else {
798 const names = that.reservedNames;
799
800 if ( !names.leaf ) {
801 names.leaf = 'leaf';
802 }
803 if ( !names.parent ) {
804 names.parent = 'parent';
805 }
806 if ( !names.expanded ) {
807 names.expanded = 'expanded';
808 }
809 if ( !names.checked ) {
810 names.checked = 'checked';
811 }
812 if ( !names.selected ) {
813 names.selected = 'selected';
814 }
815 if ( !names.level ) {
816 names.level = 'level';
817 }
818 if ( !names.data ) {
819 names.data = 'data';
820 }
821
822 }
823
824 const names = that.reservedNames;
825
826 if ( that.childrenDataField ) {
827 const hierarchy = [];
828
829 for ( let i = 0; i < that.boundSource.length; i++ ) {
830 const item = Object.assign( {}, that.boundSource[ i ] );
831
832 if ( !item ) {
833 continue;
834 }
835
836 hierarchy.push( item );
837
838 const addItems = function ( item ) {
839 const splitMap = that.childrenDataField.split( that.mapChar );
840 let children = null;
841
842 if ( splitMap.length > 1 ) {
843 let data = item;
844
845 for ( let p = 0; p < splitMap.length; p++ ) {
846 if ( data !== undefined ) {
847 data = data[ splitMap[ p ] ];
848 }
849 }
850
851 children = data;
852 }
853 else {
854 children = item[ 'children' ];
855 }
856
857 item[ 'children' ] = children;
858
859 if ( item[ 'children' ] === null || item[ 'children' ] === undefined || ( item[ 'children' ] && item[ 'children' ].length === 0 ) ) {
860 item[ names.leaf ] = true;
861 }
862 }
863
864 addItems( item );
865 item[ names.level ] = 0;
866
867 if ( !item.$ ) {
868 item.$ = {};
869 }
870
871 item[ names.parent ] = null;
872 item[ names.data ] = item;
873
874 if ( item[ names.expanded ] === undefined ) {
875 item[ names.expanded ] = false;
876 }
877
878 const drillThrough = function ( parent, children ) {
879 if ( !children ) {
880 parent[ 'children' ] = new Array();
881 return;
882 }
883
884 for ( let i = 0; i < children.length; i++ ) {
885 let item = that._getDataItem( children[ i ], i );
886
887 if ( !item ) {
888 continue;
889 }
890
891 addItems( item );
892 item[ names.level ] = parent[ names.level ] + 1;
893 item[ names.parent ] = parent;
894 item[ names.data ] = item;
895
896 if ( parent ) {
897 parent[ 'children' ][ i ] = item;
898 }
899
900
901 if ( item[ names.expanded ] === undefined ) {
902 item[ names.expanded ] = false;
903 }
904
905 drillThrough( item, item[ 'children' ] );
906 }
907 }
908
909 drillThrough( item, item[ 'children' ] );
910 }
911
912
913 that.boundHierarchy = hierarchy;
914
915 if ( !that._boundSourceUpdate ) {
916 for ( let i = 0; i < that.boundHierarchy.length; i++ ) {
917 const item = that.boundHierarchy[ i ];
918
919 if ( item.children ) {
920 const drillThrough = function ( item ) {
921 if ( !that.dataItemById[ item.$.id ] ) {
922 that.boundSource.canNotify = false;
923 that.dataItemById[ item.$.id ] = item;
924 that[ that.boundSource.length ] = item;
925 that.boundSource.push( item );
926 that.boundSource.canNotify = true;
927 }
928
929 if ( item.children ) {
930 for ( let i = 0; i < item.children.length; i++ ) {
931 const child = item.children[ i ];
932
933 if ( child.children ) {
934 drillThrough( child );
935 }
936 }
937 }
938 }
939
940 drillThrough( item );
941 }
942 }
943
944 that._boundSourceUpdate = true;
945 }
946 }
947
948 if ( that.xmlRoot && that.dataSourceType === 'xml' ) {
949 that.boundHierarchy = this._getHierarchy( 'uid', '_parentuid', 'children', null, that.boundSource );
950 }
951
952 if ( that.keyDataField && that.parentDataField ) {
953 that.boundHierarchy = this._getHierarchy( that.keyDataField, that.parentDataField, 'children', null, that.boundSource );
954 }
955
956 if ( that.groupBy && that.groupBy.length > 0 ) {
957 that.boundHierarchy = this._getGroupHierarchy( that.groupBy, 'children', 'label', null, 'data', null, 'parent', that.boundSource );
958 }
959
960 if ( that.virtualDataSourceOnExpand ) {
961 that.boundHierarchy = this._getHierarchy( 'id', 'parentId', 'children', null, that.boundSource );
962 }
963 }
964
965
966 _getGroupHierarchy( groups, collectionName, groupName, mappingFields, itemName, valueName, parentName, data, startIndex ) {
967 let that = this;
968
969 if ( !startIndex ) {
970 startIndex = 0;
971 }
972
973 let names = that.reservedNames;
974
975 const guid = function () {
976 function s4() {
977 return Math.floor( ( 1 + Math.random() ) * 0x10000 )
978 .toString( 16 )
979 .substring( 1 );
980 }
981
982 return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
983 }
984
985 let groupHashCodes = new Array();
986 for ( let iGroupColumn = 0; iGroupColumn < groups.length; iGroupColumn++ ) {
987 groupHashCodes[ iGroupColumn ] = guid();
988 }
989
990 if ( !collectionName ) {
991 collectionName = 'children';
992 }
993
994 if ( !groupName ) {
995 groupName = 'group';
996 }
997
998 if ( !itemName ) {
999 itemName = 'item';
1000 }
1001
1002 if ( !parentName ) {
1003 parentName = 'parent';
1004 }
1005
1006 if ( undefined === valueName ) {
1007 valueName = 'value';
1008 }
1009
1010 const groupboundSource = new Array();
1011 const hashItemGroups = new Array();
1012
1013 let groupboundSourceIndex = 0;
1014
1015 const getItem = function ( item ) {
1016 let itemObj = item;
1017 if ( mappingFields ) {
1018 for ( let mappingField in mappingFields ) {
1019 const mappingObject = mappingFields[ mappingField ];
1020
1021 if ( mappingObject.name && mappingObject.map ) {
1022 itemObj[ mappingObject.map ] = itemObj[ mappingObject.name ];
1023 }
1024 }
1025 }
1026
1027 return itemObj;
1028 }
1029
1030 for ( let obj = 0; obj < data.length; obj++ ) {
1031 let item = Object.assign( {}, getItem( data[ obj ] ) );
1032
1033 item[ names.leaf ] = false;
1034
1035 let itemKeysHierarchy = new Array();
1036 let keys = 0;
1037
1038 for ( let iGroupColumn = 0; iGroupColumn < groups.length; iGroupColumn++ ) {
1039 const group = groups[ iGroupColumn ];
1040 const value = item[ group ];
1041
1042 if ( null === value ) {
1043 continue;
1044 }
1045
1046 itemKeysHierarchy[ keys++ ] = { value: value, group: group, hash: groupHashCodes[ iGroupColumn ] };
1047 }
1048
1049 if ( itemKeysHierarchy.length !== groups.length ) {
1050 break;
1051 }
1052
1053 let parentItem = null;
1054 let lookupKey = '';
1055
1056 for ( let q = 0; q < itemKeysHierarchy.length; q++ ) {
1057 const itemKey = itemKeysHierarchy[ q ].value;
1058 const groupDataField = itemKeysHierarchy[ q ].group;
1059 const columnHash = itemKeysHierarchy[ q ].hash;
1060
1061 lookupKey = lookupKey + '_' + columnHash + '_' + itemKey;
1062
1063 if ( hashItemGroups[ lookupKey ] !== undefined && hashItemGroups[ lookupKey ] !== null ) {
1064 parentItem = hashItemGroups[ lookupKey ];
1065 continue;
1066 }
1067
1068 if ( parentItem === null ) {
1069 parentItem = { $: {} };
1070
1071 parentItem[ names.level ] = 0;
1072 parentItem[ names.leaf ] = false;
1073 parentItem[ parentName ] = null;
1074 parentItem[ groupName ] = itemKey;
1075 parentItem[ itemName ] = item;
1076 parentItem[ 'groupDataField' ] = groupDataField;
1077
1078 if ( !parentItem[ groupDataField ] ) {
1079 parentItem[ groupDataField ] = parentItem.data[ groupDataField ];
1080 }
1081
1082 if ( item[ names.expanded ] !== undefined ) {
1083 parentItem[ names.expanded ] = item[ names.expanded ];
1084 }
1085 else {
1086 parentItem[ names.expanded ] = false;
1087 }
1088
1089 if ( valueName ) {
1090 parentItem[ valueName ] = item[ valueName ];
1091 }
1092
1093 parentItem[ collectionName ] = new Array();
1094
1095 let uid = groupboundSource.length + startIndex;
1096
1097 if ( !this.id || typeof item.$.id === 'number' || isFinite( item.$.id ) ) {
1098 uid = 'Item' + uid;
1099 }
1100 if ( parentItem.$.id === undefined ) {
1101 parentItem.$.id = uid;
1102 }
1103
1104 groupboundSource[ groupboundSourceIndex++ ] = parentItem;
1105 }
1106 else {
1107 const subItem = { $: {} };
1108
1109 subItem[ names.level ] = parentItem[ names.level ] + 1;
1110 subItem[ parentName ] = parentItem;
1111 subItem[ groupName ] = itemKey;
1112 subItem[ collectionName ] = new Array();
1113 subItem[ itemName ] = item;
1114 subItem[ 'groupDataField' ] = groupDataField;
1115 subItem[ names.leaf ] = false;
1116
1117 if ( !subItem[ groupDataField ] ) {
1118 subItem[ groupDataField ] = subItem.data[ groupDataField ];
1119 }
1120
1121 if ( item[ names.expanded ] !== undefined ) {
1122 subItem[ names.expanded ] = item[ names.expanded ];
1123 }
1124 else {
1125 subItem[ names.expanded ] = false;
1126 }
1127
1128 if ( valueName ) {
1129 subItem[ valueName ] = item[ valueName ];
1130 }
1131
1132 if ( subItem.$.id === undefined ) {
1133 subItem.$.id = parentItem.$.id + '_' + parentItem[ collectionName ].length;
1134 }
1135
1136 parentItem[ collectionName ][ parentItem[ collectionName ].length ] = subItem;
1137 parentItem = subItem;
1138 }
1139
1140 hashItemGroups[ lookupKey ] = parentItem;
1141 }
1142
1143 if ( item ) {
1144 item[ names.leaf ] = true;
1145 }
1146
1147 if ( parentItem !== null ) {
1148 if ( this.id === null ) {
1149 if ( undefined === item.$.id ) {
1150 item.$.id = parentItem.$.id + '_' + parentItem[ collectionName ].length;
1151 }
1152 }
1153 else {
1154 if ( undefined === item.$.id ) {
1155 if ( item.$.id.toString().indexOf( parentItem.$.id ) === -1 ) {
1156 item.$.id = parentItem.$.id + '_' + item.$.id;
1157 }
1158 }
1159 }
1160
1161 item[ parentName ] = parentItem;
1162 item[ names.level ] = parentItem[ names.level ] + 1;
1163 parentItem[ collectionName ][ parentItem[ collectionName ].length ] = item;
1164 }
1165 else {
1166 if ( undefined === item.$.id ) {
1167 item.$.id = guid();
1168 }
1169 }
1170 }
1171
1172 return groupboundSource;
1173 }
1174
1175 _getHierarchy( fieldName, parentFieldName, collectionName, mappingFields, boundSource ) {
1176 const that = this;
1177
1178 const databoundHierarchy = new Array();
1179 let flatData = this.boundSource;
1180
1181 if ( boundSource ) {
1182 flatData = boundSource;
1183 }
1184
1185 if ( this.boundSource.length === 0 )
1186 return null;
1187
1188 const childrenName = collectionName !== null ? collectionName : 'children';
1189 let items = new Array();
1190 let data = flatData;
1191 let dataLength = data.length;
1192 let names = that.reservedNames;
1193
1194 const getItem = function ( item ) {
1195 let itemObj = item;
1196 if ( mappingFields ) {
1197 for ( let mappingField in mappingFields ) {
1198 const mappingObject = mappingFields[ mappingField ];
1199
1200 if ( mappingObject.name && mappingObject.map ) {
1201 itemObj[ mappingObject.map ] = itemObj[ mappingObject.name ];
1202 }
1203 }
1204 }
1205
1206 return itemObj;
1207 }
1208
1209 // build hierarchical source.
1210 for ( let i = 0; i < dataLength; i++ ) {
1211 let item = data[ i ];
1212 let parentId = item[ parentFieldName ];
1213 let id = item[ fieldName ];
1214
1215 if ( parentFieldName === 'parentId' ) {
1216 parentId = item.$.parentId;
1217 }
1218
1219 if ( fieldName === 'id' ) {
1220 id = item.$.id;
1221 }
1222
1223 item[ childrenName ] = new Array();
1224
1225 items[ id ] = { parentId: parentId, item: item };
1226 }
1227
1228 for ( let i = 0; i < dataLength; i++ ) {
1229 const item = data[ i ];
1230 let parentId = item[ parentFieldName ];
1231 let id = item[ fieldName ];
1232
1233 if ( parentFieldName === 'parentId' ) {
1234 parentId = item.$.parentId;
1235 }
1236
1237 if ( fieldName === 'id' ) {
1238 id = item.$.id;
1239 }
1240
1241 if ( items[ parentId ] !== undefined ) {
1242 let item = { parentId: parentId, item: items[ id ].item };
1243 let parentItem = items[ parentId ].item;
1244 if ( !parentItem[ childrenName ] ) {
1245 parentItem[ childrenName ] = new Array();
1246 }
1247 let length = parentItem[ childrenName ].length;
1248 item = item.item;
1249
1250 if ( !names ) {
1251 if ( item.parent === undefined ) {
1252 item.parent = parentItem;
1253 }
1254 }
1255 else {
1256 if ( item[ names.parent ] === undefined ) {
1257 item[ names.parent ] = parentItem;
1258 }
1259 }
1260
1261 const itemObj = getItem( item );
1262
1263 parentItem[ childrenName ][ length ] = itemObj;
1264 items[ parentId ].item = parentItem;
1265 items[ id ].item = item;
1266
1267 }
1268 else {
1269 let item = items[ id ].item;
1270 if ( !names ) {
1271 if ( item.parent === undefined ) {
1272 item.parent = null;
1273 }
1274 }
1275 else {
1276 if ( item[ names.parent ] === undefined ) {
1277 item[ names.parent ] = null;
1278 }
1279 }
1280
1281 const itemObj = getItem( item );
1282
1283 if ( !names ) {
1284 itemObj.level = 0;
1285 }
1286 else {
1287 itemObj[ names.level ] = 0;
1288 }
1289
1290 databoundHierarchy[ databoundHierarchy.length ] = itemObj;
1291 }
1292 }
1293 if ( databoundHierarchy.length !== 0 ) {
1294 let updateLevels = function ( level, children ) {
1295 for ( let i = 0; i < children.length; i++ ) {
1296 const child = children[ i ];
1297
1298 if ( !names ) {
1299 child.level = level;
1300 }
1301 else {
1302 child[ names.level ] = level;
1303 }
1304
1305 const childChildren = child[ childrenName ];
1306
1307 if ( childChildren ) {
1308 if ( childChildren.length > 0 ) {
1309 updateLevels( level + 1, childChildren );
1310 }
1311 else {
1312 if ( that.virtualDataSourceOnExpand ) {
1313 if ( child.leaf === undefined ) {
1314 child.leaf = false;
1315 }
1316 }
1317 else {
1318 if ( !names ) {
1319 child.leaf = true;
1320 }
1321 else {
1322 child[ names.leaf ] = true;
1323 }
1324 }
1325 }
1326 }
1327 else {
1328 if ( that.virtualDataSourceOnExpand ) {
1329 if ( child.leaf === undefined ) {
1330 child.leaf = false;
1331 }
1332 }
1333 else {
1334 if ( !names ) {
1335 child.leaf = true;
1336 }
1337 else {
1338 child[ names.leaf ] = true;
1339 }
1340 }
1341 }
1342 }
1343 };
1344 updateLevels( 0, databoundHierarchy );
1345 }
1346 return databoundHierarchy;
1347 }
1348
1349 summarize( summaryItems, boundSource ) {
1350 const that = this;
1351
1352 if ( !Array.isArray( summaryItems ) ) {
1353 summaryItems = [ summaryItems ];
1354 }
1355
1356 let tempSummaryItems = [];
1357
1358 for ( let i = 0; i < summaryItems.length; i++ ) {
1359 const summaryItem = summaryItems[ i ];
1360
1361 for ( let name in summaryItem ) {
1362 const functions = summaryItem[ name ];
1363
1364 tempSummaryItems.push( { dataField: name, functions: functions } )
1365 }
1366 }
1367
1368 summaryItems = tempSummaryItems;
1369
1370 let data = {};
1371 let summaryByDataField = new Array();
1372
1373 if ( !boundSource ) {
1374 boundSource = that.boundSource;
1375 }
1376
1377 let length = boundSource.length;
1378
1379 if ( length === 0 ) {
1380 return;
1381 }
1382
1383 if ( length === undefined ) {
1384 return;
1385 }
1386
1387 for ( let i = 0; i < length; i++ ) {
1388 let dataItem = boundSource[ i ];
1389
1390 for ( let j = 0; j < summaryItems.length; j++ ) {
1391 const summaryItem = summaryItems[ j ];
1392 let value = dataItem[ summaryItem.dataField ];
1393
1394 if ( summaryItem.functions ) {
1395 data[ summaryItem.dataField ] = data[ summaryItem.dataField ] || {};
1396 summaryByDataField[ summaryItem.dataField ] = summaryByDataField[ summaryItem.dataField ] || 0;
1397 summaryByDataField[ summaryItem.dataField ]++;
1398
1399 const _summaryItemFunction = function ( summaryItemObject ) {
1400 for ( let name in summaryItemObject ) {
1401 let oldValue = data[ summaryItem.dataField ][ name ];
1402
1403 if ( oldValue === null || oldValue === undefined ) {
1404 data[ summaryItem.dataField ][ name ] = 0;
1405 oldValue = 0;
1406 }
1407
1408 if ( typeof summaryItemObject[ name ] === 'function' ) {
1409 oldValue = summaryItemObject[ name ]( oldValue, value, summaryItem.dataField, dataItem );
1410 }
1411 data[ summaryItem.dataField ][ name ] = oldValue;
1412 }
1413 }
1414
1415 let canParse = parseFloat( value );
1416
1417 if ( isNaN( canParse ) ) {
1418 canParse = false;
1419 }
1420 else {
1421 canParse = true;
1422 }
1423
1424 if ( canParse ) {
1425 value = parseFloat( value );
1426 }
1427
1428 if ( typeof value === 'number' && isFinite( value ) ) {
1429 summaryItem.functions.forEach( function ( summaryItemFunction ) {
1430 let oldValue = data[ summaryItem.dataField ][ summaryItemFunction ];
1431
1432 if ( oldValue === null || oldValue === undefined ) {
1433 oldValue = 0;
1434
1435 if ( summaryItemFunction === 'min' ) {
1436 oldValue = 9999999999999;
1437 }
1438
1439 if ( summaryItemFunction === 'max' ) {
1440 oldValue = -9999999999999;
1441 }
1442
1443 if (summaryItemFunction === 'median') {
1444 oldValue = [];
1445 }
1446 }
1447
1448 if ( summaryItemFunction === 'sum' || summaryItemFunction === 'avg' || summaryItemFunction === 'stdev'
1449 || summaryItemFunction === 'stdevp' || summaryItemFunction === 'var' || summaryItemFunction === 'varp' ) {
1450 oldValue += parseFloat( value );
1451 }
1452 else if ( summaryItemFunction === 'product' ) {
1453 if ( i === 0 )
1454 oldValue = parseFloat( value );
1455 else
1456 oldValue *= parseFloat( value );
1457 }
1458 else if ( summaryItemFunction === 'min' ) {
1459 oldValue = Math.min( oldValue, parseFloat( value ) );
1460 }
1461 else if ( summaryItemFunction === 'max' ) {
1462 oldValue = Math.max( oldValue, parseFloat( value ) );
1463 }
1464 else if ( summaryItemFunction === 'count' ) {
1465 oldValue++;
1466 }
1467 else if (summaryItemFunction === 'median') {
1468 oldValue.push(parseFloat(value));
1469 }
1470 else if ( typeof ( summaryItemFunction ) === 'object' ) {
1471 _summaryItemFunction( summaryItemFunction );
1472 return;
1473 }
1474
1475 data[ summaryItem.dataField ][ summaryItemFunction ] = oldValue;
1476 } );
1477 }
1478 else {
1479 summaryItem.functions.forEach( function ( summaryItemFunction ) {
1480 if ( summaryItemFunction === 'min' || summaryItemFunction === 'max' || summaryItemFunction === 'count' || summaryItemFunction === 'product' || summaryItemFunction === 'sum'
1481 || summaryItemFunction === 'avg' || summaryItemFunction === 'stdev'
1482 || summaryItemFunction === 'stdevp' || summaryItemFunction === 'var' || summaryItemFunction === 'varp' ) {
1483 if ( value === null ) {
1484 return true;
1485 }
1486
1487 let oldValue = data[ summaryItem.dataField ][ summaryItemFunction ];
1488
1489 if ( oldValue === null || oldValue === undefined ) {
1490 oldValue = 0;
1491 }
1492
1493 data[ summaryItem.dataField ][ summaryItemFunction ] = oldValue;
1494
1495 return true;
1496 }
1497
1498 if ( typeof ( summaryItemFunction ) === 'object' ) {
1499 _summaryItemFunction( summaryItemFunction );
1500 }
1501 } );
1502 }
1503 }
1504 }
1505 }
1506
1507 for ( let j = 0; j < summaryItems.length; j++ ) {
1508 const summaryItem = summaryItems[ j ];
1509
1510 if ( !summaryItem.functions ) {
1511 continue;
1512 }
1513 if ( !data[ summaryItem.dataField ] ) {
1514 data[ summaryItem.dataField ] = {};
1515
1516 summaryItem.functions.forEach( function ( summaryItemFunction ) {
1517 data[ summaryItem.dataField ][ summaryItemFunction ] = 0;
1518 } );
1519 }
1520
1521 if ( data[ summaryItem.dataField ][ 'avg' ] !== undefined ) {
1522 const value = data[ summaryItem.dataField ][ 'avg' ];
1523 const dataValues = summaryByDataField[ summaryItem.dataField ];
1524
1525 if ( dataValues === 0 || dataValues === undefined ) {
1526 data[ summaryItem.dataField ][ 'avg' ] = 0;
1527 }
1528 else {
1529 data[ summaryItem.dataField ][ 'avg' ] = value / dataValues;
1530 }
1531 }
1532 else if ( data[ summaryItem.dataField ][ 'count' ] !== undefined ) {
1533 data[ summaryItem.dataField ][ 'count' ] = length;
1534 }
1535 else if (data[summaryItem.dataField]['median'] !== undefined) {
1536 let population = data[summaryItem.dataField]['median'];
1537
1538 population.sort(function (a, b) {
1539 return a - b;
1540 });
1541
1542 data[summaryItem.dataField]['median'] =
1543 0.5 * (population[Math.floor((population.length + 1) / 2) - 1] + population[Math.ceil((population.length + 1) / 2) - 1]);
1544 }
1545
1546 // stdev, stdevp, var, varp.
1547 // stdev - Standard deviation on a sample.
1548 // varp - Variance on an entire population.
1549 // let - Variance on a sample.
1550 if ( data[ summaryItem.dataField ][ 'stdev' ] || data[ summaryItem.dataField ][ 'stdevp' ]
1551 || data[ summaryItem.dataField ][ 'var' ] || data[ summaryItem.dataField ][ 'varp' ] ) {
1552 summaryItem.functions.forEach( function ( summaryItemFunction ) {
1553 if ( summaryItemFunction === 'stdev' || summaryItemFunction === 'var' || summaryItemFunction === 'varp' || summaryItemFunction === 'stdevp' ) {
1554 const value = data[ summaryItem.dataField ][ summaryItemFunction ];
1555 const count = length;
1556 const average = ( value / length );
1557 let sumSq = 0.0;
1558
1559 for ( let i = 0; i < length; i++ ) {
1560 let dataItem = boundSource[ i ];
1561 let value = dataItem[ summaryItem.dataField ];
1562
1563 sumSq += ( value - average ) * ( value - average );
1564 }
1565
1566 let denominator = ( summaryItemFunction === 'stdevp' || summaryItemFunction === 'varp' ) ? count : count - 1;
1567
1568 if ( denominator === 0 ) {
1569 denominator = 1;
1570 }
1571
1572 if ( summaryItemFunction === 'var' || summaryItemFunction === 'varp' ) {
1573 data[ summaryItem.dataField ][ summaryItemFunction ] = sumSq / denominator;
1574 }
1575 else if ( summaryItemFunction === 'stdevp' || summaryItemFunction === 'stdev' ) {
1576 data[ summaryItem.dataField ][ summaryItemFunction ] = Math.sqrt( sumSq / denominator );
1577 }
1578 }
1579 } );
1580 }
1581 }
1582 return data;
1583 }
1584
1585 deserialize(stringValue, type, nullable) {
1586 const nullValue = stringValue === 'null';
1587
1588 if (stringValue === undefined || (nullValue && !nullable)) {
1589 return undefined;
1590 }
1591
1592 if (nullValue && nullable) {
1593 return null;
1594 }
1595
1596 if (type === 'boolean' || type === 'bool') {
1597 if (stringValue === null) {
1598 return false;
1599 }
1600
1601 // Boolean properties are set based on the presence of the attribute: if the attribute exists at all, the value is true.
1602 return true;
1603 }
1604 else if (type === 'number' || type === 'float') {
1605 if (stringValue === 'NaN') {
1606 return NaN;
1607 }
1608
1609 if (stringValue === 'Infinity') {
1610 return Infinity;
1611 }
1612
1613 if (stringValue === '-Infinity') {
1614 return -Infinity;
1615 }
1616
1617 return parseFloat(stringValue);
1618 }
1619 else if (type === 'int' || type === 'integer') {
1620 if (stringValue === 'NaN') {
1621 return NaN;
1622 }
1623
1624 if (stringValue === 'Infinity') {
1625 return Infinity;
1626 }
1627
1628 if (stringValue === '-Infinity') {
1629 return -Infinity;
1630 }
1631
1632 return parseInt(stringValue);
1633 }
1634 else if (type === 'string') {
1635 return stringValue;
1636 }
1637 else if (type === 'any') {
1638 return stringValue;
1639 }
1640 else if (type === 'date') {
1641 return new Date(stringValue);
1642 }
1643 else if (type === 'function') {
1644 if (typeof window[stringValue] === 'function') {
1645 return window[stringValue];
1646 }
1647 }
1648 else if (type === 'array' || type === 'object') {
1649 try {
1650 const jsonObject = JSON.parse(stringValue);
1651
1652 if (jsonObject) {
1653 return jsonObject;
1654 }
1655 }
1656 catch (er) {
1657 if (window[stringValue] && (typeof window[stringValue] === 'object')) {
1658 return window[stringValue];
1659 }
1660 else if (type === 'array' && stringValue.indexOf('[') >= 0) {
1661 if (stringValue.indexOf('{') >= 0) {
1662 let array = stringValue.replace(/{/ig, '').replace('[', '').replace(']', '').replace(/'/ig, '').replace(/"/ig, '').trim();
1663
1664 array = array.split('},');
1665
1666 for (let i = 0; i < array.length; i++) {
1667 let parsedObject = {
1668 };
1669
1670 let parts = array[i].trim().split(',');
1671
1672 for (let j = 0; j < parts.length; j++) {
1673 const key = parts[j].split(':')[0].trim();
1674 const value = parts[j].split(':')[1].trim();
1675
1676 parsedObject[key] = value;
1677 }
1678
1679 array[i] = parsedObject;
1680 }
1681
1682 return array;
1683 }
1684
1685 const array = stringValue.replace('[', '').replace(']', '').replace(/'/ig, '').replace(/"/ig, '').trim().split(',');
1686
1687 return array;
1688 }
1689 }
1690 }
1691
1692 return undefined;
1693 }
1694
1695 _getDataItem( dataSourceItem, index ) {
1696 const that = this;
1697 const itemObject = {};
1698 const unboundMode = typeof ( that.dataSource ) === 'number' || that.dataSourceLength;
1699
1700 if ( !dataSourceItem ) {
1701 return { $: { id: index, isEmpty: true, index: index } }
1702 }
1703
1704 if ( typeof dataSourceItem === 'string' ) {
1705 dataSourceItem = { '': dataSourceItem };
1706 }
1707
1708 if ( unboundMode ) {
1709 for ( let j = 0; j < that.dataFields.length; j++ ) {
1710 const dataField = that.dataFields ? that.dataFields[ j ] : {};
1711
1712 itemObject[ dataField.name ] = '';
1713 }
1714
1715 itemObject.$ = {};
1716 itemObject.$.id = index;
1717 itemObject.$.index = index;
1718
1719 return itemObject;
1720 }
1721
1722 const dataItem = dataSourceItem;
1723
1724 if ( dataItem.expanded !== undefined ) {
1725 itemObject.expanded = dataItem.expanded;
1726
1727 if ( dataItem.expanded === 'true' || dataItem.expanded === true || dataItem.expanded === 1 ) {
1728 itemObject.expanded = true;
1729 }
1730 else {
1731 itemObject.expanded = false;
1732 }
1733 }
1734
1735 if ( that.childrenDataField ) {
1736 if ( dataItem[ that.childrenDataField ] !== undefined ) {
1737 itemObject.children = dataItem[ that.childrenDataField ];
1738 }
1739 }
1740 else {
1741 if ( dataItem.children !== undefined ) {
1742 itemObject.children = dataItem.children;
1743 }
1744 else if ( dataItem.items !== undefined ) {
1745 itemObject.children = dataItem.items;
1746 }
1747 }
1748 if ( dataItem.leaf !== undefined ) {
1749 itemObject.leaf = dataItem.leaf;
1750 }
1751
1752 if ( dataItem.level !== undefined ) {
1753 itemObject.level = dataItem.level;
1754 }
1755
1756 if ( that.keyDataField ) {
1757 if ( dataItem[ that.keyDataField ] !== undefined ) {
1758 itemObject[ that.keyDataField ] = dataItem[ that.keyDataField ];
1759 }
1760 }
1761
1762 if ( that.parentDataField ) {
1763 if ( dataItem[ that.parentDataField ] !== undefined ) {
1764 itemObject[ that.parentDataField ] = dataItem[ that.parentDataField ];
1765 }
1766 }
1767
1768 if ( that.dataFields.length === 0 ) {
1769 const names = Object.getOwnPropertyNames( dataSourceItem );
1770
1771 for ( let i = 0; i < names.length; i++ ) {
1772 if ( names[ i ] === '$' ) {
1773 continue;
1774 }
1775
1776 that.dataFields.push( { name: names[ i ], dataType: 'string' } );
1777 }
1778 }
1779
1780 for ( let j = 0; j < that.dataFields.length; j++ ) {
1781 const dataField = that.dataFields ? that.dataFields[ j ] : {};
1782 let value = '';
1783
1784 dataField.dataType = dataField.type;
1785
1786 if ( undefined === dataField || dataField === null ) {
1787 continue;
1788 }
1789
1790 if ( dataSourceItem.length ) {
1791 value = dataSourceItem[ j ];
1792 }
1793
1794 if ( dataField.map ) {
1795 let splitMap = dataField.map.split( that.mapChar );
1796
1797 if ( splitMap.length > 0 ) {
1798 let dataMappedItem = dataItem;
1799
1800 for ( let p = 0; p < splitMap.length; p++ ) {
1801 if ( !dataItem ) {
1802 continue;
1803 }
1804
1805 dataMappedItem = dataMappedItem[ splitMap[ p ] ];
1806 }
1807
1808 value = dataMappedItem;
1809 }
1810 else {
1811 value = dataItem[ dataField.map ];
1812 }
1813 }
1814
1815 if ( value !== undefined && value !== null ) {
1816 value = value.toString();
1817 }
1818 else {
1819 if ( value === undefined && value !== null ) {
1820 value = '';
1821 }
1822 }
1823
1824
1825 let isEmptyString = false;
1826 // searches by both selectors when necessary.
1827 if ( value === '' ) {
1828 isEmptyString = true;
1829 value = dataSourceItem[ dataField.name ];
1830
1831 if ( value !== undefined && value !== null ) {
1832 if ( dataField.dataType !== 'array' ) {
1833 if ( dataField.dataType !== 'date' ) {
1834 value = value.toString();
1835 }
1836 }
1837 }
1838 else {
1839 value = '';
1840 }
1841 }
1842
1843 if ( value === '[object Object]' && dataField.map && isEmptyString ) {
1844 value = '';
1845 }
1846
1847 if ( that._cachedValues[ '' + value + '_' + dataField.dataType ] ) {
1848 value = that._cachedValues[ '' + value + '_' + dataField.dataType ];
1849 }
1850 else {
1851 if ( dataField.dataType === 'bool' || dataField.dataType === 'boolean' ) {
1852 if ( value === 'true' || value === '1' ) {
1853 value = true;
1854 }
1855 else if ( value === 'false' || value === '0' ) {
1856 value = false;
1857 }
1858 }
1859 else {
1860 value = that.deserialize( '' + value, dataField.dataType, true );
1861 }
1862
1863 that._cachedValues[ value + '_' + dataField.dataType ] = value;
1864 }
1865
1866 if ( dataField.dataType !== 'string' && dataField.dataType !== 'boolean' && dataField.dataType !== 'bool' ) {
1867 if ( isNaN( value ) || value === -Infinity || value === Infinity ) {
1868 value = 0;
1869 }
1870 }
1871
1872 itemObject[ dataField.name ] = value;
1873 }
1874
1875 let itemObjectId = index;
1876
1877 if ( that.id ) {
1878 itemObjectId = dataItem[ that.id ];
1879 if ( typeof ( itemObjectId ) === 'object' ) {
1880 itemObjectId = index;
1881 }
1882 }
1883 else if ( !that.virtualDataSource && that.dataItemById && that.dataItemById[ itemObjectId ] ) {
1884 itemObjectId = that.length;
1885 }
1886
1887 if ( !itemObject.$ ) {
1888 itemObject.$ = {};
1889 }
1890
1891 itemObject.$.id = itemObjectId;
1892 itemObject.$.index = index;
1893
1894 return new Object( itemObject );
1895 }
1896
1897 _bindToArray() {
1898 const that = this;
1899
1900 const unboundMode = typeof ( that.dataSource ) === 'number' || that.dataSourceLength;
1901 const dataArray = [];
1902
1903 that.boundSource.canNotify = false;
1904
1905 for ( let i = 0; i < that.length; i++ ) {
1906 const dataSourceItem = unboundMode ? {} : that.dataSource[ i ];
1907 const itemObject = that._getDataItem( dataSourceItem, i );
1908
1909 dataArray.push( itemObject );
1910 }
1911
1912 if ( unboundMode && that.dataSourceLength && that.dataSource.length > 0 ) {
1913 for ( let i = 0; i < that.dataSource.length; i++ ) {
1914 const cell = that.dataSource[ i ].cell;
1915 const value = that.dataSource[ i ].value;
1916
1917 const row = cell.replace( /[^0-9]/g, '' );
1918 const dataField = cell.replace( /[0-9]/g, '' );
1919
1920 dataArray[ row - 1 ][ dataField ] = value;
1921 }
1922 }
1923
1924 that.boundSource = dataArray;
1925
1926 for ( let i = 0; i < that.length; i++ ) {
1927 that[ i ] = that.boundSource[ i ];
1928 that.dataItemById[ that[ i ].$.id ] = that[ i ];
1929 }
1930
1931 that.boundSource.canNotify = true;
1932 }
1933
1934 _bindToJSON() {
1935 const that = this;
1936
1937 const dataArray = [];
1938
1939 const dataEntries = Object.entries( that.dataSource );
1940
1941 that.boundSource.canNotify = false;
1942
1943 for ( let i = 0; i < dataEntries.length; i++ ) {
1944 const dataSourceItem = dataEntries[ i ];
1945 const itemObject = that._getDataItem( dataSourceItem, i );
1946
1947 dataArray.push( itemObject );
1948 }
1949
1950 that.boundSource = false === that.observable ? dataArray : new JQX.ObservableArray( dataArray );
1951
1952 for ( let i = 0; i < that.length; i++ ) {
1953 that[ i ] = that.boundSource[ i ];
1954 that.dataItemById[ that[ i ].$.id ] = that[ i ];
1955 }
1956
1957 that.boundSource.canNotify = true;
1958 }
1959
1960 sortBy( dataField, dataType, orderBy ) {
1961 const that = this;
1962
1963 if ( !dataType ) {
1964 for ( let i = 0; i < that.dataFields.length; i++ ) {
1965 const field = that.dataFields[ i ];
1966
1967 if ( field.name === dataField ) {
1968 dataType = field.dataType;
1969 break;
1970 }
1971 }
1972 }
1973
1974 if ( that.boundHierarchy ) {
1975 if ( ( !dataField || dataField.length === 0 ) && that.groupBy.length > 0 ) {
1976 that.refreshHierarchy();
1977 return;
1978 }
1979
1980 const sortBy = function ( hierarchy ) {
1981 that._sort( hierarchy, dataField, orderBy, dataType );
1982
1983 for ( let i = 0; i < hierarchy.length; i++ ) {
1984 const item = hierarchy[ i ];
1985
1986 if ( item[ 'children' ] ) {
1987 sortBy( item[ 'children' ], dataField, orderBy, dataType );
1988 }
1989 }
1990 }
1991
1992 sortBy( that.boundHierarchy );
1993 }
1994 else {
1995 that._sort( that.boundSource, dataField, orderBy, dataType );
1996 }
1997 }
1998
1999 _createFilter( dataType, filterExpressions ) {
2000 const filterOperators = {
2001 '=': 'EQUAL',
2002 '<>': 'NOT_EQUAL',
2003 '<': 'LESS_THAN',
2004 '>': 'GREATER_THAN',
2005 '<=': 'LESS_THAN_OR_EQUAL',
2006 '>=': 'GREATER_THAN_OR_EQUAL',
2007 'equal': 'EQUAL',
2008 'not equal': 'NOT_EQUAL',
2009 'less than': 'LESS_THAN',
2010 'greater than': 'GREATER_THAN',
2011 'greater than or equal': 'GREATER_THAN_OR_EQUAL',
2012 'less than or equal': 'LESS_THAN_OR_EQUAL',
2013 'starts with': 'STARTS_WITH',
2014 'ends with': 'ENDS_WITH',
2015 'null': 'null',
2016 '': 'EMPTY',
2017 'isblank': 'EMPTY',
2018 'isnotblank': 'NOT_EMPTY',
2019 'contains': 'CONTAINS',
2020 'notcontains': 'DOES_NOT_CONTAIN',
2021 'startswith': 'STARTS_WITH',
2022 'endswith': 'ENDS_WITH',
2023 'NULL': 'NULL',
2024 'NOT_NULL': 'NOT_NULL'
2025 };
2026
2027 let filterExpressionsArray = [];
2028
2029 for ( let i = 0; i < filterExpressions.length; i++ ) {
2030 const filterExpression = filterExpressions[ i ];
2031
2032 const filterExpressionParts = filterExpression.indexOf( '"' ) === -1 ? filterExpression.split( ' ' ) : filterExpression.split( '"' );
2033 let filter = [];
2034
2035 for ( let j = 0; j < filterExpressionParts.length; j++ ) {
2036 const part = filterExpressionParts[ j ];
2037
2038 if ( part !== '' ) {
2039 filter.push( part.trim() );
2040 }
2041 }
2042
2043 filterExpressionsArray.push( filter );
2044 }
2045
2046 const filterGroup = new JQX.FilterGroup();
2047 const filterGroupOperators = [];
2048 const filterSubGroups = [];
2049
2050 for ( let i = 0; i < filterExpressionsArray.length; i++ ) {
2051 const filterExpression = filterExpressionsArray[ i ];
2052
2053
2054 if ( filterExpression.length > 1 ) {
2055 const filterSubGroup = new JQX.FilterGroup();
2056
2057 let operator = 'and';
2058 let filterExpressionPartsCounter = 0;
2059
2060 for ( let j = 0; j < filterExpression.length; j++ ) {
2061 const value = filterExpression[ j ];
2062
2063 if ( value === 'and' || value === 'or' ) {
2064 operator = value;
2065 continue;
2066 }
2067
2068 filterExpressionPartsCounter++;
2069
2070 if ( filterExpressionPartsCounter === 2 ) {
2071 const filter = filterSubGroup.createFilter( dataType, value, filterOperators[ filterExpression[ j - 1 ] ] );
2072
2073 filterExpressionPartsCounter = 0;
2074
2075 if ( operator ) {
2076 filterSubGroup.addFilter( operator, filter );
2077 }
2078 }
2079 }
2080
2081 filterSubGroups.push( filterSubGroup );
2082 }
2083 else {
2084 const filterGroupOperator = filterExpression[ 0 ];
2085
2086 if ( filterGroupOperator !== 'and' && filterGroupOperator !== 'or' ) {
2087 throw new Error( 'Filter Exprresion expects "AND" or "OR", but the token is: ' + filterGroupOperator );
2088 }
2089
2090 filterGroupOperators.push( filterGroupOperator );
2091 }
2092 }
2093
2094 let operatorsCounter = 0;
2095
2096 if ( filterSubGroups.length === 1 ) {
2097 return filterSubGroups[ 0 ];
2098 }
2099
2100 for ( let i = 0; i < filterSubGroups.length; i++ ) {
2101 let operator = filterGroupOperators[ operatorsCounter ];
2102
2103 if ( ( i + 1 ) % 2 === 0 ) {
2104 operatorsCounter++;
2105 }
2106
2107 if ( !operator ) {
2108 operator = 'and';
2109 }
2110
2111 filterGroup.addFilter( operator, filterSubGroups[ i ] );
2112 }
2113
2114 return filterGroup;
2115 }
2116
2117 filterBy( dataField, ...filterExpressions ) {
2118 const that = this;
2119
2120
2121 const dataType = ( () => {
2122 for ( let i = 0; i < that.dataFields.length; i++ ) {
2123 const field = that.dataFields[ i ];
2124
2125 if ( field.name === dataField ) {
2126 return field.dataType;
2127 }
2128 }
2129 } )();
2130
2131
2132 const filterGroup = that._createFilter( dataType, filterExpressions );
2133
2134 let filteredData = that.boundSource.filter( ( value ) => {
2135 const evaluation = filterGroup.evaluate( value[ dataField ] );
2136
2137 return evaluation;
2138 } );
2139
2140 return filteredData;
2141 }
2142
2143 _filter( filters, operator = 'and' ) {
2144 const that = this;
2145 const filterGroups = [];
2146 const dataFields = [];
2147
2148 if ( filters.length === 0 ) {
2149 that.clearFilter();
2150 return;
2151 }
2152
2153 const dataType = ( dataField ) => {
2154 for ( let i = 0; i < that.dataFields.length; i++ ) {
2155 const field = that.dataFields[ i ];
2156
2157 if ( field.name === dataField ) {
2158 return field.dataType;
2159 }
2160 }
2161 };
2162 let defaultResult, operatorSpecificEval;
2163
2164 if ( operator === 'and' ) {
2165 defaultResult = true;
2166 operatorSpecificEval = function ( result, filterGroup, row ) {
2167 return result && filterGroup.evaluate( row[ filterGroup.dataField ] );
2168 };
2169 }
2170 else {
2171 defaultResult = false;
2172 operatorSpecificEval = function ( result, filterGroup, row ) {
2173 return result || filterGroup.evaluate( row[ filterGroup.dataField ] );
2174 };
2175 }
2176
2177 for ( let i = 0; i < filters.length; i++ ) {
2178 const filter = filters[ i ];
2179 const dataField = filter[ 0 ];
2180 let filterGroup = null;
2181
2182 if ( filter[ 1 ] instanceof JQX.FilterGroup ) {
2183 filterGroup = filter[ 1 ];
2184 }
2185 else {
2186 filterGroup = that._createFilter( dataType( dataField ), filter.splice( 1 ) );
2187 }
2188
2189 if ( filterGroup ) {
2190 dataFields.push( dataField );
2191 filterGroup.dataField = dataField;
2192 filterGroups.push( filterGroup );
2193 }
2194 }
2195
2196 if ( that.boundHierarchy ) {
2197 const filter = function ( row ) {
2198 let result = defaultResult;
2199
2200 for ( let j = 0; j < filterGroups.length; j++ ) {
2201 const filterGroup = filterGroups[ j ];
2202
2203 result = operatorSpecificEval( result, filterGroup, row );
2204 }
2205
2206 row.$.filtered = result;
2207
2208 return result;
2209 }
2210
2211 const filterBy = function ( hierarchy, parentItem, root ) {
2212 let filteredCount = 0;
2213
2214 for ( let i = 0; i < hierarchy.length; i++ ) {
2215 const item = hierarchy[ i ];
2216
2217 filter( item );
2218
2219 if ( item.$.filtered ) {
2220 filteredCount++;
2221 }
2222
2223 if ( item[ 'children' ] ) {
2224 filterBy( item[ 'children' ], item, parentItem );
2225 }
2226 }
2227
2228 if ( filteredCount > 0 && that.groupBy.length > 0 && parentItem ) {
2229 parentItem.$.filtered = true;
2230
2231 if ( root && !root.$.filtered ) {
2232 root.$.filtered = true;
2233 }
2234 }
2235 else {
2236 if ( filteredCount > 0 && filteredCount !== hierarchy.length && parentItem ) {
2237 parentItem.$.filtered = null;
2238
2239 if ( root && !root.$.filtered ) {
2240 root.$.filtered = null;
2241 }
2242 }
2243 }
2244 }
2245
2246 filterBy( that.boundHierarchy, null, null );
2247 }
2248 else {
2249 for ( let i = 0; i < that.boundSource.length; i++ ) {
2250 const row = that.boundSource[ i ];
2251
2252 let result = defaultResult;
2253
2254 for ( let j = 0; j < filterGroups.length; j++ ) {
2255 const filterGroup = filterGroups[ j ];
2256
2257 result = operatorSpecificEval( result, filterGroup, row );
2258 }
2259
2260 row.$.filtered = result;
2261 }
2262 }
2263
2264 if ( that.onFilter ) {
2265 that.onFilter()
2266 }
2267 }
2268
2269 clearGroup() {
2270 const that = this;
2271
2272 that.groupBy = [];
2273 that.boundHierarchy = null;
2274 that.refreshHierarchy();
2275
2276 if ( that.onGroup ) {
2277 that.onGroup()
2278 }
2279 }
2280
2281 clearFilter() {
2282 const that = this;
2283
2284 for ( let i = 0; i < that.boundSource.length; i++ ) {
2285 const row = that.boundSource[ i ];
2286
2287 row.$.filtered = true;
2288 }
2289
2290 if ( that.boundHierarchy ) {
2291 const filterBy = function ( hierarchy, parentItem, root ) {
2292 //let filteredCount = 0;
2293
2294 for ( let i = 0; i < hierarchy.length; i++ ) {
2295 const item = hierarchy[ i ];
2296
2297 item.$.filtered = true;
2298
2299 if ( item.$.filtered ) {
2300 //filteredCount++;
2301 }
2302
2303 if ( item[ 'children' ] ) {
2304 filterBy( item[ 'children' ], item, parentItem );
2305 }
2306 }
2307
2308 if ( parentItem ) {
2309 parentItem.$.filtered = true;
2310
2311 if ( root && !root.$.filtered ) {
2312 root.$.filtered = true;
2313 }
2314 }
2315 }
2316
2317 filterBy( that.boundHierarchy, null, null );
2318 }
2319
2320 if ( that.onFilter ) {
2321 that.onFilter()
2322 }
2323 }
2324
2325 clearSort() {
2326 const that = this;
2327
2328 that._sort( that.boundSource, [], [], [] );
2329 }
2330
2331 _sort( dataSource, sortColumns, directions, dataTypes, customSortingCallback ) {
2332 const that = this;
2333
2334 let isObservableArray = false;
2335
2336 if ( dataSource.length === 0 ) {
2337 return;
2338 }
2339
2340 if ( dataSource && dataSource.constructor && dataSource instanceof JQX.ObservableArray ) {
2341 isObservableArray = true;
2342 }
2343
2344 if ( !dataSource || !( Array.isArray( dataSource ) ) || dataSource.length === 0 ||
2345 !sortColumns || Array.isArray( sortColumns ) && sortColumns.length === 0 ) {
2346 if ( !isObservableArray && !that.boundHierarchy ) {
2347 throw new Error( 'sort: Missing or Invalid arguments!' );
2348 }
2349 }
2350
2351 if ( typeof sortColumns === 'string' ) {
2352 sortColumns = [ sortColumns ];
2353 }
2354
2355 const directionCoefficients = [],
2356 compareFunctions = [];
2357
2358 if ( directions === undefined ) {
2359 directions = [];
2360 }
2361
2362 const getCompareFunction = function ( a, knownDataType ) {
2363 // gets data type of column (not necessary if the Grid provides this information)
2364 const dataType = knownDataType || typeof a;
2365 let compareFunction;
2366
2367 switch ( dataType ) {
2368 case 'string':
2369 compareFunction = new Intl.Collator().compare;
2370 break;
2371 case 'number':
2372 compareFunction = function ( a, b ) {
2373 return a - b;
2374 };
2375 break;
2376 case 'boolean':
2377 case 'bool':
2378 compareFunction = function ( a, b ) {
2379 if ( a === b ) {
2380 return 0;
2381 }
2382 else if ( a === false ) {
2383 return -1;
2384 }
2385 else {
2386 return 1;
2387 }
2388 };
2389 break;
2390 case 'date':
2391 case 'time':
2392 case 'dateTime':
2393 if ( a instanceof Date ) {
2394 compareFunction = function ( a, b ) {
2395 return a.getTime() - b.getTime();
2396 };
2397 }
2398 else if ( a instanceof JQX.Utilities.DateTime ||
2399 a instanceof JQX.Utilities.BigNumber ) {
2400 compareFunction = function ( a, b ) {
2401 return a.compare( b );
2402 };
2403 }
2404 break;
2405 case 'object':
2406 if ( a instanceof Date ) {
2407 compareFunction = function ( a, b ) {
2408 return a.getTime() - b.getTime();
2409 };
2410 }
2411 else if ( a instanceof JQX.Utilities.DateTime ||
2412 a instanceof JQX.Utilities.BigNumber ) {
2413 compareFunction = function ( a, b ) {
2414 return a.compare( b );
2415 };
2416 }
2417 else if ( a instanceof JQX.Utilities.Complex || ( window.NIComplex && a instanceof window.NIComplex ) ) {
2418 const complexNumericProcessor = new JQX.Utilities.ComplexNumericProcessor();
2419
2420 compareFunction = function ( a, b ) {
2421 return complexNumericProcessor.compareComplexNumbers( a, b );
2422 }
2423 }
2424
2425 break;
2426 }
2427
2428 return compareFunction;
2429 }
2430
2431 for ( let i = 0; i < sortColumns.length; i++ ) {
2432 if ( directions[ i ] === undefined || directions[ i ] === 'asc' || directions[ i ] === 'ascending' ) {
2433 directionCoefficients[ i ] = 1;
2434 }
2435 else {
2436 directionCoefficients[ i ] = -1;
2437 }
2438
2439 const value = dataSource[ 0 ][ sortColumns[ i ] ];
2440
2441 compareFunctions[ i ] = getCompareFunction( value, dataTypes[ i ] );
2442 }
2443
2444 if ( customSortingCallback ) {
2445 customSortingCallback( dataSource, sortColumns, directions, compareFunctions );
2446 return;
2447 }
2448
2449 dataSource.sort( function ( a, b ) {
2450 for ( let i = 0; i < sortColumns.length; i++ ) {
2451 const result = compareFunctions[ i ]( a[ sortColumns[ i ] ], b[ sortColumns[ i ] ] );
2452
2453 if ( result === 0 ) {
2454 if ( sortColumns[ i + 1 ] ) {
2455 continue;
2456 }
2457 else if ( a._index !== undefined ) {
2458 // makes sorting stable
2459 return ( a._index - b._index ) * directionCoefficients[ i ];
2460 }
2461
2462 return 0;
2463 }
2464
2465 return result * directionCoefficients[ i ];
2466 }
2467
2468 if ( sortColumns.length === 0 ) {
2469 if ( a.$.index < b.$.index ) {
2470 return -1;
2471 }
2472
2473 if ( a.$.index > b.$.index ) {
2474 return 1;
2475 }
2476
2477 return 0;
2478
2479 }
2480 } );
2481
2482 for ( let i = 0; i < dataSource.length; i++ ) {
2483 that[ i ] = dataSource[ i ];
2484 }
2485 }
2486
2487 static Filter( dataSource, filterColumns, filterGroups, customFilteringCallback, operator = 'and' ) {
2488 let defaultResult, operatorSpecificEval;
2489
2490 if ( operator === 'and' ) {
2491 defaultResult = true;
2492 operatorSpecificEval = function ( result, dataItem, filterColumn, filterGroup ) {
2493 if ( customFilteringCallback ) {
2494 return result && customFilteringCallback( dataItem, filterColumn, filterGroup );
2495 }
2496
2497 return result && filterGroup.evaluate( dataItem[ filterColumn ] );
2498 };
2499 }
2500 else {
2501 defaultResult = false;
2502 operatorSpecificEval = function ( result, dataItem, filterColumn, filterGroup ) {
2503 if ( customFilteringCallback ) {
2504 return result || customFilteringCallback( dataItem, filterColumn, filterGroup );
2505 }
2506
2507 return result || filterGroup.evaluate( dataItem[ filterColumn ] );
2508 };
2509 }
2510
2511 const filteredData = dataSource.filter( ( dataItem ) => {
2512 let result = defaultResult;
2513
2514 for ( let i = 0; i < filterGroups.length; i++ ) {
2515 const filterGroup = filterGroups[ i ];
2516 const filterColumn = filterColumns[ i ];
2517
2518 result = operatorSpecificEval( result, dataItem, filterColumn, filterGroup );
2519 }
2520
2521 return result;
2522 } );
2523
2524 return filteredData;
2525 }
2526
2527 filter( filterColumns, filterGroups, customFilteringCallback ) {
2528 JQX.ExcelAdapter.Filter( this.boundSource, filterColumns, filterGroups, customFilteringCallback );
2529 }
2530
2531 sort( sortColumns, directions, customSortingCallback ) {
2532 JQX.ExcelAdapter.Sort( this.boundSource, sortColumns, directions, customSortingCallback );
2533 }
2534
2535 static Sort( dataSource, sortColumns, directions, customSortingCallback ) {
2536 const getCompareFunction = function ( a ) {
2537 // gets data type of column (not necessary if the Grid provides this information)
2538 const dataType = typeof a;
2539 let compareFunction;
2540
2541 switch ( dataType ) {
2542 case 'string':
2543 compareFunction = new Intl.Collator().compare;
2544 break;
2545 case 'number':
2546 compareFunction = function ( a, b ) {
2547 return a - b;
2548 };
2549 break;
2550 case 'boolean':
2551 compareFunction = function ( a, b ) {
2552 if ( a === b ) {
2553 return 0;
2554 }
2555 else if ( a === false ) {
2556 return -1;
2557 }
2558 else {
2559 return 1;
2560 }
2561 };
2562 break;
2563 case 'object':
2564 if ( a instanceof Date ) {
2565 compareFunction = function ( a, b ) {
2566 return a.getTime() - b.getTime();
2567 };
2568 }
2569 else if ( a instanceof JQX.Utilities.DateTime ||
2570 a instanceof BigNumberNG ) {
2571 compareFunction = function ( a, b ) {
2572 return a.compare( b );
2573 };
2574 }
2575 else if ( a instanceof JQX.Utilities.Complex || ( window.NIComplex && a instanceof window.NIComplex ) ) {
2576 const complexNumericProcessor = new JQX.Utilities.ComplexNumericProcessor();
2577
2578 compareFunction = function ( a, b ) {
2579 return complexNumericProcessor.compareComplexNumbers( a, b );
2580 }
2581 }
2582
2583 break;
2584 }
2585
2586 return compareFunction;
2587 }
2588
2589 if ( !dataSource || !( Array.isArray( dataSource ) ) || dataSource.length === 0 ||
2590 !sortColumns || Array.isArray( sortColumns ) && sortColumns.length === 0 ) {
2591 return;
2592 }
2593
2594 if ( typeof sortColumns === 'string' ) {
2595 sortColumns = [ sortColumns ];
2596 }
2597
2598 const directionCoefficients = [],
2599 compareFunctions = [];
2600
2601 if ( directions === undefined ) {
2602 directions = [];
2603 }
2604
2605 for ( let i = 0; i < sortColumns.length; i++ ) {
2606 if ( directions[ i ] === undefined || directions[ i ] === 'asc' || directions[ i ] === 'ascending' ) {
2607 directionCoefficients[ i ] = 1;
2608 }
2609 else {
2610 directionCoefficients[ i ] = -1;
2611 }
2612
2613 compareFunctions[ i ] = getCompareFunction( dataSource[ 0 ][ sortColumns[ i ] ] );
2614 }
2615
2616 if ( customSortingCallback ) {
2617 customSortingCallback( dataSource, sortColumns, directions, compareFunctions );
2618 return;
2619 }
2620
2621 const sortedData = dataSource.slice( 0 );
2622
2623 sortedData.sort( function ( a, b ) {
2624 for ( let i = 0; i < sortColumns.length; i++ ) {
2625 const result = compareFunctions[ i ]( a[ sortColumns[ i ] ], b[ sortColumns[ i ] ] );
2626
2627 if ( result === 0 ) {
2628 if ( sortColumns[ i + 1 ] ) {
2629 continue;
2630 }
2631 else if ( a._index !== undefined ) {
2632 // makes sorting stable
2633 return ( a._index - b._index ) * directionCoefficients[ i ];
2634 }
2635
2636 return 0;
2637 }
2638
2639 return result * directionCoefficients[ i ];
2640 }
2641 } );
2642
2643 return sortedData;
2644 }
2645 }
2646
2647 window.jqxDataSource = DataAdapter;
2648
2649 class Ajax {
2650 constructor ( config, callback ) {
2651 const that = this;
2652
2653 that.config = config;
2654 that.callback = callback;
2655
2656 if ( config.autoFetch === false ) {
2657 return;
2658 }
2659
2660 that.call( config );
2661 }
2662
2663 call( config ) {
2664 const that = this;
2665
2666 if ( !config ) {
2667 config = that.config;
2668 }
2669
2670 let method = 'GET',
2671 url = config.url,
2672 body = null,
2673 async = true;
2674
2675 if ( config.type ) {
2676 method = config.type;
2677 }
2678
2679 if ( config.data ) {
2680 if ( method === 'GET' ) {
2681 url += '?';
2682
2683 for ( let prop in config.data ) {
2684 if ( config.data.hasOwnProperty( prop ) ) {
2685 url += encodeURI( prop + '=' + config.data[ prop ] + '&' );
2686 }
2687 }
2688
2689 if ( url.charAt( url.length - 1 ) === '&' ) {
2690 url = url.slice( 0, url.length - 1 );
2691 }
2692 }
2693 else if ( method === 'POST' ) {
2694 body = JSON.stringify( config.data );
2695 }
2696 }
2697
2698 if ( config && config.async === false && config.dataSourceType !== 'xlsx' ) {
2699 async = false;
2700 }
2701
2702 if ( window.fetch !== undefined && async ) {
2703 that.ajaxFetch( config, method, url, body );
2704 }
2705 else {
2706 that.ajaxXMLHttpRequest( config, method, url, body, async );
2707 }
2708 }
2709
2710 ajaxFetch( config, method, url, body ) {
2711 // prepare fetch config
2712 const that = this;
2713 const fetchInit = { method: method };
2714 let parseMethod;
2715
2716 switch ( config.dataSourceType ) {
2717 case 'json':
2718 parseMethod = 'json';
2719 break;
2720 case 'xlsx':
2721 parseMethod = 'arrayBuffer';
2722 break;
2723 default:
2724 parseMethod = 'text';
2725 }
2726
2727 if ( config ) {
2728 if ( config.contentType ) {
2729 fetchInit.headers = new Headers( {
2730 'Content-Type': config.contentType
2731 } );
2732 }
2733 }
2734
2735 if ( body !== null ) {
2736 fetchInit.body = body;
2737 }
2738
2739 let status, fetchTimeout, timeouted;
2740
2741 if ( config.timeout ) {
2742 fetchTimeout = setTimeout( function () {
2743 timeouted = true;
2744 }, config.timeout );
2745 }
2746
2747 if ( config.beforeSend ) {
2748 const beforeSendResult = config.beforeSend( fetchInit, config );
2749
2750 if ( beforeSendResult === false ) {
2751 return;
2752 }
2753 }
2754
2755 // fetch resource
2756 fetch( url, fetchInit )
2757 .then( function ( response ) {
2758 if ( timeouted ) {
2759 status = 408;
2760 throw new Error( 'timeout' );
2761 }
2762
2763 if ( fetchTimeout ) {
2764 clearTimeout( fetchTimeout );
2765 }
2766
2767 status = response.status;
2768
2769 if ( !response.ok ) {
2770 throw new Error( response.statusText );
2771 }
2772
2773 return response[ parseMethod ]();
2774 } )
2775 .then( function ( data ) {
2776 if ( parseMethod === 'arrayBuffer' ) {
2777 return JSZip.loadAsync( data ).then( function ( zipData ) {
2778 // "data" represents the whole XLSX/ZIP file
2779 return zipData.files[ 'xl/worksheets/sheet1.xml' ].async( 'text' ).then( function ( sheet1 ) {
2780 return zipData.files[ 'xl/sharedStrings.xml' ].async( 'text' ).then( function ( sharedStrings ) {
2781 const parsedData = that.parseXLSXData( sheet1, sharedStrings );
2782
2783 that.ajaxComplete( config, parsedData, status );
2784 } );
2785 } );
2786 } );
2787 }
2788 else {
2789 that.ajaxComplete( config, data, status );
2790 }
2791 } )
2792 .catch( function ( error ) {
2793 if ( error.message === 'JSZip is not defined' ) {
2794 error.message = 'JSZip is not defined. Please include a reference to JSZip to be able to load data from XLSX files.';
2795 }
2796
2797 if ( config && config.loadError ) {
2798 config.loadError( status, error );
2799 }
2800
2801 if ( that.callback ) {
2802 that.callback( error, status );
2803 }
2804 } );
2805 }
2806
2807 ajaxXMLHttpRequest( config, method, url, body, async ) {
2808 const request = new XMLHttpRequest();
2809 const that = this;
2810
2811 request.open( method, url, async );
2812
2813 request.ontimeout = function () {
2814 if ( config && config.loadError ) {
2815 config.loadError( 408, 'timeout' );
2816 }
2817 };
2818
2819 request.onload = function () {
2820 if ( request.readyState === 4 ) {
2821 const status = request.status;
2822 let data = request.response;
2823
2824 if ( status >= 200 && status <= 299 ) {
2825 if ( config.dataSourceType === 'json' ) {
2826 data = JSON.parse( data );
2827 }
2828
2829 that.ajaxComplete( config, data, status );
2830 }
2831 else if ( config && config.loadError ) {
2832 config.loadError( status, data );
2833 }
2834 }
2835 };
2836
2837 request.onerror = function () {
2838 if ( config && config.loadError ) {
2839 config.loadError( request.status, request.response );
2840 }
2841 };
2842
2843 if ( config && config.contentType ) {
2844 request.setRequestHeader( 'Content-Type', config.contentType );
2845 }
2846
2847 if ( async && config.timeout ) {
2848 request.timeout = config.timeout;
2849 }
2850
2851 if ( config.beforeSend ) {
2852 const beforeSendResult = config.beforeSend( request, config );
2853
2854 if ( beforeSendResult === false ) {
2855 return;
2856 }
2857 }
2858
2859 request.send( body );
2860 }
2861
2862 ajaxComplete( config, data, status ) {
2863 if ( !config ) {
2864 return;
2865 }
2866
2867 if ( config.beforeLoadComplete ) {
2868 const processedData = config.beforeLoadComplete( data );
2869
2870 if ( processedData ) {
2871 data = processedData;
2872 }
2873 }
2874
2875 if ( config.loadComplete ) {
2876 config.loadComplete( data, status );
2877 }
2878
2879 if ( this.callback ) {
2880 this.callback( data, status );
2881 }
2882 }
2883
2884 parseXLSXData( sheet1, sharedStrings ) {
2885 const parser = new DOMParser(),
2886 sharedStringsDocument = parser.parseFromString( sharedStrings, 'text/xml' ),
2887 sharedStringsContainers = Array.from( sharedStringsDocument.getElementsByTagName( 'si' ) ),
2888 sharedStringsCollection = [],
2889 sheet1Document = parser.parseFromString( sheet1, 'text/xml' ),
2890 rows = Array.from( sheet1Document.getElementsByTagName( 'row' ) ),
2891 parsedData = [];
2892
2893 sharedStringsContainers.forEach( function ( si ) {
2894 let texts = si.getElementsByTagName( 't' );
2895
2896 if ( texts.length === 1 ) {
2897 sharedStringsCollection.push( texts[ 0 ].innerHTML );
2898 }
2899 else {
2900 let text = '';
2901
2902 texts = Array.from( texts );
2903 texts.forEach( function ( t ) {
2904 text += t.innerHTML;
2905 } );
2906 sharedStringsCollection.push( text );
2907 }
2908 } );
2909
2910 rows.forEach( function ( row ) {
2911 const rowObject = {},
2912 cells = Array.from( row.getElementsByTagName( 'c' ) );
2913
2914 cells.forEach( function ( cell/*, index*/ ) {
2915 const column = cell.getAttribute( 'r' ).match( /\D+/ )[ 0 ],
2916 type = cell.getAttribute( 't' ),
2917 xmlValue = cell.getElementsByTagName( 'v' )[ 0 ].innerHTML;
2918 let value;
2919
2920 switch ( type ) {
2921 case 's':
2922 // string
2923 value = sharedStringsCollection[ parseFloat( xmlValue ) ];
2924 break;
2925 case 'b':
2926 // boolean
2927 value = parseFloat( xmlValue ) === 1;
2928 break;
2929 default:
2930 // number or date
2931 value = parseFloat( xmlValue );
2932 }
2933
2934 rowObject[ column ] = value;
2935 } );
2936
2937 parsedData.push( rowObject );
2938 } );
2939
2940 return parsedData;
2941 }
2942 }
2943 if ($.jqx && $.jqx.dataAdapter) {
2944 $.jqx.dataAdapter.Importer = DataAdapter;
2945 }
2946 })(jqxBaseFramework);

mercurial