www/jqwidgets/jqximport.js

changeset 733
67bf19c50fcc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/www/jqwidgets/jqximport.js	Mon Apr 12 16:03:38 2021 +0200
@@ -0,0 +1,2946 @@
+
+/* tslint:disable */
+/* eslint-disable */
+(function ($) {
+    class DataAdapter {
+    constructor ( config ) {
+        if ( !config ) {
+            config = {};
+        }
+
+        const that = Object.assign( this, config );
+
+        const generateKey = function () {
+            const S4 = function () {
+                return ( ( ( 1 + Math.random() ) * 0x10000 ) | 0 ).toString( 16 ).substring( 1 );
+            };
+            return S4();
+        };
+
+        that.key = generateKey();
+
+        that.boundSource = [];
+        that.dataItemById = [];
+
+        if ( that.allowAdd === undefined ) {
+            that.allowAdd = true;
+        }
+
+        if ( that.allowRemove === undefined ) {
+            that.allowRemove = true;
+        }
+
+        if ( that.allowUpdate === undefined ) {
+            that.allowUpdate = true;
+        }
+
+        if ( config.observable === undefined ) {
+            that.observable = true;
+        }
+
+
+        if ( !config.dataSource ) {
+            that.dataSource = [];
+        }
+
+        if ( !config.dataFields ) {
+            that.dataFields = [];
+        }
+        else {
+            /* if (config.dataSource && config.dataSource.length > 0) {
+                 const keys = Object.keys(config.dataSource[0]);
+
+                 //     that.dataFields = [];
+
+                 for (let i = 0; i < keys.length; i++) {
+
+                 }
+             }
+             */
+        }
+
+        if ( !config.dataSourceType ) {
+            that.dataSourceType = 'array';
+        }
+
+        if ( !config.id ) {
+            that.id = null;
+        }
+
+        if ( !config.autoFetch ) {
+            that.autoFetch = true;
+        }
+
+        if ( config.dataFields ) {
+            that.dataFields = config.dataFields;
+        }
+
+        Object.defineProperty( that, 'groupBy', {
+            configurable: false,
+            enumerable: true,
+            get() {
+                if ( !that._groupBy ) {
+                    return [];
+                }
+
+                return that._groupBy;
+            },
+            set( value ) {
+                const updateGrouping = () => {
+                    that.boundHierarchy = null;
+                    that.refreshHierarchy();
+
+                    if ( that.onGroup ) {
+                        that.onGroup();
+                    }
+                }
+
+                that._groupBy = [].concat(value);
+              
+                if ( that.isInitialized ) {
+                    updateGrouping();
+                }
+            }
+        } );
+
+        if ( !config.groupBy ) {
+            that.groupBy = [];
+        }
+        else {
+            if ( config.groupBy.toArray ) {
+                that.groupBy = config.groupBy.toArray();
+            }
+            else {
+                that.groupBy = config.groupBy;
+            }
+        }
+
+        if ( config && config.autoBind !== false ) {
+            that.dataBind();
+        }
+
+        that.isInitialized = true;
+    }
+
+    get dataFields() {
+        const that = this;
+
+        return that._dataFields;
+    }
+
+    set dataFields( value ) {
+        const that = this;
+
+        that._dataFields = that._getDataFieldObjects( value );
+
+        return that._dataFields;
+    }
+
+    _getDataFieldObjects( dataFields ) {
+        //const that = this;
+
+        let dataFieldObjects = [];
+
+        if ( typeof dataFields === 'number' ) {
+            const charCode = 'A'.charCodeAt( 0 );
+            let prefix = '';
+            let index = 0;
+
+            for ( let i = 0; i < dataFields; i++ ) {
+                const letter = String.fromCharCode( charCode + index );
+
+                index++;
+
+                const label = prefix + letter;
+
+                dataFieldObjects.push( { name: label, dataType: 'string' } )
+
+                if ( index >= 26 ) {
+                    index = 0;
+                    prefix += 'A';
+                }
+            }
+        }
+        else if ( dataFields.length > 0 ) {
+            for ( let i = 0; i < dataFields.length; i++ ) {
+                const dataField = dataFields[ i ];
+
+                if ( typeof dataField === 'string' ) {
+                    const dataFieldParts = dataField.split( ':' );
+                    const name = dataFieldParts[ 0 ].trim();
+                    const dataType = dataFieldParts.length > 1 ? dataFieldParts[ 1 ].trim() : 'string';
+
+                    dataFieldObjects.push( { name: name, dataType: dataType } );
+                }
+                else {
+                    dataFieldObjects.push( dataField );
+                }
+            }
+        }
+
+        return dataFieldObjects;
+    }
+
+    get dataSource() {
+        const that = this;
+
+        if ( !that._dataSource ) {
+            that._dataSource = [];
+        }
+
+        return that._dataSource;
+    }
+
+    set dataSource( value ) {
+        const that = this;
+
+        that._dataSource = value;
+
+        if ( that.isInitialized ) {
+            that.boundSource = false === that.observable ? [] : new JQX.ObservableArray();
+            that.dataItemById = [];
+            that.bindingCompleted = false;
+            that.dataBind();
+        }
+    }
+
+    get canNotify() {
+        const that = this;
+
+        if ( that._canNotify === undefined ) {
+            that._canNotify = true;
+        }
+
+        return that._canNotify;
+    }
+
+    set canNotify( value ) {
+        const that = this;
+
+        that._canNotify = value;
+    }
+
+    _notify( changeArgs ) {
+        const that = this;
+
+        if ( !that.canNotify ) {
+            return;
+        }
+
+        if ( that.notifyFn ) {
+            that.notifyFn( changeArgs );
+        }
+    }
+
+    notify( notifyFn ) {
+        const that = this;
+
+        if ( notifyFn ) {
+            that.notifyFn = notifyFn;
+        }
+    }
+
+    toArray() {
+        const that = this;
+
+        return that.boundSource.toArray();
+    }
+
+    dataBind() {
+        const that = this;
+
+        that.clear();
+
+        const completed = () => {
+       
+
+            that._onBindingComplete();
+        }
+
+        if ( typeof that.dataSource === 'string' && ( that.dataSource.indexOf( '.json' ) >= 0 ) ) {
+            that.url = that.dataSource;
+            that.dataSourceType = 'json';
+
+            new Ajax( that, ( data/*, status*/ ) => {
+                that.dataSource = data;
+
+                that._bindToJSON();
+            } );
+        }
+        else if ( typeof that.dataSource === 'string' && ( that.dataSource.indexOf( '.xlsx' ) >= 0 ) ) {
+            that.url = that.dataSource;
+            that.dataSourceType = 'xlsx';
+
+            new Ajax( that, ( data/*, status*/ ) => {
+                if ( !data[ 0 ] ) {
+                    data = [];
+                    that._bindToArray();
+                    completed();
+                    return;
+                }
+
+                const keys = Object.keys( data[ 0 ] );
+                const dataFieldMap = {};
+                const dataRows = [];
+
+                if ( that.exportHeader !== false ) {
+                    let index = 0;
+
+                    for ( let key in keys ) {
+                        const name = keys[ key ];
+
+                        dataFieldMap[ name ] = that.dataFields[ index++ ].name;
+                    }
+
+                    for ( let i = 1; i < data.length; i++ ) {
+                        const row = data[ i ];
+                        const dataRow = {};
+
+                        for ( let key in keys ) {
+                            const name = keys[ key ];
+
+                            dataRow[ dataFieldMap[ name ] ] = row[ name ];
+                        }
+
+                        dataRows.push( dataRow );
+                    }
+
+                    that.dataSource = dataRows;
+                }
+
+                that._bindToArray();
+                completed();
+            } );
+        }
+        else if ( typeof that.dataSource === 'string' && ( that.dataSource.indexOf( '.csv' ) >= 0 ) ) {
+            that.dataSourceType = 'csv';
+
+            new Ajax( that, (/*data, status*/ ) => {
+                that._bindToArray();
+            } );
+        }
+        else if ( typeof that.dataSource === 'string' && ( that.dataSource.indexOf( '.tsv' ) >= 0 ) ) {
+            that.dataSourceType = 'tsv';
+
+            new Ajax( that, (/*data, status*/ ) => {
+            } );
+        }
+        else if ( that.dataSourceType === 'array' ) {
+            that._bindToArray();
+            completed();
+        }
+        else if ( that.dataSourceType === 'json' ) {
+            that._bindToJSON();
+            completed();
+        }
+    }
+
+    _onBindingComplete() {
+        const that = this;
+
+        that._buildHierarchy();
+
+        if ( that.onBindingComplete ) {
+            that.onBindingComplete( { data: that.boundSource } );
+        }
+
+        if ( that._notify ) {
+            that._notify( { action: 'bindingComplete', data: that.boundSource } );
+        }
+
+        that.bindingCompleted = true;
+    }
+
+    refreshHierarchy() {
+        const that = this;
+
+        that._buildHierarchy();
+    }
+
+    find() {
+        const that = this;
+
+        return that.boundSource.find.apply( that.boundSource, arguments );
+    }
+
+    onVirtualDataSourceRequested( requestCallback, details ) {
+        const that = this;
+
+        let first = details ? details.first : Infinity;
+        let last = details ? details.last : Infinity;
+        let row = details ? details.row : null;
+
+        if ( undefined === first ) {
+            first = Infinity;
+        }
+
+        if ( undefined === last ) {
+            last = Infinity;
+        }
+
+        that.virtualFirstIndex = first;
+        that.virtualLastIndex = last;
+
+        if ( that.virtualDataSource ) {
+            const getDataSource = function ( ExcelAdapterSettings ) {
+                if ( ExcelAdapterSettings.virtualDataSourceLength !== undefined ) {
+                    that.virtualDataSourceLength = ExcelAdapterSettings.virtualDataSourceLength;
+                }
+
+                new JQX.ExcelAdapter(
+                    {
+                        dataSource: ExcelAdapterSettings.dataSource,
+                        dataFields: ExcelAdapterSettings.dataFields || that.dataFields,
+                        data: details,
+                        onBindingComplete( event ) {
+
+                            if ( that.virtualDataSourceOnExpand && row ) {
+                                if ( event.data && event.data.length > 0 ) {
+                                    that.add( event.data, row.$.id );
+                                }
+                                else {
+                                    row.leaf = true;
+                                }
+
+                                if ( that.onFilter ) {
+                                    that.onFilter()
+                                }
+
+                                requestCallback();
+
+                                return;
+                            }
+
+                            if ( first === Infinity ) {
+                                that.add( event.data );
+                            }
+                            else {
+                                let items = [];
+                                let indexes = [];
+
+                                for ( let i = 0; i < event.data.length; i++ ) {
+                                    const item = event.data[ i ];
+
+                                    if ( first + i <= last ) {
+                                        items.push( item );
+                                        indexes.push( first + i );
+                                    }
+                                }
+
+                                that.update( indexes, items );
+                            }
+
+
+                            if ( that.onFilter ) {
+                                that.onFilter()
+                            }
+
+                            requestCallback();
+                        }
+                    } );
+            }
+
+            let hasCache = false;
+
+            const isEmpty = ( obj ) => Object.entries( obj ).length === 0 && ( obj.constructor === Object || obj.constructor === Array );
+            const canCache = isEmpty( details.sorting ) && isEmpty( details.filtering ) && isEmpty( details.grouping ) && !details.row && ( details.action !== 'filter' && details.action !== 'sort' && details.action !== 'group' );
+
+            if ( that.virtualDataSourceCache && first !== Infinity && canCache ) {
+                let cachedCount = 0;
+
+                for ( let i = first; i < last; i++ ) {
+                    if ( !that[ i ].$.isEmpty ) {
+                        cachedCount++;
+                    }
+                }
+
+                if ( cachedCount === last - first ) {
+                    hasCache = true;
+                }
+            }
+
+            if ( hasCache ) {
+                requestCallback();
+            }
+            else {
+                if ( details.action === 'expand' ) {
+                    that.virtualDataSourceOnExpand( getDataSource, {
+                        first: first,
+                        last: last,
+                        row: details.row,
+                        sorting: details.sorting,
+                        filtering: details.filtering,
+                        grouping: details.grouping,
+                        action: details.action
+                    } );
+                }
+                else {
+                    that.virtualDataSource( getDataSource, {
+                        first: first,
+                        last: last,
+                        sorting: details.sorting,
+                        filtering: details.filtering,
+                        filterOperator: details.filterOperator || 'and',
+                        grouping: details.grouping,
+                        action: details.action
+                    } );
+                }
+            }
+        }
+        else {
+            requestCallback();
+        }
+    }
+
+    add( item, parentId ) {
+        const that = this;
+
+        if ( !item ) {
+            return;
+        }
+
+        let result = true;
+
+        const addItem = function ( item ) {
+            const itemObject = that._getDataItem( item, that.boundSource.length );
+
+            that[ that.boundSource.length ] = itemObject;
+            that.dataItemById[ itemObject.$.id ] = itemObject;
+
+            const pushResult = that.boundSource.push( itemObject );
+
+            if ( parentId !== undefined ) {
+                itemObject.$.parentId = parentId;
+            }
+
+            if ( !pushResult ) {
+                result = false;
+            }
+
+            return itemObject;
+        }
+
+        if ( item.length ) {
+            let itemObjects = [];
+
+            for ( let i = 0; i < item.length; i++ ) {
+                const itemObject = addItem( item[ i ] );
+
+                itemObjects.push( itemObject );
+            }
+
+            that._notify( { action: 'add', data: itemObjects } );
+        }
+        else {
+            const itemObject = addItem( item );
+
+            that._notify( { action: 'add', data: itemObject } );
+        }
+
+        that.refreshHierarchy();
+
+        return result;
+    }
+
+    refreshIndexes() {
+        const that = this;
+
+        for (let i = 0; i < that.boundSource.length; i++) {
+            that[i] = that.boundSource[i];
+            that[i].$.index = i;
+            that.dataItemById[that[i].$.id] = that[i];
+        }
+
+        let i = that.boundSource.length;
+
+        while (that[i]) {
+            delete that[i];
+            i++;
+        }
+    }
+
+    removeLast() {
+        const that = this;
+
+        delete that[that.boundSource.length - 1];
+        const result = that.boundSource.pop();
+        delete that.dataItemById[result.$.id];
+
+        that._notify({ action: 'removeLast', data: result });
+
+        that.refreshHierarchy();
+
+        return result;
+    }
+
+    removeAt(index) {
+        const that = this;
+
+        const item = that.boundSource[index];
+
+        if (!item) {
+            throw new Error('Invalid Item Index');
+        }
+
+        that.boundSource.splice(index, 1);
+        delete that.dataItemById[item.$.id];
+        that.refreshIndexes();
+
+        that._notify({ action: 'remove', index: index, data: item });
+
+        that.refreshHierarchy();
+    }
+
+    update( index, dataSourceItem ) {
+        const that = this;
+
+        if ( JQX.Utilities.Types.isArray( index ) && JQX.Utilities.Types.isArray( dataSourceItem ) ) {
+            if ( index.length === 0 && dataSourceItem.length === 0 ) {
+                that.refreshHierarchy();
+                return;
+            }
+        }
+
+        if ( dataSourceItem.length && index.length ) {
+            let itemObjects = [];
+
+            for ( let i = 0; i < index.length; i++ ) {
+                const itemObject = that._getDataItem( dataSourceItem[ i ], index[ i ] );
+                const currentIndex = index[ i ];
+
+                itemObjects.push( itemObject );
+
+                that.boundSource[ currentIndex ] = itemObject;
+                that[ currentIndex ] = that.boundSource[ currentIndex ];
+                that.dataItemById[ itemObject.$.id ] = that[ currentIndex ];
+            }
+
+            that._notify( { action: 'update', index: index, data: itemObjects } );
+
+            that.refreshHierarchy();
+
+            return;
+        }
+
+        const itemObject = that._getDataItem( dataSourceItem, index );
+
+        that.boundSource[ index ] = itemObject;
+        that[ index ] = that.boundSource[ index ];
+        that.dataItemById[ itemObject.$.id ] = that[ index ];
+
+        that._notify( { action: 'update', index: index, data: itemObject } );
+
+        that.refreshHierarchy();
+
+        return itemObject;
+    }
+
+    insert( index, item ) {
+        const that = this;
+
+        item = that._getDataItem( item, index );
+
+        const result = that.boundSource.splice( index, 0, item );
+
+        that.refreshIndexes();
+
+        that._notify( { action: 'insert', index: index, data: item } );
+
+        that.refreshHierarchy();
+
+        return result;
+    }
+
+    move( from, to ) {
+        if ( to > from && to - from === 1 || from === to ) {
+            return;
+        }
+
+        const that = this,
+            recordToMove = that.boundSource.splice( from, 1 )[ 0 ];
+
+        if ( to > from ) {
+            to--;
+            that.boundSource.splice( to, 0, recordToMove );
+        }
+        else {
+            that.boundSource.splice( to, 0, recordToMove );
+        }
+
+        that.refreshIndexes();
+
+        that._notify( { action: 'move', index: to, data: that.boundSource[ to ] } );
+
+        that.refreshHierarchy();
+    }
+
+    indexOf( item ) {
+        const that = this;
+        const index = that.boundSource.indexOf( item );
+
+        return index;
+    }
+
+    get length() {
+        const that = this;
+
+        if ( that.virtualDataSourceLength !== undefined ) {
+            return that.virtualDataSourceLength;
+        }
+
+        if ( that.dataSourceLength ) {
+            return that.dataSourceLength;
+        }
+
+        if ( typeof ( that.dataSource ) === 'number' ) {
+            return that.dataSource;
+        }
+
+        if ( that.bindingCompleted ) {
+            return that.boundSource.length;
+        }
+
+        if ( that.dataSource && typeof that.dataSource !== 'string' && that.dataSource.length ) {
+            return that.dataSource.length;
+        }
+
+        return that.boundSource.length;
+    }
+
+    clear() {
+        const that = this;
+
+        if ( !that.isInitialized ) {
+            that._cachedValues = [];
+            that.dataItemById = [];
+            return;
+        }
+
+        for ( let i = 0; i < that.boundSource.length; i++ ) {
+            delete that[ i ];
+        }
+
+        that._cachedValues = [];
+        that.boundSource = that.observable ? new JQX.ObservableArray() : [];
+        that.dataItemById = [];
+        that.refreshHierarchy();
+    }
+
+    _getId( id, item, index ) {
+        if ( id !== null && id.name !== undefined ) {
+            if ( id.name && item.getAttribute ) {
+                let result = item.getAttribute( id.name );
+                if ( result !== null && result.toString().length > 0 ) {
+                    return result;
+                }
+                else if ( id.map ) {
+                    try {
+                        let result = item.getAttribute( id.map );
+                        if ( result !== null && result.toString().length > 0 ) {
+                            return result;
+                        }
+                    }
+                    catch ( error ) {
+                        return index;
+                    }
+                }
+                return;
+            }
+        }
+
+        if ( id ) {
+            if ( id.toString().length > 0 && item.getAttribute ) {
+                let result = item.getAttribute( id );
+                if ( result !== null && result.toString().length > 0 ) {
+                    return result.trim().split( ' ' ).join( '' ).replace( /([ #;?%&,.+*~\':'!^$[\]()=>|\/@])/g, '' );
+                }
+                else {
+                    let splitMap = id.split( this.mapChar );
+                    if ( splitMap.length > 1 ) {
+                        let d = item;
+                        for ( let p = 0; p < splitMap.length; p++ ) {
+                            if ( d !== undefined ) {
+                                d = d[ splitMap[ p ] ];
+                            }
+                        }
+                        if ( d !== undefined ) {
+                            return d;
+                        }
+                    }
+                    else {
+                        if ( item[ id ] !== undefined ) {
+                            return item[ id ];
+                        }
+                    }
+                }
+            }
+        }
+
+        return index;
+    }
+
+    _buildHierarchy() {
+        const that = this;
+
+        if ( !that.reservedNames ) {
+            that.reservedNames = {
+                leaf: 'leaf',
+                parent: 'parent',
+                expanded: 'expanded',
+                checked: 'checked',
+                selected: 'selected',
+                level: 'level',
+                icon: 'icon',
+                data: 'data'
+            }
+        }
+        else {
+            const names = that.reservedNames;
+
+            if ( !names.leaf ) {
+                names.leaf = 'leaf';
+            }
+            if ( !names.parent ) {
+                names.parent = 'parent';
+            }
+            if ( !names.expanded ) {
+                names.expanded = 'expanded';
+            }
+            if ( !names.checked ) {
+                names.checked = 'checked';
+            }
+            if ( !names.selected ) {
+                names.selected = 'selected';
+            }
+            if ( !names.level ) {
+                names.level = 'level';
+            }
+            if ( !names.data ) {
+                names.data = 'data';
+            }
+
+        }
+
+        const names = that.reservedNames;
+
+        if ( that.childrenDataField ) {
+            const hierarchy = [];
+
+            for ( let i = 0; i < that.boundSource.length; i++ ) {
+                const item = Object.assign( {}, that.boundSource[ i ] );
+
+                if ( !item ) {
+                    continue;
+                }
+
+                hierarchy.push( item );
+
+                const addItems = function ( item ) {
+                    const splitMap = that.childrenDataField.split( that.mapChar );
+                    let children = null;
+
+                    if ( splitMap.length > 1 ) {
+                        let data = item;
+
+                        for ( let p = 0; p < splitMap.length; p++ ) {
+                            if ( data !== undefined ) {
+                                data = data[ splitMap[ p ] ];
+                            }
+                        }
+
+                        children = data;
+                    }
+                    else {
+                        children = item[ 'children' ];
+                    }
+
+                    item[ 'children' ] = children;
+
+                    if ( item[ 'children' ] === null || item[ 'children' ] === undefined || ( item[ 'children' ] && item[ 'children' ].length === 0 ) ) {
+                        item[ names.leaf ] = true;
+                    }
+                }
+
+                addItems( item );
+                item[ names.level ] = 0;
+
+                if ( !item.$ ) {
+                    item.$ = {};
+                }
+
+                item[ names.parent ] = null;
+                item[ names.data ] = item;
+
+                if ( item[ names.expanded ] === undefined ) {
+                    item[ names.expanded ] = false;
+                }
+
+                const drillThrough = function ( parent, children ) {
+                    if ( !children ) {
+                        parent[ 'children' ] = new Array();
+                        return;
+                    }
+
+                    for ( let i = 0; i < children.length; i++ ) {
+                        let item = that._getDataItem( children[ i ], i );
+
+                        if ( !item ) {
+                            continue;
+                        }
+
+                        addItems( item );
+                        item[ names.level ] = parent[ names.level ] + 1;
+                        item[ names.parent ] = parent;
+                        item[ names.data ] = item;
+
+                        if ( parent ) {
+                            parent[ 'children' ][ i ] = item;
+                        }
+
+
+                        if ( item[ names.expanded ] === undefined ) {
+                            item[ names.expanded ] = false;
+                        }
+
+                        drillThrough( item, item[ 'children' ] );
+                    }
+                }
+
+                drillThrough( item, item[ 'children' ] );
+            }
+
+
+            that.boundHierarchy = hierarchy;
+
+            if ( !that._boundSourceUpdate ) {
+                for ( let i = 0; i < that.boundHierarchy.length; i++ ) {
+                    const item = that.boundHierarchy[ i ];
+
+                    if ( item.children ) {
+                        const drillThrough = function ( item ) {
+                            if ( !that.dataItemById[ item.$.id ] ) {
+                                that.boundSource.canNotify = false;
+                                that.dataItemById[ item.$.id ] = item;
+                                that[ that.boundSource.length ] = item;
+                                that.boundSource.push( item );
+                                that.boundSource.canNotify = true;
+                            }
+
+                            if ( item.children ) {
+                                for ( let i = 0; i < item.children.length; i++ ) {
+                                    const child = item.children[ i ];
+
+                                    if ( child.children ) {
+                                        drillThrough( child );
+                                    }
+                                }
+                            }
+                        }
+
+                        drillThrough( item );
+                    }
+                }
+
+                that._boundSourceUpdate = true;
+            }
+        }
+
+        if ( that.xmlRoot && that.dataSourceType === 'xml' ) {
+            that.boundHierarchy = this._getHierarchy( 'uid', '_parentuid', 'children', null, that.boundSource );
+        }
+
+        if ( that.keyDataField && that.parentDataField ) {
+            that.boundHierarchy = this._getHierarchy( that.keyDataField, that.parentDataField, 'children', null, that.boundSource );
+        }
+
+        if ( that.groupBy && that.groupBy.length > 0 ) {
+            that.boundHierarchy = this._getGroupHierarchy( that.groupBy, 'children', 'label', null, 'data', null, 'parent', that.boundSource );
+        }
+
+        if ( that.virtualDataSourceOnExpand ) {
+            that.boundHierarchy = this._getHierarchy( 'id', 'parentId', 'children', null, that.boundSource );
+        }
+    }
+
+
+    _getGroupHierarchy( groups, collectionName, groupName, mappingFields, itemName, valueName, parentName, data, startIndex ) {
+        let that = this;
+
+        if ( !startIndex ) {
+            startIndex = 0;
+        }
+
+        let names = that.reservedNames;
+
+        const guid = function () {
+            function s4() {
+                return Math.floor( ( 1 + Math.random() ) * 0x10000 )
+                    .toString( 16 )
+                    .substring( 1 );
+            }
+
+            return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
+        }
+
+        let groupHashCodes = new Array();
+        for ( let iGroupColumn = 0; iGroupColumn < groups.length; iGroupColumn++ ) {
+            groupHashCodes[ iGroupColumn ] = guid();
+        }
+
+        if ( !collectionName ) {
+            collectionName = 'children';
+        }
+
+        if ( !groupName ) {
+            groupName = 'group';
+        }
+
+        if ( !itemName ) {
+            itemName = 'item';
+        }
+
+        if ( !parentName ) {
+            parentName = 'parent';
+        }
+
+        if ( undefined === valueName ) {
+            valueName = 'value';
+        }
+
+        const groupboundSource = new Array();
+        const hashItemGroups = new Array();
+
+        let groupboundSourceIndex = 0;
+
+        const getItem = function ( item ) {
+            let itemObj = item;
+            if ( mappingFields ) {
+                for ( let mappingField in mappingFields ) {
+                    const mappingObject = mappingFields[ mappingField ];
+
+                    if ( mappingObject.name && mappingObject.map ) {
+                        itemObj[ mappingObject.map ] = itemObj[ mappingObject.name ];
+                    }
+                }
+            }
+
+            return itemObj;
+        }
+
+        for ( let obj = 0; obj < data.length; obj++ ) {
+            let item = Object.assign( {}, getItem( data[ obj ] ) );
+
+            item[ names.leaf ] = false;
+
+            let itemKeysHierarchy = new Array();
+            let keys = 0;
+
+            for ( let iGroupColumn = 0; iGroupColumn < groups.length; iGroupColumn++ ) {
+                const group = groups[ iGroupColumn ];
+                const value = item[ group ];
+
+                if ( null === value ) {
+                    continue;
+                }
+
+                itemKeysHierarchy[ keys++ ] = { value: value, group: group, hash: groupHashCodes[ iGroupColumn ] };
+            }
+
+            if ( itemKeysHierarchy.length !== groups.length ) {
+                break;
+            }
+
+            let parentItem = null;
+            let lookupKey = '';
+
+            for ( let q = 0; q < itemKeysHierarchy.length; q++ ) {
+                const itemKey = itemKeysHierarchy[ q ].value;
+                const groupDataField = itemKeysHierarchy[ q ].group;
+                const columnHash = itemKeysHierarchy[ q ].hash;
+
+                lookupKey = lookupKey + '_' + columnHash + '_' + itemKey;
+
+                if ( hashItemGroups[ lookupKey ] !== undefined && hashItemGroups[ lookupKey ] !== null ) {
+                    parentItem = hashItemGroups[ lookupKey ];
+                    continue;
+                }
+
+                if ( parentItem === null ) {
+                    parentItem = { $: {} };
+
+                    parentItem[ names.level ] = 0;
+                    parentItem[ names.leaf ] = false;
+                    parentItem[ parentName ] = null;
+                    parentItem[ groupName ] = itemKey;
+                    parentItem[ itemName ] = item;
+                    parentItem[ 'groupDataField' ] = groupDataField;
+
+                    if ( !parentItem[ groupDataField ] ) {
+                        parentItem[ groupDataField ] = parentItem.data[ groupDataField ];
+                    }
+
+                    if ( item[ names.expanded ] !== undefined ) {
+                        parentItem[ names.expanded ] = item[ names.expanded ];
+                    }
+                    else {
+                        parentItem[ names.expanded ] = false;
+                    }
+
+                    if ( valueName ) {
+                        parentItem[ valueName ] = item[ valueName ];
+                    }
+
+                    parentItem[ collectionName ] = new Array();
+
+                    let uid = groupboundSource.length + startIndex;
+
+                    if ( !this.id || typeof item.$.id === 'number' || isFinite( item.$.id ) ) {
+                        uid = 'Item' + uid;
+                    }
+                    if ( parentItem.$.id === undefined ) {
+                        parentItem.$.id = uid;
+                    }
+
+                    groupboundSource[ groupboundSourceIndex++ ] = parentItem;
+                }
+                else {
+                    const subItem = { $: {} };
+
+                    subItem[ names.level ] = parentItem[ names.level ] + 1;
+                    subItem[ parentName ] = parentItem;
+                    subItem[ groupName ] = itemKey;
+                    subItem[ collectionName ] = new Array();
+                    subItem[ itemName ] = item;
+                    subItem[ 'groupDataField' ] = groupDataField;
+                    subItem[ names.leaf ] = false;
+
+                    if ( !subItem[ groupDataField ] ) {
+                        subItem[ groupDataField ] = subItem.data[ groupDataField ];
+                    }
+
+                    if ( item[ names.expanded ] !== undefined ) {
+                        subItem[ names.expanded ] = item[ names.expanded ];
+                    }
+                    else {
+                        subItem[ names.expanded ] = false;
+                    }
+
+                    if ( valueName ) {
+                        subItem[ valueName ] = item[ valueName ];
+                    }
+
+                    if ( subItem.$.id === undefined ) {
+                        subItem.$.id = parentItem.$.id + '_' + parentItem[ collectionName ].length;
+                    }
+
+                    parentItem[ collectionName ][ parentItem[ collectionName ].length ] = subItem;
+                    parentItem = subItem;
+                }
+
+                hashItemGroups[ lookupKey ] = parentItem;
+            }
+
+            if ( item ) {
+                item[ names.leaf ] = true;
+            }
+
+            if ( parentItem !== null ) {
+                if ( this.id === null ) {
+                    if ( undefined === item.$.id ) {
+                        item.$.id = parentItem.$.id + '_' + parentItem[ collectionName ].length;
+                    }
+                }
+                else {
+                    if ( undefined === item.$.id ) {
+                        if ( item.$.id.toString().indexOf( parentItem.$.id ) === -1 ) {
+                            item.$.id = parentItem.$.id + '_' + item.$.id;
+                        }
+                    }
+                }
+
+                item[ parentName ] = parentItem;
+                item[ names.level ] = parentItem[ names.level ] + 1;
+                parentItem[ collectionName ][ parentItem[ collectionName ].length ] = item;
+            }
+            else {
+                if ( undefined === item.$.id ) {
+                    item.$.id = guid();
+                }
+            }
+        }
+
+        return groupboundSource;
+    }
+
+    _getHierarchy( fieldName, parentFieldName, collectionName, mappingFields, boundSource ) {
+        const that = this;
+
+        const databoundHierarchy = new Array();
+        let flatData = this.boundSource;
+
+        if ( boundSource ) {
+            flatData = boundSource;
+        }
+
+        if ( this.boundSource.length === 0 )
+            return null;
+
+        const childrenName = collectionName !== null ? collectionName : 'children';
+        let items = new Array();
+        let data = flatData;
+        let dataLength = data.length;
+        let names = that.reservedNames;
+
+        const getItem = function ( item ) {
+            let itemObj = item;
+            if ( mappingFields ) {
+                for ( let mappingField in mappingFields ) {
+                    const mappingObject = mappingFields[ mappingField ];
+
+                    if ( mappingObject.name && mappingObject.map ) {
+                        itemObj[ mappingObject.map ] = itemObj[ mappingObject.name ];
+                    }
+                }
+            }
+
+            return itemObj;
+        }
+
+        // build hierarchical source.
+        for ( let i = 0; i < dataLength; i++ ) {
+            let item = data[ i ];
+            let parentId = item[ parentFieldName ];
+            let id = item[ fieldName ];
+
+            if ( parentFieldName === 'parentId' ) {
+                parentId = item.$.parentId;
+            }
+
+            if ( fieldName === 'id' ) {
+                id = item.$.id;
+            }
+
+            item[ childrenName ] = new Array();
+
+            items[ id ] = { parentId: parentId, item: item };
+        }
+
+        for ( let i = 0; i < dataLength; i++ ) {
+            const item = data[ i ];
+            let parentId = item[ parentFieldName ];
+            let id = item[ fieldName ];
+
+            if ( parentFieldName === 'parentId' ) {
+                parentId = item.$.parentId;
+            }
+
+            if ( fieldName === 'id' ) {
+                id = item.$.id;
+            }
+
+            if ( items[ parentId ] !== undefined ) {
+                let item = { parentId: parentId, item: items[ id ].item };
+                let parentItem = items[ parentId ].item;
+                if ( !parentItem[ childrenName ] ) {
+                    parentItem[ childrenName ] = new Array();
+                }
+                let length = parentItem[ childrenName ].length;
+                item = item.item;
+
+                if ( !names ) {
+                    if ( item.parent === undefined ) {
+                        item.parent = parentItem;
+                    }
+                }
+                else {
+                    if ( item[ names.parent ] === undefined ) {
+                        item[ names.parent ] = parentItem;
+                    }
+                }
+
+                const itemObj = getItem( item );
+
+                parentItem[ childrenName ][ length ] = itemObj;
+                items[ parentId ].item = parentItem;
+                items[ id ].item = item;
+
+            }
+            else {
+                let item = items[ id ].item;
+                if ( !names ) {
+                    if ( item.parent === undefined ) {
+                        item.parent = null;
+                    }
+                }
+                else {
+                    if ( item[ names.parent ] === undefined ) {
+                        item[ names.parent ] = null;
+                    }
+                }
+
+                const itemObj = getItem( item );
+
+                if ( !names ) {
+                    itemObj.level = 0;
+                }
+                else {
+                    itemObj[ names.level ] = 0;
+                }
+
+                databoundHierarchy[ databoundHierarchy.length ] = itemObj;
+            }
+        }
+        if ( databoundHierarchy.length !== 0 ) {
+            let updateLevels = function ( level, children ) {
+                for ( let i = 0; i < children.length; i++ ) {
+                    const child = children[ i ];
+
+                    if ( !names ) {
+                        child.level = level;
+                    }
+                    else {
+                        child[ names.level ] = level;
+                    }
+
+                    const childChildren = child[ childrenName ];
+
+                    if ( childChildren ) {
+                        if ( childChildren.length > 0 ) {
+                            updateLevels( level + 1, childChildren );
+                        }
+                        else {
+                            if ( that.virtualDataSourceOnExpand ) {
+                                if ( child.leaf === undefined ) {
+                                    child.leaf = false;
+                                }
+                            }
+                            else {
+                                if ( !names ) {
+                                    child.leaf = true;
+                                }
+                                else {
+                                    child[ names.leaf ] = true;
+                                }
+                            }
+                        }
+                    }
+                    else {
+                        if ( that.virtualDataSourceOnExpand ) {
+                            if ( child.leaf === undefined ) {
+                                child.leaf = false;
+                            }
+                        }
+                        else {
+                            if ( !names ) {
+                                child.leaf = true;
+                            }
+                            else {
+                                child[ names.leaf ] = true;
+                            }
+                        }
+                    }
+                }
+            };
+            updateLevels( 0, databoundHierarchy );
+        }
+        return databoundHierarchy;
+    }
+
+    summarize( summaryItems, boundSource ) {
+        const that = this;
+
+        if ( !Array.isArray( summaryItems ) ) {
+            summaryItems = [ summaryItems ];
+        }
+
+        let tempSummaryItems = [];
+
+        for ( let i = 0; i < summaryItems.length; i++ ) {
+            const summaryItem = summaryItems[ i ];
+
+            for ( let name in summaryItem ) {
+                const functions = summaryItem[ name ];
+
+                tempSummaryItems.push( { dataField: name, functions: functions } )
+            }
+        }
+
+        summaryItems = tempSummaryItems;
+
+        let data = {};
+        let summaryByDataField = new Array();
+
+        if ( !boundSource ) {
+            boundSource = that.boundSource;
+        }
+
+        let length = boundSource.length;
+
+        if ( length === 0 ) {
+            return;
+        }
+
+        if ( length === undefined ) {
+            return;
+        }
+
+        for ( let i = 0; i < length; i++ ) {
+            let dataItem = boundSource[ i ];
+
+            for ( let j = 0; j < summaryItems.length; j++ ) {
+                const summaryItem = summaryItems[ j ];
+                let value = dataItem[ summaryItem.dataField ];
+
+                if ( summaryItem.functions ) {
+                    data[ summaryItem.dataField ] = data[ summaryItem.dataField ] || {};
+                    summaryByDataField[ summaryItem.dataField ] = summaryByDataField[ summaryItem.dataField ] || 0;
+                    summaryByDataField[ summaryItem.dataField ]++;
+
+                    const _summaryItemFunction = function ( summaryItemObject ) {
+                        for ( let name in summaryItemObject ) {
+                            let oldValue = data[ summaryItem.dataField ][ name ];
+
+                            if ( oldValue === null || oldValue === undefined ) {
+                                data[ summaryItem.dataField ][ name ] = 0;
+                                oldValue = 0;
+                            }
+
+                            if ( typeof summaryItemObject[ name ] === 'function' ) {
+                                oldValue = summaryItemObject[ name ]( oldValue, value, summaryItem.dataField, dataItem );
+                            }
+                            data[ summaryItem.dataField ][ name ] = oldValue;
+                        }
+                    }
+
+                    let canParse = parseFloat( value );
+
+                    if ( isNaN( canParse ) ) {
+                        canParse = false;
+                    }
+                    else {
+                        canParse = true;
+                    }
+
+                    if ( canParse ) {
+                        value = parseFloat( value );
+                    }
+
+                    if ( typeof value === 'number' && isFinite( value ) ) {
+                        summaryItem.functions.forEach( function ( summaryItemFunction ) {
+                            let oldValue = data[ summaryItem.dataField ][ summaryItemFunction ];
+
+                            if ( oldValue === null || oldValue === undefined ) {
+                                oldValue = 0;
+
+                                if ( summaryItemFunction === 'min' ) {
+                                    oldValue = 9999999999999;
+                                }
+
+                                if ( summaryItemFunction === 'max' ) {
+                                    oldValue = -9999999999999;
+                                }
+
+                                if (summaryItemFunction === 'median') {
+                                    oldValue = [];
+                                }
+                            }
+
+                            if ( summaryItemFunction === 'sum' || summaryItemFunction === 'avg' || summaryItemFunction === 'stdev'
+                                || summaryItemFunction === 'stdevp' || summaryItemFunction === 'var' || summaryItemFunction === 'varp' ) {
+                                oldValue += parseFloat( value );
+                            }
+                            else if ( summaryItemFunction === 'product' ) {
+                                if ( i === 0 )
+                                    oldValue = parseFloat( value );
+                                else
+                                    oldValue *= parseFloat( value );
+                            }
+                            else if ( summaryItemFunction === 'min' ) {
+                                oldValue = Math.min( oldValue, parseFloat( value ) );
+                            }
+                            else if ( summaryItemFunction === 'max' ) {
+                                oldValue = Math.max( oldValue, parseFloat( value ) );
+                            }
+                            else if ( summaryItemFunction === 'count' ) {
+                                oldValue++;
+                            }
+                            else if (summaryItemFunction === 'median') {
+                                oldValue.push(parseFloat(value));
+                            }
+                            else if ( typeof ( summaryItemFunction ) === 'object' ) {
+                                _summaryItemFunction( summaryItemFunction );
+                                return;
+                            }
+
+                            data[ summaryItem.dataField ][ summaryItemFunction ] = oldValue;
+                        } );
+                    }
+                    else {
+                        summaryItem.functions.forEach( function ( summaryItemFunction ) {
+                            if ( summaryItemFunction === 'min' || summaryItemFunction === 'max' || summaryItemFunction === 'count' || summaryItemFunction === 'product' || summaryItemFunction === 'sum'
+                                || summaryItemFunction === 'avg' || summaryItemFunction === 'stdev'
+                                || summaryItemFunction === 'stdevp' || summaryItemFunction === 'var' || summaryItemFunction === 'varp' ) {
+                                if ( value === null ) {
+                                    return true;
+                                }
+
+                                let oldValue = data[ summaryItem.dataField ][ summaryItemFunction ];
+
+                                if ( oldValue === null || oldValue === undefined ) {
+                                    oldValue = 0;
+                                }
+
+                                data[ summaryItem.dataField ][ summaryItemFunction ] = oldValue;
+
+                                return true;
+                            }
+
+                            if ( typeof ( summaryItemFunction ) === 'object' ) {
+                                _summaryItemFunction( summaryItemFunction );
+                            }
+                        } );
+                    }
+                }
+            }
+        }
+
+        for ( let j = 0; j < summaryItems.length; j++ ) {
+            const summaryItem = summaryItems[ j ];
+
+            if ( !summaryItem.functions ) {
+                continue;
+            }
+            if ( !data[ summaryItem.dataField ] ) {
+                data[ summaryItem.dataField ] = {};
+
+                summaryItem.functions.forEach( function ( summaryItemFunction ) {
+                    data[ summaryItem.dataField ][ summaryItemFunction ] = 0;
+                } );
+            }
+
+            if ( data[ summaryItem.dataField ][ 'avg' ] !== undefined ) {
+                const value = data[ summaryItem.dataField ][ 'avg' ];
+                const dataValues = summaryByDataField[ summaryItem.dataField ];
+
+                if ( dataValues === 0 || dataValues === undefined ) {
+                    data[ summaryItem.dataField ][ 'avg' ] = 0;
+                }
+                else {
+                    data[ summaryItem.dataField ][ 'avg' ] = value / dataValues;
+                }
+            }
+            else if ( data[ summaryItem.dataField ][ 'count' ] !== undefined ) {
+                data[ summaryItem.dataField ][ 'count' ] = length;
+            }
+            else if (data[summaryItem.dataField]['median'] !== undefined) {
+                let population = data[summaryItem.dataField]['median'];
+
+                population.sort(function (a, b) {
+                    return a - b;
+                });
+
+                data[summaryItem.dataField]['median'] =
+                    0.5 * (population[Math.floor((population.length + 1) / 2) - 1] + population[Math.ceil((population.length + 1) / 2) - 1]);
+            }
+
+            // stdev, stdevp, var, varp.
+            // stdev - Standard deviation on a sample.
+            // varp - Variance on an entire population.
+            // let - Variance on a sample.
+            if ( data[ summaryItem.dataField ][ 'stdev' ] || data[ summaryItem.dataField ][ 'stdevp' ]
+                || data[ summaryItem.dataField ][ 'var' ] || data[ summaryItem.dataField ][ 'varp' ] ) {
+                summaryItem.functions.forEach( function ( summaryItemFunction ) {
+                    if ( summaryItemFunction === 'stdev' || summaryItemFunction === 'var' || summaryItemFunction === 'varp' || summaryItemFunction === 'stdevp' ) {
+                        const value = data[ summaryItem.dataField ][ summaryItemFunction ];
+                        const count = length;
+                        const average = ( value / length );
+                        let sumSq = 0.0;
+
+                        for ( let i = 0; i < length; i++ ) {
+                            let dataItem = boundSource[ i ];
+                            let value = dataItem[ summaryItem.dataField ];
+
+                            sumSq += ( value - average ) * ( value - average );
+                        }
+
+                        let denominator = ( summaryItemFunction === 'stdevp' || summaryItemFunction === 'varp' ) ? count : count - 1;
+
+                        if ( denominator === 0 ) {
+                            denominator = 1;
+                        }
+
+                        if ( summaryItemFunction === 'var' || summaryItemFunction === 'varp' ) {
+                            data[ summaryItem.dataField ][ summaryItemFunction ] = sumSq / denominator;
+                        }
+                        else if ( summaryItemFunction === 'stdevp' || summaryItemFunction === 'stdev' ) {
+                            data[ summaryItem.dataField ][ summaryItemFunction ] = Math.sqrt( sumSq / denominator );
+                        }
+                    }
+                } );
+            }
+        }
+        return data;
+    }
+
+    deserialize(stringValue, type, nullable) {
+        const nullValue = stringValue === 'null';
+
+        if (stringValue === undefined || (nullValue && !nullable)) {
+            return undefined;
+        }
+
+        if (nullValue && nullable) {
+            return null;
+        }
+
+        if (type === 'boolean' || type === 'bool') {
+            if (stringValue === null) {
+                return false;
+            }
+
+            // Boolean properties are set based on the presence of the attribute: if the attribute exists at all, the value is true.
+            return true;
+        }
+        else if (type === 'number' || type === 'float') {
+            if (stringValue === 'NaN') {
+                return NaN;
+            }
+
+            if (stringValue === 'Infinity') {
+                return Infinity;
+            }
+
+            if (stringValue === '-Infinity') {
+                return -Infinity;
+            }
+
+            return parseFloat(stringValue);
+        }
+        else if (type === 'int' || type === 'integer') {
+            if (stringValue === 'NaN') {
+                return NaN;
+            }
+
+            if (stringValue === 'Infinity') {
+                return Infinity;
+            }
+
+            if (stringValue === '-Infinity') {
+                return -Infinity;
+            }
+
+            return parseInt(stringValue);
+        }
+        else if (type === 'string') {
+            return stringValue;
+        }
+        else if (type === 'any') {
+            return stringValue;
+        }
+        else if (type === 'date') {
+            return new Date(stringValue);
+        }
+        else if (type === 'function') {
+            if (typeof window[stringValue] === 'function') {
+                return window[stringValue];
+            }
+        }
+        else if (type === 'array' || type === 'object') {
+            try {
+                const jsonObject = JSON.parse(stringValue);
+
+                if (jsonObject) {
+                    return jsonObject;
+                }
+            }
+            catch (er) {
+                if (window[stringValue] && (typeof window[stringValue] === 'object')) {
+                    return window[stringValue];
+                }
+                else if (type === 'array' && stringValue.indexOf('[') >= 0) {
+                    if (stringValue.indexOf('{') >= 0) {
+                        let array = stringValue.replace(/{/ig, '').replace('[', '').replace(']', '').replace(/'/ig, '').replace(/"/ig, '').trim();
+
+                        array = array.split('},');
+
+                        for (let i = 0; i < array.length; i++) {
+                            let parsedObject = {
+                            };
+
+                            let parts = array[i].trim().split(',');
+
+                            for (let j = 0; j < parts.length; j++) {
+                                const key = parts[j].split(':')[0].trim();
+                                const value = parts[j].split(':')[1].trim();
+
+                                parsedObject[key] = value;
+                            }
+
+                            array[i] = parsedObject;
+                        }
+
+                        return array;
+                    }
+
+                    const array = stringValue.replace('[', '').replace(']', '').replace(/'/ig, '').replace(/"/ig, '').trim().split(',');
+
+                    return array;
+                }
+            }
+        }
+
+        return undefined;
+    }
+
+    _getDataItem( dataSourceItem, index ) {
+        const that = this;
+        const itemObject = {};
+        const unboundMode = typeof ( that.dataSource ) === 'number' || that.dataSourceLength;
+
+        if ( !dataSourceItem ) {
+            return { $: { id: index, isEmpty: true, index: index } }
+        }
+
+        if ( typeof dataSourceItem === 'string' ) {
+            dataSourceItem = { '': dataSourceItem };
+        }
+
+        if ( unboundMode ) {
+            for ( let j = 0; j < that.dataFields.length; j++ ) {
+                const dataField = that.dataFields ? that.dataFields[ j ] : {};
+
+                itemObject[ dataField.name ] = '';
+            }
+
+            itemObject.$ = {};
+            itemObject.$.id = index;
+            itemObject.$.index = index;
+
+            return itemObject;
+        }
+
+        const dataItem = dataSourceItem;
+
+        if ( dataItem.expanded !== undefined ) {
+            itemObject.expanded = dataItem.expanded;
+
+            if ( dataItem.expanded === 'true' || dataItem.expanded === true || dataItem.expanded === 1 ) {
+                itemObject.expanded = true;
+            }
+            else {
+                itemObject.expanded = false;
+            }
+        }
+
+        if ( that.childrenDataField ) {
+            if ( dataItem[ that.childrenDataField ] !== undefined ) {
+                itemObject.children = dataItem[ that.childrenDataField ];
+            }
+        }
+        else {
+            if ( dataItem.children !== undefined ) {
+                itemObject.children = dataItem.children;
+            }
+            else if ( dataItem.items !== undefined ) {
+                itemObject.children = dataItem.items;
+            }
+        }
+        if ( dataItem.leaf !== undefined ) {
+            itemObject.leaf = dataItem.leaf;
+        }
+
+        if ( dataItem.level !== undefined ) {
+            itemObject.level = dataItem.level;
+        }
+
+        if ( that.keyDataField ) {
+            if ( dataItem[ that.keyDataField ] !== undefined ) {
+                itemObject[ that.keyDataField ] = dataItem[ that.keyDataField ];
+            }
+        }
+
+        if ( that.parentDataField ) {
+            if ( dataItem[ that.parentDataField ] !== undefined ) {
+                itemObject[ that.parentDataField ] = dataItem[ that.parentDataField ];
+            }
+        }
+
+        if ( that.dataFields.length === 0 ) {
+            const names = Object.getOwnPropertyNames( dataSourceItem );
+
+            for ( let i = 0; i < names.length; i++ ) {
+                if ( names[ i ] === '$' ) {
+                    continue;
+                }
+
+                that.dataFields.push( { name: names[ i ], dataType: 'string' } );
+            }
+        }
+
+        for ( let j = 0; j < that.dataFields.length; j++ ) {
+            const dataField = that.dataFields ? that.dataFields[ j ] : {};
+            let value = '';
+
+            dataField.dataType = dataField.type;
+            
+            if ( undefined === dataField || dataField === null ) {
+                continue;
+            }
+
+            if ( dataSourceItem.length ) {
+                value = dataSourceItem[ j ];
+            }
+
+            if ( dataField.map ) {
+                let splitMap = dataField.map.split( that.mapChar );
+
+                if ( splitMap.length > 0 ) {
+                    let dataMappedItem = dataItem;
+
+                    for ( let p = 0; p < splitMap.length; p++ ) {
+                        if ( !dataItem ) {
+                            continue;
+                        }
+
+                        dataMappedItem = dataMappedItem[ splitMap[ p ] ];
+                    }
+
+                    value = dataMappedItem;
+                }
+                else {
+                    value = dataItem[ dataField.map ];
+                }
+            }
+
+            if ( value !== undefined && value !== null ) {
+                value = value.toString();
+            }
+            else {
+                if ( value === undefined && value !== null ) {
+                    value = '';
+                }
+            }
+
+
+            let isEmptyString = false;
+            // searches by both selectors when necessary.
+            if ( value === '' ) {
+                isEmptyString = true;
+                value = dataSourceItem[ dataField.name ];
+
+                if ( value !== undefined && value !== null ) {
+                    if ( dataField.dataType !== 'array' ) {
+                        if ( dataField.dataType !== 'date' ) {
+                            value = value.toString();
+                        }
+                    }
+                }
+                else {
+                    value = '';
+                }
+            }
+
+            if ( value === '[object Object]' && dataField.map && isEmptyString ) {
+                value = '';
+            }
+
+            if ( that._cachedValues[ '' + value + '_' + dataField.dataType ] ) {
+                value = that._cachedValues[ '' + value + '_' + dataField.dataType ];
+            }
+            else {
+                if ( dataField.dataType === 'bool' || dataField.dataType === 'boolean' ) {
+                    if ( value === 'true' || value === '1' ) {
+                        value = true;
+                    }
+                    else if ( value === 'false' || value === '0' ) {
+                        value = false;
+                    }
+                }
+                else {
+                    value = that.deserialize( '' + value, dataField.dataType, true );
+                }
+
+                that._cachedValues[ value + '_' + dataField.dataType ] = value;
+            }
+
+            if ( dataField.dataType !== 'string' && dataField.dataType !== 'boolean' && dataField.dataType !== 'bool' ) {
+                if ( isNaN( value ) || value === -Infinity || value === Infinity ) {
+                    value = 0;
+                }
+            }
+
+            itemObject[ dataField.name ] = value;
+        }
+
+        let itemObjectId = index;
+
+        if ( that.id ) {
+            itemObjectId = dataItem[ that.id ];
+            if ( typeof ( itemObjectId ) === 'object' ) {
+                itemObjectId = index;
+            }
+        }
+        else if ( !that.virtualDataSource && that.dataItemById && that.dataItemById[ itemObjectId ] ) {
+            itemObjectId = that.length;
+        }
+
+        if ( !itemObject.$ ) {
+            itemObject.$ = {};
+        }
+
+        itemObject.$.id = itemObjectId;
+        itemObject.$.index = index;
+
+        return new Object( itemObject );
+    }
+
+    _bindToArray() {
+        const that = this;
+
+        const unboundMode = typeof ( that.dataSource ) === 'number' || that.dataSourceLength;
+        const dataArray = [];
+
+        that.boundSource.canNotify = false;
+
+        for ( let i = 0; i < that.length; i++ ) {
+            const dataSourceItem = unboundMode ? {} : that.dataSource[ i ];
+            const itemObject = that._getDataItem( dataSourceItem, i );
+
+            dataArray.push( itemObject );
+        }
+
+        if ( unboundMode && that.dataSourceLength && that.dataSource.length > 0 ) {
+            for ( let i = 0; i < that.dataSource.length; i++ ) {
+                const cell = that.dataSource[ i ].cell;
+                const value = that.dataSource[ i ].value;
+
+                const row = cell.replace( /[^0-9]/g, '' );
+                const dataField = cell.replace( /[0-9]/g, '' );
+
+                dataArray[ row - 1 ][ dataField ] = value;
+            }
+        }
+
+        that.boundSource = dataArray;
+
+        for ( let i = 0; i < that.length; i++ ) {
+            that[ i ] = that.boundSource[ i ];
+            that.dataItemById[ that[ i ].$.id ] = that[ i ];
+        }
+
+        that.boundSource.canNotify = true;
+    }
+
+    _bindToJSON() {
+        const that = this;
+
+        const dataArray = [];
+
+        const dataEntries = Object.entries( that.dataSource );
+
+        that.boundSource.canNotify = false;
+
+        for ( let i = 0; i < dataEntries.length; i++ ) {
+            const dataSourceItem = dataEntries[ i ];
+            const itemObject = that._getDataItem( dataSourceItem, i );
+
+            dataArray.push( itemObject );
+        }
+
+        that.boundSource = false === that.observable ? dataArray : new JQX.ObservableArray( dataArray );
+
+        for ( let i = 0; i < that.length; i++ ) {
+            that[ i ] = that.boundSource[ i ];
+            that.dataItemById[ that[ i ].$.id ] = that[ i ];
+        }
+
+        that.boundSource.canNotify = true;
+    }
+
+    sortBy( dataField, dataType, orderBy ) {
+        const that = this;
+
+        if ( !dataType ) {
+            for ( let i = 0; i < that.dataFields.length; i++ ) {
+                const field = that.dataFields[ i ];
+
+                if ( field.name === dataField ) {
+                    dataType = field.dataType;
+                    break;
+                }
+            }
+        }
+
+        if ( that.boundHierarchy ) {
+            if ( ( !dataField || dataField.length === 0 ) && that.groupBy.length > 0 ) {
+                that.refreshHierarchy();
+                return;
+            }
+
+            const sortBy = function ( hierarchy ) {
+                that._sort( hierarchy, dataField, orderBy, dataType );
+
+                for ( let i = 0; i < hierarchy.length; i++ ) {
+                    const item = hierarchy[ i ];
+
+                    if ( item[ 'children' ] ) {
+                        sortBy( item[ 'children' ], dataField, orderBy, dataType );
+                    }
+                }
+            }
+
+            sortBy( that.boundHierarchy );
+        }
+        else {
+            that._sort( that.boundSource, dataField, orderBy, dataType );
+        }
+    }
+
+    _createFilter( dataType, filterExpressions ) {
+        const filterOperators = {
+            '=': 'EQUAL',
+            '<>': 'NOT_EQUAL',
+            '<': 'LESS_THAN',
+            '>': 'GREATER_THAN',
+            '<=': 'LESS_THAN_OR_EQUAL',
+            '>=': 'GREATER_THAN_OR_EQUAL',
+            'equal': 'EQUAL',
+            'not equal': 'NOT_EQUAL',
+            'less than': 'LESS_THAN',
+            'greater than': 'GREATER_THAN',
+            'greater than or equal': 'GREATER_THAN_OR_EQUAL',
+            'less than or equal': 'LESS_THAN_OR_EQUAL',
+            'starts with': 'STARTS_WITH',
+            'ends with': 'ENDS_WITH',
+            'null': 'null',
+            '': 'EMPTY',
+            'isblank': 'EMPTY',
+            'isnotblank': 'NOT_EMPTY',
+            'contains': 'CONTAINS',
+            'notcontains': 'DOES_NOT_CONTAIN',
+            'startswith': 'STARTS_WITH',
+            'endswith': 'ENDS_WITH',
+            'NULL': 'NULL',
+            'NOT_NULL': 'NOT_NULL'
+        };
+
+        let filterExpressionsArray = [];
+
+        for ( let i = 0; i < filterExpressions.length; i++ ) {
+            const filterExpression = filterExpressions[ i ];
+
+            const filterExpressionParts = filterExpression.indexOf( '"' ) === -1 ? filterExpression.split( ' ' ) : filterExpression.split( '"' );
+            let filter = [];
+
+            for ( let j = 0; j < filterExpressionParts.length; j++ ) {
+                const part = filterExpressionParts[ j ];
+
+                if ( part !== '' ) {
+                    filter.push( part.trim() );
+                }
+            }
+
+            filterExpressionsArray.push( filter );
+        }
+
+        const filterGroup = new JQX.FilterGroup();
+        const filterGroupOperators = [];
+        const filterSubGroups = [];
+
+        for ( let i = 0; i < filterExpressionsArray.length; i++ ) {
+            const filterExpression = filterExpressionsArray[ i ];
+
+
+            if ( filterExpression.length > 1 ) {
+                const filterSubGroup = new JQX.FilterGroup();
+
+                let operator = 'and';
+                let filterExpressionPartsCounter = 0;
+
+                for ( let j = 0; j < filterExpression.length; j++ ) {
+                    const value = filterExpression[ j ];
+
+                    if ( value === 'and' || value === 'or' ) {
+                        operator = value;
+                        continue;
+                    }
+
+                    filterExpressionPartsCounter++;
+
+                    if ( filterExpressionPartsCounter === 2 ) {
+                        const filter = filterSubGroup.createFilter( dataType, value, filterOperators[ filterExpression[ j - 1 ] ] );
+
+                        filterExpressionPartsCounter = 0;
+
+                        if ( operator ) {
+                            filterSubGroup.addFilter( operator, filter );
+                        }
+                    }
+                }
+
+                filterSubGroups.push( filterSubGroup );
+            }
+            else {
+                const filterGroupOperator = filterExpression[ 0 ];
+
+                if ( filterGroupOperator !== 'and' && filterGroupOperator !== 'or' ) {
+                    throw new Error( 'Filter Exprresion expects "AND" or "OR", but the token is: ' + filterGroupOperator );
+                }
+
+                filterGroupOperators.push( filterGroupOperator );
+            }
+        }
+
+        let operatorsCounter = 0;
+
+        if ( filterSubGroups.length === 1 ) {
+            return filterSubGroups[ 0 ];
+        }
+
+        for ( let i = 0; i < filterSubGroups.length; i++ ) {
+            let operator = filterGroupOperators[ operatorsCounter ];
+
+            if ( ( i + 1 ) % 2 === 0 ) {
+                operatorsCounter++;
+            }
+
+            if ( !operator ) {
+                operator = 'and';
+            }
+
+            filterGroup.addFilter( operator, filterSubGroups[ i ] );
+        }
+
+        return filterGroup;
+    }
+
+    filterBy( dataField, ...filterExpressions ) {
+        const that = this;
+
+
+        const dataType = ( () => {
+            for ( let i = 0; i < that.dataFields.length; i++ ) {
+                const field = that.dataFields[ i ];
+
+                if ( field.name === dataField ) {
+                    return field.dataType;
+                }
+            }
+        } )();
+
+
+        const filterGroup = that._createFilter( dataType, filterExpressions );
+
+        let filteredData = that.boundSource.filter( ( value ) => {
+            const evaluation = filterGroup.evaluate( value[ dataField ] );
+
+            return evaluation;
+        } );
+
+        return filteredData;
+    }
+
+    _filter( filters, operator = 'and' ) {
+        const that = this;
+        const filterGroups = [];
+        const dataFields = [];
+
+        if ( filters.length === 0 ) {
+            that.clearFilter();
+            return;
+        }
+
+        const dataType = ( dataField ) => {
+            for ( let i = 0; i < that.dataFields.length; i++ ) {
+                const field = that.dataFields[ i ];
+
+                if ( field.name === dataField ) {
+                    return field.dataType;
+                }
+            }
+        };
+        let defaultResult, operatorSpecificEval;
+
+        if ( operator === 'and' ) {
+            defaultResult = true;
+            operatorSpecificEval = function ( result, filterGroup, row ) {
+                return result && filterGroup.evaluate( row[ filterGroup.dataField ] );
+            };
+        }
+        else {
+            defaultResult = false;
+            operatorSpecificEval = function ( result, filterGroup, row ) {
+                return result || filterGroup.evaluate( row[ filterGroup.dataField ] );
+            };
+        }
+
+        for ( let i = 0; i < filters.length; i++ ) {
+            const filter = filters[ i ];
+            const dataField = filter[ 0 ];
+            let filterGroup = null;
+
+            if ( filter[ 1 ] instanceof JQX.FilterGroup ) {
+                filterGroup = filter[ 1 ];
+            }
+            else {
+                filterGroup = that._createFilter( dataType( dataField ), filter.splice( 1 ) );
+            }
+
+            if ( filterGroup ) {
+                dataFields.push( dataField );
+                filterGroup.dataField = dataField;
+                filterGroups.push( filterGroup );
+            }
+        }
+
+        if ( that.boundHierarchy ) {
+            const filter = function ( row ) {
+                let result = defaultResult;
+
+                for ( let j = 0; j < filterGroups.length; j++ ) {
+                    const filterGroup = filterGroups[ j ];
+
+                    result = operatorSpecificEval( result, filterGroup, row );
+                }
+
+                row.$.filtered = result;
+
+                return result;
+            }
+
+            const filterBy = function ( hierarchy, parentItem, root ) {
+                let filteredCount = 0;
+
+                for ( let i = 0; i < hierarchy.length; i++ ) {
+                    const item = hierarchy[ i ];
+
+                    filter( item );
+
+                    if ( item.$.filtered ) {
+                        filteredCount++;
+                    }
+
+                    if ( item[ 'children' ] ) {
+                        filterBy( item[ 'children' ], item, parentItem );
+                    }
+                }
+
+                if ( filteredCount > 0 && that.groupBy.length > 0 && parentItem ) {
+                    parentItem.$.filtered = true;
+
+                    if ( root && !root.$.filtered ) {
+                        root.$.filtered = true;
+                    }
+                }
+                else {
+                    if ( filteredCount > 0 && filteredCount !== hierarchy.length && parentItem ) {
+                        parentItem.$.filtered = null;
+
+                        if ( root && !root.$.filtered ) {
+                            root.$.filtered = null;
+                        }
+                    }
+                }
+            }
+
+            filterBy( that.boundHierarchy, null, null );
+        }
+        else {
+            for ( let i = 0; i < that.boundSource.length; i++ ) {
+                const row = that.boundSource[ i ];
+
+                let result = defaultResult;
+
+                for ( let j = 0; j < filterGroups.length; j++ ) {
+                    const filterGroup = filterGroups[ j ];
+
+                    result = operatorSpecificEval( result, filterGroup, row );
+                }
+
+                row.$.filtered = result;
+            }
+        }
+
+        if ( that.onFilter ) {
+            that.onFilter()
+        }
+    }
+
+    clearGroup() {
+        const that = this;
+
+        that.groupBy = [];
+        that.boundHierarchy = null;
+        that.refreshHierarchy();
+
+        if ( that.onGroup ) {
+            that.onGroup()
+        }
+    }
+
+    clearFilter() {
+        const that = this;
+
+        for ( let i = 0; i < that.boundSource.length; i++ ) {
+            const row = that.boundSource[ i ];
+
+            row.$.filtered = true;
+        }
+
+        if ( that.boundHierarchy ) {
+            const filterBy = function ( hierarchy, parentItem, root ) {
+                //let filteredCount = 0;
+
+                for ( let i = 0; i < hierarchy.length; i++ ) {
+                    const item = hierarchy[ i ];
+
+                    item.$.filtered = true;
+
+                    if ( item.$.filtered ) {
+                        //filteredCount++;
+                    }
+
+                    if ( item[ 'children' ] ) {
+                        filterBy( item[ 'children' ], item, parentItem );
+                    }
+                }
+
+                if ( parentItem ) {
+                    parentItem.$.filtered = true;
+
+                    if ( root && !root.$.filtered ) {
+                        root.$.filtered = true;
+                    }
+                }
+            }
+
+            filterBy( that.boundHierarchy, null, null );
+        }
+
+        if ( that.onFilter ) {
+            that.onFilter()
+        }
+    }
+
+    clearSort() {
+        const that = this;
+
+        that._sort( that.boundSource, [], [], [] );
+    }
+
+    _sort( dataSource, sortColumns, directions, dataTypes, customSortingCallback ) {
+        const that = this;
+
+        let isObservableArray = false;
+
+        if ( dataSource.length === 0 ) {
+            return;
+        }
+
+        if ( dataSource && dataSource.constructor && dataSource instanceof JQX.ObservableArray ) {
+            isObservableArray = true;
+        }
+
+        if ( !dataSource || !( Array.isArray( dataSource ) ) || dataSource.length === 0 ||
+            !sortColumns || Array.isArray( sortColumns ) && sortColumns.length === 0 ) {
+            if ( !isObservableArray && !that.boundHierarchy ) {
+                throw new Error( 'sort: Missing or Invalid arguments!' );
+            }
+        }
+
+        if ( typeof sortColumns === 'string' ) {
+            sortColumns = [ sortColumns ];
+        }
+
+        const directionCoefficients = [],
+            compareFunctions = [];
+
+        if ( directions === undefined ) {
+            directions = [];
+        }
+
+        const getCompareFunction = function ( a, knownDataType ) {
+            // gets data type of column (not necessary if the Grid provides this information)
+            const dataType = knownDataType || typeof a;
+            let compareFunction;
+
+            switch ( dataType ) {
+                case 'string':
+                    compareFunction = new Intl.Collator().compare;
+                    break;
+                case 'number':
+                    compareFunction = function ( a, b ) {
+                        return a - b;
+                    };
+                    break;
+                case 'boolean':
+                case 'bool':
+                    compareFunction = function ( a, b ) {
+                        if ( a === b ) {
+                            return 0;
+                        }
+                        else if ( a === false ) {
+                            return -1;
+                        }
+                        else {
+                            return 1;
+                        }
+                    };
+                    break;
+                case 'date':
+                case 'time':
+                case 'dateTime':
+                    if ( a instanceof Date ) {
+                        compareFunction = function ( a, b ) {
+                            return a.getTime() - b.getTime();
+                        };
+                    }
+                    else if ( a instanceof JQX.Utilities.DateTime ||
+                        a instanceof JQX.Utilities.BigNumber ) {
+                        compareFunction = function ( a, b ) {
+                            return a.compare( b );
+                        };
+                    }
+                    break;
+                case 'object':
+                    if ( a instanceof Date ) {
+                        compareFunction = function ( a, b ) {
+                            return a.getTime() - b.getTime();
+                        };
+                    }
+                    else if ( a instanceof JQX.Utilities.DateTime ||
+                        a instanceof JQX.Utilities.BigNumber ) {
+                        compareFunction = function ( a, b ) {
+                            return a.compare( b );
+                        };
+                    }
+                    else if ( a instanceof JQX.Utilities.Complex || ( window.NIComplex && a instanceof window.NIComplex ) ) {
+                        const complexNumericProcessor = new JQX.Utilities.ComplexNumericProcessor();
+
+                        compareFunction = function ( a, b ) {
+                            return complexNumericProcessor.compareComplexNumbers( a, b );
+                        }
+                    }
+
+                    break;
+            }
+
+            return compareFunction;
+        }
+
+        for ( let i = 0; i < sortColumns.length; i++ ) {
+            if ( directions[ i ] === undefined || directions[ i ] === 'asc' || directions[ i ] === 'ascending' ) {
+                directionCoefficients[ i ] = 1;
+            }
+            else {
+                directionCoefficients[ i ] = -1;
+            }
+
+            const value = dataSource[ 0 ][ sortColumns[ i ] ];
+
+            compareFunctions[ i ] = getCompareFunction( value, dataTypes[ i ] );
+        }
+
+        if ( customSortingCallback ) {
+            customSortingCallback( dataSource, sortColumns, directions, compareFunctions );
+            return;
+        }
+
+        dataSource.sort( function ( a, b ) {
+            for ( let i = 0; i < sortColumns.length; i++ ) {
+                const result = compareFunctions[ i ]( a[ sortColumns[ i ] ], b[ sortColumns[ i ] ] );
+
+                if ( result === 0 ) {
+                    if ( sortColumns[ i + 1 ] ) {
+                        continue;
+                    }
+                    else if ( a._index !== undefined ) {
+                        // makes sorting stable
+                        return ( a._index - b._index ) * directionCoefficients[ i ];
+                    }
+
+                    return 0;
+                }
+
+                return result * directionCoefficients[ i ];
+            }
+
+            if ( sortColumns.length === 0 ) {
+                if ( a.$.index < b.$.index ) {
+                    return -1;
+                }
+
+                if ( a.$.index > b.$.index ) {
+                    return 1;
+                }
+
+                return 0;
+
+            }
+        } );
+
+        for ( let i = 0; i < dataSource.length; i++ ) {
+            that[ i ] = dataSource[ i ];
+        }
+    }
+
+    static Filter( dataSource, filterColumns, filterGroups, customFilteringCallback, operator = 'and' ) {
+        let defaultResult, operatorSpecificEval;
+
+        if ( operator === 'and' ) {
+            defaultResult = true;
+            operatorSpecificEval = function ( result, dataItem, filterColumn, filterGroup ) {
+                if ( customFilteringCallback ) {
+                    return result && customFilteringCallback( dataItem, filterColumn, filterGroup );
+                }
+
+                return result && filterGroup.evaluate( dataItem[ filterColumn ] );
+            };
+        }
+        else {
+            defaultResult = false;
+            operatorSpecificEval = function ( result, dataItem, filterColumn, filterGroup ) {
+                if ( customFilteringCallback ) {
+                    return result || customFilteringCallback( dataItem, filterColumn, filterGroup );
+                }
+
+                return result || filterGroup.evaluate( dataItem[ filterColumn ] );
+            };
+        }
+
+        const filteredData = dataSource.filter( ( dataItem ) => {
+            let result = defaultResult;
+
+            for ( let i = 0; i < filterGroups.length; i++ ) {
+                const filterGroup = filterGroups[ i ];
+                const filterColumn = filterColumns[ i ];
+
+                result = operatorSpecificEval( result, dataItem, filterColumn, filterGroup );
+            }
+
+            return result;
+        } );
+
+        return filteredData;
+    }
+
+    filter( filterColumns, filterGroups, customFilteringCallback ) {
+        JQX.ExcelAdapter.Filter( this.boundSource, filterColumns, filterGroups, customFilteringCallback );
+    }
+
+    sort( sortColumns, directions, customSortingCallback ) {
+        JQX.ExcelAdapter.Sort( this.boundSource, sortColumns, directions, customSortingCallback );
+    }
+
+    static Sort( dataSource, sortColumns, directions, customSortingCallback ) {
+        const getCompareFunction = function ( a ) {
+            // gets data type of column (not necessary if the Grid provides this information)
+            const dataType = typeof a;
+            let compareFunction;
+
+            switch ( dataType ) {
+                case 'string':
+                    compareFunction = new Intl.Collator().compare;
+                    break;
+                case 'number':
+                    compareFunction = function ( a, b ) {
+                        return a - b;
+                    };
+                    break;
+                case 'boolean':
+                    compareFunction = function ( a, b ) {
+                        if ( a === b ) {
+                            return 0;
+                        }
+                        else if ( a === false ) {
+                            return -1;
+                        }
+                        else {
+                            return 1;
+                        }
+                    };
+                    break;
+                case 'object':
+                    if ( a instanceof Date ) {
+                        compareFunction = function ( a, b ) {
+                            return a.getTime() - b.getTime();
+                        };
+                    }
+                    else if ( a instanceof JQX.Utilities.DateTime ||
+                        a instanceof BigNumberNG ) {
+                        compareFunction = function ( a, b ) {
+                            return a.compare( b );
+                        };
+                    }
+                    else if ( a instanceof JQX.Utilities.Complex || ( window.NIComplex && a instanceof window.NIComplex ) ) {
+                        const complexNumericProcessor = new JQX.Utilities.ComplexNumericProcessor();
+
+                        compareFunction = function ( a, b ) {
+                            return complexNumericProcessor.compareComplexNumbers( a, b );
+                        }
+                    }
+
+                    break;
+            }
+
+            return compareFunction;
+        }
+
+        if ( !dataSource || !( Array.isArray( dataSource ) ) || dataSource.length === 0 ||
+            !sortColumns || Array.isArray( sortColumns ) && sortColumns.length === 0 ) {
+            return;
+        }
+
+        if ( typeof sortColumns === 'string' ) {
+            sortColumns = [ sortColumns ];
+        }
+
+        const directionCoefficients = [],
+            compareFunctions = [];
+
+        if ( directions === undefined ) {
+            directions = [];
+        }
+
+        for ( let i = 0; i < sortColumns.length; i++ ) {
+            if ( directions[ i ] === undefined || directions[ i ] === 'asc' || directions[ i ] === 'ascending' ) {
+                directionCoefficients[ i ] = 1;
+            }
+            else {
+                directionCoefficients[ i ] = -1;
+            }
+
+            compareFunctions[ i ] = getCompareFunction( dataSource[ 0 ][ sortColumns[ i ] ] );
+        }
+
+        if ( customSortingCallback ) {
+            customSortingCallback( dataSource, sortColumns, directions, compareFunctions );
+            return;
+        }
+
+        const sortedData = dataSource.slice( 0 );
+
+        sortedData.sort( function ( a, b ) {
+            for ( let i = 0; i < sortColumns.length; i++ ) {
+                const result = compareFunctions[ i ]( a[ sortColumns[ i ] ], b[ sortColumns[ i ] ] );
+
+                if ( result === 0 ) {
+                    if ( sortColumns[ i + 1 ] ) {
+                        continue;
+                    }
+                    else if ( a._index !== undefined ) {
+                        // makes sorting stable
+                        return ( a._index - b._index ) * directionCoefficients[ i ];
+                    }
+
+                    return 0;
+                }
+
+                return result * directionCoefficients[ i ];
+            }
+        } );
+
+        return sortedData;
+    }
+}
+
+window.jqxDataSource = DataAdapter;
+
+class Ajax {
+    constructor ( config, callback ) {
+        const that = this;
+
+        that.config = config;
+        that.callback = callback;
+
+        if ( config.autoFetch === false ) {
+            return;
+        }
+
+        that.call( config );
+    }
+
+    call( config ) {
+        const that = this;
+
+        if ( !config ) {
+            config = that.config;
+        }
+
+        let method = 'GET',
+            url = config.url,
+            body = null,
+            async = true;
+
+        if ( config.type ) {
+            method = config.type;
+        }
+
+        if ( config.data ) {
+            if ( method === 'GET' ) {
+                url += '?';
+
+                for ( let prop in config.data ) {
+                    if ( config.data.hasOwnProperty( prop ) ) {
+                        url += encodeURI( prop + '=' + config.data[ prop ] + '&' );
+                    }
+                }
+
+                if ( url.charAt( url.length - 1 ) === '&' ) {
+                    url = url.slice( 0, url.length - 1 );
+                }
+            }
+            else if ( method === 'POST' ) {
+                body = JSON.stringify( config.data );
+            }
+        }
+
+        if ( config && config.async === false && config.dataSourceType !== 'xlsx' ) {
+            async = false;
+        }
+
+        if ( window.fetch !== undefined && async ) {
+            that.ajaxFetch( config, method, url, body );
+        }
+        else {
+            that.ajaxXMLHttpRequest( config, method, url, body, async );
+        }
+    }
+
+    ajaxFetch( config, method, url, body ) {
+        // prepare fetch config
+        const that = this;
+        const fetchInit = { method: method };
+        let parseMethod;
+
+        switch ( config.dataSourceType ) {
+            case 'json':
+                parseMethod = 'json';
+                break;
+            case 'xlsx':
+                parseMethod = 'arrayBuffer';
+                break;
+            default:
+                parseMethod = 'text';
+        }
+
+        if ( config ) {
+            if ( config.contentType ) {
+                fetchInit.headers = new Headers( {
+                    'Content-Type': config.contentType
+                } );
+            }
+        }
+
+        if ( body !== null ) {
+            fetchInit.body = body;
+        }
+
+        let status, fetchTimeout, timeouted;
+
+        if ( config.timeout ) {
+            fetchTimeout = setTimeout( function () {
+                timeouted = true;
+            }, config.timeout );
+        }
+
+        if ( config.beforeSend ) {
+            const beforeSendResult = config.beforeSend( fetchInit, config );
+
+            if ( beforeSendResult === false ) {
+                return;
+            }
+        }
+
+        // fetch resource
+        fetch( url, fetchInit )
+            .then( function ( response ) {
+                if ( timeouted ) {
+                    status = 408;
+                    throw new Error( 'timeout' );
+                }
+
+                if ( fetchTimeout ) {
+                    clearTimeout( fetchTimeout );
+                }
+
+                status = response.status;
+
+                if ( !response.ok ) {
+                    throw new Error( response.statusText );
+                }
+
+                return response[ parseMethod ]();
+            } )
+            .then( function ( data ) {
+                if ( parseMethod === 'arrayBuffer' ) {
+                    return JSZip.loadAsync( data ).then( function ( zipData ) {
+                        // "data" represents the whole XLSX/ZIP file
+                        return zipData.files[ 'xl/worksheets/sheet1.xml' ].async( 'text' ).then( function ( sheet1 ) {
+                            return zipData.files[ 'xl/sharedStrings.xml' ].async( 'text' ).then( function ( sharedStrings ) {
+                                const parsedData = that.parseXLSXData( sheet1, sharedStrings );
+
+                                that.ajaxComplete( config, parsedData, status );
+                            } );
+                        } );
+                    } );
+                }
+                else {
+                    that.ajaxComplete( config, data, status );
+                }
+            } )
+            .catch( function ( error ) {
+                if ( error.message === 'JSZip is not defined' ) {
+                    error.message = 'JSZip is not defined. Please include a reference to JSZip to be able to load data from XLSX files.';
+                }
+
+                if ( config && config.loadError ) {
+                    config.loadError( status, error );
+                }
+
+                if ( that.callback ) {
+                    that.callback( error, status );
+                }
+            } );
+    }
+
+    ajaxXMLHttpRequest( config, method, url, body, async ) {
+        const request = new XMLHttpRequest();
+        const that = this;
+
+        request.open( method, url, async );
+
+        request.ontimeout = function () {
+            if ( config && config.loadError ) {
+                config.loadError( 408, 'timeout' );
+            }
+        };
+
+        request.onload = function () {
+            if ( request.readyState === 4 ) {
+                const status = request.status;
+                let data = request.response;
+
+                if ( status >= 200 && status <= 299 ) {
+                    if ( config.dataSourceType === 'json' ) {
+                        data = JSON.parse( data );
+                    }
+
+                    that.ajaxComplete( config, data, status );
+                }
+                else if ( config && config.loadError ) {
+                    config.loadError( status, data );
+                }
+            }
+        };
+
+        request.onerror = function () {
+            if ( config && config.loadError ) {
+                config.loadError( request.status, request.response );
+            }
+        };
+
+        if ( config && config.contentType ) {
+            request.setRequestHeader( 'Content-Type', config.contentType );
+        }
+
+        if ( async && config.timeout ) {
+            request.timeout = config.timeout;
+        }
+
+        if ( config.beforeSend ) {
+            const beforeSendResult = config.beforeSend( request, config );
+
+            if ( beforeSendResult === false ) {
+                return;
+            }
+        }
+
+        request.send( body );
+    }
+
+    ajaxComplete( config, data, status ) {
+        if ( !config ) {
+            return;
+        }
+
+        if ( config.beforeLoadComplete ) {
+            const processedData = config.beforeLoadComplete( data );
+
+            if ( processedData ) {
+                data = processedData;
+            }
+        }
+
+        if ( config.loadComplete ) {
+            config.loadComplete( data, status );
+        }
+
+        if ( this.callback ) {
+            this.callback( data, status );
+        }
+    }
+
+    parseXLSXData( sheet1, sharedStrings ) {
+        const parser = new DOMParser(),
+            sharedStringsDocument = parser.parseFromString( sharedStrings, 'text/xml' ),
+            sharedStringsContainers = Array.from( sharedStringsDocument.getElementsByTagName( 'si' ) ),
+            sharedStringsCollection = [],
+            sheet1Document = parser.parseFromString( sheet1, 'text/xml' ),
+            rows = Array.from( sheet1Document.getElementsByTagName( 'row' ) ),
+            parsedData = [];
+
+        sharedStringsContainers.forEach( function ( si ) {
+            let texts = si.getElementsByTagName( 't' );
+
+            if ( texts.length === 1 ) {
+                sharedStringsCollection.push( texts[ 0 ].innerHTML );
+            }
+            else {
+                let text = '';
+
+                texts = Array.from( texts );
+                texts.forEach( function ( t ) {
+                    text += t.innerHTML;
+                } );
+                sharedStringsCollection.push( text );
+            }
+        } );
+
+        rows.forEach( function ( row ) {
+            const rowObject = {},
+                cells = Array.from( row.getElementsByTagName( 'c' ) );
+
+            cells.forEach( function ( cell/*, index*/ ) {
+                const column = cell.getAttribute( 'r' ).match( /\D+/ )[ 0 ],
+                    type = cell.getAttribute( 't' ),
+                    xmlValue = cell.getElementsByTagName( 'v' )[ 0 ].innerHTML;
+                let value;
+
+                switch ( type ) {
+                    case 's':
+                        // string
+                        value = sharedStringsCollection[ parseFloat( xmlValue ) ];
+                        break;
+                    case 'b':
+                        // boolean
+                        value = parseFloat( xmlValue ) === 1;
+                        break;
+                    default:
+                        // number or date
+                        value = parseFloat( xmlValue );
+                }
+
+                rowObject[ column ] = value;
+            } );
+
+            parsedData.push( rowObject );
+        } );
+
+        return parsedData;
+    }
+}
+if ($.jqx && $.jqx.dataAdapter) {
+    $.jqx.dataAdapter.Importer = DataAdapter;
+}
+})(jqxBaseFramework);

mercurial