PageSourceSearch

https://www.parmley-graham.co.uk/catalog/view/theme/pg-responsive-…/main_navigation.js?v=2026.09.17.2

js parmley-graham.co.uk collected 2026-10-04 17:52:14 UTC 12,618 bytes, 384 lines download raw bytes

1/**
2 * Handle the interaction with the main navigation.
3 *
4 * @class MainNavigation
5 */
6var MainNavigation = function(options){
7 
8    /*
9     * Variables accessible
10     * in the class
11     */
12    var vars = {
13        element: {}, // The parent element holding the nav actions
14        active_category: {}, // The currently active category element
15        active_sub_category: {}, // The currently active sub category element
16        container: {}, // The element to contain the dynamic nav
17        seach_container: {}, // The element to contain the search nav
18        sub_prefix: {}, // The element precede the subnav response
19        cache: {}, // Store previously retrieved data so we dont keep request on each interaction
20        trigger_selector: '[category-main-nav-toggle]',
21        active_class: 'active',
22        sub_active_class: 'head',
23        container_selector: '#main-nav',
24        search_container_selector: '#search-nav',
25        header_selector: '#header-fixed-nav',
26        sub_prefix_selector: '#main-sub-prefix',
27        sub_level_selector: '[next-level]',
28        sub_item_selector: '[next-level-item]',
29        sub_content_selector: '[next-level-item]',
30        close_selector: '[category-main-nav-close]',
31        search_toggle_selector: '[search-nav-toggle]',
32        more_selector: '[category-main-nav-more]',
33        category_data_key: 'category',
34        sub_category_data_key: null,
35        main_route_prefix: 'index.php?route=product/category/navigation&hierarchy=0&category_id=',
36        sub_route_prefix: 'index.php?route=product/category/navigation&hierarchy=1&category_id=',
37        bind_events: true,
38        auto_activate_first_sub: true,
39        manage_header_class: true,
40        header_open_class: 'category-nav-open',
41        on_open: null,
42        on_close: null,
43        filter_sub_toggle: null
44    };
45 
46    /*
47     * Can access this.method
48     * inside other methods using
49     * root.method()
50     */
51    var root = this;
52 
53    /*
54     * Constructor
55     */
56    this.construct = function(options){
57        $.extend(vars , options);
58
59        vars.element = $(vars.trigger_selector).closest('nav');
60        vars.container = $(vars.container_selector);
61        vars.search_container = $(vars.search_container_selector);
62        vars.header = $(vars.header_selector);
63        
64        // Check if we're performing any actions on page load
65        if (window.location.hash) {
66          if (window.location.hash == '#search') {
67            root.searchToggle($(this));
68          }
69
70          if (window.location.hash == '#search-multi') {
71            root.searchToggle($(this), true);
72          }
73        }
74        
75        root.bindEvents();
76    };
77
78    this.bindEvents = function() {
79        if (!vars.bind_events) {
80            return;
81        }
82
83        // Display a main category nav level
84        $(document).on('click', vars.trigger_selector, function(){
85            return root.toggle($(this));
86        });
87
88        // Display a secondary nav level
89        $(vars.container).on('click', vars.sub_level_selector, function(){
90            return root.toggleSub($(this));
91        });
92
93        // Close on click
94        $(vars.container).on('click', vars.sub_item_selector + ' a', function(){
95            root.close($(this));
96        });
97
98        // Hide the main nav
99        if (vars.close_selector) {
100            $(vars.container).on('click', vars.close_selector, function(){
101                return root.close($(this));
102            });
103
104            if ($(vars.search_container).length) {
105                $(vars.search_container).on('click', vars.close_selector, function(){
106                    return root.close($(this));
107                });
108            }
109        }
110
111        // Show and hide the search nav
112        if (vars.search_toggle_selector) {
113            $(document).on('click', vars.search_toggle_selector, function(){
114                return root.searchToggle($(this));
115            });
116        }
117
118        // Extend the main nav on "more" click
119        if (vars.more_selector) {
120            $(document).on('click', vars.more_selector, function(){
121                return root.nextPage($(this));
122            });
123        }
124    };
125    
126    /**
127     * Activate the current nav.
128     *
129     * @return void
130     */
131    this.activateCurrentNav = function(){
132        var category_id = $(vars.active_category).data(vars.category_data_key);
133        var cache_key = 'h0_' + category_id;
134
135        // Check cache
136        if (typeof vars.cache[cache_key] != 'undefined') {
137            $(vars.container).html(vars.cache[cache_key]).show();
138            
139            // Find the new sub nav container on the dom
140            vars.sub_prefix = $(vars.container).find(vars.sub_prefix_selector);
141            
142            // Activate click on second level
143            if (vars.auto_activate_first_sub) {
144                $(vars.container).find(vars.sub_level_selector + ':first').click();
145            }
146        } else {
147            // Retrieve the html
148            LocalApi.call(vars.main_route_prefix + category_id, {}, 'GET', function(response){
149                if (response.status == 'success') {
150                    $(vars.container).html(response.data).show();
151                    vars.cache[cache_key] = response.data;
152                    
153                    // Find the new sub nav container on the dom
154                    vars.sub_prefix = $(vars.container).find(vars.sub_prefix_selector);
155                    
156                    // Activate click on second level
157                    if (vars.auto_activate_first_sub) {
158                        $(vars.container).find(vars.sub_level_selector + ':first').click();
159                    }
160                }
161            });
162        }
163    }
164    
165    /**
166     * Activate a sub/second level nav.
167     *
168     * @return void
169     */
170    this.activateSubNav = function(){
171        var sub_data_key = vars.sub_category_data_key || vars.category_data_key;
172        var category_id = $(vars.active_sub_category).data(sub_data_key);
173        if (typeof category_id == 'undefined' || category_id === null || category_id === '') {
174            return;
175        }
176        var cache_key = 'h1_' + category_id;
177        
178        // Check cache
179        if (typeof vars.cache[cache_key] != 'undefined') {
180            $(vars.sub_prefix).after(vars.cache[cache_key]).show();
181        } else {
182            // Retrieve the html
183            LocalApi.call(vars.sub_route_prefix + category_id, {}, 'GET', function(response){
184                if (response.status == 'success') {
185                    $(vars.sub_prefix).after(response.data).show();
186                    vars.cache[cache_key] = response.data;
187                }
188            });
189        }
190    }
191    
192    /**
193     * Close the nave.
194     *
195     * @param object trigger
196     *
197     * @return object
198     */
199    this.close = function(trigger, callback){
200        // Empty the container
201        if (window.location.hash === "#search" || window.location.hash === "#search-multi") {
202            window.history.back();
203        } else {
204            root.resetContainer();
205        }
206        vars.active_category = {};
207        return false;
208    }
209    
210    /**
211     * Show the next page of nav items.
212     *
213     * @param object trigger
214     * @return object
215     */
216    this.nextPage = function(trigger){
217        this.close();
218        
219        let initialMinKey = trigger.data('initial_min_key'); // The point we should paginate around
220        let minKey = trigger.data('min_key'); // The minimum key for the second half of results
221        let maxKey = trigger.data('max_key'); // The maximum key to display
222        let highestDisplayedKey = 0;
223        let highestKey = 0;
224        
225        $('[category-main-nav-item]').each(function(key, item){
226            if ($(item).data('key') >= minKey && $(item).data('key') <= maxKey) {
227                $(item).attr('style', 'display: block !important');
228                highestDisplayedKey = $(item).data('key');
229            } else {
230                $(item).attr('style', 'display: none !important');
231            }
232            highestKey = $(item).data('key');
233        });
234        
235        if (highestKey == highestDisplayedKey) {
236            trigger.data('min_key', 0);
237            trigger.data('max_key', (initialMinKey-1));
238            trigger.html('&laquo; More');
239        } else {
240            trigger.data('min_key', initialMinKey);
241            trigger.data('max_key', 999);
242            trigger.html('More &raquo;');
243        }
244    }
245    
246    /**
247     * Reset the containing element ready for new nav.
248     *
249     * @return void
250     */
251    this.resetContainer = function(){
252        // Close other nav
253        $('[content-main-nav-close]').each(function(key, item){
254            item.click();
255        });
256        
257        $(vars.trigger_selector).removeClass(vars.active_class);
258        $(vars.container).empty();
259        $(vars.container).hide();
260        if ($(vars.search_container).length) {
261            $(vars.search_container).hide();
262        }
263
264        if (vars.manage_header_class && $(vars.header).length) {
265            $(vars.header).removeClass(vars.header_open_class);
266            var headerOffsetHeight = $(vars.header).height();
267            if ($('#search-panel').length) {
268                $('#search-panel').css({
269                    'margin-top': headerOffsetHeight + 'px'
270                });
271            }
272        }
273
274        if (typeof vars.on_close === 'function') {
275            vars.on_close.call(root);
276        }
277    }
278    
279    /**
280     * Reset the sub-containing element ready for new nav.
281     *
282     * @return void
283     */
284    this.resetSubContainer = function(){
285        $(vars.container).find(vars.sub_content_selector).remove();
286    }
287    
288    /**
289     * Trigger a toggle of the search nav.
290     *
291     * @param object trigger
292     *
293     * @return object
294     */
295    this.searchToggle = function(trigger, multi = false, callback){
296        // Empty the container
297        root.resetContainer();
298        
299        // Disable any existing category that is open
300        vars.active_category = {};
301
302        $(vars.search_container).show();
303        $(vars.active_category).addClass(vars.active_class);
304        
305        // Point the user to the correct input
306        $(vars.search_container).find('input[type="text"]').focus();
307        
308        // The fixed header will need styling updates to account for the growth of content
309        if (vars.manage_header_class && $(vars.header).length) {
310            $(vars.header).addClass(vars.header_open_class);
311        }
312
313        // Toggle to multi-part search if required
314        if (multi) {
315            $('.single-search').trigger('click');
316        }
317        
318        return false;
319    };
320    
321    /**
322     * Trigger a toggle on an element.
323     *
324     * @param object trigger
325     *
326     * @return object
327     */
328    this.toggle = function(trigger, callback){
329        // Empty the container
330        root.resetContainer();
331
332        // Check if this is a switch to a new category
333        if ($(vars.active_category).data(vars.category_data_key) != $(trigger).data(vars.category_data_key)) {
334            vars.active_category = trigger;
335            root.activateCurrentNav();
336            $(vars.active_category).addClass(vars.active_class);
337            
338            // The fixed header will need styling updates to account for the growth of content
339            if (vars.manage_header_class && $(vars.header).length) {
340                $(vars.header).addClass(vars.header_open_class);
341            }
342
343            if (typeof vars.on_open === 'function') {
344                vars.on_open.call(root, trigger);
345            }
346        } else {
347            vars.active_category = {};
348        }
349        
350        return false;
351    };
352    
353    /**
354     * Trigger a toggle on an element for the sub/secondary level navigation.
355     *
356     * @param object trigger
357     *
358     * @return object
359     */
360    this.toggleSub = function(trigger, callback){
361        if (typeof vars.filter_sub_toggle === 'function') {
362            if (vars.filter_sub_toggle.call(root, trigger) === false) {
363                return false;
364            }
365        }
366
367        // Change the state of the item
368        $(vars.container).find(vars.sub_level_selector + '.' + vars.sub_active_class).removeClass(vars.sub_active_class);
369        $(trigger).addClass(vars.sub_active_class);
370        
371        // Empty the container
372        root.resetSubContainer();
373        vars.active_sub_category = trigger;
374        root.activateSubNav();
375        
376        return false;
377    };
378    
379    /*
380     * Pass options when class instantiated
381     */
382    this.construct(options);
383 
384}

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.