PageSourceSearch

https://www.harrywinston.com/-/media/base-themes/hw-search-theme/s…&hash=6526DADB7375C339EF62A534FE11864B

js harrywinston.com collected 2026-10-02 06:20:30 UTC 18,995 bytes, 456 lines download raw bytes

1/**
2 * Location filter component functionality
3 * @module locationfilter
4 * @param  {jQuery} $ Instance of jQuery
5 * @param  {Document} document dom document object
6 * @return {Object} list of methods for working with location filter
7*/
8XA.component.search.locationfilter = (function ($, document) {
9
10    "use strict";
11  /**
12    * This object stores all public api methods
13    * @type {Object.<Methods>}
14    * @memberOf module:locationfilter
15    */
16    var api = {},
17        locationFilters = [],
18        views = [],
19        queryModel,
20        urlHelperModel,
21        initialized = false,
22        scriptsLoaded = false,
23        mapsConnector = XA.connector.mapsConnector,
24        LocationFilterModel,
25        LocationFilterView,
26        initialize;
27	/**
28     * @memberOf module:locationfilter
29     * @alias module:locationfilter.initialize
30     * For each location filter component on a page creates instance of 
31     * ["LocationFilterModel"]{@link module:locationfilter.LocationFilterModel} and 
32     * ["LocationFilterView"]{@link module:locationfilter.LocationFilterView} 
33     * @private
34     */
35    initialize = function() {
36        var i, view;
37        for (i = 0; i < locationFilters.length; i++) {
38            //creates Backbone.js views - one view per component on the page
39            view = new LocationFilterView({el: locationFilters[i], model: new LocationFilterModel()});
40        }
41    };
42	/**
43    * @name module:locationfilter.LocationFilterModel
44    * @constructor
45    * @augments Backbone.Model
46    */
47    LocationFilterModel = Backbone.Model.extend(
48	        /** @lends module:locationfilter.LocationFilterModel.prototype **/
49	{
50		/**
51        * Default model options
52        * @default
53        */
54        defaults: {
55            dataProperties: {},
56            sig: []
57        },
58		/**
59        * Initializes ["Bloodhound"]{@link https://github.com/twitter/typeahead.js/blob/master/doc/bloodhound.md}
60        * suggestion engine with dataProperties data
61        * @memberof module:locationfilter.LocationFilterModel
62        * @alias module:locationfilter.LocationFilterModel#initAutocompleteEngine
63        */
64        initAutocompleteEngine : function() {
65            var _this = this,
66                searchEngine;
67
68            //initializes predictive only when number of predictions is not zero
69            if(_this.get("dataProperties").p > 0){
70                    searchEngine = new Bloodhound({
71                        datumTokenizer: Bloodhound.tokenizers.obj.whitespace("name"),
72                        queryTokenizer: Bloodhound.tokenizers.whitespace,
73                        limit :  _this.get("dataProperties").p,
74                        remote : {
75                            url : "-",
76                            replace : function(){
77                                return Date.now().toString();
78                            },
79                            transport : function(options, onSuccess, onError){
80                                var queryParams =  _this.get("queryParams");
81                                if(!queryParams.text){
82                                    onSuccess([]);
83                                    return;
84                                }
85                                mapsConnector.locationAutocomplete(queryParams,
86                                    function (results) { //success
87                                        var simplifiedResults = results.map(function(result) {
88                                            if (result.hasOwnProperty("text")) {
89                                                return result.text;
90                                            }
91                                            return result;
92                                        });
93
94                                        onSuccess(simplifiedResults);
95                                    },
96                                    function(){ //fail
97                                        onError("Could not autocomplete");
98                                    });
99                            }
100                        }
101                });
102                searchEngine.initialize();
103                this.set({"searchEngine" : searchEngine});
104            }
105        }
106    });
107    /**
108    * @name module:locationfilter.LocationFilterView
109    * @constructor
110    * @augments Backbone.View
111    */
112    LocationFilterView = XA.component.search.baseView.extend(
113	    /** @lends module:locationfilter.LocationFilterView.prototype **/
114	{
115		/**
116        * Initially sets data to model, initializes typeahead.js, and watches events on which
117        * view should be updated
118        * @memberof module:locationfilter.LocationFilterView
119        */
120        initialize: function () {
121            var inst = this,
122                dataProperties = this.$el.data(),
123                $textBox = this.$el.find(".location-search-box-input"),
124                signatures = dataProperties.properties.searchResultsSignature.split(',');
125
126            if (dataProperties.properties.searchResultsSignature === null) {
127                dataProperties.properties.searchResultsSignature = "";
128            }
129
130            this.model.set({ dataProperties: dataProperties.properties });
131            this.model.set({ sig: signatures});
132            this.model.set({ queryParams: { maxResults: dataProperties.p, text: "" } });
133            this.model.initAutocompleteEngine();
134
135            var autocompleteEngine = this.model.get("searchEngine");
136
137            if(autocompleteEngine){
138                $textBox.typeahead({
139                    hint : true,
140                    minLength : 2
141                },
142                {
143                    source : autocompleteEngine.ttAdapter(),
144                    templates : {
145                        suggestion : function(data) {
146                            return '<div class="suggestion-item">' + data + '</div>';
147                        }
148                    }
149                }).on("typeahead:selected",function(args, selected){
150                    inst.translateUserLocation(selected);
151                    $textBox.typeahead("val", selected);
152                });
153            }
154
155            this.addressLookup(true);
156
157            XA.component.search.vent.on("hashChanged", function(hash) {
158                var address = hash[signatures.length > 0 && signatures[0] !== "" ? signatures[0] + "_a" : "a"];
159                if (typeof address !== "undefined" && address !== null && address !== "") {
160                    $textBox.val(address);
161                }
162            });
163        },
164		/**
165        * list of events for Backbone View
166        * @memberof module:locationfilter.LocationFilterView
167        * @alias module:locationfilter.LocationFilterView#events
168        */
169        events: {
170            "click .location-search-box-button": "addressLookup",
171            "keypress .location-search-box-input": "searchTextChanges",
172            "keyup .location-search-box-input" : "autocomplete"
173        },
174		/**
175        * Looks for an address based on a value that is specified by user
176        * in input
177        * @memberof module:locationfilter.LocationFilterView
178        * @alias module:locationfilter.LocationFilterView#addressLookup
179        */
180        addressLookup: function(isInitialRender){
181            var properties = this.model.get("dataProperties"),
182                lookupQuery = {
183                    text: this.getAddress(isInitialRender),
184                    maxResults: 1
185                },
186                hashObj;
187
188            if (lookupQuery.text === "") {
189                hashObj = this.createHashObject("", "", "");
190                this.updateHash(hashObj, properties);
191            }
192
193            switch (properties.mode) {
194                case "Location": {
195                    //Uses browser to detect location
196                    this.detectLocation();
197                    break;
198                }
199                case "UserProvided": {
200                    //Takes address entered by user and tries to convert it to latitude and longitude
201                    this.translateUserLocation(lookupQuery);
202                    break;
203                }
204                case "Mixed": {
205                    //Uses user address or tries to detect location by browser
206                    if (typeof(lookupQuery.text) === "undefined" || lookupQuery.text === "") {
207                        this.detectLocation();
208                    } else {
209                        this.translateUserLocation(lookupQuery);
210                    }
211                    break;
212                }
213            }
214        },
215		/**
216        * Get Address from input or from hash 
217        * @memberof module:locationfilter.LocationFilterView
218        * @alias module:locationfilter.LocationFilterView#getAddress
219        * @returns {String} address from input or hash 
220        */
221        getAddress: function (isInitialRender) {
222            var $textBox = this.$el.find(".location-search-box-input.tt-input"),
223                text = $textBox.length !== 0 ? $textBox.val() : this.$el.find(".location-search-box-input").val(),
224                hash = queryModel.parseHashParameters(window.location.hash),
225                signatures = this.model.get("sig"),
226                address;
227
228            if (isInitialRender !== true && (text === "" || typeof text === "undefined")) {
229                return "";
230            }
231
232            if (text === "" || typeof text === "undefined") {
233                address = hash[signatures.length > 0 && signatures[0] !== "" ? signatures[0] + "_a" : "a"];
234                if (address !== "") {
235                    text = address;
236                }
237                if (typeof text === "undefined") {
238                    text = "";
239                }
240            }
241
242            return text;
243        },
244		/**
245        * Gets text selected from typeahead suggestion and sets to model
246        * @param {Event} args Event object
247        * @memberof module:locationfilter.LocationFilterView
248        * @alias module:locationfilter.LocationFilterView#autocomplete
249        */
250        autocomplete : function(args){
251            var $textBox,
252                queryParams,
253                properties = this.model.get("dataProperties");
254
255            args.stopPropagation();
256            if (args.keyCode === 13) {
257                return;
258            }
259
260            $textBox = this.$el.find(".location-search-box-input.tt-input");
261
262            queryParams = {
263                text : $textBox.length !== 0 ? $textBox.val() : this.$el.find(".location-search-box-input").val(),
264                maxResults : properties.p
265            };
266            this.model.set({queryParams : queryParams});
267        },
268		/**
269        * If enter was pressed, method calls ["addressLookup"]{@link module:locationfilter.addressLookup}
270        * @param {Event} e Event object
271        * @memberof module:locationfilter.LocationFilterView
272        * @alias module:locationfilter.LocationFilterView#searchTextChanges
273        * @returns {Boolean}  False if pressed enter. All other cases: True
274        */
275        searchTextChanges: function(e) {
276            e.stopPropagation();
277            if (e.keyCode === 13) {
278                this.addressLookup(false);
279                return false;
280            }
281            return true;
282        },
283		/**
284        * Looks for an address and calls ["XA.connector.mapsConnector.addressLookup"]{@link module:XA.connector.mapsConnector.addressLookup}
285        * @param {jQuery<DomElement>} lookupQuery Element where stored text for search location
286        * @memberof module:locationfilter.LocationFilterView
287        * @alias module:locationfilter.LocationFilterView#translateUserLocation
288        */
289        translateUserLocation: function(lookupQuery) {
290            var that = this,
291                properties = this.model.get("dataProperties"),
292                text = typeof lookupQuery.text !== "undefined" ? lookupQuery.text : lookupQuery,
293                $textBox = this.$el.find(".location-search-box-input.tt-input"),
294                hashObj = {};
295
296            if (text === "") {
297                return;
298            }
299
300            mapsConnector.addressLookup({ text: text }, function(data) {
301                hashObj = that.createHashObject(data[0] + "|" + data[1], properties.f + ",Ascending");
302                that.updateHash(hashObj, properties);
303            }, function () {
304                console.error("Error while getting '" + text + "' location");
305            });
306            $textBox.blur();
307            if ($textBox.val() === "") {
308                $textBox.val(text);
309            }
310        },
311		/**
312        * Call ["locationService.addressLookup"]{@link module:locationService.detectLocation}
313        * @memberof module:locationfilter.LocationFilterView
314        * @alias module:locationfilter.LocationFilterView#detectLocation
315        */
316        detectLocation: function () {
317            var properties = this.model.get("dataProperties"),
318                $textBox = this.$el.find(".location-search-box-input"),
319                sig = this.model.get("sig"),
320                hash = queryModel.parseHashParameters(window.location.hash),
321                param,
322                hashObj = {},
323                that = this;
324
325            XA.component.locationService.detectLocation(
326                function (location) {                    
327                    hashObj = that.createHashObject(location[0] + "|" + location[1], properties.f + ",Ascending");
328                    that.updateHash.call(that, hashObj, properties);
329                    if ($textBox.length > 0) {
330                        $textBox.attr("placeholder", properties.myLocationText);
331                    }
332                },
333                function (errorMessage) {
334                    //Do not update the hash in any way when the location is not available
335                    console.log(errorMessage);
336                }
337            );
338        },
339		/**
340        * Updates hash according to setup of component and selected values by user
341        * @memberof module:locationfilter.LocationFilterView
342        * @alias module:locationfilter.LocationFilterView#updateHash
343        */
344        updateHash: function (params, properties) {
345            var sig = this.model.get("sig"),
346                signature,
347                searchModels = typeof XA.component.search !== "undefined" ? XA.component.search.results.searchResultModels : [],
348                $textBox = this.$el.find(".location-search-box-input.tt-input"),
349                text = $textBox.length !== 0 ? $textBox.val() : this.$el.find(".location-search-box-input").val(),
350                i, j;
351
352            //Clears load more offset in each of search results with the same signature when location is changed
353            //Now, this is needed to clear offset but should be handle in search service in the future
354            for (i = 0; i < searchModels.length; i++) {
355                for (j = 0; j < sig.length; j++) {
356                    signature = sig[j];
357                    if (searchModels[i].get("dataProperties").sig === signature) {
358                        searchModels[i].set("loadMoreOffset", 0);
359                    }
360                }
361            }
362
363            if (text !== null && text !== "") {
364                for (i = 0; i < sig.length; i++) {
365                    signature = sig[i];
366                    params[signature !== "" ? signature + "_a" : "a"] = text;
367                }
368            }
369
370            queryModel.updateHash(params, properties.targetUrl);
371            for (i = 0; i < sig.length; i++) {
372                XA.component.search.vent.trigger("my-location-coordinates-changed", {
373                    sig: sig[i],
374                    coordinates: params[sig[i] !== "" ? sig[i] + "_g" : "g"].split("|")
375                });
376            }
377        },
378		/**
379        * Updates hash according to setup of component and selected values by user
380        * @memberof module:locationfilter.LocationFilterView
381        * @alias module:locationfilter.LocationFilterView#updateHash
382        * @param {String} g location
383        * @param {String} o facet name and sort method
384        * @returns {Object} hash as an object
385        */
386        createHashObject: function(g, o, a) {
387            var sig = this.model.get("sig"),
388                signature, 
389                hashObj = {},
390                i;
391
392            for (i = 0; i < sig.length; i++) {
393                signature = sig[i];
394                hashObj[signature !== "" ? signature + "_g" : "g"] = g;
395                hashObj[signature !== "" ? signature + "_o" : "o"] = o;
396                if (typeof a !== "undefined") {
397                    hashObj[signature !== "" ? signature + "_a" : "a"] = a;
398                }
399            }
400
401            return hashObj;
402        }
403    });
404   /**
405    * Fills locationFilters variable with location filter components,
406    * and loads script for proper map provider
407    * @memberOf module:locationfilter
408    * @alias module:locationfilter.init
409    */
410    api.init = function() {
411        if ($("body").hasClass("on-page-editor") || initialized) {
412            return;
413        }
414
415        queryModel = XA.component.search.query;
416        urlHelperModel = XA.component.search.url;
417
418        var components = $(".location-filter:not(.initialized)");
419        _.each(components, function(elem) {
420            var $el = $(elem),
421                properties = $el.data("properties");
422
423            //Collects all found components - we will use them later to create views
424            locationFilters.push($el);
425
426            //Loads google or bing scripts in order to properly use address lookup functionality but only
427            //when we are not in Location mode (in this mode we are taking location from the browser)
428            if (!scriptsLoaded && properties.mode !== "Location") {
429                mapsConnector.loadScript(properties.key, XA.component.search.locationfilter.scriptsLoaded);
430            } else {
431                initialize();
432            }
433
434            $el.addClass("initialized");
435        });
436        initialized = true;
437    };
438	/**
439     * Calls ['initialize']{@link module:locationfilter.initialize} if it wasn`t 
440     * loaded previously and marked as loaded
441     * @memberOf module:locationfilter
442     * @alias module:locationfilter.scriptsLoaded
443    */
444    api.scriptsLoaded = function() {
445        if (!scriptsLoaded) {
446            console.log("Maps api loaded");
447            scriptsLoaded = true;
448            initialize();
449        }
450    };
451
452    return api;
453
454}(jQuery, document));
455
456XA.register("locationfilter", XA.component.search.locationfilter);

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.