PageSourceSearch

https://www.nstgroup.co.uk/assets/js/tourFilterUpdate.js

js nstgroup.co.uk collected 2026-09-26 00:26:58 UTC 14,652 bytes, 413 lines download raw bytes

1// NOTES:
2/*
3Currently it's relyign ona settimeout in generateFilters().
4    This is potentially an issue if the each loop and dom updates take more than 100ms, we'll need to look at a callback system to ensure this runs the parent callback only when it's actually done
5    
6Memory management isn't great and there are potentially a fair few unused variables and checks, unfortunately time constraints mean I can't review it and rewrite portions of it.
7
8A lot of functions/logic should be in methods but aren't
9
10*/
11if (!Array.prototype.indexOf) {
12    Array.prototype.indexOf = function(obj, start) {
13        for (var i = (start || 0), j = this.length; i < j; i++) {
14            if (this[i] === obj) { return i; }
15        }
16        return -1;
17    }
18}
19
20/**
21  * Declaring class Filters
22  * 
23  */
24var Filters = function() {
25    // constructor method
26    this.filterJSON = {}; // empty vars for JSON (GLOBALS)
27    this.tripsJSON = {}; // empty vars for JSON (GLOBALS)
28    this.loadedJSON = false; // loadedJSON BOOL
29    this.loadingJSON = false;
30    this.filtering = false;
31    this.parentClass = '.js-dynamic-filters';
32    this.selects = this.parentClass + ' select';
33    this.json = 'filters.json';
34    
35    this.tripProperties = []; // create empty array of trip properties that we'll be accessing (this will be populated on page load)
36    this.numFilters = 0;
37    
38    
39    this.firstFilterNames = {'subject': 'Subjects', 'country': 'Country', 'destination': 'Destination', 'schoolTypes': 'School/College type'};
40    this.selectedOptions = {}; // create empty object to store selected options
41    this.numberSelected = 0;
42    
43    // END CONSTRUCTOR
44};
45
46
47/**
48  * Filters method preselected
49  * - make changes to the filters if they are pre selected
50  * 
51  */
52Filters.prototype.preselected = function() {
53    var self = this;
54    
55    // store selected
56    var selected = [];
57    var values = [];
58    var numselected = 0;
59    
60    // for each of the filters get the types
61    $(this.selects).each(function(){
62        var type = $(this).attr('data-type'), // get type
63            val = $(this).val(); // get val
64            
65        self.tripProperties.push(type);
66        self.selectedOptions[type] = ['', []]; // store empty strings as selected options along with empty array for trips
67        
68        if (val !== "") {
69            selected.push(type);
70            values.push(val);
71            numselected++;
72        }
73    });
74    
75    this.numFilters = this.tripProperties.length; // store number of filters present for use later
76    
77    
78    var count = 0;    
79    function process() {
80        if (selected[count] !== "") {
81        $('select[data-type="' + selected[count] + '"]').val(values[count]);
82            var $select = $('select[data-type="' + selected[count] + '"]');
83            self.select($select, function() {
84                if (count < numselected) {
85                    process();
86                }
87            });
88        }
89        count++;
90    }
91    if (numselected > 0) {
92        process();
93    }
94};
95
96/**
97  * Filters method listen
98  * - Listens for select changes, loads JSON then changes and generates relevant filters
99  * 
100  */
101Filters.prototype.listen = function() {
102    var self = this; // store instance of object
103    
104    // Listen for select change event
105    $(self.selects).on('change', function(e){
106        e.preventDefault;
107        
108        self.select($(this), function(){
109            
110        });
111        
112        return false;
113    });
114};
115
116Filters.prototype.select = function($selected, callback) {
117    var self = this;
118    
119    $(this.parentClass).addClass('loading');
120    console.log('start');
121    
122    // store changedselect in object property
123    this.changedSelect = $selected;
124    //add js-filtered class to selected filter if not selected empty value
125    if (this.changedSelect.val() === '') {
126        this.changedSelect.removeClass('js-filtered');
127        this.selectedOptions[this.changedSelect.attr('data-type')] = ['', []];
128    } else {
129        this.changedSelect.addClass('js-filtered');
130    }
131    
132    this.numberSelected = $(this.selects + '.js-filtered').length; // recalc number of selected
133
134    // load the JSON ready for searching
135    this.loadJSON(function(){
136        $(self.parentClass).removeClass('loading');
137        if (typeof callback == "function") {
138            callback();
139        }
140    });
141};
142
143Filters.prototype.loadJSON = function(callback) {
144    // dynamically load json files then 
145    if (!this.loadedJSON) {
146        var self = this; // store instance of object
147        
148        this.loadingJSON = true;
149        
150        // get JSON
151        $.getJSON( self.json, function(data) {
152            self.filterJSON = data["relationships"]; // store relationship JSON
153            self.tripsJSON = data["trips"]; // store trips JSON
154            self.allTrips = data["alltrips"]; // store array of all trips
155            self.loadedJSON = true; // set JSON variable to true
156            self.loadingJSON = false;
157            $(self.parentClass).removeClass('loading');
158        }).done(function(){
159            // do change function only after JSON has loaded
160            self.loadFilters(function(){
161                callback();
162            });
163        });
164    } else {
165        // do change function if the JSON is already loaded straight away
166        this.loadFilters(function(){
167            if (typeof callback == "function") {
168                callback();
169            }
170        });
171    }
172};
173
174
175/**
176  * Filters method loadFilters
177  * - Gets the relational filters and loads and generates them
178  * 
179  */
180Filters.prototype.loadFilters = function(callback) {
181    var self = this,
182        value = self.changedSelect.val(), // get value of changed select
183        filtertype = self.changedSelect.attr('data-type'); // get type of filter that was changed
184    
185    // first get filters then when done, generate filters and load into dom, when complete callback
186    self.getFilters(filtertype, value, function(){
187        self.generateFilters(filtertype, function(){
188            if (typeof callback == "function") {
189                callback();
190            }
191        });
192    });
193};
194
195/**
196  * Method function to getFilters
197  * - specifically gets filters from the stored JSON and passes it to be generated
198  * 
199  */
200Filters.prototype.getFilters = function(filtertype, value, callback) {
201    var basetrips = this.filterJSON[filtertype][value] === undefined ? [] : this.filterJSON[filtertype][value], // get the trips associated with the selection
202        trips = basetrips,
203        self = this;
204    
205    this.selectedOptions[filtertype] = [value, trips]; //store in the Filters selectedOptions property and the associated trips
206    
207    // loop over the select types
208    this.filters = {};
209    for (var i = 0; i < this.numFilters; i++) {
210        this.filters[this.tripProperties[i]] = []; // create empty arrays for storing new filter results in
211        
212        // concatenates all the currently selected options arrays except for the just changed type
213        if (filtertype != this.tripProperties[i]) {
214            trips = trips.concat(this.selectedOptions[this.tripProperties[i]][1]);
215        }
216    }
217    
218    // this will ensure that if the user has selected multuple options then it will only use the trips ID's that are available in all the options
219    var newTrips = trips;
220    if (this.numberSelected > 1) {
221        
222        var newArr = trips, // create new array copying trips
223            newArrCount = newArr.length; // count array for looping
224            
225        trips = []; // empty trips array
226        newTrips = [];
227            
228        for (var i = 0; i < newArrCount; i++) { // loop through elements in new Arr (trips copy)
229            if (newArr[i] !== null) {
230                var tempItem = newArr[i]; // store ith value in a temp var
231                newArr[i] = null; // store value in array as null
232                
233                
234                var itemCount = 1; // init at 1 as there is already one instance
235                // loop through the number selected to work out how many times a value appears in array
236                for (var j = 0; j < this.numberSelected; j++) {
237                    // if it exists
238                    var index = newArr.indexOf(tempItem);
239                    if (index >= 0) {
240                        //up count and set to null
241                        newArr[index] = null;
242                        itemCount++;
243                    }
244                }
245                // check if there is a duplicate in the concatenated array, if there is then add to new trips selected array
246                if (itemCount >= this.numberSelected - 1) {
247                    trips.push(tempItem);
248                }
249                // check if there is a duplicate in the concatenated array, if there is then add to new trips selected array
250                if (itemCount == this.numberSelected) {
251                    newTrips.push(tempItem);
252                }
253            }
254        }
255    }
256    
257    
258    // NEED TO OPTIMISE
259    // To sort out when nothing is selected
260    if (this.numberSelected < 2 && basetrips.length === 0) {
261        trips = this.allTrips;
262    }
263    if (this.numberSelected < 1 && basetrips.length === 0) {
264        newTrips = trips;
265    }
266    var tripsCount = trips.length;
267    
268    
269    generateFilterArray('selected', trips);
270    generateFilterArray('not-selected', newTrips);
271
272
273    //SORT THE NEW FILTERS
274    //iterate over each of the 4 arrays and sort
275    var count = 0;
276    for (var key in self.filters) {
277        self.sort(self.filters[key], function(){
278            if (count >= self.numFilters - 1) {
279                if (typeof callback == "function") {
280                    callback();
281                }
282            }
283            count++;
284        });
285    }
286    
287    function generateFilterArray(type, trips) {
288        // loop through the trips for that selection
289        for (var i = 0, tripsCount = trips.length; i < tripsCount; i++) {
290            var trip = self.tripsJSON[trips[i]]; // get the trip properties for the current trip in array
291            
292            // loop through the trip properties
293            for (var property in trip) {
294                // Check if the property we are on is in the trip properties we created on page load
295                if ($.inArray(property, self.tripProperties) >= 0) {
296                    var operator = '===';
297                    if (type == 'selected') {
298                        operator = '!==';
299                    }
300                    console.log("self.selectedOptions[property][0] " + operator + " ''");
301                    // depending on type either update the ones that have already been selected or not
302                    if (eval("self.selectedOptions[property][0] " + operator + " ''")) {
303                        // check if the property is an array, if it is go in and get each value
304                        if ($.isArray(trip[property])) {
305                            for (var j = 0, propLen = trip[property].length; j < propLen; j++) {
306                                self.addToArray(property, trip[property][j]);
307                            }
308                        } else {
309                            self.addToArray(property, trip[property]);
310                        }
311                    }
312                }
313            }//end trip property loop
314        } //end trip id loop
315    }
316    
317} // END GETFILTERS
318
319
320/**
321  * Filters method to sort an array passed in
322  * 
323  */
324Filters.prototype.sort = function(array, callback){
325    if (!$.isArray(array)) {
326        return false;
327    }
328    array.sort(function(a, b){
329        if(a < b) return -1;
330        if(a > b) return 1;
331        return 0;
332    });
333    if (typeof callback == "function") {
334        callback();
335    }
336};
337
338
339/**
340  * Filters method to addToArray
341  * - takes a key and property and then adds it to the filters array (the generated array of new options)
342  * 
343  */
344Filters.prototype.addToArray = function(key, property){
345    //check if already exists in array
346    if (property !== '' && $.inArray(property, this.filters[key]) == -1) {
347        this.filters[key].push(property); // push property into array
348    }
349};
350
351
352/**
353  * Filters method to generateFilters
354  * - specifically generates the new filters into the DOM
355  * 
356  */
357Filters.prototype.generateFilters = function(filtertype, callback){
358    var self = this,
359        count = 0;
360    //iterate over the selects that weren't changed and update their contents
361    $(self.selects).each(function () {
362        var currentType = $(this).attr('data-type'); // get current type of select box
363        // console.log(filtertype, currentType);            
364        if (currentType != filtertype) {
365            var newOptions = '<option value="">' + self.firstFilterNames[currentType] + '</option>'; // initial option
366            for (var i = 0, len = self.filters[currentType].length; i < len; i++) {
367                var val = self.filters[currentType][i], // get current value to use
368                    selected = false;
369                    
370                // if the value is the currently selected one set selected to true
371                if (val == self.selectedOptions[currentType][0]) {
372                    selected = true;
373                }
374                
375                //create new option string
376                newOptions += '<option value="' + val + '"' + (selected ? ' selected': '') + '>' + val + '</option>';
377            }
378            
379            //empty the current select box and append the new options   
380            $(this).empty().append(newOptions);
381            count++;
382        }
383        
384    });
385
386    var timer = window.setInterval(function(){
387        if (count >= self.numFilters - 1) {
388            console.log('done!');
389            window.clearInterval(timer);
390            if (typeof callback == "function") {
391                callback();
392            }
393        }
394    },20);
395};
396
397
398/**
399  * In main.js we run runFilters once the file has been loaded
400  * 
401  */
402function runFilters(){
403    // don't do anything for IE8 and below
404    if (!$('html').hasClass('lt-ie9')) {
405        var filter = new Filters();
406        
407        //check if any preselected
408        filter.preselected();
409        
410        // listen for change
411        filter.listen();
412    }
413}

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.