mirror of
https://github.com/c0de-archive/CDRParser.git
synced 2025-08-13 01:58:44 +00:00
Initial Commit
This commit is contained in:
504
static/js/angular-tablesort.js
vendored
Normal file
504
static/js/angular-tablesort.js
vendored
Normal file
@@ -0,0 +1,504 @@
|
||||
/*
|
||||
angular-tablesort v1.3.1
|
||||
(c) 2013-2016 Mattias Holmlund, http://mattiash.github.io/angular-tablesort
|
||||
License: MIT
|
||||
*/
|
||||
|
||||
var tableSortModule = angular.module( 'tableSort', [] );
|
||||
|
||||
tableSortModule.provider( 'tableSortConfig', function () {
|
||||
this.filterTemplate = ''; //no filtering by default unless a template is provided
|
||||
this.filterFunction = null; //empty by default - use the built in filter function when left blank
|
||||
this.paginationTemplate = ''; //no pagination by default unless a template is provided
|
||||
this.perPageOptions = [10, 25, 50, 100];
|
||||
this.perPageDefault = this.perPageOptions[0]; //first option by default
|
||||
this.itemNameSingular = 'item';
|
||||
this.itemNamePlural = this.itemNameSingular + 's';
|
||||
this.noDataText = 'No ' + this.itemNamePlural;
|
||||
|
||||
if( !isNaN(this.perPageDefault) && this.perPageOptions.indexOf(this.perPageDefault) === -1 ) {
|
||||
//If a default per-page option was added that isn't in the array, add it and sort the array
|
||||
this.perPageOptions.push(this.perPageDefault);
|
||||
}
|
||||
|
||||
//Sort the array
|
||||
this.perPageOptions.sort(function (a,b) {return a - b;});
|
||||
|
||||
this.$get = function () {
|
||||
return this;
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
tableSortModule.directive( 'tsWrapper', ['$parse', '$compile', function( $parse, $compile ) {
|
||||
'use strict';
|
||||
|
||||
function replaceTemplateTokens($scope, templateString) {
|
||||
//Replace some strings with the proper expressions to be compiled
|
||||
return templateString
|
||||
.replace(/FILTER_STRING/g, 'filtering.filterString')
|
||||
.replace(/CURRENT_PAGE_RANGE/g, 'pagination.getPageRangeString(TOTAL_COUNT)')
|
||||
.replace(/TOTAL_COUNT/g, $scope.itemsArrayExpression + '.length')
|
||||
.replace(/PER_PAGE_OPTIONS/g, 'pagination.perPageOptions')
|
||||
.replace(/ITEMS_PER_PAGE/g, 'pagination.perPage')
|
||||
.replace(/ITEM_NAME_SINGULAR/g, 'itemNameSingular')
|
||||
.replace(/ITEM_NAME_PLURAL/g, 'itemNamePlural')
|
||||
.replace(/FILTERED_COUNT/g, 'filtering.filteredCount')
|
||||
.replace(/CURRENT_PAGE_NUMBER/g, 'pagination.currentPage');
|
||||
}
|
||||
|
||||
return {
|
||||
scope: true,
|
||||
controller: ['$scope', 'tableSortConfig', function($scope, tableSortConfig ) {
|
||||
//local scope vars for this directive
|
||||
$scope.pagination = {
|
||||
template: tableSortConfig.paginationTemplate,
|
||||
perPageOptions: tableSortConfig.perPageOptions.concat(), //copy the array, not a reference
|
||||
perPage: tableSortConfig.perPageDefault,
|
||||
currentPage: 1,
|
||||
getPageRangeString: function(total) {
|
||||
//TODO: Format these numbers, perhaps optionally
|
||||
var maxOnPage = total !== $scope.filtering.filteredCount ? $scope.filtering.filteredCount : total;
|
||||
//This keeps the first page labeled as 1, not 0
|
||||
var startPage = Math.max(($scope.pagination.currentPage - 1) * $scope.pagination.perPage + 1, 1);
|
||||
var endPage = Math.min($scope.pagination.currentPage * $scope.pagination.perPage, maxOnPage);
|
||||
//This prevents the range from showing when the total number of items can be shown on a single page
|
||||
return $scope.filtering.filteredCount === 0 ? '' : (endPage === maxOnPage && startPage === 1 ? '' : startPage + '-') + endPage;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.filtering = {
|
||||
template: tableSortConfig.filterTemplate,
|
||||
filterString: '',
|
||||
filterFunction: tableSortConfig.filterFunction,
|
||||
filteredCount: 0,
|
||||
filterFields: []
|
||||
};
|
||||
|
||||
$scope.itemsArrayExpression = ''; //this will contain the string expression for the array of items in the table
|
||||
$scope.itemNameSingular = tableSortConfig.itemNameSingular;
|
||||
$scope.itemNamePlural = tableSortConfig.itemNamePlural;
|
||||
$scope.noDataText = tableSortConfig.noDataText;
|
||||
$scope.sortExpression = [];
|
||||
$scope.headings = [];
|
||||
|
||||
var parse_sortexpr = function( expr, name ) {
|
||||
return [$parse( expr ), null, false, name ? name : expr];
|
||||
};
|
||||
|
||||
this.setSortField = function( sortexpr, element, name ) {
|
||||
var i;
|
||||
var expr = parse_sortexpr( sortexpr, name );
|
||||
if( $scope.sortExpression.length === 1 && $scope.sortExpression[0][0] === expr[0] ) {
|
||||
if( $scope.sortExpression[0][2] ) {
|
||||
element.removeClass( 'tablesort-desc' );
|
||||
element.addClass( 'tablesort-asc' );
|
||||
$scope.sortExpression[0][2] = false;
|
||||
} else {
|
||||
element.removeClass( 'tablesort-asc' );
|
||||
element.addClass( 'tablesort-desc' );
|
||||
$scope.sortExpression[0][2] = true;
|
||||
}
|
||||
$scope.$emit( 'tablesort:sortOrder', [{
|
||||
name: $scope.sortExpression[0][3],
|
||||
order: $scope.sortExpression[0][2]
|
||||
}]);
|
||||
} else {
|
||||
for( i=0; i<$scope.headings.length; i=i+1 ) {
|
||||
$scope.headings[i]
|
||||
.removeClass( 'tablesort-desc' )
|
||||
.removeClass( 'tablesort-asc' );
|
||||
}
|
||||
element.addClass( 'tablesort-asc' );
|
||||
$scope.sortExpression = [expr];
|
||||
$scope.$emit( 'tablesort:sortOrder', [{
|
||||
name: expr[3],
|
||||
order: expr[2]
|
||||
}]);
|
||||
}
|
||||
};
|
||||
|
||||
this.addSortField = function( sortexpr, element, name ) {
|
||||
var i;
|
||||
var toggle_order = false;
|
||||
var expr = parse_sortexpr( sortexpr, name );
|
||||
for( i=0; i<$scope.sortExpression.length; i=i+1 ) {
|
||||
if( $scope.sortExpression[i][0] === expr[0] ) {
|
||||
if( $scope.sortExpression[i][2] ) {
|
||||
element.removeClass( 'tablesort-desc' );
|
||||
element.addClass( 'tablesort-asc' );
|
||||
$scope.sortExpression[i][2] = false;
|
||||
} else {
|
||||
element.removeClass( 'tablesort-asc' );
|
||||
element.addClass( 'tablesort-desc' );
|
||||
$scope.sortExpression[i][2] = true;
|
||||
}
|
||||
toggle_order = true;
|
||||
}
|
||||
}
|
||||
if( !toggle_order ) {
|
||||
element.addClass( 'tablesort-asc' );
|
||||
$scope.sortExpression.push( expr );
|
||||
}
|
||||
|
||||
$scope.$emit( 'tablesort:sortOrder', $scope.sortExpression.map(function (a) {
|
||||
return {
|
||||
name: a[3],
|
||||
order: a[2]
|
||||
};
|
||||
}));
|
||||
|
||||
};
|
||||
|
||||
this.setTrackBy = function( trackBy ) {
|
||||
$scope.trackBy = trackBy;
|
||||
};
|
||||
|
||||
this.registerHeading = function( headingelement ) {
|
||||
$scope.headings.push( headingelement );
|
||||
};
|
||||
|
||||
this.addFilterField = function( sortexpr, element ) {
|
||||
var expr = parse_sortexpr( sortexpr );
|
||||
$scope.filtering.filterFields.push( expr );
|
||||
};
|
||||
|
||||
this.setArrayExpr = function( dataArrayExp ) {
|
||||
$scope.itemsArrayExpression = dataArrayExp;
|
||||
};
|
||||
}],
|
||||
link: function($scope, $element, $attrs, tsWrapperCtrl) {
|
||||
|
||||
if( $attrs.tsItemName ) {
|
||||
var originalNoDataText = 'No ' + $scope.itemNamePlural;
|
||||
|
||||
//if the table attributes has an item name on it, this takes priority
|
||||
$scope.itemNameSingular = $attrs.tsItemName;
|
||||
|
||||
if( $attrs.tsItemNamePlural ) {
|
||||
//if a plural name was specified, use that
|
||||
$scope.itemNamePlural = $attrs.tsItemNamePlural;
|
||||
} else {
|
||||
//otherwise just add 's' to the singular name
|
||||
$scope.itemNamePlural = $attrs.tsItemName + 's';
|
||||
}
|
||||
|
||||
if( !$attrs.tsNoDataText && $scope.noDataText === originalNoDataText ) {
|
||||
//If the noDataText was NOT specified AND it's in the same 'No ITEMS' format as the default , update it to contain the new item name
|
||||
$scope.noDataText = 'No ' + $scope.itemNamePlural;
|
||||
}
|
||||
}
|
||||
|
||||
if( $attrs.tsNoDataText ) {
|
||||
//If the noDataText was specified, update it
|
||||
$scope.noDataText = $attrs.tsNoDataText;
|
||||
}
|
||||
|
||||
//local attribute usages of the pagination/filtering options will override the global config
|
||||
if( $attrs.tsPerPageOptions ) {
|
||||
$scope.pagination.perPageOptions = $scope.$eval($attrs.tsPerPageOptions);
|
||||
}
|
||||
|
||||
if( $attrs.tsPerPageDefault ) {
|
||||
var defaultPerPage = $scope.$eval($attrs.tsPerPageDefault);
|
||||
if( !isNaN(defaultPerPage) ) {
|
||||
$scope.pagination.perPage = defaultPerPage;
|
||||
if( $scope.pagination.perPageOptions.indexOf($scope.pagination.perPage) === -1 ) {
|
||||
//If a default per-page option was added that isn't in the array, add it and sort the array
|
||||
$scope.pagination.perPageOptions.push($scope.pagination.perPage);
|
||||
$scope.pagination.perPageOptions.sort(function (a,b) {return a - b;});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( $attrs.tsFilterFields ) {
|
||||
var filterFields = $attrs.tsFilterFields.split(',')
|
||||
.filter(function(item) {
|
||||
return item && item.trim() !== '';
|
||||
});
|
||||
for( var i=0; i<filterFields.length; i=i+1 ) {
|
||||
tsWrapperCtrl.addFilterField(filterFields[i]);
|
||||
}
|
||||
}
|
||||
|
||||
var $filterHtml;
|
||||
if( $attrs.tsDisplayFiltering !== 'false' && $scope.filtering.template !== '' && $scope.filtering.filterFields.length>0 ) {
|
||||
var filterString = replaceTemplateTokens($scope, $scope.filtering.template);
|
||||
$filterHtml = $compile(filterString)($scope);
|
||||
//Add filtering HTML BEFORE the table
|
||||
$element.parent()[0].insertBefore($filterHtml[0], $element[0]);
|
||||
}
|
||||
|
||||
if( $attrs.tsFilterFunction ) {
|
||||
//if the table attributes has a filter function on it, this takes priority
|
||||
$scope.filtering.filterFunction = $scope.$eval($attrs.tsFilterFunction);
|
||||
}
|
||||
|
||||
if( !angular.isFunction($scope.filtering.filterFunction) ) {
|
||||
//No custom filter was provided...
|
||||
if( $scope.filtering.filterFields.length===0 ) {
|
||||
//There are no filter fields, so always return everything
|
||||
$scope.filtering.filterFunction = function(item) {
|
||||
return true;
|
||||
};
|
||||
} else {
|
||||
//This is the default filter function. It does a lowercase string match
|
||||
$scope.filtering.filterFunction = function(item) {
|
||||
var shouldInclude = false;
|
||||
for( var i=0; i<$scope.filtering.filterFields.length; i=i+1 ) {
|
||||
if( !shouldInclude ) {
|
||||
var str = ($scope.filtering.filterFields[i][0](item) || '').toString().toLowerCase(); //parse the item's property using the `ts-criteria` value & filter
|
||||
shouldInclude = str.indexOf($scope.filtering.filterString.toLowerCase()) > -1;
|
||||
}
|
||||
}
|
||||
return shouldInclude;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
$scope.filterLimitFun = function(array) {
|
||||
if( !$attrs.tsFilterFunction && $scope.filtering.filterString === '' ) {
|
||||
//Return unfiltered when NOT using a custom filter function and when nothing is being searched
|
||||
$scope.filtering.filteredCount = array.length;
|
||||
return array;
|
||||
}
|
||||
var filteredArr = array.filter($scope.filtering.filterFunction);
|
||||
$scope.filtering.filteredCount = filteredArr.length;
|
||||
return filteredArr;
|
||||
};
|
||||
|
||||
$scope.sortFun = function( a, b ) {
|
||||
var i, aval, bval, descending, filterFun, compResult;
|
||||
var collator = new Intl.Collator(undefined, {sensitivity: 'case'});
|
||||
for( i=0; i<$scope.sortExpression.length; i=i+1 ) {
|
||||
aval = $scope.sortExpression[i][0](a);
|
||||
bval = $scope.sortExpression[i][0](b);
|
||||
filterFun = b[$scope.sortExpression[i][1]];
|
||||
if( filterFun ) {
|
||||
aval = filterFun( aval );
|
||||
bval = filterFun( bval );
|
||||
}
|
||||
if( aval === undefined || aval === null ) {
|
||||
aval = '';
|
||||
}
|
||||
if( bval === undefined || bval === null ) {
|
||||
bval = '';
|
||||
}
|
||||
descending = $scope.sortExpression[i][2];
|
||||
compResult = collator.compare(aval, bval);
|
||||
if( compResult === 1 ) {
|
||||
return descending ? -1 : 1;
|
||||
} else if( compResult === -1 ) {
|
||||
return descending ? 1 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
// All the sort fields were equal. If there is a 'track by'' expression,
|
||||
// use that as a tiebreaker to make the sort result stable.
|
||||
if( $scope.trackBy ) {
|
||||
aval = a[$scope.trackBy];
|
||||
bval = b[$scope.trackBy];
|
||||
if( aval === undefined || aval === null ) {
|
||||
aval = '';
|
||||
}
|
||||
if( bval === undefined || bval === null ) {
|
||||
bval = '';
|
||||
}
|
||||
compResult = collator.compare(aval, bval);
|
||||
if( compResult === 1 ) {
|
||||
return descending ? -1 : 1;
|
||||
} else if( compResult === -1 ) {
|
||||
return descending ? 1 : -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
$scope.pageLimitFun = function(array) {
|
||||
if( $attrs.tsDisplayPagination === 'false' || $scope.pagination.template === '') {
|
||||
//pagination is disabled on this table or there is no template, so return everything
|
||||
return array;
|
||||
}
|
||||
//Only return the items that are in the correct index range for the currently selected page
|
||||
var begin = ($scope.pagination.currentPage - 1) * $scope.pagination.perPage;
|
||||
var end = $scope.pagination.currentPage * $scope.pagination.perPage;
|
||||
var final=[];
|
||||
for( var i=0; i < array.length; i++ ) {
|
||||
if( i >= begin && i < end ) {
|
||||
final.push(array[i]);
|
||||
}
|
||||
}
|
||||
return final;
|
||||
};
|
||||
|
||||
var $paginationHtml;
|
||||
if( $attrs.tsDisplayPagination !== 'false' && $scope.pagination.template !== '' ) {
|
||||
var pagerString = replaceTemplateTokens($scope, $scope.pagination.template);
|
||||
$paginationHtml = $compile(pagerString)($scope);
|
||||
//Add pagination HTML AFTER the table
|
||||
$element.after($paginationHtml);
|
||||
}
|
||||
|
||||
if( $attrs.tsGetTableDataFunction ) {
|
||||
var getter = $parse($attrs.tsGetTableDataFunction);
|
||||
var setter = getter.assign;
|
||||
|
||||
//If this attribute has a value, then we want to turn it into a function on the parent scope
|
||||
//so that it can be passed into other functions and run on the parent controllers as needed
|
||||
var fn = function( shouldApplySorting, shouldApplyFiltering, limitToCurrentPageOnly ) {
|
||||
var arr = $parse($scope.itemsArrayExpression)($scope);
|
||||
|
||||
if( shouldApplySorting ) {
|
||||
arr = arr.sort($scope.sortFun);
|
||||
}
|
||||
|
||||
if( shouldApplyFiltering ) {
|
||||
arr = $scope.filterLimitFun(arr);
|
||||
}
|
||||
|
||||
if( limitToCurrentPageOnly ) {
|
||||
arr = $scope.pageLimitFun(arr);
|
||||
}
|
||||
|
||||
return arr;
|
||||
};
|
||||
|
||||
setter($scope.$parent, fn);
|
||||
}
|
||||
|
||||
$scope.$on( '$destroy', function() {
|
||||
//When the directive is destroyed, also remove the filter & pagination HTML
|
||||
if( $filterHtml ) {
|
||||
$filterHtml.remove();
|
||||
}
|
||||
if( $paginationHtml ) {
|
||||
$paginationHtml.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
tableSortModule.directive( 'tsCriteria', function() {
|
||||
return {
|
||||
require: '^tsWrapper',
|
||||
link: function(scope, element, attrs, tsWrapperCtrl) {
|
||||
var clickingCallback = function(event) {
|
||||
scope.$apply( function() {
|
||||
if( event.shiftKey ) {
|
||||
tsWrapperCtrl.addSortField(attrs.tsCriteria, element, attrs.tsName);
|
||||
} else {
|
||||
tsWrapperCtrl.setSortField(attrs.tsCriteria, element, attrs.tsName);
|
||||
}
|
||||
} );
|
||||
};
|
||||
element.bind( 'click', clickingCallback );
|
||||
element.addClass( 'tablesort-sortable' );
|
||||
if( 'tsDefault' in attrs && attrs.tsDefault !== '0' ) {
|
||||
tsWrapperCtrl.addSortField( attrs.tsCriteria, element, attrs.tsName );
|
||||
if( attrs.tsDefault === 'descending' ) {
|
||||
tsWrapperCtrl.addSortField( attrs.tsCriteria, element, attrs.tsName );
|
||||
}
|
||||
}
|
||||
if( 'tsFilter' in attrs) {
|
||||
tsWrapperCtrl.addFilterField( attrs.tsCriteria, element );
|
||||
}
|
||||
tsWrapperCtrl.registerHeading( element );
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
tableSortModule.directive( 'tsRepeat', ['$compile', '$interpolate', function($compile, $interpolate) {
|
||||
return {
|
||||
terminal: true,
|
||||
multiElement: true,
|
||||
require: '^tsWrapper',
|
||||
priority: 1000000,
|
||||
link: function(scope, element, attrs, tsWrapperCtrl) {
|
||||
var repeatAttrs = ['ng-repeat', 'data-ng-repeat', 'ng-repeat-start', 'data-ng-repeat-start'];
|
||||
var ngRepeatDirective = repeatAttrs[0];
|
||||
var tsRepeatDirective = 'ts-repeat';
|
||||
for (var i = 0; i < repeatAttrs.length; i++) {
|
||||
if (angular.isDefined(element.attr(repeatAttrs[i]))) {
|
||||
ngRepeatDirective = repeatAttrs[i];
|
||||
tsRepeatDirective = ngRepeatDirective.replace(/^(data-)?ng/, '$1ts');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var tsExpr = 'tablesortOrderBy:sortFun | tablesortLimit:filterLimitFun | tablesortLimit:pageLimitFun';
|
||||
var repeatExpr = element.attr(ngRepeatDirective);
|
||||
var repeatExprRegex = /^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(\s+track\s+by\s+[\s\S]+?)?\s*$/;
|
||||
var trackByMatch = repeatExpr.match(/\s+track\s+by\s+\S+?\.(\S+)/);
|
||||
var repeatInMatch = repeatExpr.match(repeatExprRegex);
|
||||
if (trackByMatch) {
|
||||
tsWrapperCtrl.setTrackBy(trackByMatch[1]);
|
||||
}
|
||||
|
||||
//Limit Sort the results, then limit them to only include what matches the filter, then only what's on the current page
|
||||
if (repeatExpr.search(/tablesort/) !== -1) {
|
||||
repeatExpr = repeatExpr.replace(/tablesort/, tsExpr);
|
||||
if (trackByMatch) {
|
||||
//Move the 'track by'' statement to the end
|
||||
repeatExpr = repeatExpr.replace(trackByMatch[0], '') + trackByMatch[0];
|
||||
}
|
||||
} else {
|
||||
repeatExpr = repeatExpr.replace(repeatExprRegex, '$1 in $2 | ' + tsExpr + '$3');
|
||||
}
|
||||
|
||||
if (angular.isUndefined(attrs.tsHideNoData)) {
|
||||
var startSym = $interpolate.startSymbol();
|
||||
var endSym = $interpolate.endSymbol();
|
||||
|
||||
var noDataRow = angular.element(element[0]).clone();
|
||||
noDataRow.removeAttr(ngRepeatDirective);
|
||||
noDataRow.removeAttr(tsRepeatDirective);
|
||||
noDataRow.addClass( 'showIfLast' );
|
||||
noDataRow.children().remove();
|
||||
noDataRow.append( '<td colspan="' + element[0].childElementCount + '">' + startSym + 'noDataText' + endSym + '</td>' );
|
||||
noDataRow = $compile(noDataRow)(scope);
|
||||
element.parent().prepend(noDataRow);
|
||||
}
|
||||
|
||||
//pass the `itemsList` from `item in itemsList` to the master directive as a string so it can be used in expressions
|
||||
tsWrapperCtrl.setArrayExpr(repeatInMatch[2]);
|
||||
|
||||
angular.element(element[0]).attr(ngRepeatDirective, repeatExpr);
|
||||
$compile(element, null, 1000000)(scope);
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
tableSortModule.filter( 'tablesortLimit', function() {
|
||||
return function(array, limitFun) {
|
||||
if(!array) return;
|
||||
return limitFun(array);
|
||||
};
|
||||
} );
|
||||
|
||||
tableSortModule.filter( 'tablesortOrderBy', function() {
|
||||
return function(array, sortfun ) {
|
||||
if(!array) return;
|
||||
var arrayCopy = array.concat();
|
||||
return arrayCopy.sort( sortfun );
|
||||
};
|
||||
} );
|
||||
|
||||
tableSortModule.filter( 'parseInt', function() {
|
||||
return function(input) {
|
||||
return parseInt( input ) || null;
|
||||
};
|
||||
} );
|
||||
|
||||
tableSortModule.filter( 'parseFloat', function() {
|
||||
return function(input) {
|
||||
return parseFloat( input ) || null;
|
||||
};
|
||||
} );
|
||||
|
||||
tableSortModule.filter( 'parseDate', function () {
|
||||
return function (input) {
|
||||
var timestamp = Date.parse(input);
|
||||
return isNaN(timestamp) ? null : timestamp;
|
||||
};
|
||||
} );
|
Reference in New Issue
Block a user