diff -r db4de4f37b65 -r 67bf19c50fcc www/jqwidgets/jqximport.js --- /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);