PageSourceSearch

https://www.bluerealty.com/themes/public-common-files/bower_compon…/angular-tablesort/js/angular-tablesort.js

js bluerealty.com collected 2026-10-08 19:05:30 UTC 20,314 bytes, 462 lines download raw bytes

1/*
2angular-tablesort v1.2.0
3(c) 2013-2016 Mattias Holmlund, http://mattiash.github.io/angular-tablesort
4License: MIT
5*/
6
7var tableSortModule = angular.module( 'tableSort', [] );
8
9tableSortModule.provider('tableSortConfig', function () {
10    this.filterTemplate = ""; //no filtering by default unless a template is provided
11    this.filterFunction = null; //empty by default - use the built in filter function when left blank
12    this.paginationTemplate = ""; //no pagination by default unless a template is provided
13    this.perPageOptions = [10, 25, 50, 100];
14    this.perPageDefault = this.perPageOptions[0]; //first option by default
15    this.itemNameSingular = "item";
16    this.itemNamePlural = this.itemNameSingular + "s";
17    this.noDataText = "No " + this.itemNamePlural;
18
19    if(!isNaN(this.perPageDefault) && this.perPageOptions.indexOf(this.perPageDefault) === -1){
20        //If a default per-page option was added that isn't in the array, add it and sort the array
21        this.perPageOptions.push(this.perPageDefault);
22    }
23
24    //Sort the array
25    this.perPageOptions.sort(function (a,b) {return a - b;});
26
27    this.$get = function () {
28        return this;
29    };
30
31});
32
33tableSortModule.directive('tsWrapper', ['$parse', '$compile', function( $parse, $compile ) {
34    'use strict';
35
36    function replaceTemplateTokens($scope, templateString){
37        //Replace some strings with the proper expressions to be compiled
38        return templateString
39            .replace(/FILTER_STRING/g, "filtering.filterString")
40            .replace(/CURRENT_PAGE_RANGE/g, "pagination.getPageRangeString(TOTAL_COUNT)")
41            .replace(/TOTAL_COUNT/g, $scope.pagination.itemsArrayExpression + ".length")
42            .replace(/PER_PAGE_OPTIONS/g, 'pagination.perPageOptions')
43            .replace(/ITEMS_PER_PAGE/g, 'pagination.perPage')
44            .replace(/ITEM_NAME_SINGULAR/g, 'itemNameSingular')
45            .replace(/ITEM_NAME_PLURAL/g, 'itemNamePlural')
46            .replace(/FILTERED_COUNT/g, "filtering.filteredCount")
47            .replace(/CURRENT_PAGE_NUMBER/g, "pagination.currentPage");
48    }
49
50    return {
51        scope: true,
52        controller: ['$scope', 'tableSortConfig', function($scope, tableSortConfig ) {
53            //local scope vars for this directive
54            $scope.pagination = {
55                template: tableSortConfig.paginationTemplate,
56                perPageOptions: tableSortConfig.perPageOptions.concat(), //copy the array, not a reference
57                perPage: tableSortConfig.perPageDefault,
58                itemsArrayExpression: "", //this will contain the string expression for the array of items in the table
59                currentPage: 1,
60                getPageRangeString: function(total) {
61                    //TODO: Format these numbers, perhaps optionally
62                    var maxOnPage = total !== $scope.filtering.filteredCount ? $scope.filtering.filteredCount : total;
63                    var startPage = ($scope.pagination.currentPage - 1) * ($scope.pagination.perPage + 1);
64                    var endPage = Math.min($scope.pagination.currentPage * $scope.pagination.perPage, maxOnPage);
65                    return $scope.filtering.filteredCount === 0 ? "" : startPage + "-" + endPage;
66                }
67            };
68
69            $scope.filtering = {
70                template: tableSortConfig.filterTemplate,
71                filterString: "",
72                filterFunction: tableSortConfig.filterFunction,
73                filteredCount: 0,
74                filterFields: []
75            };
76
77            $scope.itemNameSingular = tableSortConfig.itemNameSingular;
78            $scope.itemNamePlural = tableSortConfig.itemNamePlural;
79            $scope.noDataText = tableSortConfig.noDataText;
80            $scope.sortExpression = [];
81            $scope.headings = [];
82
83            //Private vars
84            var parse_sortexpr = function( expr ) {
85                return [$parse( expr ), null, false];
86            };
87
88            //Public directive vars for the other directives that depend on this
89            this.setSortField = function( sortexpr, element ) {
90                var i;
91                var expr = parse_sortexpr( sortexpr );
92                if( $scope.sortExpression.length === 1
93                    && $scope.sortExpression[0][0] === expr[0] ) {
94                    if( $scope.sortExpression[0][2] ) {
95                        element.removeClass( "tablesort-desc" );
96                        element.addClass( "tablesort-asc" );
97                        $scope.sortExpression[0][2] = false;
98                    }
99                    else {
100                        element.removeClass( "tablesort-asc" );
101                        element.addClass( "tablesort-desc" );
102                        $scope.sortExpression[0][2] = true;
103                    }
104                }
105                else {
106                    for( i=0; i<$scope.headings.length; i=i+1 ) {
107                        $scope.headings[i]
108                            .removeClass( "tablesort-desc" )
109                            .removeClass( "tablesort-asc" );
110                    }
111                    element.addClass( "tablesort-asc" );
112                    $scope.sortExpression = [expr];
113                }
114            };
115
116            this.addSortField = function( sortexpr, element ) {
117                var i;
118                var toggle_order = false;
119                var expr = parse_sortexpr( sortexpr );
120                for( i=0; i<$scope.sortExpression.length; i=i+1 ) {
121                    if( $scope.sortExpression[i][0] === expr[0] ) {
122                        if( $scope.sortExpression[i][2] ) {
123                            element.removeClass( "tablesort-desc" );
124                            element.addClass( "tablesort-asc" );
125                            $scope.sortExpression[i][2] = false;
126                        }
127                        else {
128                            element.removeClass( "tablesort-asc" );
129                            element.addClass( "tablesort-desc" );
130                            $scope.sortExpression[i][2] = true;
131                        }
132                        toggle_order = true;
133                    }
134                }
135                if( !toggle_order ) {
136                    element.addClass( "tablesort-asc" );
137                    $scope.sortExpression.push( expr );
138                }
139            };
140
141            this.setTrackBy = function( trackBy ) {
142                $scope.trackBy = trackBy;
143            };
144
145            this.registerHeading = function( headingelement ) {
146                $scope.headings.push( headingelement );
147            };
148
149            this.addFilterField = function( sortexpr, element ) {
150                var expr = parse_sortexpr( sortexpr );
151                $scope.filtering.filterFields.push( expr );
152            };
153
154            this.setDataForPager = function( dataArrayExp ) {
155                $scope.pagination.itemsArrayExpression = dataArrayExp;
156            };
157        }],
158        link: function($scope, $element, $attrs, tsWrapperCtrl){
159
160            if($attrs.tsItemName){
161                var originalNoDataText = "No " + $scope.itemNamePlural;
162
163                //if the table attributes has an item name on it, this takes priority
164                $scope.itemNameSingular = $attrs.tsItemName;
165
166                if($attrs.tsItemNamePlural){
167                    //if a plural name was specified, use that
168                    $scope.itemNamePlural = $attrs.tsItemNamePlural;
169                }else{
170                    //otherwise just add "s" to the singular name
171                    $scope.itemNamePlural = $attrs.tsItemName + "s";
172                }
173
174                if(!$attrs.tsNoDataText && $scope.noDataText === originalNoDataText){
175                    //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
176                    $scope.noDataText = "No " + $scope.itemNamePlural;
177                }
178            }
179
180            if($attrs.tsNoDataText){
181                //If the noDataText was specified, update it
182                $scope.noDataText = $attrs.tsNoDataText;
183            }
184
185            //local attribute usages of the pagination/filtering options will override the global config
186            if($attrs.tsPerPageOptions){
187                $scope.pagination.perPageOptions = $scope.$eval($attrs.tsPerPageOptions);
188            }
189
190            if($attrs.tsPerPageDefault){
191                var defaultPerPage = $scope.$eval($attrs.tsPerPageDefault);
192                if(!isNaN(defaultPerPage)){
193                    $scope.pagination.perPage = defaultPerPage;
194                    if($scope.pagination.perPageOptions.indexOf($scope.pagination.perPage) === -1){
195                        //If a default per-page option was added that isn't in the array, add it and sort the array
196                        $scope.pagination.perPageOptions.push($scope.pagination.perPage);
197                        $scope.pagination.perPageOptions.sort(function (a,b) {return a - b;});
198                    }
199                }
200            }
201
202            if($attrs.tsFilterFields){
203                var filterFields = $attrs.tsFilterFields.split(",")
204                    .filter(function(item){
205                        return item && item.trim() !== "";
206                    });
207                for( var i=0; i<filterFields.length; i=i+1 ){
208                    tsWrapperCtrl.addFilterField(filterFields[i]);
209                }
210            }
211
212            var $filterHtml;
213            if($attrs.tsDisplayFiltering !== "false" && $scope.filtering.template !== "" && $scope.filtering.filterFields.length>0){
214                var filterString = replaceTemplateTokens($scope, $scope.filtering.template);
215                $filterHtml = $compile(filterString)($scope);
216                //Add filtering HTML BEFORE the table
217                $element.parent()[0].insertBefore($filterHtml[0], $element[0]);
218            }
219
220            if($attrs.tsFilterFunction){
221                //if the table attributes has a filter function on it, this takes priority
222                $scope.filtering.filterFunction = $scope.$eval($attrs.tsFilterFunction);
223            }
224
225            if(!angular.isFunction($scope.filtering.filterFunction)){
226                //No custom filter was provided...
227                if($scope.filtering.filterFields.length===0){
228                    //There are no filter fields, so always return everything
229                    $scope.filtering.filterFunction = function(item){
230                        return true;
231                    };
232                }else{
233                    //This is the default filter function. It does a lowercase string match
234                    $scope.filtering.filterFunction = function(item) {
235                        var shouldInclude = false;
236                        for( var i=0; i<$scope.filtering.filterFields.length; i=i+1 ) {
237                            if(!shouldInclude){
238                                var str = ($scope.filtering.filterFields[i][0](item) || "").toString().toLowerCase(); //parse the item's property using the `ts-criteria` value & filter
239                                shouldInclude = str.indexOf($scope.filtering.filterString.toLowerCase()) > -1;
240                            }
241                        }
242                        return shouldInclude;
243                    };
244                }
245            }
246
247            $scope.filterLimitFun = function(array){
248                if(!$attrs.tsFilterFunction && $scope.filtering.filterString === ""){
249                    //Return unfiltered when NOT using a custom filter function and when nothing is being searched
250                    $scope.filtering.filteredCount = array.length;
251                    return array;
252                }
253                var filteredArr = array.filter($scope.filtering.filterFunction);
254                $scope.filtering.filteredCount = filteredArr.length;
255                return filteredArr;
256            };
257
258            $scope.sortFun = function( a, b ) {
259                var i, aval, bval, descending, filterFun;
260                for( i=0; i<$scope.sortExpression.length; i=i+1 ){
261                    aval = $scope.sortExpression[i][0](a);
262                    bval = $scope.sortExpression[i][0](b);
263                    filterFun = b[$scope.sortExpression[i][1]];
264                    if( filterFun ) {
265                        aval = filterFun( aval );
266                        bval = filterFun( bval );
267                    }
268                    if( aval === undefined || aval === null ) {
269                        aval = "";
270                    }
271                    if( bval === undefined || bval === null ) {
272                    bval = "";
273                    }
274                    descending = $scope.sortExpression[i][2];
275                    if( aval > bval ) {
276                        return descending ? -1 : 1;
277                    }
278                    else if( aval < bval ) {
279                        return descending ? 1 : -1;
280                    }
281                }
282
283                // All the sort fields were equal. If there is a "track by" expression,
284                // use that as a tiebreaker to make the sort result stable.
285                if( $scope.trackBy ) {
286                    aval = a[$scope.trackBy];
287                    bval = b[$scope.trackBy];
288                    if( aval === undefined || aval === null ) {
289                        aval = "";
290                    }
291                    if( bval === undefined || bval === null ) {
292                        bval = "";
293                    }
294                    if( aval > bval ) {
295                        return descending ? -1 : 1;
296                    }
297                    else if( aval < bval ) {
298                        return descending ? 1 : -1;
299                    }
300                }
301                return 0;
302            };
303
304            $scope.pageLimitFun = function(array){
305                if($attrs.tsDisplayPagination === "false"){
306                    //pagination is disabled, so return everything
307                    return array;
308                }
309                //Only return the items that are in the correct index range for the currently selected page
310                var begin = ($scope.pagination.currentPage-1) * $scope.pagination.perPage;
311                var end = $scope.pagination.currentPage * $scope.pagination.perPage;
312                var final=[];
313                for(var i=0; i < array.length; i++){
314                    if(i >= begin && i < end){
315                        final.push(array[i]);
316                    }
317                }
318                return final;
319            };
320
321            var $paginationHtml;
322            if($attrs.tsDisplayPagination !== "false" && $scope.pagination.template !== ""){
323                var pagerString = replaceTemplateTokens($scope, $scope.pagination.template);
324                $paginationHtml = $compile(pagerString)($scope);
325                //Add pagination HTML AFTER the table
326                $element.after($paginationHtml);
327            }
328
329            $scope.$on("$destroy", function(){
330                //When the directive is destroyed, also remove the filter & pagination HTML
331                if($filterHtml){
332                    $filterHtml.remove();
333                }
334                if($paginationHtml){
335                    $paginationHtml.remove();
336                }
337            });
338        }
339    };
340}]);
341
342tableSortModule.directive('tsCriteria', function() {
343    return {
344        require: "^tsWrapper",
345        link: function(scope, element, attrs, tsWrapperCtrl) {
346            var clickingCallback = function(event) {
347                scope.$apply( function() {
348                    if( event.shiftKey ) {
349                        tsWrapperCtrl.addSortField(attrs.tsCriteria, element);
350                    }
351                    else {
352                        tsWrapperCtrl.setSortField(attrs.tsCriteria, element);
353                    }
354                } );
355            };
356            element.bind('click', clickingCallback);
357            element.addClass('tablesort-sortable');
358            if( "tsDefault" in attrs && attrs.tsDefault !== "0" ) {
359                tsWrapperCtrl.addSortField( attrs.tsCriteria, element );
360                if( attrs.tsDefault === "descending" ) {
361                    tsWrapperCtrl.addSortField( attrs.tsCriteria, element );
362                }
363            }
364            if( "tsFilter" in attrs) {
365                tsWrapperCtrl.addFilterField( attrs.tsCriteria, element );
366            }
367            tsWrapperCtrl.registerHeading( element );
368        }
369    };
370});
371
372tableSortModule.directive("tsRepeat", ['$compile', function($compile) {
373    return {
374        terminal: true,
375        multiElement: true,
376        require: "^tsWrapper",
377        priority: 1000000,
378        link: function(scope, element, attrs, tsWrapperCtrl) {
379            var repeatAttrs = ["ng-repeat", "data-ng-repeat", "ng-repeat-start", "data-ng-repeat-start"];
380            var ngRepeatDirective = repeatAttrs[0];
381            var tsRepeatDirective = "ts-repeat";
382            for (var i = 0; i < repeatAttrs.length; i++) {
383                if (angular.isDefined(element.attr(repeatAttrs[i]))) {
384                    ngRepeatDirective = repeatAttrs[i];
385                    tsRepeatDirective = ngRepeatDirective.replace(/^(data-)?ng/, '$1ts');
386                    break;
387                }
388            }
389
390            var tsExpr = "tablesortOrderBy:sortFun | tablesortLimit:filterLimitFun | tablesortLimit:pageLimitFun";
391            var repeatExpr = element.attr(ngRepeatDirective);
392            var repeatExprRegex = /^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(\s+track\s+by\s+[\s\S]+?)?\s*$/;
393            var trackByMatch = repeatExpr.match(/\s+track\s+by\s+\S+?\.(\S+)/);
394            var repeatInMatch = repeatExpr.match(repeatExprRegex);
395            if (trackByMatch) {
396                tsWrapperCtrl.setTrackBy(trackByMatch[1]);
397            }
398
399            //Limit Sort the results, then limit them to only include what matches the filter, then only what's on the current page
400            if (repeatExpr.search(/tablesort/) !== -1) {
401                repeatExpr = repeatExpr.replace(/tablesort/, tsExpr);
402                if (trackByMatch) {
403                    //Move the "track by" statement to the end
404                    repeatExpr = repeatExpr.replace(trackByMatch[0], "") + trackByMatch[0];
405                }
406            } else {
407                repeatExpr = repeatExpr.replace(repeatExprRegex, "$1 in $2 | " + tsExpr + "$3");
408            }
409
410            if (angular.isUndefined(attrs.tsHideNoData)) {
411                var noDataRow = angular.element(element[0]).clone();
412                noDataRow.removeAttr(ngRepeatDirective);
413                noDataRow.removeAttr(tsRepeatDirective);
414                noDataRow.addClass("showIfLast");
415                noDataRow.children().remove();
416                noDataRow.append('<td colspan="' + element[0].childElementCount + '">{{noDataText}}</td>');
417                noDataRow = $compile(noDataRow)(scope);
418                element.parent().prepend(noDataRow);
419            }
420
421            //pass the `itemsList` from `item in itemsList` to the master directive as a string so it can be used in expressions
422            tsWrapperCtrl.setDataForPager(repeatInMatch[2]);
423
424            angular.element(element[0]).attr(ngRepeatDirective, repeatExpr);
425            $compile(element, null, 1000000)(scope);
426        }
427    };
428}]);
429
430tableSortModule.filter( 'tablesortLimit', function(){
431    return function(array, limitFun) {
432    if(!array) return;
433    return limitFun(array);
434    };
435} );
436
437tableSortModule.filter( 'tablesortOrderBy', function(){
438    return function(array, sortfun ) {
439        if(!array) return;
440        var arrayCopy = array.concat();
441        return arrayCopy.sort( sortfun );
442    };
443} );
444
445tableSortModule.filter( 'parseInt', function(){
446    return function(input) {
447        return parseInt( input ) || null;
448    };
449} );
450
451tableSortModule.filter( 'parseFloat', function(){
452    return function(input) {
453        return parseFloat( input ) || null;
454    };
455} );
456
457tableSortModule.filter('parseDate', function () {
458    return function (input) {
459        var timestamp = Date.parse(input);
460        return isNaN(timestamp) ? null : timestamp;
461    };
462} );

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.