PageSourceSearch

https://www.lavalsehotel.co.kr/assets/core/xe-ui-component/slickgrid/slick.dataview.js

js lavalsehotel.co.kr collected 2026-09-24 13:28:51 UTC 36,226 bytes, 1,220 lines download raw bytes

1(function ($) {
2  $.extend(true, window, {
3    Slick: {
4      Data: {
5        DataView: DataView,
6        Aggregators: {
7          Avg: AvgAggregator,
8          Min: MinAggregator,
9          Max: MaxAggregator,
10          Sum: SumAggregator
11        }
12      }
13    }
14  });
15
16
17  /***
18   * A sample Model implementation.
19   * Provides a filtered view of the underlying data.
20   *
21   * Relies on the data item having an "id" property uniquely identifying it.
22   */
23  function DataView(options) {
24    var self = this;
25
26    var defaults = {
27      groupItemMetadataProvider: null,
28      inlineFilters: false
29    };
30
31
32    // private
33    var idProperty = "id";  // property holding a unique row id
34    var items = [];         // data by index
35    var rows = [];          // data by row
36    var idxById = {};       // indexes by id
37    var rowsById = null;    // rows by id; lazy-calculated
38    var filter = null;      // filter function
39    var updated = null;     // updated item ids
40    var suspend = false;    // suspends the recalculation
41    var sortAsc = true;
42    var fastSortField;
43    var sortComparer;
44    var refreshHints = {};
45    var prevRefreshHints = {};
46    var filterArgs;
47    var filteredItems = [];
48    var compiledFilter;
49    var compiledFilterWithCaching;
50    var filterCache = [];
51
52    // grouping
53    var groupingInfoDefaults = {
54      getter: null,
55      formatter: null,
56      comparer: function(a, b) {
57        return (a.value === b.value ? 0 :
58          (a.value > b.value ? 1 : -1)
59        );
60      },
61      predefinedValues: [],
62      aggregators: [],
63      aggregateEmpty: false,
64      aggregateCollapsed: false,
65      aggregateChildGroups: false,
66      collapsed: false,
67      displayTotalsRow: true,
68      lazyTotalsCalculation: false
69    };
70    var groupingInfos = [];
71    var groups = [];
72    var toggledGroupsByLevel = [];
73    var groupingDelimiter = ':|:';
74
75    var pagesize = 0;
76    var pagenum = 0;
77    var totalRows = 0;
78
79    // events
80    var onRowCountChanged = new Slick.Event();
81    var onRowsChanged = new Slick.Event();
82    var onPagingInfoChanged = new Slick.Event();
83
84    options = $.extend(true, {}, defaults, options);
85
86
87    function beginUpdate() {
88      suspend = true;
89    }
90
91    function endUpdate() {
92      suspend = false;
93      refresh();
94    }
95
96    function setRefreshHints(hints) {
97      refreshHints = hints;
98    }
99
100    function setFilterArgs(args) {
101      filterArgs = args;
102    }
103
104    function updateIdxById(startingIndex) {
105      startingIndex = startingIndex || 0;
106      var id;
107      for (var i = startingIndex, l = items.length; i < l; i++) {
108        id = items[i][idProperty];
109        if (id === undefined) {
110          throw new Error("Each data element must implement a unique 'id' property");
111        }
112        idxById[id] = i;
113      }
114    }
115
116    function ensureIdUniqueness() {
117      var id;
118      for (var i = 0, l = items.length; i < l; i++) {
119        id = items[i][idProperty];
120        if (id === undefined || idxById[id] !== i) {
121          throw new Error("Each data element must implement a unique 'id' property");
122        }
123      }
124    }
125
126    function getItems() {
127      return items;
128    }
129
130    function setItems(data, objectIdProperty) {
131      if (objectIdProperty !== undefined) {
132        idProperty = objectIdProperty;
133      }
134      items = filteredItems = data;
135      idxById = {};
136      updateIdxById();
137      ensureIdUniqueness();
138      refresh();
139    }
140
141    function setPagingOptions(args) {
142      if (args.pageSize != undefined) {
143        pagesize = args.pageSize;
144        pagenum = pagesize ? Math.min(pagenum, Math.max(0, Math.ceil(totalRows / pagesize) - 1)) : 0;
145      }
146
147      if (args.pageNum != undefined) {
148        pagenum = Math.min(args.pageNum, Math.max(0, Math.ceil(totalRows / pagesize) - 1));
149      }
150
151      onPagingInfoChanged.notify(getPagingInfo(), null, self);
152
153      refresh();
154    }
155
156    function getPagingInfo() {
157      var totalPages = pagesize ? Math.max(1, Math.ceil(totalRows / pagesize)) : 1;
158      return {pageSize: pagesize, pageNum: pagenum, totalRows: totalRows, totalPages: totalPages, dataView: self};
159    }
160
161    function sort(comparer, ascending) {
162      sortAsc = ascending;
163      sortComparer = comparer;
164      fastSortField = null;
165      if (ascending === false) {
166        items.reverse();
167      }
168      items.sort(comparer);
169      if (ascending === false) {
170        items.reverse();
171      }
172      idxById = {};
173      updateIdxById();
174      refresh();
175    }
176
177    /***
178     * Provides a workaround for the extremely slow sorting in IE.
179     * Does a [lexicographic] sort on a give column by temporarily overriding Object.prototype.toString
180     * to return the value of that field and then doing a native Array.sort().
181     */
182    function fastSort(field, ascending) {
183      sortAsc = ascending;
184      fastSortField = field;
185      sortComparer = null;
186      var oldToString = Object.prototype.toString;
187      Object.prototype.toString = (typeof field == "function") ? field : function () {
188        return this[field]
189      };
190      // an extra reversal for descending sort keeps the sort stable
191      // (assuming a stable native sort implementation, which isn't true in some cases)
192      if (ascending === false) {
193        items.reverse();
194      }
195      items.sort();
196      Object.prototype.toString = oldToString;
197      if (ascending === false) {
198        items.reverse();
199      }
200      idxById = {};
201      updateIdxById();
202      refresh();
203    }
204
205    function reSort() {
206      if (sortComparer) {
207        sort(sortComparer, sortAsc);
208      } else if (fastSortField) {
209        fastSort(fastSortField, sortAsc);
210      }
211    }
212
213    function getFilteredItems(){
214      return filteredItems;
215    }
216
217
218    function getFilter(){
219      return filter;
220    }
221    
222    function setFilter(filterFn) {
223      filter = filterFn;
224      if (options.inlineFilters) {
225        compiledFilter = compileFilter();
226        compiledFilterWithCaching = compileFilterWithCaching();
227      }
228      refresh();
229    }
230
231    function getGrouping() {
232      return groupingInfos;
233    }
234
235    function setGrouping(groupingInfo) {
236      if (!options.groupItemMetadataProvider) {
237        options.groupItemMetadataProvider = new Slick.Data.GroupItemMetadataProvider();
238      }
239
240      groups = [];
241      toggledGroupsByLevel = [];
242      groupingInfo = groupingInfo || [];
243      groupingInfos = (groupingInfo instanceof Array) ? groupingInfo : [groupingInfo];
244
245      for (var i = 0; i < groupingInfos.length; i++) {
246        var gi = groupingInfos[i] = $.extend(true, {}, groupingInfoDefaults, groupingInfos[i]);
247        gi.getterIsAFn = typeof gi.getter === "function";
248
249        // pre-compile accumulator loops
250        gi.compiledAccumulators = [];
251        var idx = gi.aggregators.length;
252        while (idx--) {
253          gi.compiledAccumulators[idx] = compileAccumulatorLoop(gi.aggregators[idx]);
254        }
255
256        toggledGroupsByLevel[i] = {};
257      }
258
259      refresh();
260    }
261
262    /**
263     * @deprecated Please use {@link setGrouping}.
264     */
265    function groupBy(valueGetter, valueFormatter, sortComparer) {
266      if (valueGetter == null) {
267        setGrouping([]);
268        return;
269      }
270
271      setGrouping({
272        getter: valueGetter,
273        formatter: valueFormatter,
274        comparer: sortComparer
275      });
276    }
277
278    /**
279     * @deprecated Please use {@link setGrouping}.
280     */
281    function setAggregators(groupAggregators, includeCollapsed) {
282      if (!groupingInfos.length) {
283        throw new Error("At least one grouping must be specified before calling setAggregators().");
284      }
285
286      groupingInfos[0].aggregators = groupAggregators;
287      groupingInfos[0].aggregateCollapsed = includeCollapsed;
288
289      setGrouping(groupingInfos);
290    }
291
292    function getItemByIdx(i) {
293      return items[i];
294    }
295
296    function getIdxById(id) {
297      return idxById[id];
298    }
299
300    function ensureRowsByIdCache() {
301      if (!rowsById) {
302        rowsById = {};
303        for (var i = 0, l = rows.length; i < l; i++) {
304          rowsById[rows[i][idProperty]] = i;
305        }
306      }
307    }
308
309    function getRowByItem(item) {
310      ensureRowsByIdCache();
311      return rowsById[item[idProperty]];
312    }
313
314    function getRowById(id) {
315      ensureRowsByIdCache();
316      return rowsById[id];
317    }
318
319    function getItemById(id) {
320      return items[idxById[id]];
321    }
322
323    function mapItemsToRows(itemArray) {
324      var rows = [];
325      ensureRowsByIdCache();
326      for (var i = 0, l = itemArray.length; i < l; i++) {
327        var row = rowsById[itemArray[i][idProperty]];
328        if (row != null) {
329          rows[rows.length] = row;
330        }
331      }
332      return rows;
333    }
334
335    function mapIdsToRows(idArray) {
336      var rows = [];
337      ensureRowsByIdCache();
338      for (var i = 0, l = idArray.length; i < l; i++) {
339        var row = rowsById[idArray[i]];
340        if (row != null) {
341          rows[rows.length] = row;
342        }
343      }
344      return rows;
345    }
346
347    function mapRowsToIds(rowArray) {
348      var ids = [];
349      for (var i = 0, l = rowArray.length; i < l; i++) {
350        if (rowArray[i] < rows.length) {
351          ids[ids.length] = rows[rowArray[i]][idProperty];
352        }
353      }
354      return ids;
355    }
356
357    function updateItem(id, item) {
358      if (idxById[id] === undefined || id !== item[idProperty]) {
359        throw new Error("Invalid or non-matching id");
360      }
361      items[idxById[id]] = item;
362      if (!updated) {
363        updated = {};
364      }
365      updated[id] = true;
366      refresh();
367    }
368
369    function insertItem(insertBefore, item) {
370      items.splice(insertBefore, 0, item);
371      updateIdxById(insertBefore);
372      refresh();
373    }
374
375    function addItem(item) {
376      items.push(item);
377      updateIdxById(items.length - 1);
378      refresh();
379    }
380
381    function deleteItem(id) {
382      var idx = idxById[id];
383      if (idx === undefined) {
384        throw new Error("Invalid id");
385      }
386      delete idxById[id];
387      items.splice(idx, 1);
388      updateIdxById(idx);
389      refresh();
390    }
391
392    function sortedAddItem(item) {
393      if(!sortComparer) {
394        throw new Error("sortedAddItem() requires a sort comparer, use sort()");
395      }
396      insertItem(sortedIndex(item), item);
397    }
398
399    function sortedUpdateItem(id, item) {
400      if (idxById[id] === undefined || id !== item[idProperty]) {
401        throw new Error("Invalid or non-matching id " + idxById[id]);
402      }
403      if(!sortComparer) {
404        throw new Error("sortedUpdateItem() requires a sort comparer, use sort()");
405      }
406      var oldItem = getItemById(id);
407      if(sortComparer(oldItem, item) !== 0) {
408        // item affects sorting -> must use sorted add
409        deleteItem(id);
410        sortedAddItem(item);
411      }
412      else { // update does not affect sorting -> regular update works fine
413        updateItem(id, item);
414      }
415    }
416
417    function sortedIndex(searchItem) {
418      var low = 0, high = items.length;
419
420      while (low < high) {
421        var mid = low + high >>> 1;
422        if (sortComparer(items[mid], searchItem) === -1) {
423          low = mid + 1;
424        }
425        else {
426          high = mid;
427        }
428      }
429      return low;
430    }
431      
432    function getLength() {
433      return rows.length;
434    }
435
436    function getItem(i) {
437      var item = rows[i];
438
439      // if this is a group row, make sure totals are calculated and update the title
440      if (item && item.__group && item.totals && !item.totals.initialized) {
441        var gi = groupingInfos[item.level];
442        if (!gi.displayTotalsRow) {
443          calculateTotals(item.totals);
444          item.title = gi.formatter ? gi.formatter(item) : item.value;
445        }
446      }
447      // if this is a totals row, make sure it's calculated
448      else if (item && item.__groupTotals && !item.initialized) {
449        calculateTotals(item);
450      }
451
452      return item;
453    }
454
455    function getItemMetadata(i) {
456      var item = rows[i];
457      if (item === undefined) {
458        return null;
459      }
460
461      // overrides for grouping rows
462      if (item.__group) {
463        return options.groupItemMetadataProvider.getGroupRowMetadata(item);
464      }
465
466      // overrides for totals rows
467      if (item.__groupTotals) {
468        return options.groupItemMetadataProvider.getTotalsRowMetadata(item);
469      }
470
471      return null;
472    }
473
474    function expandCollapseAllGroups(level, collapse) {
475      if (level == null) {
476        for (var i = 0; i < groupingInfos.length; i++) {
477          toggledGroupsByLevel[i] = {};
478          groupingInfos[i].collapsed = collapse;
479        }
480      } else {
481        toggledGroupsByLevel[level] = {};
482        groupingInfos[level].collapsed = collapse;
483      }
484      refresh();
485    }
486
487    /**
488     * @param level {Number} Optional level to collapse.  If not specified, applies to all levels.
489     */
490    function collapseAllGroups(level) {
491      expandCollapseAllGroups(level, true);
492    }
493
494    /**
495     * @param level {Number} Optional level to expand.  If not specified, applies to all levels.
496     */
497    function expandAllGroups(level) {
498      expandCollapseAllGroups(level, false);
499    }
500
501    function expandCollapseGroup(level, groupingKey, collapse) {
502      toggledGroupsByLevel[level][groupingKey] = groupingInfos[level].collapsed ^ collapse;
503      refresh();
504    }
505
506    /**
507     * @param varArgs Either a Slick.Group's "groupingKey" property, or a
508     *     variable argument list of grouping values denoting a unique path to the row.  For
509     *     example, calling collapseGroup('high', '10%') will collapse the '10%' subgroup of
510     *     the 'high' group.
511     */
512    function collapseGroup(varArgs) {
513      var args = Array.prototype.slice.call(arguments);
514      var arg0 = args[0];
515      if (args.length == 1 && arg0.indexOf(groupingDelimiter) != -1) {
516        expandCollapseGroup(arg0.split(groupingDelimiter).length - 1, arg0, true);
517      } else {
518        expandCollapseGroup(args.length - 1, args.join(groupingDelimiter), true);
519      }
520    }
521
522    /**
523     * @param varArgs Either a Slick.Group's "groupingKey" property, or a
524     *     variable argument list of grouping values denoting a unique path to the row.  For
525     *     example, calling expandGroup('high', '10%') will expand the '10%' subgroup of
526     *     the 'high' group.
527     */
528    function expandGroup(varArgs) {
529      var args = Array.prototype.slice.call(arguments);
530      var arg0 = args[0];
531      if (args.length == 1 && arg0.indexOf(groupingDelimiter) != -1) {
532        expandCollapseGroup(arg0.split(groupingDelimiter).length - 1, arg0, false);
533      } else {
534        expandCollapseGroup(args.length - 1, args.join(groupingDelimiter), false);
535      }
536    }
537
538    function getGroups() {
539      return groups;
540    }
541
542    function extractGroups(rows, parentGroup) {
543      var group;
544      var val;
545      var groups = [];
546      var groupsByVal = {};
547      var r;
548      var level = parentGroup ? parentGroup.level + 1 : 0;
549      var gi = groupingInfos[level];
550
551      for (var i = 0, l = gi.predefinedValues.length; i < l; i++) {
552        val = gi.predefinedValues[i];
553        group = groupsByVal[val];
554        if (!group) {
555          group = new Slick.Group();
556          group.value = val;
557          group.level = level;
558          group.groupingKey = (parentGroup ? parentGroup.groupingKey + groupingDelimiter : '') + val;
559          groups[groups.length] = group;
560          groupsByVal[val] = group;
561        }
562      }
563
564      for (var i = 0, l = rows.length; i < l; i++) {
565        r = rows[i];
566        val = gi.getterIsAFn ? gi.getter(r) : r[gi.getter];
567        group = groupsByVal[val];
568        if (!group) {
569          group = new Slick.Group();
570          group.value = val;
571          group.level = level;
572          group.groupingKey = (parentGroup ? parentGroup.groupingKey + groupingDelimiter : '') + val;
573          groups[groups.length] = group;
574          groupsByVal[val] = group;
575        }
576
577        group.rows[group.count++] = r;
578      }
579
580      if (level < groupingInfos.length - 1) {
581        for (var i = 0; i < groups.length; i++) {
582          group = groups[i];
583          group.groups = extractGroups(group.rows, group);
584        }
585      }      
586
587      groups.sort(groupingInfos[level].comparer);
588
589      return groups;
590    }
591
592    function calculateTotals(totals) {
593      var group = totals.group;
594      var gi = groupingInfos[group.level];
595      var isLeafLevel = (group.level == groupingInfos.length);
596      var agg, idx = gi.aggregators.length;
597
598      if (!isLeafLevel && gi.aggregateChildGroups) {
599        // make sure all the subgroups are calculated
600        var i = group.groups.length;
601        while (i--) {
602          if (!group.groups[i].totals.initialized) {
603            calculateTotals(group.groups[i].totals);
604          }
605        }
606      }
607
608      while (idx--) {
609        agg = gi.aggregators[idx];
610        agg.init();
611        if (!isLeafLevel && gi.aggregateChildGroups) {
612          gi.compiledAccumulators[idx].call(agg, group.groups);
613        } else {
614          gi.compiledAccumulators[idx].call(agg, group.rows);
615        }
616        agg.storeResult(totals);
617      }
618      totals.initialized = true;
619    }
620
621    function addGroupTotals(group) {
622      var gi = groupingInfos[group.level];
623      var totals = new Slick.GroupTotals();
624      totals.group = group;
625      group.totals = totals;
626      if (!gi.lazyTotalsCalculation) {
627        calculateTotals(totals);
628      }
629    }
630
631    function addTotals(groups, level) {
632      level = level || 0;
633      var gi = groupingInfos[level];
634      var groupCollapsed = gi.collapsed;
635      var toggledGroups = toggledGroupsByLevel[level];      
636      var idx = groups.length, g;
637      while (idx--) {
638        g = groups[idx];
639
640        if (g.collapsed && !gi.aggregateCollapsed) {
641          continue;
642        }
643
644        // Do a depth-first aggregation so that parent group aggregators can access subgroup totals.
645        if (g.groups) {
646          addTotals(g.groups, level + 1);
647        }
648
649        if (gi.aggregators.length && (
650            gi.aggregateEmpty || g.rows.length || (g.groups && g.groups.length))) {
651          addGroupTotals(g);
652        }
653
654        g.collapsed = groupCollapsed ^ toggledGroups[g.groupingKey];
655        g.title = gi.formatter ? gi.formatter(g) : g.value;
656      }
657    } 
658
659    function flattenGroupedRows(groups, level) {
660      level = level || 0;
661      var gi = groupingInfos[level];
662      var groupedRows = [], rows, gl = 0, g;
663      for (var i = 0, l = groups.length; i < l; i++) {
664        g = groups[i];
665        groupedRows[gl++] = g;
666
667        if (!g.collapsed) {
668          rows = g.groups ? flattenGroupedRows(g.groups, level + 1) : g.rows;
669          for (var j = 0, jj = rows.length; j < jj; j++) {
670            groupedRows[gl++] = rows[j];
671          }
672        }
673
674        if (g.totals && gi.displayTotalsRow && (!g.collapsed || gi.aggregateCollapsed)) {
675          groupedRows[gl++] = g.totals;
676        }
677      }
678      return groupedRows;
679    }
680
681    function getFunctionInfo(fn) {
682      var fnRegex = /^function[^(]*\(([^)]*)\)\s*{([\s\S]*)}$/;
683      var matches = fn.toString().match(fnRegex);
684      return {
685        params: matches[1].split(","),
686        body: matches[2]
687      };
688    }
689
690    function compileAccumulatorLoop(aggregator) {
691      var accumulatorInfo = getFunctionInfo(aggregator.accumulate);
692      var fn = new Function(
693          "_items",
694          "for (var " + accumulatorInfo.params[0] + ", _i=0, _il=_items.length; _i<_il; _i++) {" +
695              accumulatorInfo.params[0] + " = _items[_i]; " +
696              accumulatorInfo.body +
697          "}"
698      );
699      fn.displayName = fn.name = "compiledAccumulatorLoop";
700      return fn;
701    }
702
703    function compileFilter() {
704      var filterInfo = getFunctionInfo(filter);
705
706      var filterPath1 = "{ continue _coreloop; }$1";
707      var filterPath2 = "{ _retval[_idx++] = $item$; continue _coreloop; }$1";
708      // make some allowances for minification - there's only so far we can go with RegEx
709      var filterBody = filterInfo.body
710          .replace(/return false\s*([;}]|\}|$)/gi, filterPath1)
711          .replace(/return!1([;}]|\}|$)/gi, filterPath1)
712          .replace(/return true\s*([;}]|\}|$)/gi, filterPath2)
713          .replace(/return!0([;}]|\}|$)/gi, filterPath2)
714          .replace(/return ([^;}]+?)\s*([;}]|$)/gi,
715          "{ if ($1) { _retval[_idx++] = $item$; }; continue _coreloop; }$2");
716
717      // This preserves the function template code after JS compression,
718      // so that replace() commands still work as expected.
719      var tpl = [
720        //"function(_items, _args) { ",
721        "var _retval = [], _idx = 0; ",
722        "var $item$, $args$ = _args; ",
723        "_coreloop: ",
724        "for (var _i = 0, _il = _items.length; _i < _il; _i++) { ",
725        "$item$ = _items[_i]; ",
726        "$filter$; ",
727        "} ",
728        "return _retval; "
729        //"}"
730      ].join("");
731      tpl = tpl.replace(/\$filter\$/gi, filterBody);
732      tpl = tpl.replace(/\$item\$/gi, filterInfo.params[0]);
733      tpl = tpl.replace(/\$args\$/gi, filterInfo.params[1]);
734
735      var fn = new Function("_items,_args", tpl);
736      fn.displayName = fn.name = "compiledFilter";
737      return fn;
738    }
739
740    function compileFilterWithCaching() {
741      var filterInfo = getFunctionInfo(filter);
742
743      var filterPath1 = "{ continue _coreloop; }$1";
744      var filterPath2 = "{ _cache[_i] = true;_retval[_idx++] = $item$; continue _coreloop; }$1";
745      // make some allowances for minification - there's only so far we can go with RegEx
746      var filterBody = filterInfo.body
747          .replace(/return false\s*([;}]|\}|$)/gi, filterPath1)
748          .replace(/return!1([;}]|\}|$)/gi, filterPath1)
749          .replace(/return true\s*([;}]|\}|$)/gi, filterPath2)
750          .replace(/return!0([;}]|\}|$)/gi, filterPath2)
751          .replace(/return ([^;}]+?)\s*([;}]|$)/gi,
752          "{ if ((_cache[_i] = $1)) { _retval[_idx++] = $item$; }; continue _coreloop; }$2");
753
754      // This preserves the function template code after JS compression,
755      // so that replace() commands still work as expected.
756      var tpl = [
757        //"function(_items, _args, _cache) { ",
758        "var _retval = [], _idx = 0; ",
759        "var $item$, $args$ = _args; ",
760        "_coreloop: ",
761        "for (var _i = 0, _il = _items.length; _i < _il; _i++) { ",
762        "$item$ = _items[_i]; ",
763        "if (_cache[_i]) { ",
764        "_retval[_idx++] = $item$; ",
765        "continue _coreloop; ",
766        "} ",
767        "$filter$; ",
768        "} ",
769        "return _retval; "
770        //"}"
771      ].join("");
772      tpl = tpl.replace(/\$filter\$/gi, filterBody);
773      tpl = tpl.replace(/\$item\$/gi, filterInfo.params[0]);
774      tpl = tpl.replace(/\$args\$/gi, filterInfo.params[1]);
775
776      var fn = new Function("_items,_args,_cache", tpl);
777      fn.displayName = fn.name = "compiledFilterWithCaching";
778      return fn;
779    }
780
781    function uncompiledFilter(items, args) {
782      var retval = [], idx = 0;
783
784      for (var i = 0, ii = items.length; i < ii; i++) {
785        if (filter(items[i], args)) {
786          retval[idx++] = items[i];
787        }
788      }
789
790      return retval;
791    }
792
793    function uncompiledFilterWithCaching(items, args, cache) {
794      var retval = [], idx = 0, item;
795
796      for (var i = 0, ii = items.length; i < ii; i++) {
797        item = items[i];
798        if (cache[i]) {
799          retval[idx++] = item;
800        }
800 else if (filter(item, args)) {
801          retval[idx++] = item;
802          cache[i] = true;
803        }
804      }
805
806      return retval;
807    }
808
809    function getFilteredAndPagedItems(items) {
810      if (filter) {
811        var batchFilter = options.inlineFilters ? compiledFilter : uncompiledFilter;
812        var batchFilterWithCaching = options.inlineFilters ? compiledFilterWithCaching : uncompiledFilterWithCaching;
813
814        if (refreshHints.isFilterNarrowing) {
815          filteredItems = batchFilter(filteredItems, filterArgs);
816        } else if (refreshHints.isFilterExpanding) {
817          filteredItems = batchFilterWithCaching(items, filterArgs, filterCache);
818        } else if (!refreshHints.isFilterUnchanged) {
819          filteredItems = batchFilter(items, filterArgs);
820        }
821      } else {
822        // special case:  if not filtering and not paging, the resulting
823        // rows collection needs to be a copy so that changes due to sort
824        // can be caught
825        filteredItems = pagesize ? items : items.concat();
826      }
827
828      // get the current page
829      var paged;
830      if (pagesize) {
831        if (filteredItems.length <= pagenum * pagesize) {
832          if (filteredItems.length === 0) {
833            pagenum = 0;
834          } else {
835            pagenum = Math.floor((filteredItems.length - 1) / pagesize);
836          }
837        }
838        paged = filteredItems.slice(pagesize * pagenum, pagesize * pagenum + pagesize);
839      } else {
840        paged = filteredItems;
841      }
842      return {totalRows: filteredItems.length, rows: paged};
843    }
844
845    function getRowDiffs(rows, newRows) {
846      var item, r, eitherIsNonData, diff = [];
847      var from = 0, to = newRows.length;
848
849      if (refreshHints && refreshHints.ignoreDiffsBefore) {
850        from = Math.max(0,
851            Math.min(newRows.length, refreshHints.ignoreDiffsBefore));
852      }
853
854      if (refreshHints && refreshHints.ignoreDiffsAfter) {
855        to = Math.min(newRows.length,
856            Math.max(0, refreshHints.ignoreDiffsAfter));
857      }
858
859      for (var i = from, rl = rows.length; i < to; i++) {
860        if (i >= rl) {
861          diff[diff.length] = i;
862        } else {
863          item = newRows[i];
864          r = rows[i];
865
866          if ((groupingInfos.length && (eitherIsNonData = (item.__nonDataRow) || (r.__nonDataRow)) &&
867              item.__group !== r.__group ||
868              item.__group && !item.equals(r))
869              || (eitherIsNonData &&
870              // no good way to compare totals since they are arbitrary DTOs
871              // deep object comparison is pretty expensive
872              // always considering them 'dirty' seems easier for the time being
873              (item.__groupTotals || r.__groupTotals))
874              || item[idProperty] != r[idProperty]
875              || (updated && updated[item[idProperty]])
876              ) {
877            diff[diff.length] = i;
878          }
879        }
880      }
881      return diff;
882    }
883
884    function recalc(_items) {
885      rowsById = null;
886
887      if (refreshHints.isFilterNarrowing != prevRefreshHints.isFilterNarrowing ||
888          refreshHints.isFilterExpanding != prevRefreshHints.isFilterExpanding) {
889        filterCache = [];
890      }
891
892      var filteredItems = getFilteredAndPagedItems(_items);
893      totalRows = filteredItems.totalRows;
894      var newRows = filteredItems.rows;
895
896      groups = [];
897      if (groupingInfos.length) {
898        groups = extractGroups(newRows);
899        if (groups.length) {
900          addTotals(groups);
901          newRows = flattenGroupedRows(groups);
902        }
903      }
904
905      var diff = getRowDiffs(rows, newRows);
906
907      rows = newRows;
908
909      return diff;
910    }
911
912    function refresh() {
913      if (suspend) {
914        return;
915      }
916
917      var countBefore = rows.length;
918      var totalRowsBefore = totalRows;
919
920      var diff = recalc(items, filter); // pass as direct refs to avoid closure perf hit
921
922      // if the current page is no longer valid, go to last page and recalc
923      // we suffer a performance penalty here, but the main loop (recalc) remains highly optimized
924      if (pagesize && totalRows < pagenum * pagesize) {
925        pagenum = Math.max(0, Math.ceil(totalRows / pagesize) - 1);
926        diff = recalc(items, filter);
927      }
928
929      updated = null;
930      prevRefreshHints = refreshHints;
931      refreshHints = {};
932
933      if (totalRowsBefore !== totalRows) {
934        onPagingInfoChanged.notify(getPagingInfo(), null, self);
935      }
936      if (countBefore !== rows.length) {
937        onRowCountChanged.notify({previous: countBefore, current: rows.length, dataView: self}, null, self);
938      }
939      if (diff.length > 0) {
940        onRowsChanged.notify({rows: diff, dataView: self}, null, self);
941      }
942    }
943
944    /***
945     * Wires the grid and the DataView together to keep row selection tied to item ids.
946     * This is useful since, without it, the grid only knows about rows, so if the items
947     * move around, the same rows stay selected instead of the selection moving along
948     * with the items.
949     *
950     * NOTE:  This doesn't work with cell selection model.
951     *
952     * @param grid {Slick.Grid}
952 The grid to sync selection with.
953     * @param preserveHidden {Boolean} Whether to keep selected items that go out of the
954     *     view due to them getting filtered out.
955     * @param preserveHiddenOnSelectionChange {Boolean} Whether to keep selected items
956     *     that are currently out of the view (see preserveHidden) as selected when selection
957     *     changes.
958     * @return {Slick.Event} An event that notifies when an internal list of selected row ids
959     *     changes.  This is useful since, in combination with the above two options, it allows
960     *     access to the full list selected row ids, and not just the ones visible to the grid.
961     * @method syncGridSelection
962     */
963    function syncGridSelection(grid, preserveHidden, preserveHiddenOnSelectionChange) {
964      var self = this;
965      var inHandler;
966      var selectedRowIds = self.mapRowsToIds(grid.getSelectedRows());
967      var onSelectedRowIdsChanged = new Slick.Event();
968
969      function setSelectedRowIds(rowIds) {
970        if (selectedRowIds.join(",") == rowIds.join(",")) {
971          return;
972        }
973
974        selectedRowIds = rowIds;
975
976        onSelectedRowIdsChanged.notify({
977          "grid": grid,
978          "ids": selectedRowIds,
979          "dataView": self
980        }, new Slick.EventData(), self);
981      }
982
983      function update() {
984        if (selectedRowIds.length > 0) {
985          inHandler = true;
986          var selectedRows = self.mapIdsToRows(selectedRowIds);
987          if (!preserveHidden) {
988            setSelectedRowIds(self.mapRowsToIds(selectedRows));       
989          }
990          grid.setSelectedRows(selectedRows);
991          inHandler = false;
992        }
993      }
994
995      grid.onSelectedRowsChanged.subscribe(function(e, args) {
996        if (inHandler) { return; }
997        var newSelectedRowIds = self.mapRowsToIds(grid.getSelectedRows());
998        if (!preserveHiddenOnSelectionChange || !grid.getOptions().multiSelect) {
999          setSelectedRowIds(newSelectedRowIds);
1000        } else {
1001          // keep the ones that are hidden
1002          var existing = $.grep(selectedRowIds, function(id) { return self.getRowById(id) === undefined; });
1003          // add the newly selected ones
1004          setSelectedRowIds(existing.concat(newSelectedRowIds));
1005        }
1006      });
1007
1008      this.onRowsChanged.subscribe(update);
1009
1010      this.onRowCountChanged.subscribe(update);
1011
1012      return onSelectedRowIdsChanged;
1013    }
1014
1015    function syncGridCellCssStyles(grid, key) {
1016      var hashById;
1017      var inHandler;
1018
1019      // since this method can be called after the cell styles have been set,
1020      // get the existing ones right away
1021      storeCellCssStyles(grid.getCellCssStyles(key));
1022
1023      function storeCellCssStyles(hash) {
1024        hashById = {};
1025        for (var row in hash) {
1026          var id = rows[row][idProperty];
1027          hashById[id] = hash[row];
1028        }
1029      }
1030
1031      function update() {
1032        if (hashById) {
1033          inHandler = true;
1034          ensureRowsByIdCache();
1035          var newHash = {};
1036          for (var id in hashById) {
1037            var row = rowsById[id];
1038            if (row != undefined) {
1039              newHash[row] = hashById[id];
1040            }
1041          }
1042          grid.setCellCssStyles(key, newHash);
1043          inHandler = false;
1044        }
1045      }
1046
1047      grid.onCellCssStylesChanged.subscribe(function(e, args) {
1048        if (inHandler) { return; }
1049        if (key != args.key) { return; }
1050        if (args.hash) {
1051          storeCellCssStyles(args.hash);
1052        } else {
1053          grid.onCellCssStylesChanged.unsubscribe(styleChanged);
1054          self.onRowsChanged.unsubscribe(update);
1055          self.onRowCountChanged.unsubscribe(update);          
1056        }
1057      });
1058
1059      this.onRowsChanged.subscribe(update);
1060
1061      this.onRowCountChanged.subscribe(update);
1062    }
1063
1064    $.extend(this, {
1065      // methods
1066      "beginUpdate": beginUpdate,
1067      "endUpdate": endUpdate,
1068      "setPagingOptions": setPagingOptions,
1069      "getPagingInfo": getPagingInfo,
1070      "getItems": getItems,
1071      "setItems": setItems,
1072      "setFilter": setFilter,
1073      "getFilter": getFilter,
1074      "getFilteredItems": getFilteredItems,
1075      "sort": sort,
1076      "fastSort": fastSort,
1077      "reSort": reSort,
1078      "setGrouping": setGrouping,
1079      "getGrouping": getGrouping,
1080      "groupBy": groupBy,
1081      "setAggregators": setAggregators,
1082      "collapseAllGroups": collapseAllGroups,
1083      "expandAllGroups": expandAllGroups,
1084      "collapseGroup": collapseGroup,
1085      "expandGroup": expandGroup,
1086      "getGroups": getGroups,
1087      "getIdxById": getIdxById,
1088      "getRowByItem": getRowByItem,
1089      "getRowById": getRowById,
1090      "getItemById": getItemById,
1091      "getItemByIdx": getItemByIdx,
1092      "mapItemsToRows": mapItemsToRows,
1093      "mapRowsToIds": mapRowsToIds,
1094      "mapIdsToRows": mapIdsToRows,
1095      "setRefreshHints": setRefreshHints,
1096      "setFilterArgs": setFilterArgs,
1097      "refresh": refresh,
1098      "updateItem": updateItem,
1099      "insertItem": insertItem,
1100      "addItem": addItem,
1101      "deleteItem": deleteItem,
1102      "sortedAddItem": sortedAddItem,
1103      "sortedUpdateItem": sortedUpdateItem,
1104      "syncGridSelection": syncGridSelection,
1105      "syncGridCellCssStyles": syncGridCellCssStyles,
1106
1107      // data provider methods
1108      "getLength": getLength,
1109      "getItem": getItem,
1110      "getItemMetadata": getItemMetadata,
1111
1112      // events
1113      "onRowCountChanged": onRowCountChanged,
1114      "onRowsChanged": onRowsChanged,
1115      "onPagingInfoChanged": onPagingInfoChanged
1116    });
1117  }
1118
1119  function AvgAggregator(field) {
1120    this.field_ = field;
1121
1122    this.init = function () {
1123      this.count_ = 0;
1124      this.nonNullCount_ = 0;
1125      this.sum_ = 0;
1126    };
1127
1128    this.accumulate = function (item) {
1129      var val = item[this.field_];
1130      this.count_++;
1131      if (val != null && val !== "" && !isNaN(val)) {
1132        this.nonNullCount_++;
1133        this.sum_ += parseFloat(val);
1134      }
1135    };
1136
1137    this.storeResult = function (groupTotals) {
1138      if (!groupTotals.avg) {
1139        groupTotals.avg = {};
1140      }
1141      if (this.nonNullCount_ != 0) {
1142        groupTotals.avg[this.field_] = this.sum_ / this.nonNullCount_;
1143      }
1144    };
1145  }
1146
1147  function MinAggregator(field) {
1148    this.field_ = field;
1149
1150    this.init = function () {
1151      this.min_ = null;
1152    };
1153
1154    this.accumulate = function (item) {
1155      var val = item[this.field_];
1156      if (val != null && val !== "" && !isNaN(val)) {
1157        if (this.min_ == null || val < this.min_) {
1158          this.min_ = val;
1159        }
1160      }
1161    };
1162
1163    this.storeResult = function (groupTotals) {
1164      if (!groupTotals.min) {
1165        groupTotals.min = {};
1166      }
1167      groupTotals.min[this.field_] = this.min_;
1168    }
1169  }
1170
1171  function MaxAggregator(field) {
1172    this.field_ = field;
1173
1174    this.init = function () {
1175      this.max_ = null;
1176    };
1177
1178    this.accumulate = function (item) {
1179      var val = item[this.field_];
1180      if (val != null && val !== "" && !isNaN(val)) {
1181        if (this.max_ == null || val > this.max_) {
1182          this.max_ = val;
1183        }
1184      }
1185    };
1186
1187    this.storeResult = function (groupTotals) {
1188      if (!groupTotals.max) {
1189        groupTotals.max = {};
1190      }
1191      groupTotals.max[this.field_] = this.max_;
1192    }
1193  }
1194
1195  function SumAggregator(field) {
1196    this.field_ = field;
1197
1198    this.init = function () {
1199      this.sum_ = null;
1200    };
1201
1202    this.accumulate = function (item) {
1203      var val = item[this.field_];
1204      if (val != null && val !== "" && !isNaN(val)) {
1205        this.sum_ += parseFloat(val);
1206      }
1207    };
1208
1209    this.storeResult = function (groupTotals) {
1210      if (!groupTotals.sum) {
1211        groupTotals.sum = {};
1212      }
1213      groupTotals.sum[this.field_] = this.sum_;
1214    }
1215  }
1216
1217  // TODO:  add more built-in aggregators
1218  // TODO:  merge common aggregators in one to prevent needles iterating
1219
1220})(jQuery);

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.