PageSourceSearch

https://shop.tate.org.uk/on/demandware.static/Sites-tate-Site/-/default/v1790839080446/tate/js/TateUI.min.js

js tate.org.uk collected 2026-10-01 08:29:49 UTC 326,046 bytes, 9,640 lines download raw bytes

1(function (root, factory) {
2	if (typeof define === "function" && define.amd) {
3		define([], factory);
4	} else {
5		root.TateUI = factory();
6	}
7}(this, function () {
8	
vendor: 15,332 bytes, lines 9-444
9/**
10 * @license almond 0.3.3 Copyright jQuery Foundation and other contributors.
11 * Released under MIT license, http://github.com/requirejs/almond/LICENSE
12 */
13//Going sloppy to avoid 'use strict' string cost, but strict practices should
14//be followed.
15/*global setTimeout: false */
16
17var requirejs, require, define;
18(function (undef) {
19    var main, req, makeMap, handlers,
20        defined = {},
21        waiting = {},
22        config = {},
23        defining = {},
24        hasOwn = Object.prototype.hasOwnProperty,
25        aps = [].slice,
26        jsSuffixRegExp = /\.js$/;
27
28    function hasProp(obj, prop) {
29        return hasOwn.call(obj, prop);
30    }
31
32    /**
33     * Given a relative module name, like ./something, normalize it to
34     * a real name that can be mapped to a path.
35     * @param {String} name the relative name
36     * @param {String} baseName a real name that the name arg is relative
37     * to.
38     * @returns {String} normalized name
39     */
40    function normalize(name, baseName) {
41        var nameParts, nameSegment, mapValue, foundMap, lastIndex,
42            foundI, foundStarMap, starI, i, j, part, normalizedBaseParts,
43            baseParts = baseName && baseName.split("/"),
44            map = config.map,
45            starMap = (map && map['*']) || {};
46
47        //Adjust any relative paths.
48        if (name) {
49            name = name.split('/');
50            lastIndex = name.length - 1;
51
52            // If wanting node ID compatibility, strip .js from end
53            // of IDs. Have to do this here, and not in nameToUrl
54            // because node allows either .js or non .js to map
55            // to same file.
56            if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
57                name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
58            }
59
60            // Starts with a '.' so need the baseName
61            if (name[0].charAt(0) === '.' && baseParts) {
62                //Convert baseName to array, and lop off the last part,
63                //so that . matches that 'directory' and not name of the baseName's
64                //module. For instance, baseName of 'one/two/three', maps to
65                //'one/two/three.js', but we want the directory, 'one/two' for
66                //this normalization.
67                normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
68                name = normalizedBaseParts.concat(name);
69            }
70
71            //start trimDots
72            for (i = 0; i < name.length; i++) {
73                part = name[i];
74                if (part === '.') {
75                    name.splice(i, 1);
76                    i -= 1;
77                } else if (part === '..') {
78                    // If at the start, or previous value is still ..,
79                    // keep them so that when converted to a path it may
80                    // still work when converted to a path, even though
81                    // as an ID it is less than ideal. In larger point
82                    // releases, may be better to just kick out an error.
83                    if (i === 0 || (i === 1 && name[2] === '..') || name[i - 1] === '..') {
84                        continue;
85                    } else if (i > 0) {
86                        name.splice(i - 1, 2);
87                        i -= 2;
88                    }
89                }
90            }
91            //end trimDots
92
93            name = name.join('/');
94        }
95
96        //Apply map config if available.
97        if ((baseParts || starMap) && map) {
98            nameParts = name.split('/');
99
100            for (i = nameParts.length; i > 0; i -= 1) {
101                nameSegment = nameParts.slice(0, i).join("/");
102
103                if (baseParts) {
104                    //Find the longest baseName segment match in the config.
105                    //So, do joins on the biggest to smallest lengths of baseParts.
106                    for (j = baseParts.length; j > 0; j -= 1) {
107                        mapValue = map[baseParts.slice(0, j).join('/')];
108
109                        //baseName segment has  config, find if it has one for
110                        //this name.
111                        if (mapValue) {
112                            mapValue = mapValue[nameSegment];
113                            if (mapValue) {
114                                //Match, update name to the new value.
115                                foundMap = mapValue;
116                                foundI = i;
117                                break;
118                            }
119                        }
120                    }
121                }
122
123                if (foundMap) {
124                    break;
125                }
126
127                //Check for a star map match, but just hold on to it,
128                //if there is a shorter segment match later in a matching
129                //config, then favor over this star map.
130                if (!foundStarMap && starMap && starMap[nameSegment]) {
131                    foundStarMap = starMap[nameSegment];
132                    starI = i;
133                }
134            }
135
136            if (!foundMap && foundStarMap) {
137                foundMap = foundStarMap;
138                foundI = starI;
139            }
140
141            if (foundMap) {
142                nameParts.splice(0, foundI, foundMap);
143                name = nameParts.join('/');
144            }
145        }
146
147        return name;
148    }
149
150    function makeRequire(relName, forceSync) {
151        return function () {
152            //A version of a require function that passes a moduleName
153            //value for items that may need to
154            //look up paths relative to the moduleName
155            var args = aps.call(arguments, 0);
156
157            //If first arg is not require('string'), and there is only
158            //one arg, it is the array form without a callback. Insert
159            //a null so that the following concat is correct.
160            if (typeof args[0] !== 'string' && args.length === 1) {
161                args.push(null);
162            }
163            return req.apply(undef, args.concat([relName, forceSync]));
164        };
165    }
166
167    function makeNormalize(relName) {
168        return function (name) {
169            return normalize(name, relName);
170        };
171    }
172
173    function makeLoad(depName) {
174        return function (value) {
175            defined[depName] = value;
176        };
177    }
178
179    function callDep(name) {
180        if (hasProp(waiting, name)) {
181            var args = waiting[name];
182            delete waiting[name];
183            defining[name] = true;
184            main.apply(undef, args);
185        }
186
187        if (!hasProp(defined, name) && !hasProp(defining, name)) {
188            throw new Error('No ' + name);
189        }
190        return defined[name];
191    }
192
193    //Turns a plugin!resource to [plugin, resource]
194    //with the plugin being undefined if the name
195    //did not have a plugin prefix.
196    function splitPrefix(name) {
197        var prefix,
198            index = name ? name.indexOf('!') : -1;
199        if (index > -1) {
200            prefix = name.substring(0, index);
201            name = name.substring(index + 1, name.length);
202        }
203        return [prefix, name];
204    }
205
206    //Creates a parts array for a relName where first part is plugin ID,
207    //second part is resource ID. Assumes relName has already been normalized.
208    function makeRelParts(relName) {
209        return relName ? splitPrefix(relName) : [];
210    }
211
212    /**
213     * Makes a name map, normalizing the name, and using a plugin
214     * for normalization if necessary. Grabs a ref to plugin
215     * too, as an optimization.
216     */
217    makeMap = function (name, relParts) {
218        var plugin,
219            parts = splitPrefix(name),
220            prefix = parts[0],
221            relResourceName = relParts[1];
222
223        name = parts[1];
224
225        if (prefix) {
226            prefix = normalize(prefix, relResourceName);
227            plugin = callDep(prefix);
228        }
229
230        //Normalize according
231        if (prefix) {
232            if (plugin && plugin.normalize) {
233                name = plugin.normalize(name, makeNormalize(relResourceName));
234            } else {
235                name = normalize(name, relResourceName);
236            }
237        } else {
238            name = normalize(name, relResourceName);
239            parts = splitPrefix(name);
240            prefix = parts[0];
241            name = parts[1];
242            if (prefix) {
243                plugin = callDep(prefix);
244            }
245        }
246
247        //Using ridiculous property names for space reasons
248        return {
249            f: prefix ? prefix + '!' + name : name, //fullName
250            n: name,
251            pr: prefix,
252            p: plugin
253        };
254    };
255
256    function makeConfig(name) {
257        return function () {
258            return (config && config.config && config.config[name]) || {};
259        };
260    }
261
262    handlers = {
263        require: function (name) {
264            return makeRequire(name);
265        },
266        exports: function (name) {
267            var e = defined[name];
268            if (typeof e !== 'undefined') {
269                return e;
270            } else {
271                return (defined[name] = {});
272            }
273        },
274        module: function (name) {
275            return {
276                id: name,
277                uri: '',
278                exports: defined[name],
279                config: makeConfig(name)
280            };
281        }
282    };
283
284    main = function (name, deps, callback, relName) {
285        var cjsModule, depName, ret, map, i, relParts,
286            args = [],
287            callbackType = typeof callback,
288            usingExports;
289
290        //Use name if no relName
291        relName = relName || name;
292        relParts = makeRelParts(relName);
293
294        //Call the callback to define the module, if necessary.
295        if (callbackType === 'undefined' || callbackType === 'function') {
296            //Pull out the defined dependencies and pass the ordered
297            //values to the callback.
298            //Default to [require, exports, module] if no deps
299            deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps;
300            for (i = 0; i < deps.length; i += 1) {
301                map = makeMap(deps[i], relParts);
302                depName = map.f;
303
304                //Fast path CommonJS standard dependencies.
305                if (depName === "require") {
306                    args[i] = handlers.require(name);
307                } else if (depName === "exports") {
308                    //CommonJS module spec 1.1
309                    args[i] = handlers.exports(name);
310                    usingExports = true;
311                } else if (depName === "module") {
312                    //CommonJS module spec 1.1
313                    cjsModule = args[i] = handlers.module(name);
314                } else if (hasProp(defined, depName) ||
315                           hasProp(waiting, depName) ||
316                           hasProp(defining, depName)) {
317                    args[i] = callDep(depName);
318                } else if (map.p) {
319                    map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {});
320                    args[i] = defined[depName];
321                } else {
322                    throw new Error(name + ' missing ' + depName);
323                }
324            }
325
326            ret = callback ? callback.apply(defined[name], args) : undefined;
327
328            if (name) {
329                //If setting exports via "module" is in play,
330                //favor that over return value and exports. After that,
331                //favor a non-undefined return value over exports use.
332                if (cjsModule && cjsModule.exports !== undef &&
333                        cjsModule.exports !== defined[name]) {
334                    defined[name] = cjsModule.exports;
335                } else if (ret !== undef || !usingExports) {
336                    //Use the return value from the function.
337                    defined[name] = ret;
338                }
339            }
340        } else if (name) {
341            //May just be an object definition for the module. Only
342            //worry about defining if have a module name.
343            defined[name] = callback;
344        }
345    };
346
347    requirejs = require = req = function (deps, callback, relName, forceSync, alt) {
348        if (typeof deps === "string") {
349            if (handlers[deps]) {
350                //callback in this case is really relName
351                return handlers[deps](callback);
352            }
353            //Just return the module wanted. In this scenario, the
354            //deps arg is the module name, and second arg (if passed)
355            //is just the relName.
356            //Normalize module name, if it contains . or ..
357            return callDep(makeMap(deps, makeRelParts(callback)).f);
358        } else if (!deps.splice) {
359            //deps is a config object, not an array.
360            config = deps;
361            if (config.deps) {
362                req(config.deps, config.callback);
363            }
364            if (!callback) {
365                return;
366            }
367
368            if (callback.splice) {
369                //callback is an array, which means it is a dependency list.
370                //Adjust args if there are dependencies
371                deps = callback;
372                callback = relName;
373                relName = null;
374            } else {
375                deps = undef;
376            }
377        }
378
379        //Support require(['a'])
380        callback = callback || function () {};
381
382        //If relName is a function, it is an errback handler,
383        //so remove it.
384        if (typeof relName === 'function') {
385            relName = forceSync;
386            forceSync = alt;
387        }
388
389        //Simulate async callback;
390        if (forceSync) {
391            main(undef, deps, callback, relName);
392        } else {
393            //Using a non-zero value because of concern for what old browsers
394            //do, and latest browsers "upgrade" to 4 if lower value is used:
395            //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout:
396            //If want a value immediately, use require('id') instead -- something
397            //that works in almond on the global level, but not guaranteed and
398            //unlikely to work in other AMD implementations.
399            setTimeout(function () {
400                main(undef, deps, callback, relName);
401            }, 4);
402        }
403
404        return req;
405    };
406
407    /**
408     * Just drops the config on the floor, but returns req in case
409     * the config return value is used.
410     */
411    req.config = function (cfg) {
412        return req(cfg);
413    };
414
415    /**
416     * Expose module registry for debugging and tooling
417     */
418    requirejs._defined = defined;
419
420    define = function (name, deps, callback) {
421        if (typeof name !== 'string') {
422            throw new Error('See almond README: incorrect module build, no module name');
423        }
424
425        //This module may not have dependencies
426        if (!deps.splice) {
427            //deps is not an array, so probably means
428            //an object literal or factory function for
429            //the value. Adjust args.
430            callback = deps;
431            deps = [];
432        }
433
434        if (!hasProp(defined, name) && !hasProp(waiting, name)) {
435            waiting[name] = [name, deps, callback];
436        }
437    };
438
439    define.amd = {
440        jQuery: true
441    };
442}());
443
444define("
444../../../../../node_modules/almond/almond", function(){});
445
446define('components/TatePolyfill',[], function () {
447
448    // collection of patches for the lesser fortunate browsers... won't give any name.
449
450    function TatePolyfill(options) {
451        var me = this;
452
453        // String.startsWith not supported in IE11, Edge
454        // http://stackoverflow.com/questions/30867172/code-not-running-in-ie-11-works-fine-in-chrome
455        if (!String.prototype.startsWith) {
456          String.prototype.startsWith = function(searchString, position) {
457            position = position || 0;
458            return this.indexOf(searchString, position) === position;
459          };
460        }
461    }
462
463    return TatePolyfill;
464});
465
466define('components/TateWindow',[], function () {
467
468    /**
469    *   @class TateWindow tells us things about the current window.
470    */
471    function TateWindow (options) {
472        var me = this;
473        this.init();
474    }
475
476    TateWindow.prototype = {
477        /**
478        *   @private
479        *   @description Initialises the TateWindow class.
480        */
481        init: function () {
482        	//TODO
483        },
484
485        
486        /*
487         *	@method
488         *	@desc - Discovers if device has touch capabilities
489         *	@return {boolean}
490         */
491        isTouchDevice: function (){
492            return 'ontouchstart' in window || 'onmsgesturechange' in window;
493        },
494
495	    // Cross-browser solution taken from http://www.softcomplex.com/docs/get_window_size_and_scrollbar_position.html, with thanks!
496	    width: function() {
497	        return this.f_filterResults (
498	            window.innerWidth ? window.innerWidth : 0,
499	            document.documentElement ? document.documentElement.clientWidth : 0,
500	            document.body ? document.body.clientWidth : 0
501	        );
502	    },
503
504	    height: function() {
505	        return this.f_filterResults (
506	            window.innerHeight ? window.innerHeight : 0,
507	            document.documentElement ? document.documentElement.clientHeight : 0,
508	            document.body ? document.body.clientHeight : 0
509	        );
510	    },
511
512	    f_filterResults: function(n_win, n_docel, n_body) {
513	        var n_result = n_win ? n_win : 0;
514	        if (n_docel && (!n_result || (n_result > n_docel)))
515	            n_result = n_docel;
516	        return n_body && (!n_result || (n_result > n_body)) ? n_body : n_result;
517	    }
518    
519    };
520
521    return TateWindow;
522});
523
524define('components/TateStorage',[], function () {
525    /**
526    *   @class TateStorage decides whether to use localStorage or cookies.
527    */
528    function TateStorage (options) {
529        var me = this;
530
531        /**
532        *   @constant
533        */
534        this.T_COOKIE = {
535            get: function (key) {
536            	key = me.encode(key);
537                var name = key + "=";
538                var cookies = document.cookie.split(';');
539
540                for(var i=0; i < cookies.length; i++) {
541                    var cookie = cookies[i];
542
543                    while (cookie.charAt(0)==' ') {
544                    	cookie = cookie.substring(1);
545                    }
546
547                    if (cookie.indexOf(name) === 0) {
548                    	var value = cookie.substring(key.length, cookie.length);
549                    	value = value.substring(1);
550
551		                return {
552		                    'key': me.decode(key),
553		                    'val': me.decode(value)
554		                };
555                    }
556                }
557
558                return false;
559            },
560            set: function (key, val) {
561                if (val === "") return false;
562                key = me.encode(key);
563                val = me.encode(val);
564
565                document.cookie = key + "=" + val + "; expires=Thu, 31 Dec 2099 23:59:59 GMT; path=/";
566
567                if (document.cookie.indexOf(key) == -1) {
568                    return false;
569                } else {
570                    return this.get(me.decode(key));
571                }
572            },
573            patch: function (key, val) {
574                if (!this.get(key)) {
575                    return false;
576                } else {
577                	this.delete(key);
578                	return this.set(key, val);
579                }
580            },
581            delete: function (key) {
582                key = me.encode(key);
583
584                document.cookie = key + "=null; expires=Thu, 01 Jan 1970 00:00:00 GMT";
585
586                if (!this.get(me.decode(key))) {
587                	return true;
588                } else {
589                	return false;
590                }
591            }
592        };
593
594        /**
595        *   @constant
596        */
597        this.T_LOCALSTORE = {
598            get: function (key) {
599                var rtn = {},
600                    item;
601
602                key = me.encode(key);
603
604                try {
605                    item = window.localStorage.getItem(key);
606                } catch (err) {
607                    return false;
608                }
609
610                if (item === null) {
611                    return false;
612                }
613
614                return {
615                    'key': me.decode(key),
616                    'val': me.decode(item)
617                };
618            },
619            set: function (key, val) {
620                var rtn = {},
621                    item;
622
623                if (this.get(key)) {
624                    return false;
625                }
626
627                key = me.encode(key);
628                val = me.encode(val);
629
630                try {
631                    item = window.localStorage.setItem(key, val);
632                } catch (err) {
633                    return false;
634                }
635
636                return {
637                    'key': key,
638                    'val': val
639                };
640            },
641            patch: function (key, val) {
642                if (!this.get(key)) {
643                    return false;
644                } else {
645                	this.delete(key);
646                	return this.set(key, val);
647                }
648            },
649            delete: function (key) {
650                var item;
651
652                key = me.encode(key);
653
654                try {
655                    item = window.localStorage.removeItem(key);
656                } catch (err) {
657                    return false;
658                }
659
660                return true;
661            }
662        };
663
664        if (typeof options !== 'undefined' && typeof options.useCookie !== 'undefined' && options.useCookie) {
665            this.init(true);
666        } else {
667            this.init(false);
668        }
669    }
670
671    TateStorage.prototype = {
672        /**
673        *   @private
674        *   @description Initialises the TateStorage class, chooses the storage type.
675        */
676        init: function (useCookie) {
677            if ('localStorage' in window && window.localStorage !== null && !useCookie) {
678                this.storage_method = "LOCALSTORE";
679            } else {
680                this.storage_method = "COOKIE";
681            }
682        },
683
684        /**
685        *   @method
686        *   @description Decodes a b64 encoded string
687        *   @param {string} b64_string The base64 string to decode
688        *   @return {string} The decoded base64 string
689        */
690        decode: function (b64_string) {
691            return atob(b64_string);
692        },
693
694        /**
695        *   @method
696        *   @description Proxy to the storage type and deletes the keyword stored
697        *   @param {string} keyword to be encoded and stored
698        *   @return {boolean} true or false based on the result of the delete method
699        */
700        delete: function (keyword) {
701            return this['T_' + this.storage_method].delete(keyword);
702        },
703
704        /**
705        *   @method
706        *   @description Encodes a string to base64
707        *   @param {string} to_string The string to encode
708        *   @return {string} The base64 encoded string
709        */
710        encode: function (to_string) {
711            return btoa(to_string);
712        },
713
714        /**
715        *   @method
716        *   @description Proxy to the storage type and find the object by keyword param
717        *   @param {string} keyword to find
718        *   @return {object} The object that has been found
719        */
720        find: function (keyword) {
721            return this['T_' + this.storage_method].get(keyword);
722        },
723
724        /**
725        *   @method
726        *   @description Proxy to the storage type to find and alter the object param
727        *   @param {string} keyword The keyword to find
728        *   @param {string} value The value to alter
729        *   @return {object} The object that has been altered
730        */
731        patch: function (keyword, value) {
732        	return this['T_' + this.storage_method].patch(keyword, value);
733        },
734
735        /**
736        *   @method
737        *   @description Proxy to the storage type and store the keyword and value
738        *   @param {string} keyword The keyword to store
739        *   @param {string} value The value to alter
740        *   @return {object} The object that has been stored
741        */
742        store: function (keyword, value) {
743            return this['T_' + this.storage_method].set(keyword, value);
744        },
745    };
746
747    return TateStorage;
748});
749
750define('jquery',[],function () {
751	/*
752	*	Shims jQuery in our build process. Since jQuery is used by a lot of library,
753	*	we need to expose jQuery through AMD.. but since that's not really possible
754	*	include the script as usual (before TateUI) and throw an error if it's not included.
755	*/
756	try {
757		return jQuery;
758	} catch (e) {
759		throw new Error("TateUI requires jQuery!");
760	}
761});
762define('components/TateNotification',['jquery', 'components/TateStorage'], function ($, TateStorage) {
763    var TateNotification = function (options) {
764        options = options || {};
765        var me = this;
766
767        if ('storage_item' in options && options.storage_item !== null) {
768            this.storage_item = options.storage_item;
769        } else {
770		 	throw new Error('Tate Storage needs a \'storage_item\' parameter');
771        }
772
773        this.markup = [
774            '<div id="' + this.storage_item + '_NOTIFICATION" class="notification-banner notification-banner--p
774opup notification-banner--type__' + options.type + ' notification-banner--format__' + options.format + '">',
775            '	<div class="notification-banner__inner">'
776        ].join('\n');
777
778        if (options.title) {
779            this.markup += [
780                '		<h2 class="notification-banner__title">' + options.title + '</h2>'
781            ].join('\n');
782        } else {
783            this.paragraph_style = '_no-title'
784        };
785
786        if (options.format == 'slim') {
787            this.markup += [
788                '		<p class="notification-banner__text">' + options.message + '',
789                '			<a class="notification-banner__anchor" href="' + options.anchor_link + '">' + options.anchor_text + '</a>',
790                '		</p>',
791                '		<div class="btn-row notification-banner__dismiss-button">',
792                '			<button id="DISMISS_' + this.storage_item + '_NOTIFICATION" class="btn btn--style__icon">',
793                '				<i class="btn__icon icon--close"></i>',
794                '				<span class="btn__text">Dismiss</span>',
795                '			</button>',
796                '		</div>',
797                '	</div>',
798                '</div>'
799            ].join('\n');
800        } else {
801            // Can now add multiple links to a message by using the 'shortcodes' "link0, link1, link2" and having the appropriate links and text in the arrays
802            link_counter = 0;
803            $(options.anchor_link).each(function(){
804                //new RegExp(search, 'g')
805                options.message = options.message.replace(new RegExp('Link'+link_counter, 'g'), '<a class="notification-banner__anchor" href="' + options.anchor_link[link_counter] + '">' + options.anchor_text[link_counter] + '</a>' );
806                link_counter++;
807            });
808
809            this.markup += [
810                '		<p class="notification-banner__text ' + this.paragraph_style + '">' + options.message + '',
811                '		</p>',
812                '		<div class="btn-row notification-banner__dismiss-button">',
813                '			<button id="DISMISS_' + this.storage_item + '_NOTIFICATION" class="btn--type__white">',
814                '				<span class="btn__text">' + options.button_text + '</span>',
815                '			</button>',
816                '		</div>',
817                '	</div>',
818                '</div>'
819            ].join('\n');
820        }
821        $(document).ready(function () {
822            me.init();
823        });
824    };
825
826    TateNotification.prototype = {
827        init: function () {
828            this.storage = new TateStorage({
829                useCookie: true
830            });
831
832            if (!this.get_storage()) {
833                this.inject_markup();
834            }
835        },
836
837        get_storage: function () {
838            return this.storage.find(this.storage_item);
839        },
840
841        inject_markup: function () {
842            var me = this;
843            $('#notifications-footer').append(this.markup);
844            $('#' + me.storage_item + '_NOTIFICATION').css('display', 'none').slideToggle();
845            this.register_events();
846        },
847
848        register_events: function () {
849            var me = this;
850
851            $('#DISMISS_' + this.storage_item + '_NOTIFICATION').on('click', function (e) {
852                e.preventDefault();
853
854                me.set_localstorage();
855                $('#' + me.storage_item + '_NOTIFICATION').remove();
856            });
857        },
858
859        set_localstorage: function () {
860            this.storage.store(this.storage_item, true);
861        },
862    };
863
864    return TateNotification;
865});
866
867
868define('components/TateGOTO',['jquery'], function ($) {
869	/*
870	*	@constructor
871	*	@description Calls various methods on document ready
872	*/
873	var TateGOTO = function (options) {
874		var me = this;
875		options = options || {};
876		this.anchors = [];
877
878		if ('bufftop' in options) {
879			this.bufftop = options.bufftop;
880		} else {
881			this.bufftop = 0;
882		}
883
884		$(document).ready(function () {
885			me.extend_easing();
886			me.init();
887		});
888	};
889
890	TateGOTO.prototype = {
891		/*
892		*	@method
893		*	@description Factory method for generating TateGOTO anchors
894		*/
895		init: function () {
896			var me = this;
897
898			$('[data-role="tate-goto"]').each(function (){
899				var $anchor = $(this),
900					$destination = $('[name="' + $(this).attr('href').replace("#", "") + '"]');
901
902				$anchor.on('click', function (e) {
903					e.preventDefault();
904					$('html, body').stop().animate({
905						'scrollTop': $destination.offset().top - me.bufftop
906					}, function () {
907						window.anim_lock = null;
908					});
909				});
910			});
911		},
912
913		/*
914		*	@method
915		*	@description extends jQuery easing object
916		*/
917		extend_easing: function () {
918			/* Source taken from jQuery UI animation */
919			if (!('easeInOutQuart' in $.easing)) {
920				$.extend($.easing, {
921					easeInOutQuart: function (x, t, b, c, d) {
922						if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
923						return -c/2 * ((t-=2)*t*t*t - 2) + b;
924					}
925				});
926			}
927		},
928	};
929
930	return TateGOTO;
931});
932/*
933     _ _      _       _
934 ___| (_) ___| | __  (_)___
935/ __| | |/ __| |/ /  | / __|
936\__ \ | | (__|   < _ | \__ \
937|___/_|_|\___|_|\_(_)/ |___/
938                   |__/
939
940 Version: 1.8.1
941  Author: Ken Wheeler
942 Website: http://kenwheeler.github.io
943    Docs: http://kenwheeler.github.io/slick
944    Repo: http://github.com/kenwheeler/slick
945  Issues: http://github.com/kenwheeler/slick/issues
946
947 */
948/* global window, document, define, jQuery, setInterval, clearInterval */
949;(function(factory) {
950    'use strict';
951    if (typeof define === 'function' && define.amd) {
952        define('vendor/slick',['jquery'], factory);
953    } else if (typeof exports !== 'undefined') {
954        module.exports = factory(require('jquery'));
955    } else {
956        factory(jQuery);
957    }
958
959}(function($) {
960    'use strict';
vendor: 29,656 bytes, lines 960-1948
960
961    var Slick = window.Slick || {};
962
963    Slick = (function() {
964
965        var instanceUid = 0;
966
967        function Slick(element, settings) {
968
969            var _ = this, dataSettings;
970
971            _.defaults = {
972                accessibility: true,
973                adaptiveHeight: false,
974                appendArrows: $(element),
975                appendDots: $(element),
976                arrows: true,
977                asNavFor: null,
978                prevArrow: '<button class="slick-prev" aria-label="Previous" type="button">Previous</button>',
979                nextArrow: '<button class="slick-next" aria-label="Next" type="button">Next</button>',
980                autoplay: false,
981                autoplaySpeed: 3000,
982                centerMode: false,
983                centerPadding: '50px',
984                cssEase: 'ease',
985                customPaging: function(slider, i) {
986                    return $('<button type="button" />').text(i + 1);
987                },
988                dots: false,
989                dotsClass: 'slick-dots',
990                draggable: true,
991                easing: 'linear',
992                edgeFriction: 0.35,
993                fade: false,
994                focusOnSelect: false,
995                focusOnChange: false,
996                infinite: true,
997                initialSlide: 0,
998                lazyLoad: 'ondemand',
999                mobileFirst: false,
1000                pauseOnHover: true,
1001                pauseOnFocus: true,
1002                pauseOnDotsHover: false,
1003                respondTo: 'window',
1004                responsive: null,
1005                rows: 1,
1006                rtl: false,
1007                slide: '',
1008                slidesPerRow: 1,
1009                slidesToShow: 1,
1010                slidesToScroll: 1,
1011                speed: 500,
1012                swipe: true,
1013                swipeToSlide: false,
1014                touchMove: true,
1015                touchThreshold: 5,
1016                useCSS: true,
1017                useTransform: true,
1018                variableWidth: false,
1019                vertical: false,
1020                verticalSwiping: false,
1021                waitForAnimate: true,
1022                zIndex: 1000
1023            };
1024
1025            _.initials = {
1026                animating: false,
1027                dragging: false,
1028                autoPlayTimer: null,
1029                currentDirection: 0,
1030                currentLeft: null,
1031                currentSlide: 0,
1032                direction: 1,
1033                $dots: null,
1034                listWidth: null,
1035                listHeight: null,
1036                loadIndex: 0,
1037                $nextArrow: null,
1038                $prevArrow: null,
1039                scrolling: false,
1040                slideCount: null,
1041                slideWidth: null,
1042                $slideTrack: null,
1043                $slides: null,
1044                sliding: false,
1045                slideOffset: 0,
1046                swipeLeft: null,
1047                swiping: false,
1048                $list: null,
1049                touchObject: {},
1050                transformsEnabled: false,
1051                unslicked: false
1052            };
1053
1054            $.extend(_, _.initials);
1055
1056            _.activeBreakpoint = null;
1057            _.animType = null;
1058            _.animProp = null;
1059            _.breakpoints = [];
1060            _.breakpointSettings = [];
1061            _.cssTransitions = false;
1062            _.focussed = false;
1063            _.interrupted = false;
1064            _.hidden = 'hidden';
1065            _.paused = true;
1066            _.positionProp = null;
1067            _.respondTo = null;
1068            _.rowCount = 1;
1069            _.shouldClick = true;
1070            _.$slider = $(element);
1071            _.$slidesCache = null;
1072            _.transformType = null;
1073            _.transitionType = null;
1074            _.visibilityChange = 'visibilitychange';
1075            _.windowWidth = 0;
1076            _.windowTimer = null;
1077
1078            dataSettings = $(element).data('slick') || {};
1079
1080            _.options = $.extend({}, _.defaults, settings, dataSettings);
1081
1082            _.currentSlide = _.options.initialSlide;
1083
1084            _.originalSettings = _.options;
1085
1086            if (typeof document.mozHidden !== 'undefined') {
1087                _.hidden = 'mozHidden';
1088                _.visibilityChange = 'mozvisibilitychange';
1089            } else if (typeof document.webkitHidden !== 'undefined') {
1090                _.hidden = 'webkitHidden';
1091                _.visibilityChange = 'webkitvisibilitychange';
1092            }
1093
1094            _.autoPlay = $.proxy(_.autoPlay, _);
1095            _.autoPlayClear = $.proxy(_.autoPlayClear, _);
1096            _.autoPlayIterator = $.proxy(_.autoPlayIterator, _);
1097            _.changeSlide = $.proxy(_.changeSlide, _);
1098            _.clickHandler = $.proxy(_.clickHandler, _);
1099            _.selectHandler = $.proxy(_.selectHandler, _);
1100            _.setPosition = $.proxy(_.setPosition, _);
1101            _.swipeHandler = $.proxy(_.swipeHandler, _);
1102            _.dragHandler = $.proxy(_.dragHandler, _);
1103            _.keyHandler = $.proxy(_.keyHandler, _);
1104
1105            _.instanceUid = instanceUid++;
1106
1107            // A simple way to check for HTML strings
1108            // Strict HTML recognition (must start with <)
1109            // Extracted from jQuery v1.11 source
1110            _.htmlExpr = /^(?:\s*(<[\w\W]+>)[^>]*)$/;
1111
1112
1113            _.registerBreakpoints();
1114            _.init(true);
1115
1116        }
1117
1118        return Slick;
1119
1120    }());
1121
1122    Slick.prototype.activateADA = function() {
1123        var _ = this;
1124
1125        _.$slideTrack.find('.slick-active').attr({
1126            'aria-hidden': 'false'
1127        }).find('a, input, button, select').attr({
1128            'tabindex': '0'
1129        });
1130
1131    };
1132
1133    Slick.prototype.addSlide = Slick.prototype.slickAdd = function(markup, index, addBefore) {
1134
1135        var _ = this;
1136
1137        if (typeof(index) === 'boolean') {
1138            addBefore = index;
1139            index = null;
1140        } else if (index < 0 || (index >= _.slideCount)) {
1141            return false;
1142        }
1143
1144        _.unload();
1145
1146        if (typeof(index) === 'number') {
1147            if (index === 0 && _.$slides.length === 0) {
1148                $(markup).appendTo(_.$slideTrack);
1149            } else if (addBefore) {
1150                $(markup).insertBefore(_.$slides.eq(index));
1151            } else {
1152                $(markup).insertAfter(_.$slides.eq(index));
1153            }
1154        } else {
1155            if (addBefore === true) {
1156                $(markup).prependTo(_.$slideTrack);
1157            } else {
1158                $(markup).appendTo(_.$slideTrack);
1159            }
1160        }
1161
1162        _.$slides = _.$slideTrack.children(this.options.slide);
1163
1164        _.$slideTrack.children(this.options.slide).detach();
1165
1166        _.$slideTrack.append(_.$slides);
1167
1168        _.$slides.each(function(index, element) {
1169            $(element).attr('data-slick-index', index);
1170        });
1171
1172        _.$slidesCache = _.$slides;
1173
1174        _.reinit();
1175
1176    };
1177
1178    Slick.prototype.animateHeight = function() {
1179        var _ = this;
1180        if (_.options.slidesToShow === 1 && _.options.adaptiveHeight === true && _.options.vertical === false) {
1181            var targetHeight = _.$slides.eq(_.currentSlide).outerHeight(true);
1182            _.$list.animate({
1183                height: targetHeight
1184            }, _.options.speed);
1185        }
1186    };
1187
1188    Slick.prototype.animateSlide = function(targetLeft, callback) {
1189
1190        var animProps = {},
1191            _ = this;
1192
1193        _.animateHeight();
1194
1195        if (_.options.rtl === true && _.options.vertical === false) {
1196            targetLeft = -targetLeft;
1197        }
1198        if (_.transformsEnabled === false) {
1199            if (_.options.vertical === false) {
1200                _.$slideTrack.animate({
1201                    left: targetLeft
1202                }, _.options.speed, _.options.easing, callback);
1203            } else {
1204                _.$slideTrack.animate({
1205                    top: targetLeft
1206                }, _.options.speed, _.options.easing, callback);
1207            }
1208
1209        } else {
1210
1211            if (_.cssTransitions === false) {
1212                if (_.options.rtl === true) {
1213                    _.currentLeft = -(_.currentLeft);
1214                }
1215                $({
1216                    animStart: _.currentLeft
1217                }).animate({
1218                    animStart: targetLeft
1219                }, {
1220                    duration: _.options.speed,
1221                    easing: _.options.easing,
1222                    step: function(now) {
1223                        now = Math.ceil(now);
1224                        if (_.options.vertical === false) {
1225                            animProps[_.animType] = 'translate(' +
1226                                now + 'px, 0px)';
1227                            _.$slideTrack.css(animProps);
1228                        } else {
1229                            animProps[_.animType] = 'translate(0px,' +
1230                                now + 'px)';
1231                            _.$slideTrack.css(animProps);
1232                        }
1233                    },
1234                    complete: function() {
1235                        if (callback) {
1236                            callback.call();
1237                        }
1238                    }
1239                });
1240
1241            } else {
1242
1243                _.applyTransition();
1244                targetLeft = Math.ceil(targetLeft);
1245
1246                if (_.options.vertical === false) {
1247                    animProps[_.animType] = 'translate3d(' + targetLeft + 'px, 0px, 0px)';
1248                } else {
1249                    animProps[_.animType] = 'translate3d(0px,' + targetLeft + 'px, 0px)';
1250                }
1251                _.$slideTrack.css(animProps);
1252
1253                if (callback) {
1254                    setTimeout(function() {
1255
1256                        _.disableTransition();
1257
1258                        callback.call();
1259                    }, _.options.speed);
1260                }
1261
1262            }
1263
1264        }
1265
1266    };
1267
1268    Slick.prototype.getNavTarget = function() {
1269
1270        var _ = this,
1271            asNavFor = _.options.asNavFor;
1272
1273        if ( asNavFor && asNavFor !== null ) {
1274            asNavFor = $(asNavFor).not(_.$slider);
1275        }
1276
1277        return asNavFor;
1278
1279    };
1280
1281    Slick.prototype.asNavFor = function(index) {
1282
1283        var _ = this,
1284            asNavFor = _.getNavTarget();
1285
1286        if ( asNavFor !== null && typeof asNavFor === 'object' ) {
1287            asNavFor.each(function() {
1288                var target = $(this).slick('getSlick');
1289                if(!target.unslicked) {
1290                    target.slideHandler(index, true);
1291                }
1292            });
1293        }
1294
1295    };
1296
1297    Slick.prototype.applyTransition = function(slide) {
1298
1299        var _ = this,
1300            transition = {};
1301
1302        if (_.options.fade === false) {
1303            transition[_.transitionType] = _.transformType + ' ' + _.options.speed + 'ms ' + _.options.cssEase;
1304        } else {
1305            transition[_.transitionType] = 'opacity ' + _.options.speed + 'ms ' + _.options.cssEase;
1306        }
1307
1308        if (_.options.fade === false) {
1309            _.$slideTrack.css(transition);
1310        } else {
1311            _.$slides.eq(slide).css(transition);
1312        }
1313
1314    };
1315
1316    Slick.prototype.autoPlay = function() {
1317
1318        var _ = this;
1319
1320        _.autoPlayClear();
1321
1322        if ( _.slideCount > _.options.slidesToShow ) {
1323            _.autoPlayTimer = setInterval( _.autoPlayIterator, _.options.autoplaySpeed );
1324        }
1325
1326    };
1327
1328    Slick.prototype.autoPlayClear = function() {
1329
1330        var _ = this;
1331
1332        if (_.autoPlayTimer) {
1333            clearInterval(_.autoPlayTimer);
1334        }
1335
1336    };
1337
1338    Slick.prototype.autoPlayIterator = function() {
1339
1340        var _ = this,
1341            slideTo = _.currentSlide + _.options.slidesToScroll;
1342
1343        if ( !_.paused && !_.interrupted && !_.focussed ) {
1344
1345            if ( _.options.infinite === false ) {
1346
1347                if ( _.direction === 1 && ( _.currentSlide + 1 ) === ( _.slideCount - 1 )) {
1348                    _.direction = 0;
1349                }
1350
1351                else if ( _.direction === 0 ) {
1352
1353                    slideTo = _.currentSlide - _.options.slidesToScroll;
1354
1355                    if ( _.currentSlide - 1 === 0 ) {
1356                        _.direction = 1;
1357                    }
1358
1359                }
1360
1361            }
1362
1363            _.slideHandler( slideTo );
1364
1365        }
1366
1367    };
1368
1369    Slick.prototype.buildArrows = function() {
1370
1371        var _ = this;
1372
1373        if (_.options.arrows === true ) {
1374
1375            _.$prevArrow = $(_.options.prevArrow).addClass('slick-arrow');
1376            _.$nextArrow = $(_.options.nextArrow).addClass('slick-arrow');
1377
1378            if( _.slideCount > _.options.slidesToShow ) {
1379
1380                _.$prevArrow.removeClass('slick-hidden').removeAttr('aria-hidden tabindex');
1381                _.$nextArrow.removeClass('slick-hidden').removeAttr('aria-hidden tabindex');
1382
1383                if (_.htmlExpr.test(_.options.prevArrow)) {
1384                    _.$prevArrow.prependTo(_.options.appendArrows);
1385                }
1386
1387                if (_.htmlExpr.test(_.options.nextArrow)) {
1388                    _.$nextArrow.appendTo(_.options.appendArrows);
1389                }
1390
1391                if (_.options.infinite !== true) {
1392                    _.$prevArrow
1393                        .addClass('slick-disabled')
1394                        .attr('aria-disabled', 'true');
1395                }
1396
1397            } else {
1398
1399                _.$prevArrow.add( _.$nextArrow )
1400
1401                    .addClass('slick-hidden')
1402                    .attr({
1403                        'aria-disabled': 'true',
1404                        'tabindex': '-1'
1405                    });
1406
1407            }
1408
1409        }
1410
1411    };
1412
1413    Slick.prototype.buildDots = function() {
1414
1415        var _ = this,
1416            i, dot;
1417
1418        if (_.options.dots === true && _.slideCount > _.options.slidesToShow) {
1419
1420            _.$slider.addClass('slick-dotted');
1421
1422            dot = $('<ul />').addClass(_.options.dotsClass);
1423
1424            for (i = 0; i <= _.getDotCount(); i += 1) {
1425                dot.append($('<li />').append(_.options.customPaging.call(this, _, i)));
1426            }
1427
1428            _.$dots = dot.appendTo(_.options.appendDots);
1429
1430            _.$dots.find('li').first().addClass('slick-active');
1431
1432        }
1433
1434    };
1435
1436    Slick.prototype.buildOut = function() {
1437
1438        var _ = this;
1439
1440        _.$slides =
1441            _.$slider
1442                .children( _.options.slide + ':not(.slick-cloned)')
1443                .addClass('slick-slide');
1444
1445        _.slideCount = _.$slides.length;
1446
1447        _.$slides.each(function(index, element) {
1448            $(element)
1449                .attr('data-slick-index', index)
1450                .data('originalStyling', $(element).attr('style') || '');
1451        });
1452
1453        _.$slider.addClass('slick-slider');
1454
1455        _.$slideTrack = (_.slideCount === 0) ?
1456            $('<div class="slick-track"/>').appendTo(_.$slider) :
1457            _.$slides.wrapAll('<div class="slick-track"/>').parent();
1458
1459        _.$list = _.$slideTrack.wrap(
1460            '<div class="slick-list"/>').parent();
1461        _.$slideTrack.css('opacity', 0);
1462
1463        if (_.options.centerMode === true || _.options.swipeToSlide === true) {
1464            _.options.slidesToScroll = 1;
1465        }
1466
1467        $('img[data-lazy]', _.$slider).not('[src]').addClass('slick-loading');
1468
1469        _.setupInfinite();
1470
1471        _.buildArrows();
1472
1473        _.buildDots();
1474
1475        _.updateDots();
1476
1477
1478        _.setSlideClasses(typeof _.currentSlide === 'number' ? _.currentSlide : 0);
1479
1480        if (_.options.draggable === true) {
1481            _.$list.addClass('draggable');
1482        }
1483
1484    };
1485
1486    Slick.prototype.buildRows = function() {
1487
1488        var _ = this, a, b, c, newSlides, numOfSlides, originalSlides,slidesPerSection;
1489
1490        newSlides = document.createDocumentFragment();
1491        originalSlides = _.$slider.children();
1492
1493        if(_.options.rows > 0) {
1494
1495            slidesPerSection = _.options.slidesPerRow * _.options.rows;
1496            numOfSlides = Math.ceil(
1497                originalSlides.length / slidesPerSection
1498            );
1499
1500            for(a = 0; a < numOfSlides; a++){
1501                var slide = document.createElement('div');
1502                for(b = 0; b < _.options.rows; b++) {
1503                    var row = document.createElement('div');
1504                    for(c = 0; c < _.options.slidesPerRow; c++) {
1505                        var target = (a * slidesPerSection + ((b * _.options.slidesPerRow) + c));
1506                        if (originalSlides.get(target)) {
1507                            row.appendChild(originalSlides.get(target));
1508                        }
1509                    }
1510                    slide.appendChild(row);
1511                }
1512                newSlides.appendChild(slide);
1513            }
1514
1515            _.$slider.empty().append(newSlides);
1516            _.$slider.children().children().children()
1517                .css({
1518                    'width':(100 / _.options.slidesPerRow) + '%',
1519                    'display': 'inline-block'
1520                });
1521
1522        }
1523
1524    };
1525
1526    Slick.prototype.checkResponsive = function(initial, forceUpdate) {
1527
1528        var _ = this,
1529            breakpoint, targetBreakpoint, respondToWidth, triggerBreakpoint = false;
1530        var sliderWidth = _.$slider.width();
1531        var windowWidth = window.innerWidth || $(window).width();
1532
1533        if (_.respondTo === 'window') {
1534            respondToWidth = windowWidth;
1535        } else if (_.respondTo === 'slider') {
1536            respondToWidth = sliderWidth;
1537        } else if (_.respondTo === 'min') {
1538            respondToWidth = Math.min(windowWidth, sliderWidth);
1539        }
1540
1541        if ( _.options.responsive &&
1542            _.options.responsive.length &&
1543            _.options.responsive !== null) {
1544
1545            targetBreakpoint = null;
1546
1547            for (breakpoint in _.breakpoints) {
1548                if (_.breakpoints.hasOwnProperty(breakpoint)) {
1549                    if (_.originalSettings.mobileFirst === false) {
1550                        if (respondToWidth < _.breakpoints[breakpoint]) {
1551                            targetBreakpoint = _.breakpoints[breakpoint];
1552                        }
1553                    } else {
1554                        if (respondToWidth > _.breakpoints[breakpoint]) {
1555                            targetBreakpoint = _.breakpoints[breakpoint];
1556                        }
1557                    }
1558                }
1559            }
1560
1561            if (targetBreakpoint !== null) {
1562                if (_.activeBreakpoint !== null) {
1563                    if (targetBreakpoint !== _.activeBreakpoint || forceUpdate) {
1564                        _.activeBreakpoint =
1565                            targetBreakpoint;
1566                        if (_.breakpointSettings[targetBreakpoint] === 'unslick') {
1567                            _.unslick(targetBreakpoint);
1568                        } else {
1569                            _.options = $.extend({}, _.originalSettings,
1570                                _.breakpointSettings[
1571                                    targetBreakpoint]);
1572                            if (initial === true) {
1573                                _.currentSlide = _.options.initialSlide;
1574                            }
1575                            _.refresh(initial);
1576                        }
1577                        triggerBreakpoint = targetBreakpoint;
1578                    }
1579                } else {
1580                    _.activeBreakpoint = targetBreakpoint;
1581                    if (_.breakpointSettings[targetBreakpoint] === 'unslick') {
1582                        _.unslick(targetBreakpoint);
1583                    } else {
1584                        _.options = $.extend({}, _.originalSettings,
1585                            _.breakpointSettings[
1586                                targetBreakpoint]);
1587                        if (initial === true) {
1588                            _.currentSlide = _.options.initialSlide;
1589                        }
1590                        _.refresh(initial);
1591                    }
1592                    triggerBreakpoint = targetBreakpoint;
1593                }
1594            } else {
1595                if (_.activeBreakpoint !== null) {
1596                    _.activeBreakpoint = null;
1597                    _.options = _.originalSettings;
1598                    if (initial === true) {
1599                        _.currentSlide = _.options.initialSlide;
1600                    }
1601                    _.refresh(initial);
1602                    triggerBreakpoint = targetBreakpoint;
1603                }
1604            }
1605
1606            // only trigger breakpoints during an actual break. not on initialize.
1607            if( !initial && triggerBreakpoint !== false ) {
1608                _.$slider.trigger('breakpoint', [_, triggerBreakpoint]);
1609            }
1610        }
1611
1612    };
1613
1614    Slick.prototype.changeSlide = function(event, dontAnimate) {
1615
1616        var _ = this,
1617            $target = $(event.currentTarget),
1618            indexOffset, slideOffset, unevenOffset;
1619
1620        // If target is a link, prevent default action.
1621        if($target.is('a')) {
1622            event.preventDefault();
1623        }
1624
1625        // If target is not the <li> element (ie: a child), find the <li>.
1626        if(!$target.is('li')) {
1627            $target = $target.closest('li');
1628        }
1629
1630        unevenOffset = (_.slideCount % _.options.slidesToScroll !== 0);
1631        indexOffset = unevenOffset ? 0 : (_.slideCount - _.currentSlide) % _.options.slidesToScroll;
1632
1633        switch (event.data.message) {
1634
1635            case 'previous':
1636                slideOffset = indexOffset === 0 ? _.options.slidesToScroll : _.options.slidesToShow - indexOffset;
1637                if (_.slideCount > _.options.slidesToShow) {
1638                    _.slideHandler(_.currentSlide - slideOffset, false, dontAnimate);
1639                }
1640                break;
1641
1642            case 'next':
1643                slideOffset = indexOffset === 0 ? _.options.slidesToScroll : indexOffset;
1644                if (_.slideCount > _.options.slidesToShow) {
1645                    _.slideHandler(_.currentSlide + slideOffset, false, dontAnimate);
1646                }
1647                break;
1648
1649            case 'index':
1650                var index = event.data.index === 0 ? 0 :
1651                    event.data.index || $target.index() * _.options.slidesToScroll;
1652
1653                _.slideHandler(_.checkNavigable(index), false, dontAnimate);
1654                $target.children().trigger('focus');
1655                break;
1656
1657            default:
1658                return;
1659        }
1660
1661    };
1662
1663    Slick.prototype.checkNavigable = function(index) {
1664
1665        var _ = this,
1666            navigables, prevNavigable;
1667
1668        navigables = _.getNavigableIndexes();
1669        prevNavigable = 0;
1670        if (index > navigables[navigables.length - 1]) {
1671            index = navigables[navigables.length - 1];
1672        } else {
1673            for (var n in navigables) {
1674                if (index < navigables[n]) {
1675                    index = prevNavigable;
1676                    break;
1677                }
1678                prevNavigable = navigables[n];
1679            }
1680        }
1681
1682        return index;
1683    };
1684
1685    Slick.prototype.cleanUpEvents = function() {
1686
1687        var _ = this;
1688
1689        if (_.options.dots && _.$dots !== null) {
1690
1691            $('li', _.$dots)
1692                .off('click.slick', _.changeSlide)
1693                .off('mouseenter.slick', $.proxy(_.interrupt, _, true))
1694                .off('mouseleave.slick', $.proxy(_.interrupt, _, false));
1695
1696            if (_.options.accessibility === true) {
1697                _.$dots.off('keydown.slick', _.keyHandler);
1698            }
1699        }
1700
1701        _.$slider.off('focus.slick blur.slick');
1702
1703        if (_.options.arrows === true && _.slideCount > _.options.slidesToShow) {
1704            _.$prevArrow && _.$prevArrow.off('click.slick', _.changeSlide);
1705            _.$nextArrow && _.$nextArrow.off('click.slick', _.changeSlide);
1706
1707            if (_.options.accessibility === true) {
1708                _.$prevArrow && _.$prevArrow.off('keydown.slick', _.keyHandler);
1709                _.$nextArrow && _.$nextArrow.off('keydown.slick', _.keyHandler);
1710            }
1711        }
1712
1713        _.$list.off('touchstart.slick mousedown.slick', _.swipeHandler);
1714        _.$list.off('touchmove.slick mousemove.slick', _.swipeHandler);
1715        _.$list.off('touchend.slick mouseup.slick', _.swipeHandler);
1716        _.$list.off('touchcancel.slick mouseleave.slick', _.swipeHandler);
1717
1718        _.$list.off('click.slick', _.clickHandler);
1719
1720        $(document).off(_.visibilityChange, _.visibility);
1721
1722        _.cleanUpSlideEvents();
1723
1724        if (_.options.accessibility === true) {
1725            _.$list.off('keydown.slick', _.keyHandler);
1726        }
1727
1728        if (_.options.focusOnSelect === true) {
1729            $(_.$slideTrack).children().off('click.slick', _.selectHandler);
1730        }
1731
1732        $(window).off('orientationchange.slick.slick-' + _.instanceUid, _.orientationChange);
1733
1734        $(window).off('resize.slick.slick-' + _.instanceUid, _.resize);
1735
1736        $('[draggable!=true]', _.$slideTrack).off('dragstart', _.preventDefault);
1737
1738        $(window).off('load.slick.slick-' + _.instanceUid, _.setPosition);
1739
1740    };
1741
1742    Slick.prototype.cleanUpSlideEvents = function() {
1743
1744        var _ = this;
1745
1746        _.$list.off('mouseenter.slick', $.proxy(_.interrupt, _, true));
1747        _.$list.off('mouseleave.slick', $.proxy(_.interrupt, _, false));
1748
1749    };
1750
1751    Slick.prototype.cleanUpRows = function() {
1752
1753        var _ = this, originalSlides;
1754
1755        if(_.options.rows > 0) {
1756            originalSlides = _.$slides.children().children();
1757            originalSlides.removeAttr('style');
1758            _.$slider.empty().append(originalSlides);
1759        }
1760
1761    };
1762
1763    Slick.prototype.clickHandler = function(event) {
1764
1765        var _ = this;
1766
1767        if (_.shouldClick === false) {
1768            event.stopImmediatePropagation();
1769            event.stopPropagation();
1770            event.preventDefault();
1771        }
1772
1773    };
1774
1775    Slick.prototype.destroy = function(refresh) {
1776
1777        var _ = this;
1778
1779        _.autoPlayClear();
1780
1781        _.touchObject = {};
1782
1783        _.cleanUpEvents();
1784
1785        $('.slick-cloned', _.$slider).detach();
1786
1787        if (_.$dots) {
1788            _.$dots.remove();
1789        }
1790
1791        if ( _.$prevArrow && _.$prevArrow.length ) {
1792
1793            _.$prevArrow
1794                .removeClass('slick-disabled slick-arrow slick-hidden')
1795                .removeAttr('aria-hidden aria-disabled tabindex')
1796                .css('display','');
1797
1798            if ( _.htmlExpr.test( _.options.prevArrow )) {
1799                _.$prevArrow.remove();
1800            }
1801        }
1802
1803        if ( _.$nextArrow && _.$nextArrow.length ) {
1804
1805            _.$nextArrow
1806                .removeClass('slick-disabled slick-arrow slick-hidden')
1807                .removeAttr('aria-hidden aria-disabled tabindex')
1808                .css('display','');
1809
1810            if ( _.htmlExpr.test( _.options.nextArrow )) {
1811                _.$nextArrow.remove();
1812            }
1813        }
1814
1815
1816        if (_.$slides) {
1817
1818            _.$slides
1819                .removeClass('slick-slide slick-active slick-center slick-visible slick-current')
1820                .removeAttr('aria-hidden')
1821                .removeAttr('data-slick-index')
1822                .each(function(){
1823                    $(this).attr('style', $(this).data('originalStyling'));
1824                });
1825
1826            _.$slideTrack.children(this.options.slide).detach();
1827
1828            _.$slideTrack.detach();
1829
1830            _.$list.detach();
1831
1832            _.$slider.append(_.$slides);
1833        }
1834
1835        _.cleanUpRows();
1836
1837        _.$slider.removeClass('slick-slider');
1838        _.$slider.removeClass('slick-initialized');
1839        _.$slider.removeClass('slick-dotted');
1840
1841        _.unslicked = true;
1842
1843        if(!refresh) {
1844            _.$slider.trigger('destroy', [_]);
1845        }
1846
1847    };
1848
1849    Slick.prototype.disableTransition = function(slide) {
1850
1851        var _ = this,
1852            transition = {};
1853
1854        transition[_.transitionType] = '';
1855
1856        if (_.options.fade === false) {
1857            _.$slideTrack.css(transition);
1858        } else {
1859            _.$slides.eq(slide).css(transition);
1860        }
1861
1862    };
1863
1864    Slick.prototype.fadeSlide = function(slideIndex, callback) {
1865
1866        var _ = this;
1867
1868        if (_.cssTransitions === false) {
1869
1870            _.$slides.eq(slideIndex).css({
1871                zIndex: _.options.zIndex
1872            });
1873
1874            _.$slides.eq(slideIndex).animate({
1875                opacity: 1
1876            }, _.options.speed, _.options.easing, callback);
1877
1878        } else {
1879
1880            _.applyTransition(slideIndex);
1881
1882            _.$slides.eq(slideIndex).css({
1883                opacity: 1,
1884                zIndex: _.options.zIndex
1885            });
1886
1887            if (callback) {
1888                setTimeout(function() {
1889
1890                    _.disableTransition(slideIndex);
1891
1892                    callback.call();
1893                }, _.options.speed);
1894            }
1895
1896        }
1897
1898    };
1899
1900    Slick.prototype.fadeSlideOut = function(slideIndex) {
1901
1902        var _ = this;
1903
1904        if (_.cssTransitions === false) {
1905
1906            _.$slides.eq(slideIndex).animate({
1907                opacity: 0,
1908                zIndex: _.options.zIndex - 2
1909            }, _.options.speed, _.options.easing);
1910
1911        } else {
1912
1913            _.applyTransition(slideIndex);
1914
1915            _.$slides.eq(slideIndex).css({
1916                opacity: 0,
1917                zIndex: _.options.zIndex - 2
1918            });
1919
1920        }
1921
1922    };
1923
1924    Slick.prototype.filterSlides = Slick.prototype.slickFilter = function(filter) {
1925
1926        var _ = this;
1927
1928        if (filter !== null) {
1929
1930            _.$slidesCache = _.$slides;
1931
1932            _.unload();
1933
1934            _.$slideTrack.children(this.options.slide).detach();
1935
1936            _.$slidesCache.filter(filter).appendTo(_.$slideTrack);
1937
1938            _.reinit();
1939
1940        }
1941
1942    };
1943
1944    Slick.prototype.focusHandler = function() {
1945
1946        var _ = this;
1947
1948        
vendor: 3,201 bytes, lines 1948-2043
1948// If any child element receives focus within the slider we need to pause the autoplay
1949        _.$slider
1950            .off('focus.slick blur.slick')
1951            .on(
1952                'focus.slick',
1953                '*',
1954                function(event) {
1955                    var $sf = $(this);
1956
1957                    setTimeout(function() {
1958                        if( _.options.pauseOnFocus ) {
1959                            if ($sf.is(':focus')) {
1960                                _.focussed = true;
1961                                _.autoPlay();
1962                            }
1963                        }
1964                    }, 0);
1965                }
1966            ).on(
1967                'blur.slick',
1968                '*',
1969                function(event) {
1970                    var $sf = $(this);
1971
1972                    // When a blur occurs on any elements within the slider we become unfocused
1973                    if( _.options.pauseOnFocus ) {
1974                        _.focussed = false;
1975                        _.autoPlay();
1976                    }
1977                }
1978            );
1979    };
1980
1981    Slick.prototype.getCurrent = Slick.prototype.slickCurrentSlide = function() {
1982
1983        var _ = this;
1984        return _.currentSlide;
1985
1986    };
1987
1988    Slick.prototype.getDotCount = function() {
1989
1990        var _ = this;
1991
1992        var breakPoint = 0;
1993        var counter = 0;
1994        var pagerQty = 0;
1995
1996        if (_.options.infinite === true) {
1997            if (_.slideCount <= _.options.slidesToShow) {
1998                 ++pagerQty;
1999            } else {
2000                while (breakPoint < _.slideCount) {
2001                    ++pagerQty;
2002                    breakPoint = counter + _.options.slidesToScroll;
2003                    counter += _.options.slidesToScroll <= _.options.slidesToShow ? _.options.slidesToScroll : _.options.slidesToShow;
2004                }
2005            }
2006        } else if (_.options.centerMode === true) {
2007            pagerQty = _.slideCount;
2008        } else if(!_.options.asNavFor) {
2009            pagerQty = 1 + Math.ceil((_.slideCount - _.options.slidesToShow) / _.options.slidesToScroll);
2010        }else {
2011            while (breakPoint < _.slideCount) {
2012                ++pagerQty;
2013                breakPoint = counter + _.options.slidesToScroll;
2014                counter += _.options.slidesToScroll <= _.options.slidesToShow ? _.options.slidesToScroll : _.options.slidesToShow;
2015            }
2016        }
2017
2018        return pagerQty - 1;
2019
2020    };
2021
2022    Slick.prototype.getLeft = function(slideIndex) {
2023
2024        var _ = this,
2025            targetLeft,
2026            verticalHeight,
2027            verticalOffset = 0,
2028            targetSlide,
2029            coef;
2030
2031        _.slideOffset = 0;
2032        verticalHeight = _.$slides.first().outerHeight(true);
2033
2034        if (_.options.infinite === true) {
2035            if (_.slideCount > _.options.slidesToShow) {
2036                _.slideOffset = (_.slideWidth * _.options.slidesToShow) * -1;
2037                coef = -1
2038
2039                if (_.options.vertical === true && _.options.centerMode === true) {
2040                    if (_.options.slidesToShow === 2) {
2041                        coef = -1.5;
2042                    } else if (_.options.slidesToShow === 1) {
2043                        coef = -2
vendor: 4,950 bytes, lines 2043-2172
2043
2044                    }
2045                }
2046                verticalOffset = (verticalHeight * _.options.slidesToShow) * coef;
2047            }
2048            if (_.slideCount % _.options.slidesToScroll !== 0) {
2049                if (slideIndex + _.options.slidesToScroll > _.slideCount && _.slideCount > _.options.slidesToShow) {
2050                    if (slideIndex > _.slideCount) {
2051                        _.slideOffset = ((_.options.slidesToShow - (slideIndex - _.slideCount)) * _.slideWidth) * -1;
2052                        verticalOffset = ((_.options.slidesToShow - (slideIndex - _.slideCount)) * verticalHeight) * -1;
2053                    } else {
2054                        _.slideOffset = ((_.slideCount % _.options.slidesToScroll) * _.slideWidth) * -1;
2055                        verticalOffset = ((_.slideCount % _.options.slidesToScroll) * verticalHeight) * -1;
2056                    }
2057                }
2058            }
2059        } else {
2060            if (slideIndex + _.options.slidesToShow > _.slideCount) {
2061                _.slideOffset = ((slideIndex + _.options.slidesToShow) - _.slideCount) * _.slideWidth;
2062                verticalOffset = ((slideIndex + _.options.slidesToShow) - _.slideCount) * verticalHeight;
2063            }
2064        }
2065
2066        if (_.slideCount <= _.options.slidesToShow) {
2067            _.slideOffset = 0;
2068            verticalOffset = 0;
2069        }
2070
2071        if (_.options.centerMode === true && _.slideCount <= _.options.slidesToShow) {
2072            _.slideOffset = ((_.slideWidth * Math.floor(_.options.slidesToShow)) / 2) - ((_.slideWidth * _.slideCount) / 2);
2073        } else if (_.options.centerMode === true && _.options.infinite === true) {
2074            _.slideOffset += _.slideWidth * Math.floor(_.options.slidesToShow / 2) - _.slideWidth;
2075        } else if (_.options.centerMode === true) {
2076            _.slideOffset = 0;
2077            _.slideOffset += _.slideWidth * Math.floor(_.options.slidesToShow / 2);
2078        }
2079
2080        if (_.options.vertical === false) {
2081            targetLeft = ((slideIndex * _.slideWidth) * -1) + _.slideOffset;
2082        } else {
2083            targetLeft = ((slideIndex * verticalHeight) * -1) + verticalOffset;
2084        }
2085
2086        if (_.options.variableWidth === true) {
2087
2088            if (_.slideCount <= _.options.slidesToShow || _.options.infinite === false) {
2089                targetSlide = _.$slideTrack.children('.slick-slide').eq(slideIndex);
2090            } else {
2091                targetSlide = _.$slideTrack.children('.slick-slide').eq(slideIndex + _.options.slidesToShow);
2092            }
2093
2094            if (_.options.rtl === true) {
2095                if (targetSlide[0]) {
2096                    targetLeft = (_.$slideTrack.width() - targetSlide[0].offsetLeft - targetSlide.width()) * -1;
2097                } else {
2098                    targetLeft =  0;
2099                }
2100            } else {
2101                targetLeft = targetSlide[0] ? targetSlide[0].offsetLeft * -1 : 0;
2102            }
2103
2104            if (_.options.centerMode === true) {
2105                if (_.slideCount <= _.options.slidesToShow || _.options.infinite === false) {
2106                    targetSlide = _.$slideTrack.children('.slick-slide').eq(slideIndex);
2107                } else {
2108                    targetSlide = _.$slideTrack.children('.slick-slide').eq(slideIndex + _.options.slidesToShow + 1);
2109                }
2110
2111                if (_.options.rtl === true) {
2112                    if (targetSlide[0]) {
2113                        targetLeft = (_.$slideTrack.width() - targetSlide[0].offsetLeft - targetSlide.width()) * -1;
2114                    } else {
2115                        targetLeft =  0;
2116                    }
2117                } else {
2118                    targetLeft = targetSlide[0] ? targetSlide[0].offsetLeft * -1 : 0;
2119                }
2120
2121                targetLeft += (_.$list.width() - targetSlide.outerWidth()) / 2;
2122            }
2123        }
2124
2125        return targetLeft;
2126
2127    };
2128
2129    Slick.prototype.getOption = Slick.prototype.slickGetOption = function(option) {
2130
2131        var _ = this;
2132
2133        return _.options[option];
2134
2135    };
2136
2137    Slick.prototype.getNavigableIndexes = function() {
2138
2139        var _ = this,
2140            breakPoint = 0,
2141            counter = 0,
2142            indexes = [],
2143            max;
2144
2145        if (_.options.infinite === false) {
2146            max = _.slideCount;
2147        } else {
2148            breakPoint = _.options.slidesToScroll * -1;
2149            counter = _.options.slidesToScroll * -1;
2150            max = _.slideCount * 2;
2151        }
2152
2153        while (breakPoint < max) {
2154            indexes.push(breakPoint);
2155            breakPoint = counter + _.options.slidesToScroll;
2156            counter += _.options.slidesToScroll <= _.options.slidesToShow ? _.options.slidesToScroll : _.options.slidesToShow;
2157        }
2158
2159        return indexes;
2160
2161    };
2162
2163    Slick.prototype.getSlick = function() {
2164
2165        return this;
2166
2167    };
2168
2169    Slick.prototype.getSlideCount = function() {
2170
2171        var _ = this,
2172            slidesTraversed, swipedSlide, 
2172swipeTarget, centerOffset;
2173
2174        centerOffset = _.options.centerMode === true ? Math.floor(_.$list.width() / 2) : 0;
2175        swipeTarget = (_.swipeLeft * -1) + centerOffset;
2176
2177        if (_.options.swipeToSlide === true) {
2178
2179            _.$slideTrack.find('.slick-slide').each(function(index, slide) {
2180
2181                var slideOuterWidth, slideOffset, slideRightBoundary;
2182                slideOuterWidth = $(slide).outerWidth();
2183                slideOffset = slide.offsetLeft;
2184                if (_.options.centerMode !== true) {
2185                    slideOffset += (slideOuterWidth / 2);
2186                }
2187
2188                slideRightBoundary = slideOffset + (slideOuterWidth);
2189
2190                if (swipeTarget < slideRightBoundary) {
2191                    swipedSlide = slide;
2192                    return false;
2193                }
2194            });
2195
2196            slidesTraversed = Math.abs($(swipedSlide).attr('data-slick-index') - _.currentSlide) || 1;
2197
2198            return slidesTraversed;
2199
2200        } else {
2201            return _.options.slidesToScroll;
2202        }
2203
2204    };
2205
2206    Slick.prototype.goTo = Slick.prototype.slickGoTo = function(slide, dontAnimate) {
2207
2208        var _ = this;
2209
2210        _.changeSlide({
2211            data: {
2212                message: 'index',
2213                index: parseInt(slide)
2214            }
2215        }, dontAnimate);
2216
2217    };
2218
2219    Slick.prototype.init = function(creation) {
2220
2221        var _ = this;
2222
2223        if (!$(_.$slider).hasClass('slick-initialized')) {
2224
2225            $(_.$slider).addClass('slick-initialized');
2226
2227            _.buildRows();
2228            _.buildOut();
2229            _.setProps();
2230            _.startLoad();
2231            _.loadSlider();
2232            _.initializeEvents();
2233            _.updateArrows();
2234            _.updateDots();
2235            _.checkResponsive(true);
2236            _.focusHandler();
2237
2238        }
2239
2240        if (creation) {
2241            _.$slider.trigger('init', [_]);
2242        }
2243
2244        if (_.options.accessibility === true) {
2245            _.initADA();
2246        }
2247
2248        if ( _.options.autoplay ) {
2249
2250            _.paused = false;
vendor: 1,103 bytes, lines 2251-2282
2251            _.autoPlay();
2252
2253        }
2254
2255    };
2256
2257    Slick.prototype.initADA = function() {
2258        var _ = this,
2259                numDotGroups = Math.ceil(_.slideCount / _.options.slidesToShow),
2260                tabControlIndexes = _.getNavigableIndexes().filter(function(val) {
2261                    return (val >= 0) && (val < _.slideCount);
2262                });
2263
2264        _.$slides.add(_.$slideTrack.find('.slick-cloned')).attr({
2265            'aria-hidden': 'true',
2266            'tabindex': '-1'
2267        }).find('a, input, button, select').attr({
2268            'tabindex': '-1'
2269        });
2270
2271        if (_.$dots !== null) {
2272            _.$slides.not(_.$slideTrack.find('.slick-cloned')).each(function(i) {
2273                var slideControlIndex = tabControlIndexes.indexOf(i);
2274
2275                $(this).attr({
2276                    'role': 'tabpanel',
2277                    'id': 'slick-slide' + _.instanceUid + i,
2278                    'tabindex': -1
2279                });
2280
2281                if (slideControlIndex !== -1) {
2282                   var ariaButtonControl = 'slick-slide-control' + _.instanceUid + slideControlIndex
vendor: 29,020 bytes, lines 2282-3288
2282
2283                   if ($('#' + ariaButtonControl).length) {
2284                     $(this).attr({
2285                         'aria-describedby': ariaButtonControl
2286                     });
2287                   }
2288                }
2289            });
2290
2291            _.$dots.attr('role', 'tablist').find('li').each(function(i) {
2292                var mappedSlideIndex = tabControlIndexes[i];
2293
2294                $(this).attr({
2295                    'role': 'presentation'
2296                });
2297
2298                $(this).find('button').first().attr({
2299                    'role': 'tab',
2300                    'id': 'slick-slide-control' + _.instanceUid + i,
2301                    'aria-controls': 'slick-slide' + _.instanceUid + mappedSlideIndex,
2302                    'aria-label': (i + 1) + ' of ' + numDotGroups,
2303                    'aria-selected': null,
2304                    'tabindex': '-1'
2305                });
2306
2307            }).eq(_.currentSlide).find('button').attr({
2308                'aria-selected': 'true',
2309                'tabindex': '0'
2310            }).end();
2311        }
2312
2313        for (var i=_.currentSlide, max=i+_.options.slidesToShow; i < max; i++) {
2314          if (_.options.focusOnChange) {
2315            _.$slides.eq(i).attr({'tabindex': '0'});
2316          } else {
2317            _.$slides.eq(i).removeAttr('tabindex');
2318          }
2319        }
2320
2321        _.activateADA();
2322
2323    };
2324
2325    Slick.prototype.initArrowEvents = function() {
2326
2327        var _ = this;
2328
2329        if (_.options.arrows === true && _.slideCount > _.options.slidesToShow) {
2330            _.$prevArrow
2331               .off('click.slick')
2332               .on('click.slick', {
2333                    message: 'previous'
2334               }, _.changeSlide);
2335            _.$nextArrow
2336               .off('click.slick')
2337               .on('click.slick', {
2338                    message: 'next'
2339               }, _.changeSlide);
2340
2341            if (_.options.accessibility === true) {
2342                _.$prevArrow.on('keydown.slick', _.keyHandler);
2343                _.$nextArrow.on('keydown.slick', _.keyHandler);
2344            }
2345        }
2346
2347    };
2348
2349    Slick.prototype.initDotEvents = function() {
2350
2351        var _ = this;
2352
2353        if (_.options.dots === true && _.slideCount > _.options.slidesToShow) {
2354            $('li', _.$dots).on('click.slick', {
2355                message: 'index'
2356            }, _.changeSlide);
2357
2358            if (_.options.accessibility === true) {
2359                _.$dots.on('keydown.slick', _.keyHandler);
2360            }
2361        }
2362
2363        if (_.options.dots === true && _.options.pauseOnDotsHover === true && _.slideCount > _.options.slidesToShow) {
2364
2365            $('li', _.$dots)
2366                .on('mouseenter.slick', $.proxy(_.interrupt, _, true))
2367                .on('mouseleave.slick', $.proxy(_.interrupt, _, false));
2368
2369        }
2370
2371    };
2372
2373    Slick.prototype.initSlideEvents = function() {
2374
2375        var _ = this;
2376
2377        if ( _.options.pauseOnHover ) {
2378
2379            _.$list.on('mouseenter.slick', $.proxy(_.interrupt, _, true));
2380            _.$list.on('mouseleave.slick', $.proxy(_.interrupt, _, false));
2381
2382        }
2383
2384    };
2385
2386    Slick.prototype.initializeEvents = function() {
2387
2388        var _ = this;
2389
2390        _.initArrowEvents();
2391
2392        _.initDotEvents();
2393        _.initSlideEvents();
2394
2395        _.$list.on('touchstart.slick mousedown.slick', {
2396            action: 'start'
2397        }, _.swipeHandler);
2398        _.$list.on('touchmove.slick mousemove.slick', {
2399            action: 'move'
2400        }, _.swipeHandler);
2401        _.$list.on('touchend.slick mouseup.slick', {
2402            action: 'end'
2403        }, _.swipeHandler);
2404        _.$list.on('touchcancel.slick mouseleave.slick', {
2405            action: 'end'
2406        }, _.swipeHandler);
2407
2408        _.$list.on('click.slick', _.clickHandler);
2409
2410        $(document).on(_.visibilityChange, $.proxy(_.visibility, _));
2411
2412        if (_.options.accessibility === true) {
2413            _.$list.on('keydown.slick', _.keyHandler);
2414        }
2415
2416        if (_.options.focusOnSelect === true) {
2417            $(_.$slideTrack).children().on('click.slick', _.selectHandler);
2418        }
2419
2420        $(window).on('orientationchange.slick.slick-' + _.instanceUid, $.proxy(_.orientationChange, _));
2421
2422        $(window).on('resize.slick.slick-' + _.instanceUid, $.proxy(_.resize, _));
2423
2424        $('[draggable!=true]', _.$slideTrack).on('dragstart', _.preventDefault);
2425
2426        $(window).on('load.slick.slick-' + _.instanceUid, _.setPosition);
2427        $(_.setPosition);
2428
2429    };
2430
2431    Slick.prototype.initUI = function() {
2432
2433        var _ = this;
2434
2435        if (_.options.arrows === true && _.slideCount > _.options.slidesToShow) {
2436
2437            _.$prevArrow.show();
2438            _.$nextArrow.show();
2439
2440        }
2441
2442        if (_.options.dots === true && _.slideCount > _.options.slidesToShow) {
2443
2444            _.$dots.show();
2445
2446        }
2447
2448    };
2449
2450    Slick.prototype.keyHandler = function(event) {
2451
2452        var _ = this;
2453         //Dont slide if the cursor is inside the form fields and arrow keys are pressed
2454        if(!event.target.tagName.match('TEXTAREA|INPUT|SELECT')) {
2455            if (event.keyCode === 37 && _.options.accessibility === true) {
2456                _.changeSlide({
2457                    data: {
2458                        message: _.options.rtl === true ? 'next' :  'previous'
2459                    }
2460                });
2461            } else if (event.keyCode === 39 && _.options.accessibility === true) {
2462                _.changeSlide({
2463                    data: {
2464                        message: _.options.rtl === true ? 'previous' : 'next'
2465                    }
2466                });
2467            }
2468        }
2469
2470    };
2471
2472    Slick.prototype.lazyLoad = function() {
2473
2474        var _ = this,
2475            loadRange, cloneRange, rangeStart, rangeEnd;
2476
2477        function loadImages(imagesScope) {
2478
2479            $('img[data-lazy]', imagesScope).each(function() {
2480
2481                var image = $(this),
2482                    imageSource = $(this).attr('data-lazy'),
2483                    imageSrcSet = $(this).attr('data-srcset'),
2484                    imageSizes  = $(this).attr('data-sizes') || _.$slider.attr('data-sizes'),
2485                    imageToLoad = document.createElement('img');
2486
2487                imageToLoad.onload = function() {
2488
2489                    image
2490                        .animate({ opacity: 0 }, 100, function() {
2491
2492                            if (imageSrcSet) {
2493                                image
2494                                    .attr('srcset', imageSrcSet );
2495
2496                                if (imageSizes) {
2497                                    image
2498                                        .attr('sizes', imageSizes );
2499                                }
2500                            }
2501
2502                            image
2503                                .attr('src', imageSource)
2504                                .animate({ opacity: 1 }, 200, function() {
2505                                    image
2506                                        .removeAttr('data-lazy data-srcset data-sizes')
2507                                        .removeClass('slick-loading');
2508                                });
2509                            _.$slider.trigger('lazyLoaded', [_, image, imageSource]);
2510                        });
2511
2512                };
2513
2514                imageToLoad.onerror = function() {
2515
2516                    image
2517                        .removeAttr( 'data-lazy' )
2518                        .removeClass( 'slick-loading' )
2519                        .addClass( 'slick-lazyload-error' );
2520
2521                    _.$slider.trigger('lazyLoadError', [ _, image, imageSource ]);
2522
2523                };
2524
2525                imageToLoad.src = imageSource;
2526
2527            });
2528
2529        }
2530
2531        if (_.options.centerMode === true) {
2532            if (_.options.infinite === true) {
2533                rangeStart = _.currentSlide + (_.options.slidesToShow / 2 + 1);
2534                rangeEnd = rangeStart + _.options.slidesToShow + 2;
2535            } else {
2536                rangeStart = Math.max(0, _.currentSlide - (_.options.slidesToShow / 2 + 1));
2537                rangeEnd = 2 + (_.options.slidesToShow / 2 + 1) + _.currentSlide;
2538            }
2539        } else {
2540            rangeStart = _.options.infinite ? _.options.slidesToShow + _.currentSlide : _.currentSlide;
2541            rangeEnd = Math.ceil(rangeStart + _.options.slidesToShow);
2542            if (_.options.fade === true) {
2543                if (rangeStart > 0) rangeStart--;
2544                if (rangeEnd <= _.slideCount) rangeEnd++;
2545            }
2546        }
2547
2548        loadRange = _.$slider.find('.slick-slide').slice(rangeStart, rangeEnd);
2549
2550        if (_.options.lazyLoad === 'anticipated') {
2551            var prevSlide = rangeStart - 1,
2552                nextSlide = rangeEnd,
2553                $slides = _.$slider.find('.slick-slide');
2554
2555            for (var i = 0; i < _.options.slidesToScroll; i++) {
2556                if (prevSlide < 0) prevSlide = _.slideCount - 1;
2557                loadRange = loadRange.add($slides.eq(prevSlide));
2558                loadRange = loadRange.add($slides.eq(nextSlide));
2559                prevSlide--;
2560                nextSlide++;
2561            }
2562        }
2563
2564        loadImages(loadRange);
2565
2566        if (_.slideCount <= _.options.slidesToShow) {
2567            cloneRange = _.$slider.find('.slick-slide');
2568            loadImages(cloneRange);
2569        } else
2570        if (_.currentSlide >= _.slideCount - _.options.slidesToShow) {
2571            cloneRange = _.$slider.find('.slick-cloned').slice(0, _.options.slidesToShow);
2572            loadImages(cloneRange);
2573        } else if (_.currentSlide === 0) {
2574            cloneRange = _.$slider.find('.slick-cloned').slice(_.options.slidesToShow * -1);
2575            loadImages(cloneRange);
2576        }
2577
2578    };
2579
2580    Slick.prototype.loadSlider = function() {
2581
2582        var _ = this;
2583
2584        _.setPosition();
2585
2586        _.$slideTrack.css({
2587            opacity: 1
2588        });
2589
2590        _.$slider.removeClass('slick-loading');
2591
2592        _.initUI();
2593
2594        if (_.options.lazyLoad === 'progressive') {
2595            _.progressiveLazyLoad();
2596        }
2597
2598    };
2599
2600    Slick.prototype.next = Slick.prototype.slickNext = function() {
2601
2602        var _ = this;
2603
2604        _.changeSlide({
2605            data: {
2606                message: 'next'
2607            }
2608        });
2609
2610    };
2611
2612    Slick.prototype.orientationChange = function() {
2613
2614        var _ = this;
2615
2616        _.checkResponsive();
2617        _.setPosition();
2618
2619    };
2620
2621    Slick.prototype.pause = Slick.prototype.slickPause = function() {
2622
2623        var _ = this;
2624
2625        _.autoPlayClear();
2626        _.paused = true;
2627
2628    };
2629
2630    Slick.prototype.play = Slick.prototype.slickPlay = function() {
2631
2632        var _ = this;
2633
2634        _.autoPlay();
2635        _.options.autoplay = true;
2636        _.paused = false;
2637        _.focussed = false;
2638        _.interrupted = false;
2639
2640    };
2641
2642    Slick.prototype.postSlide = function(index) {
2643
2644        var _ = this;
2645
2646        if( !_.unslicked ) {
2647
2648            _.$slider.trigger('afterChange', [_, index]);
2649
2650            _.animating = false;
2651
2652            if (_.slideCount > _.options.slidesToShow) {
2653                _.setPosition();
2654            }
2655
2656            _.swipeLeft = null;
2657
2658            if ( _.options.autoplay ) {
2659                _.autoPlay();
2660            }
2661
2662            if (_.options.accessibility === true) {
2663                _.initADA();
2664
2665                if (_.options.focusOnChange) {
2666                    var $currentSlide = $(_.$slides.get(_.currentSlide));
2667                    $currentSlide.attr('tabindex', 0).focus();
2668                }
2669            }
2670
2671        }
2672
2673    };
2674
2675    Slick.prototype.prev = Slick.prototype.slickPrev = function() {
2676
2677        var _ = this;
2678
2679        _.changeSlide({
2680            data: {
2681                message: 'previous'
2682            }
2683        });
2684
2685    };
2686
2687    Slick.prototype.preventDefault = function(event) {
2688
2689        event.preventDefault();
2690
2691    };
2692
2693    Slick.prototype.progressiveLazyLoad = function( tryCount ) {
2694
2695        tryCount = tryCount || 1;
2696
2697        var _ = this,
2698            $imgsToLoad = $( 'img[data-lazy]', _.$slider ),
2699            image,
2700            imageSource,
2701            imageSrcSet,
2702            imageSizes,
2703            imageToLoad;
2704
2705        if ( $imgsToLoad.length ) {
2706
2707            image = $imgsToLoad.first();
2708            imageSource = image.attr('data-lazy');
2709            imageSrcSet = image.attr('data-srcset');
2710            imageSizes  = image.attr('data-sizes') || _.$slider.attr('data-sizes');
2711            imageToLoad = document.createElement('img');
2712
2713            imageToLoad.onload = function() {
2714
2715                if (imageSrcSet) {
2716                    image
2717                        .attr('srcset', imageSrcSet );
2718
2719                    if (imageSizes) {
2720                        image
2721                            .attr('sizes', imageSizes );
2722                    }
2723                }
2724
2725                image
2726                    .attr( 'src', imageSource )
2727                    .removeAttr('data-lazy data-srcset data-sizes')
2728                    .removeClass('slick-loading');
2729
2730                if ( _.options.adaptiveHeight === true ) {
2731                    _.setPosition();
2732                }
2733
2734                _.$slider.trigger('lazyLoaded', [ _, image, imageSource ]);
2735                _.progressiveLazyLoad();
2736
2737            };
2738
2739            imageToLoad.onerror = function() {
2740
2741                if ( tryCount < 3 ) {
2742
2743                    /**
2744                     * try to load the image 3 times,
2745                     * leave a slight delay so we don't get
2746                     * servers blocking the request.
2747                     */
2748                    setTimeout( function() {
2749                        _.progressiveLazyLoad( tryCount + 1 );
2750                    }, 500 );
2751
2752                } else {
2753
2754                    image
2755                        .removeAttr( 'data-lazy' )
2756                        .removeClass( 'slick-loading' )
2757                        .addClass( 'slick-lazyload-error' );
2758
2759                    _.$slider.trigger('lazyLoadError', [ _, image, imageSource ]);
2760
2761                    _.progressiveLazyLoad();
2762
2763                }
2764
2765            };
2766
2767            imageToLoad.src = imageSource;
2768
2769        } else {
2770
2771            _.$slider.trigger('allImagesLoaded', [ _ ]);
2772
2773        }
2774
2775    };
2776
2777    Slick.prototype.refresh = function( initializing ) {
2778
2779        var _ = this, currentSlide, lastVisibleIndex;
2780
2781        lastVisibleIndex = _.slideCount - _.options.slidesToShow;
2782
2783        // in non-infinite sliders, we don't want to go past the
2784        // last visible index.
2785        if( !_.options.infinite && ( _.currentSlide > lastVisibleIndex )) {
2786            _.currentSlide = lastVisibleIndex;
2787        }
2788
2789        // if less slides than to show, go to start.
2790        if ( _.slideCount <= _.options.slidesToShow ) {
2791            _.currentSlide = 0;
2792
2793        }
2794
2795        currentSlide = _.currentSlide;
2796
2797        _.destroy(true);
2798
2799        $.extend(_, _.initials, { currentSlide: currentSlide });
2800
2801        _.init();
2802
2803        if( !initializing ) {
2804
2805            _.changeSlide({
2806                data: {
2807                    message: 'index',
2808                    index: currentSlide
2809                }
2810            }, false);
2811
2812        }
2813
2814    };
2815
2816    Slick.prototype.registerBreakpoints = function() {
2817
2818        var _ = this, breakpoint, currentBreakpoint, l,
2819            responsiveSettings = _.options.responsive || null;
2820
2821        if ( $.type(responsiveSettings) === 'array' && responsiveSettings.length ) {
2822
2823            _.respondTo = _.options.respondTo || 'window';
2824
2825            for ( breakpoint in responsiveSettings ) {
2826
2827                l = _.breakpoints.length-1;
2828
2829                if (responsiveSettings.hasOwnProperty(breakpoint)) {
2830                    currentBreakpoint = responsiveSettings[breakpoint].breakpoint;
2831
2832                    // loop through the breakpoints and cut out any existing
2833                    // ones with the same breakpoint number, we don't want dupes.
2834                    while( l >= 0 ) {
2835                        if( _.breakpoints[l] && _.breakpoints[l] === currentBreakpoint ) {
2836                            _.breakpoints.splice(l,1);
2837                        }
2838                        l--;
2839                    }
2840
2841                    _.breakpoints.push(currentBreakpoint);
2842                    _.breakpointSettings[currentBreakpoint] = responsiveSettings[breakpoint].settings;
2843
2844                }
2845
2846            }
2847
2848            _.breakpoints.sort(function(a, b) {
2849                return ( _.options.mobileFirst ) ? a-b : b-a;
2850            });
2851
2852        }
2853
2854    };
2855
2856    Slick.prototype.reinit = function() {
2857
2858        var _ = this;
2859
2860        _.$slides =
2861            _.$slideTrack
2862                .children(_.options.slide)
2863                .addClass('slick-slide');
2864
2865        _.slideCount = _.$slides.length;
2866
2867        if (_.currentSlide >= _.slideCount && _.currentSlide !== 0) {
2868            _.currentSlide = _.currentSlide - _.options.slidesToScroll;
2869        }
2870
2871        if (_.slideCount <= _.options.slidesToShow) {
2872            _.currentSlide = 0;
2873        }
2874
2875        _.registerBreakpoints();
2876
2877        _.setProps();
2878        _.setupInfinite();
2879        _.buildArrows();
2880        _.updateArrows();
2881        _.initArrowEvents();
2882        _.buildDots();
2883        _.updateDots();
2884        _.initDotEvents();
2885        _.cleanUpSlideEvents();
2886        _.initSlideEvents();
2887
2888        _.checkResponsive(false, true);
2889
2890        if (_.options.focusOnSelect === true) {
2891            $(_.$slideTrack).children().on('click.slick', _.selectHandler);
2892        }
2893
2894        _.setSlideClasses(typeof _.currentSlide === 'number' ? _.currentSlide : 0);
2895
2896        _.setPosition();
2897        _.focusHandler();
2898
2899        _.paused = !_.options.autoplay;
2900        _.autoPlay();
2901
2902        _.$slider.trigger('reInit', [_]);
2903
2904    };
2905
2906    Slick.prototype.resize = function() {
2907
2908        var _ = this;
2909
2910        if ($(window).width() !== _.windowWidth) {
2911            clearTimeout(_.windowDelay);
2912            _.windowDelay = window.setTimeout(function() {
2913                _.windowWidth = $(window).width();
2914                _.checkResponsive();
2915                if( !_.unslicked ) { _.setPosition(); }
2916            }, 50);
2917        }
2918    };
2919
2920    Slick.prototype.removeSlide = Slick.prototype.slickRemove = function(index, removeBefore, removeAll) {
2921
2922        var _ = this;
2923
2924        if (typeof(index) === 'boolean') {
2925            removeBefore = index;
2926            index = removeBefore === true ? 0 : _.slideCount - 1;
2927        } else {
2928            index = removeBefore === true ? --index : index;
2929        }
2930
2931        if (_.slideCount < 1 || index < 0 || index > _.slideCount - 1) {
2932            return false;
2933        }
2934
2935        _.unload();
2936
2937        if (removeAll === true) {
2938            _.$slideTrack.children().remove();
2939        } else {
2940            _.$slideTrack.children(this.options.slide).eq(index).remove();
2941        }
2942
2943        _.$slides = _.$slideTrack.children(this.options.slide);
2944
2945        _.$slideTrack.children(this.options.slide).detach();
2946
2947        _.$slideTrack.append(_.$slides);
2948
2949        _.$slidesCache = _.$slides;
2950
2951        _.reinit();
2952
2953    };
2954
2955    Slick.prototype.setCSS = function(position) {
2956
2957        var _ = this,
2958            positionProps = {},
2959            x, y;
2960
2961        if (_.options.rtl === true) {
2962            position = -position;
2963        }
2964        x = _.positionProp == 'left' ? Math.ceil(position) + 'px' : '0px';
2965        y = _.positionProp == 'top' ? Math.ceil(position) + 'px' : '0px';
2966
2967        positionProps[_.positionProp] = position;
2968
2969        if (_.transformsEnabled === false) {
2970            _.$slideTrack.css(positionProps);
2971        } else {
2972            positionProps = {};
2973            if (_.cssTransitions === false) {
2974                positionProps[_.animType] = 'translate(' + x + ', ' + y + ')';
2975                _.$slideTrack.css(positionProps);
2976            } else {
2977                positionProps[_.animType] = 'translate3d(' + x + ', ' + y + ', 0px)';
2978                _.$slideTrack.css(positionProps);
2979            }
2980        }
2981
2982    };
2983
2984    Slick.prototype.setDimensions = function() {
2985
2986        var _ = this;
2987
2988        if (_.options.vertical === false) {
2989            if (_.options.centerMode === true) {
2990                _.$list.css({
2991                    padding: ('0px ' + _.options.centerPadding)
2992                });
2993            }
2994        } else {
2995            _.$list.height(_.$slides.first().outerHeight(true) * _.options.slidesToShow);
2996            if (_.options.centerMode === true) {
2997                _.$list.css({
2998                    padding: (_.options.centerPadding + ' 0px')
2999                });
3000            }
3001        }
3002
3003        _.listWidth = _.$list.width();
3004        _.listHeight = _.$list.height();
3005
3006
3007        if (_.options.vertical === false && _.options.variableWidth === false) {
3008            _.slideWidth = Math.ceil(_.listWidth / _.options.slidesToShow);
3009            _.$slideTrack.width(Math.ceil((_.slideWidth * _.$slideTrack.children('.slick-slide').length)));
3010
3011        } else if (_.options.variableWidth === true) {
3012            _.$slideTrack.width(5000 * _.slideCount);
3013        } else {
3014            _.slideWidth = Math.ceil(_.listWidth);
3015            _.$slideTrack.height(Math.ceil((_.$slides.first().outerHeight(true) * _.$slideTrack.children('.slick-slide').length)));
3016        }
3017
3018        var offset = _.$slides.first().outerWidth(true) - _.$slides.first().width();
3019        if (_.options.variableWidth === false) _.$slideTrack.children('.slick-slide').width(_.slideWidth - offset);
3020
3021    };
3022
3023    Slick.prototype.setFade = function() {
3024
3025        var _ = this,
3026            targetLeft;
3027
3028        _.$slides.each(function(index, element) {
3029            targetLeft = (_.slideWidth * index) * -1;
3030            if (_.options.rtl === true) {
3031                $(element).css({
3032                    position: 'relative',
3033                    right: targetLeft,
3034                    top: 0,
3035                    zIndex: _.options.zIndex - 2,
3036                    opacity: 0
3037                });
3038            } else {
3039                $(element).css({
3040                    position: 'relative',
3041                    left: targetLeft,
3042                    top: 0,
3043                    zIndex: _.options.zIndex - 2,
3044                    opacity: 0
3045                });
3046            }
3047        });
3048
3049        _.$slides.eq(_.currentSlide).css({
3050            zIndex: _.options.zIndex - 1,
3051            opacity: 1
3052        });
3053
3054    };
3055
3056    Slick.prototype.setHeight = function() {
3057
3058        var _ = this;
3059
3060        if (_.options.slidesToShow === 1 && _.options.adaptiveHeight === true && _.options.vertical === false) {
3061            var targetHeight = _.$slides.eq(_.currentSlide).outerHeight(true);
3062            _.$list.css('height', targetHeight);
3063        }
3064
3065    };
3066
3067    Slick.prototype.setOption =
3068    Slick.prototype.slickSetOption = function() {
3069
3070        /**
3071         * accepts arguments in format of:
3072         *
3073         *  - for changing a single option's value:
3074         *     .slick("setOption", option, value, refresh )
3075         *
3076         *  - for changing a set of responsive options:
3077         *     .slick("setOption", 'responsive', [{}, ...], refresh )
3078         *
3079         *  - for updating multiple values at once (not responsive)
3080         *     .slick("setOption", { 'option': value, ... }, refresh )
3081         */
3082
3083        var _ = this, l, item, option, value, refresh = false, type;
3084
3085        if( $.type( arguments[0] ) === 'object' ) {
3086
3087            option =  arguments[0];
3088            refresh = arguments[1];
3089            type = 'multiple';
3090
3091        } else if ( $.type( arguments[0] ) === 'string' ) {
3092
3093            option =  arguments[0];
3094            value = arguments[1];
3095            refresh = arguments[2];
3096
3097            if ( arguments[0] === 'responsive' && $.type( arguments[1] ) === 'array' ) {
3098
3099                type = 'responsive';
3100
3101            } else if ( typeof arguments[1] !== 'undefined' ) {
3102
3103                type = 'single';
3104
3105            }
3106
3107        }
3108
3109        if ( type === 'single' ) {
3110
3111            _.options[option] = value;
3112
3113
3114        } else if ( type === 'multiple' ) {
3115
3116            $.each( option , function( opt, val ) {
3117
3118                _.options[opt] = val;
3119
3120            });
3121
3122
3123        } else if ( type === 'responsive' ) {
3124
3125            for ( item in value ) {
3126
3127                if( $.type( _.options.responsive ) !== 'array' ) {
3128
3129                    _.options.responsive = [ value[item] ];
3130
3131                } else {
3132
3133                    l = _.options.responsive.length-1;
3134
3135                    // loop through the responsive object and splice out duplicates.
3136                    while( l >= 0 ) {
3137
3138                        if( _.options.responsive[l].breakpoint === value[item].breakpoint ) {
3139
3140                            _.options.responsive.splice(l,1);
3141
3142                        }
3143
3144                        l--;
3145
3146                    }
3147
3148                    _.options.responsive.push( value[item] );
3149
3150                }
3151
3152            }
3153
3154        }
3155
3156        if ( refresh ) {
3157
3158            _.unload();
3159            _.reinit();
3160
3161        }
3162
3163    };
3164
3165    Slick.prototype.setPosition = function() {
3166
3167        var _ = this;
3168
3169        _.setDimensions();
3170
3171        _.setHeight();
3172
3173        if (_.options.fade === false) {
3174            _.setCSS(_.getLeft(_.currentSlide));
3175        } else {
3176            _.setFade();
3177        }
3178
3179        _.$slider.trigger('setPosition', [_]);
3180
3181    };
3182
3183    Slick.prototype.setProps = function() {
3184
3185        var _ = this,
3186            bodyStyle = document.body.style;
3187
3188        _.positionProp = _.options.vertical === true ? 'top' : 'left';
3189
3190        if (_.positionProp === 'top') {
3191            _.$slider.addClass('slick-vertical');
3192        } else {
3193            _.$slider.removeClass('slick-vertical');
3194        }
3195
3196        if (bodyStyle.WebkitTransition !== undefined ||
3197            bodyStyle.MozTransition !== undefined ||
3198            bodyStyle.msTransition !== undefined) {
3199            if (_.options.useCSS === true) {
3200                _.cssTransitions = true;
3201            }
3202        }
3203
3204        if ( _.options.fade ) {
3205            if ( typeof _.options.zIndex === 'number' ) {
3206                if( _.options.zIndex < 3 ) {
3207                    _.options.zIndex = 3;
3208                }
3209            } else {
3210                _.options.zIndex = _.defaults.zIndex;
3211            }
3212        }
3213
3214        if (bodyStyle.OTransform !== undefined) {
3215            _.animType = 'OTransform';
3216            _.transformType = '-o-transform';
3217            _.transitionType = 'OTransition';
3218            if (bodyStyle.perspectiveProperty === undefined && bodyStyle.webkitPerspective === undefined) _.animType = false;
3219        }
3220        if (bodyStyle.MozTransform !== undefined) {
3221            _.animType = 'MozTransform';
3222            _.transformType = '-moz-transform';
3223            _.transitionType = 'MozTransition';
3224            if (bodyStyle.perspectiveProperty === undefined && bodyStyle.MozPerspective === undefined) _.animType = false;
3225        }
3226        if (bodyStyle.webkitTransform !== undefined) {
3227            _.animType = 'webkitTransform';
3228            _.transformType = '-webkit-transform';
3229            _.transitionType = 'webkitTransition';
3230            if (bodyStyle.perspectiveProperty === undefined && bodyStyle.webkitPerspective === undefined) _.animType = false;
3231        }
3232        if (bodyStyle.msTransform !== undefined) {
3233            _.animType = 'msTransform';
3234            _.transformType = '-ms-transform';
3235            _.transitionType = 'msTransition';
3236            if (bodyStyle.msTransform === undefined) _.animType = false;
3237        }
3238        if (bodyStyle.transform !== undefined && _.animType !== false) {
3239            _.animType = 'transform';
3240            _.transformType = 'transform';
3241            _.transitionType = 'transition';
3242        }
3243        _.transformsEnabled = _.options.useTransform && (_.animType !== null && _.animType !== false);
3244    };
3245
3246
3247    Slick.prototype.setSlideClasses = function(index) {
3248
3249        var _ = this,
3250            centerOffset, allSlides, indexOffset, remainder;
3251
3252        allSlides = _.$slider
3253            .find('.slick-slide')
3254            .removeClass('slick-active slick-center slick-current')
3255            .attr('aria-hidden', 'true');
3256
3257        _.$slides
3258            .eq(index)
3259            .addClass('slick-current');
3260
3261        if (_.options.centerMode === true) {
3262
3263            var evenCoef = _.options.slidesToShow % 2 === 0 ? 1 : 0;
3264
3265            centerOffset = Math.floor(_.options.slidesToShow / 2);
3266
3267            if (_.options.infinite === true) {
3268
3269                if (index >= centerOffset && index <= (_.slideCount - 1) - centerOffset) {
3270                    _.$slides
3271                        .slice(index - centerOffset + evenCoef, index + centerOffset + 1)
3272                        .addClass('slick-active')
3273                        .attr('aria-hidden', 'false');
3274
3275                } else {
3276
3277                    indexOffset = _.options.slidesToShow + index;
3278                    allSlides
3279                        .slice(indexOffset - centerOffset + 1 + evenCoef, indexOffset + centerOffset + 2)
3280                        .addClass('slick-active')
3281                        .attr('aria-hidden', 'false');
3282
3283                }
3284
3285                if (index === 0) {
3286
3287                    allSlides
3288                        .eq(
3288 _.options.slidesToShow + _.slideCount + 1 
vendor: 10,708 bytes, lines 3288-3658
3288)
3289                        .addClass('slick-center');
3290
3291                } else if (index === _.slideCount - 1) {
3292
3293                    allSlides
3294                        .eq(_.options.slidesToShow)
3295                        .addClass('slick-center');
3296
3297                }
3298
3299            }
3300
3301            _.$slides
3302                .eq(index)
3303                .addClass('slick-center');
3304
3305        } else {
3306
3307            if (index >= 0 && index <= (_.slideCount - _.options.slidesToShow)) {
3308
3309                _.$slides
3310                    .slice(index, index + _.options.slidesToShow)
3311                    .addClass('slick-active')
3312                    .attr('aria-hidden', 'false');
3313
3314            } else if (allSlides.length <= _.options.slidesToShow) {
3315
3316                allSlides
3317                    .addClass('slick-active')
3318                    .attr('aria-hidden', 'false');
3319
3320            } else {
3321
3322                remainder = _.slideCount % _.options.slidesToShow;
3323                indexOffset = _.options.infinite === true ? _.options.slidesToShow + index : index;
3324
3325                if (_.options.slidesToShow == _.options.slidesToScroll && (_.slideCount - index) < _.options.slidesToShow) {
3326
3327                    allSlides
3328                        .slice(indexOffset - (_.options.slidesToShow - remainder), indexOffset + remainder)
3329                        .addClass('slick-active')
3330                        .attr('aria-hidden', 'false');
3331
3332                } else {
3333
3334                    allSlides
3335                        .slice(indexOffset, indexOffset + _.options.slidesToShow)
3336                        .addClass('slick-active')
3337                        .attr('aria-hidden', 'false');
3338
3339                }
3340
3341            }
3342
3343        }
3344
3345        if (_.options.lazyLoad === 'ondemand' || _.options.lazyLoad === 'anticipated') {
3346            _.lazyLoad();
3347        }
3348    };
3349
3350    Slick.prototype.setupInfinite = function() {
3351
3352        var _ = this,
3353            i, slideIndex, infiniteCount;
3354
3355        if (_.options.fade === true) {
3356            _.options.centerMode = false;
3357        }
3358
3359        if (_.options.infinite === true && _.options.fade === false) {
3360
3361            slideIndex = null;
3362
3363            if (_.slideCount > _.options.slidesToShow) {
3364
3365                if (_.options.centerMode === true) {
3366                    infiniteCount = _.options.slidesToShow + 1;
3367                } else {
3368                    infiniteCount = _.options.slidesToShow;
3369                }
3370
3371                for (i = _.slideCount; i > (_.slideCount -
3372                        infiniteCount); i -= 1) {
3373                    slideIndex = i - 1;
3374                    $(_.$slides[slideIndex]).clone(true).attr('id', '')
3375                        .attr('data-slick-index', slideIndex - _.slideCount)
3376                        .prependTo(_.$slideTrack).addClass('slick-cloned');
3377                }
3378                for (i = 0; i < infiniteCount  + _.slideCount; i += 1) {
3379                    slideIndex = i;
3380                    $(_.$slides[slideIndex]).clone(true).attr('id', '')
3381                        .attr('data-slick-index', slideIndex + _.slideCount)
3382                        .appendTo(_.$slideTrack).addClass('slick-cloned');
3383                }
3384                _.$slideTrack.find('.slick-cloned').find('[id]').each(function() {
3385                    $(this).attr('id', '');
3386                });
3387
3388            }
3389
3390        }
3391
3392    };
3393
3394    Slick.prototype.interrupt = function( toggle ) {
3395
3396        var _ = this;
3397
3398        if( !toggle ) {
3399            _.autoPlay();
3400        }
3401        _.interrupted = toggle;
3402
3403    };
3404
3405    Slick.prototype.selectHandler = function(event) {
3406
3407        var _ = this;
3408
3409        var targetElement =
3410            $(event.target).is('.slick-slide') ?
3411                $(event.target) :
3412                $(event.target).parents('.slick-slide');
3413
3414        var index = parseInt(targetElement.attr('data-slick-index'));
3415
3416        if (!index) index = 0;
3417
3418        if (_.slideCount <= _.options.slidesToShow) {
3419
3420            _.slideHandler(index, false, true);
3421            return;
3422
3423        }
3424
3425        _.slideHandler(index);
3426
3427    };
3428
3429    Slick.prototype.slideHandler = function(index, sync, dontAnimate) {
3430
3431        var targetSlide, animSlide, oldSlide, slideLeft, targetLeft = null,
3432            _ = this, navTarget;
3433
3434        sync = sync || false;
3435
3436        if (_.animating === true && _.options.waitForAnimate === true) {
3437            return;
3438        }
3439
3440        if (_.options.fade === true && _.currentSlide === index) {
3441            return;
3442        }
3443
3444        if (sync === false) {
3445            _.asNavFor(index);
3446        }
3447
3448        targetSlide = index;
3449        targetLeft = _.getLeft(targetSlide);
3450        slideLeft = _.getLeft(_.currentSlide);
3451
3452        _.currentLeft = _.swipeLeft === null ? slideLeft : _.swipeLeft;
3453
3454        if (_.options.infinite === false && _.options.centerMode === false && (index < 0 || index > _.getDotCount() * _.options.slidesToScroll)) {
3455            if (_.options.fade === false) {
3456                targetSlide = _.currentSlide;
3457                if (dontAnimate !== true && _.slideCount > _.options.slidesToShow) {
3458                    _.animateSlide(slideLeft, function() {
3459                        _.postSlide(targetSlide);
3460                    });
3461                } else {
3462                    _.postSlide(targetSlide);
3463                }
3464            }
3465            return;
3466        } else if (_.options.infinite === false && _.options.centerMode === true && (index < 0 || index > (_.slideCount - _.options.slidesToScroll))) {
3467            if (_.options.fade === false) {
3468                targetSlide = _.currentSlide;
3469                if (dontAnimate !== true && _.slideCount > _.options.slidesToShow) {
3470                    _.animateSlide(slideLeft, function() {
3471                        _.postSlide(targetSlide);
3472                    });
3473                } else {
3474                    _.postSlide(targetSlide);
3475                }
3476            }
3477            return;
3478        }
3479
3480        if ( _.options.autoplay ) {
3481            clearInterval(_.autoPlayTimer);
3482        }
3483
3484        if (targetSlide < 0) {
3485            if (_.slideCount % _.options.slidesToScroll !== 0) {
3486                animSlide = _.slideCount - (_.slideCount % _.options.slidesToScroll);
3487            } else {
3488                animSlide = _.slideCount + targetSlide;
3489            }
3490        } else if (targetSlide >= _.slideCount) {
3491            if (_.slideCount % _.options.slidesToScroll !== 0) {
3492                animSlide = 0;
3493            } else {
3494                animSlide = targetSlide - _.slideCount;
3495            }
3496        } else {
3497            animSlide = targetSlide;
3498        }
3499
3500        _.animating = true;
3501
3502        _.$slider.trigger('beforeChange', [_, _.currentSlide, animSlide]);
3503
3504        oldSlide = _.currentSlide;
3505        _.currentSlide = animSlide;
3506
3507        _.setSlideClasses(_.currentSlide);
3508
3509        if ( _.options.asNavFor ) {
3510
3511            navTarget = _.getNavTarget();
3512            navTarget = navTarget.slick('getSlick');
3513
3514            if ( navTarget.slideCount <= navTarget.options.slidesToShow ) {
3515                navTarget.setSlideClasses(_.currentSlide);
3516            }
3517
3518        }
3519
3520        _.updateDots();
3521        _.updateArrows();
3522
3523        if (_.options.fade === true) {
3524            if (dontAnimate !== true) {
3525
3526                _.fadeSlideOut(oldSlide);
3527
3528                _.fadeSlide(animSlide, function() {
3529                    _.postSlide(animSlide);
3530                });
3531
3532            } else {
3533                _.postSlide(animSlide);
3534            }
3535            _.animateHeight();
3536            return;
3537        }
3538
3539        if (dontAnimate !== true && _.slideCount > _.options.slidesToShow) {
3540            _.animateSlide(targetLeft, function() {
3541                _.postSlide(animSlide);
3542            });
3543        } else {
3544            _.postSlide(animSlide);
3545        }
3546
3547    };
3548
3549    Slick.prototype.startLoad = function() {
3550
3551        var _ = this;
3552
3553        if (_.options.arrows === true && _.slideCount > _.options.slidesToShow) {
3554
3555            _.$prevArrow.hide();
3556            _.$nextArrow.hide();
3557
3558        }
3559
3560        if (_.options.dots === true && _.slideCount > _.options.slidesToShow) {
3561
3562            _.$dots.hide();
3563
3564        }
3565
3566        _.$slider.addClass('slick-loading');
3567
3568    };
3569
3570    Slick.prototype.swipeDirection = function() {
3571
3572        var xDist, yDist, r, swipeAngle, _ = this;
3573
3574        xDist = _.touchObject.startX - _.touchObject.curX;
3575        yDist = _.touchObject.startY - _.touchObject.curY;
3576        r = Math.atan2(yDist, xDist);
3577
3578        swipeAngle = Math.round(r * 180 / Math.PI);
3579        if (swipeAngle < 0) {
3580            swipeAngle = 360 - Math.abs(swipeAngle);
3581        }
3582
3583        if ((swipeAngle <= 45) && (swipeAngle >= 0)) {
3584            return (_.options.rtl === false ? 'left' : 'right');
3585        }
3586        if ((swipeAngle <= 360) && (swipeAngle >= 315)) {
3587            return (_.options.rtl === false ? 'left' : 'right');
3588        }
3589        if ((swipeAngle >= 135) && (swipeAngle <= 225)) {
3590            return (_.options.rtl === false ? 'right' : 'left');
3591        }
3592        if (_.options.verticalSwiping === true) {
3593            if ((swipeAngle >= 35) && (swipeAngle <= 135)) {
3594                return 'down';
3595            } else {
3596                return 'up';
3597            }
3598        }
3599
3600        return 'vertical';
3601
3602    };
3603
3604    Slick.prototype.swipeEnd = function(event) {
3605
3606        var _ = this,
3607            slideCount,
3608            direction;
3609
3610        _.dragging = false;
3611        _.swiping = false;
3612
3613        if (_.scrolling) {
3614            _.scrolling = false;
3615            return false;
3616        }
3617
3618        _.interrupted = false;
3619        _.shouldClick = ( _.touchObject.swipeLength > 10 ) ? false : true;
3620
3621        if ( _.touchObject.curX === undefined ) {
3622            return false;
3623        }
3624
3625        if ( _.touchObject.edgeHit === true ) {
3626            _.$slider.trigger('edge', [_, _.swipeDirection() ]);
3627        }
3628
3629        if ( _.touchObject.swipeLength >= _.touchObject.minSwipe ) {
3630
3631            direction = _.swipeDirection();
3632
3633            switch ( direction ) {
3634
3635                case 'left':
3636                case 'down':
3637
3638                    slideCount =
3639                        _.options.swipeToSlide ?
3640                            _.checkNavigable( _.currentSlide + _.getSlideCount() ) :
3641                            _.currentSlide + _.getSlideCount();
3642
3643                    _.currentDirection = 0;
3644
3645                    break;
3646
3647                case 'right':
3648                case 'up':
3649
3650                    slideCount =
3651                        _.options.swipeToSlide ?
3652                            _.checkNavigable( _.currentSlide - _.getSlideCount() ) :
3653                            _.currentSlide - _.getSlideCount();
3654
3655                    _.currentDirection = 1;
3656
3657                    break;
3658
vendor: 3,992 bytes, lines 3659-3790
3659                default:
3660
3661
3662            }
3663
3664            if( direction != 'vertical' ) {
3665
3666                _.slideHandler( slideCount );
3667                _.touchObject = {};
3668                _.$slider.trigger('swipe', [_, direction ]);
3669
3670            }
3671
3672        } else {
3673
3674            if ( _.touchObject.startX !== _.touchObject.curX ) {
3675
3676                _.slideHandler( _.currentSlide );
3677                _.touchObject = {};
3678
3679            }
3680
3681        }
3682
3683    };
3684
3685    Slick.prototype.swipeHandler = function(event) {
3686
3687        var _ = this;
3688
3689        if ((_.options.swipe === false) || ('ontouchend' in document && _.options.swipe === false)) {
3690            return;
3691        } else if (_.options.draggable === false && event.type.indexOf('mouse') !== -1) {
3692            return;
3693        }
3694
3695        _.touchObject.fingerCount = event.originalEvent && event.originalEvent.touches !== undefined ?
3696            event.originalEvent.touches.length : 1;
3697
3698        _.touchObject.minSwipe = _.listWidth / _.options
3699            .touchThreshold;
3700
3701        if (_.options.verticalSwiping === true) {
3702            _.touchObject.minSwipe = _.listHeight / _.options
3703                .touchThreshold;
3704        }
3705
3706        switch (event.data.action) {
3707
3708            case 'start':
3709                _.swipeStart(event);
3710                break;
3711
3712            case 'move':
3713                _.swipeMove(event);
3714                break;
3715
3716            case 'end':
3717                _.swipeEnd(event);
3718                break;
3719
3720        }
3721
3722    };
3723
3724    Slick.prototype.swipeMove = function(event) {
3725
3726        var _ = this,
3727            edgeWasHit = false,
3728            curLeft, swipeDirection, swipeLength, positionOffset, touches, verticalSwipeLength;
3729
3730        touches = event.originalEvent !== undefined ? event.originalEvent.touches : null;
3731
3732        if (!_.dragging || _.scrolling || touches && touches.length !== 1) {
3733            return false;
3734        }
3735
3736        curLeft = _.getLeft(_.currentSlide);
3737
3738        _.touchObject.curX = touches !== undefined ? touches[0].pageX : event.clientX;
3739        _.touchObject.curY = touches !== undefined ? touches[0].pageY : event.clientY;
3740
3741        _.touchObject.swipeLength = Math.round(Math.sqrt(
3742            Math.pow(_.touchObject.curX - _.touchObject.startX, 2)));
3743
3744        verticalSwipeLength = Math.round(Math.sqrt(
3745            Math.pow(_.touchObject.curY - _.touchObject.startY, 2)));
3746
3747        if (!_.options.verticalSwiping && !_.swiping && verticalSwipeLength > 4) {
3748            _.scrolling = true;
3749            return false;
3750        }
3751
3752        if (_.options.verticalSwiping === true) {
3753            _.touchObject.swipeLength = verticalSwipeLength;
3754        }
3755
3756        swipeDirection = _.swipeDirection();
3757
3758        if (event.originalEvent !== undefined && _.touchObject.swipeLength > 4) {
3759            _.swiping = true;
3760            event.preventDefault();
3761        }
3762
3763        positionOffset = (_.options.rtl === false ? 1 : -1) * (_.touchObject.curX > _.touchObject.startX ? 1 : -1);
3764        if (_.options.verticalSwiping === true) {
3765            positionOffset = _.touchObject.curY > _.touchObject.startY ? 1 : -1;
3766        }
3767
3768
3769        swipeLength = _.touchObject.swipeLength;
3770
3771        _.touchObject.edgeHit = false;
3772
3773        if (_.options.infinite === false) {
3774            if ((_.currentSlide === 0 && swipeDirection === 'right') || (_.currentSlide >= _.getDotCount() && swipeDirection === 'left')) {
3775                swipeLength = _.touchObject.swipeLength * _.options.edgeFriction;
3776                _.touchObject.edgeHit = true;
3777            }
3778        }
3779
3780        if (_.options.vertical === false) {
3781            _.swipeLeft = curLeft + swipeLength * positionOffset;
3782        } else {
3783            _.swipeLeft = curLeft + (swipeLength * (_.$list.height() / _.listWidth)) * positionOffset;
3784        }
3785        if (_.options.verticalSwiping === true) {
3786            _.swipeLeft = curLeft + swipeLength * positionOffset;
3787        }
3788
3789        if (_.options.fade === true || _.options.touchMove === false) {
3790            return false;
3791        }
3792
3793        if (_.animating === true) {
3794            _.swipeLeft = null;
3795            return false;
3796        }
3797
3798        _.setCSS(_.swipeLeft);
3799
3800    };
3801
3802    Slick.prototype.swipeStart = function(event) {
3803
3804        var _ = this,
3805            touches;
3806
3807        _.interrupted = true;
3808
3809        if (_.touchObject.fingerCount !== 1 || _.slideCount <= _.options.slidesToShow) {
3810            _.touchObject = {};
3811            return false;
3812        }
3813
3814        if (event.originalEvent !== undefined && event.originalEvent.touches !== undefined) {
3815            touches = event.originalEvent.touches[0];
3816        }
3817
3818        _.touchObject.startX = _.touchObject.curX = touches !== undefined ? touches.pageX : event.clientX;
3819        _.touchObject.startY = _.touchObject.curY = touches !== undefined ? touches.pageY : event.clientY;
3820
3821        _.dragging = true;
3822
3823    };
3824
3825    Slick.prototype.unfilterSlides = Slick.prototype.slickUnfilter = function() {
3826
3827        var _ = this;
3828
3829        if (_.$slidesCache !== null) {
3830
3831            _.unload();
3832
3833            _.$slideTrack.children(this.options.slide).detach();
3834
3835            _.$slidesCache.appendTo(_.$slideTrack);
3836
3837            _.reinit();
3838
3839        }
3840
3841    };
3842
3843    Slick.prototype.unload = function() {
3844
3845        var _ = this;
3846
3847        $('.slick-cloned', _.$slider).remove();
3848
3849        if (_.$dots) {
3850            _.$dots.remove();
3851        }
3852
3853        if (_.$prevArrow && _.htmlExpr.test(_.options.prevArrow)) {
3854            _.$prevArrow.remove();
3855        }
3856
3857        if (_.$nextArrow && _.htmlExpr.test(_.options.nextArrow)) {
3858            _.$nextArrow.remove();
3859        }
3860
3861        _.$slides
3862            .removeClass('slick-slide slick-active slick-visible slick-current')
3863            .attr('aria-hidden', 'true')
3864            .css('width', '');
3865
3866    };
3867
3868    Slick.prototype.unslick = function(fromBreakpoint) {
3869
3870        var _ = this;
3871        _.$slider.trigger('unslick', [_, fromBreakpoint]);
3872        _.destroy();
3873
3874    };
3875
3876    Slick.prototype.updateArrows = function() {
3877
3878        var _ = this,
3879            centerOffset;
3880
3881        centerOffset = Math.floor(_.options.slidesToShow / 2);
3882
3883        if ( _.options.arrows === true &&
3884            _.slideCount > _.options.slidesToShow &&
3885            !_.options.infinite ) {
3886
3887            _.$prevArrow.removeClass('slick-disabled').attr('aria-disabled', 'false');
3888            _.$nextArrow.removeClass('slick-disabled').attr('aria-disabled', 'false');
3889
3890            if (_.currentSlide === 0) {
3891
3892                _.$prevArrow.addClass('slick-disabled').attr('aria-disabled', 'true');
3893                _.$nextArrow.removeClass('slick-disabled').attr('aria-disabled', 'false');
3894
3895            } else if (_.currentSlide >= _.slideCount - _.options.slidesToShow && _.options.centerMode === false) {
3896
3897                _.$nextArrow.addClass('slick-disabled').attr('aria-disabled', 'true');
3898                _.$prevArrow.removeClass('slick-disabled').attr('aria-disabled', 'false');
3899
3900            } else if (_.currentSlide >= _.slideCount - 1 && _.options.centerMode === true) {
3901
3902                _.$nextArrow.addClass('slick-disabled').attr('aria-disabled', 'true');
3903                _.$prevArrow.removeClass('slick-disabled').attr('aria-disabled', 'false');
3904
3905            }
3906
3907        }
3908
3909    };
3910
3911    Slick.prototype.updateDots = function() {
3912
3913        var _ = this;
3914
3915        if (_.$dots !== null) {
3916
3917            _.$dots
3918                .find('li')
3919                    .removeClass('slick-active')
3920                    .end();
3921
3922            _.$dots
3923                .find('li')
3924                .eq(Math.floor(_.currentSlide / _.options.slidesToScroll))
3925                .addClass('slick-active');
3926
3927        }
3928
3929    };
3930
3931    Slick.prototype.visibility = function() {
3932
3933        var _ = this;
3934
3935        if ( _.options.autoplay ) {
3936
3937            if ( document[_.hidden] ) {
3938
3939                _.interrupted = true;
3940
3941            } else {
3942
3943                _.interrupted = false;
3944
3945            }
3946
3947        }
3948
3949    };
3950
3951    $.fn.slick = function() {
3952        var _ = this,
3953            opt = arguments[0],
3954            args = Array.prototype.slice.call(arguments, 1),
3955            l = _.length,
3956            i,
3957            ret;
3958        for (i = 0; i < l; i++) {
3959            if (typeof opt == 'object' || typeof opt == 'undefined')
3960                _[i].slick = new Slick(_[i], opt);
3961            else
3962                ret = _[i].slick[opt].apply(_[i].slick, args);
3963            if (typeof ret != 'undefined') return ret;
3964        }
3965        return _;
3966    };
3967
3968}));
3969
3970define('components/TateReady',['jquery', 'vendor/slick', 'components/TateGOTO', 'components/TateNotification'], function ($, Slick, TateGOTO, TateNotification) {
3971    var TateReady = function () {
3972        this.init();
3973    };
3974
3975    TateReady.prototype = {
3976        init: function () {
3977            /** LOCAL CONFIGURATION OBJECTS **/
3978            var slick_options = {
3979                slidesToShow: 4, slidesToScroll: 1, infinite: false,
3980                responsive: [{
3981                    breakpoint: 1024,
3982                    settings: { slidesToShow: 3, slidesToScroll: 1, infinite: false}
3983                }, {
3984                    breakpoint: 720,
3985                    settings: { slidesToShow: 2, slidesToScroll: 1, infinite: false}
3986                }, {
3987                    breakpoint: 480,
3988                    settings: { slidesToShow: 1, slidesToScroll: 1, infinite: false}
3989                }]
3990            };
3991
3992            /** DOM READY SCRIPTS **/
3993            $(document).ready(function () {
3994                $('body').removeClass('no-js');
3995
3996                $('.img-display__slideshow').each(function () {
3997                    var $me = $(this);
3998                    $me.find('.slideshow--slides').slick({
3999                        infinite: false,
4000                        prevArrow: $me.find('.carousel--left'),
4001                        nextArrow: $me.find('.carousel--right')
4002                    });
4003
4004                    $me.find('.slideshow--slides').on('afterChange', function (event, slick, currentslide) {
4005                        $me.trigger('lazyload:slideshow');
4006                    });
4007                });
4008
4009                var bufftop = 0
4010                if ($('header').length) { bufftop += $('header').height(); }
4011                if ($('#breadcrumb').length) { bufftop += $('#breadcrumb').height(); }
4012                var goto_links = new TateGOTO({'bufftop': bufftop});
4013
4014                $('.card--carousel').each(function () {
4015                    var $me = $(this);
4016
4017                    slick_options.prevArrow = $me.find('.carousel--left');
4018                    slick_options.nextArrow = $me.find('.carousel--right');
4019
4020                    $me.find('.card-list').slick(slick_options);
4021
4022                    $me.find('.card-list').on('afterChange', function (event, slick, currentSlide) {
4023                        $me.trigger('lazyload:carousel');
4024                    });
4025                });
4026
4027                // Initialises brightcove videos
4028                $('.brightcove-player').each(function() {
4029                    var el = $(this);
4030                    var vid = el.attr('data-video-id');
4031                    if (!vid || typeof(vid) === 'undefined') {
4032                        el.remove();
4033                        return;
4034                    }
4035                    el.removeAttr('data-video-id');
4036                    brightcode = '<video data-vi
4036deo-id=' + vid + ' data-account="1854890877" data-player="4811b2e3-75b4-4489-b1a5-21a18a61075e" data-embed="default" class="video-js" controls="" style="width: 100%; height: 100%; position: absolute; top: 0px; left: 0px;"></video>';
4037                    el.html(brightcode);
4038                    return;
4039                });
4040
4041                if ($('.brightcove-player')[0]) {
4042                    $('body').append('<script src="//players.brightcove.net/1854890877/4811b2e3-75b4-4489-b1a5-21a18a61075e_default/index.min.js"></script>');
4043                }
4044
4045                // Show/hide details in slideshows for archive items and Turner sketchbooks
4046                $('.piece-meta__trigger').on('click', function(e){
4047                    e.preventDefault();
4048                    var list = $(this).siblings('.piece-meta__list');
4049                    if (list.hasClass('h-e--mobile')) {
4050                        list.removeClass('h-e--mobile');
4051                        $(this).text('Resources.HIDE_DETAILS');
4052                    } else {
4053                        list.addClass('h-e--mobile');
4054                        $(this).text('Resources.SHOW_DETAILS');
4055                    }
4056                });
4057
4058            });
4059        }
4060    };
4061
4062    return TateReady;
4063});
4064
4065define('components/TateBTT',["jquery"], function ($) {
4066
4067    var TateBTT = function() {
4068        // Global vars and constants.
4069        var limit_height = window.innerHeight * 2;
4070        // jQuery selector for the anchor (REMEMBER TO CHANGE THE MARKUP!)
4071        var data_tag = "a[data-role='btt']";
4072        var animation_speed = 750; // MS
4073        // Change the markup here.
4074        var markup = [
4075            '<a href="#" class="page--back-to-top" data-role="btt">',
4076            'Back to top',
4077            '</a>',
4078        ].join("\n");
4079
4080        var scroll_callback = function () {
4081            // Write your callback code in here.
4082        }; 
4083
4084        var init = function () {
4085            // Extend jQuery easing.. We should probably find if this exists already? (Other modules do this...)
4086            if (!('easeInOutQuart' in $.easing)) {
4087                $.extend($.easing, {
4088                    easeInOutQuart: function (x, t, b, c, d) {
4089                        if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
4090                        return -c/2 * ((t-=2)*t*t*t - 2) + b;
4091                    }
4092                });
4093            }
4094            
4095            // DOM injection
4096            if ($(data_tag).length === 0) {
4097                $('body').append(markup);
4098            }
4099
4100            // Events
4101            $(window).on('scroll', window_scroll);
4102            $(data_tag).on('click', scrollToTop);
4103
4104            // Just in case we're loading half way down the page
4105            window_scroll();
4106        };
4107
4108        var scrollToTop = function (e) {
4109            e.preventDefault();
4110
4111            $('html, body').stop().animate({
4112                'scrollTop': 0,
4113            }, animation_speed, 'easeInOutQuart', scroll_callback);
4114        };
4115
4116        var hide_dom_element = function () {
4117            var $dom_element = $(data_tag);
4118            if ($dom_element.is(":visible")) {
4119                $dom_element.removeClass("page--back-to-top__active");
4120            }
4121        };
4122
4123        var show_dom_element = function () {
4124            var $dom_element = $(data_tag);
4125            if (!$dom_element.is(":visible")) {
4126                $dom_element.addClass("page--back-to-top__active");
4127            }
4128        };
4129
4130        var window_scroll = function () {
4131            var body_scrolltop = $(document).scrollTop(),
4132                $dom_element = $(data_tag);
4133
4134            if (body_scrolltop >= limit_height) {
4135                show_dom_element();
4136            } else {
4137                hide_dom_element();
4138            }
4139        };
4140
4141        init();
4142    };
4143
4144    return TateBTT;
4145    
4146});
4147
4148define('components/TateLazyLoad',["jquery", "components/TateWindow"], function ($, TateWindow) {
4149
4150	var CONTAINER_SELECTOR = '.responsive-container',
4151		SRC_DATA_ATTRIBUTE = 'data-original',
4152		lazyLoad_throttle;
4153	
4154	$.fn.tateResponsiveImage = function(options) {
4155
4156		var settings = $.extend({
4157            selector: CONTAINER_SELECTOR,
4158            srcDataAttribute: SRC_DATA_ATTRIBUTE,
4159            forceVisible: false,
4160            resize: false
4161        }, options );
4162
4163		var tateWindow = new TateWindow(),
4164	        window_width = tateWindow.width(),
4165	        window_height = tateWindow.height(),
4166	        max_height = Math.max(405, Math.round(0.9 * window_height));
4167		
4168		var setup_attributes = function(el){
4169			var target_width = el.width();             
4170			var banner = el.hasClass('splash-header__media--inner'); 
4171
4172			el.find('img').each(function () {
4173				var sizes = [];
4174				var image = $(this);
4175				if (image.attr('data-visible') && !settings.resize) { return; }
4176				$(image[0].attributes).each(function(){
4177					var name = this.name;
4178					if (name.indexOf("data-src-") === 0) {
4179						var dims = (name.substr(9)).split("-"),
4180							w = parseInt(dims[0]),
4181							h = (dims[1]) ? parseInt(dims[1]) : 0;
4182						sizes.push({"width": w, "height": h, "attrName": name});
4183					}
4184				});
4185				sizes.sort(function(a,b){
4186					return a.width - b.width;
4187				});
4188				var selected_size = {"width": 0, "height": 0, "attrName": settings.srcDataAttribute};
4189				for (var i = 0; i < sizes.length; i++) {
4190					selected_size = sizes[i];
4191					if (selected_size.width >= target_width || (!banner && selected_size.height && selected_size.height >= max_height)) { break; }
4192				}
4193				var current_size = $(this).attr('data-current-size'),
4194					current_width = (current_size) ? (current_size.split("-"))[0] : 0,
4195					new_size = selected_size.width + "-" + selected_size.height;
4196
4197				if (!current_width || current_width < selected_size.width) {
4198					image.attr(settings.srcDataAttribute, image.attr(selected_size.attrName)).attr('data-current-size', new_size).attr('data-visible', '');
4199				}
4200				
4201				if (el.closest('.image-canvas').length) { // We need to add a lightbox attribute
4202					max_w = window_width - 64;
4203					max_h = window_height - 64;
4204					for (var i = 0; i < sizes.length; i++) {
4205						selected_size = sizes[i];
4206						if (selected_size.width >= max_w || (selected_size.width != selected_size.height && selected_size.height >= max_h)) { break; }
4207					}					
4208					image.attr('data-lightbox', image.attr(selected_size.attrName));
4209				}
4210			});
4211		};
4212
4213		return this.each(function(){
4214			var $img_containers;
4215			if ($(this).is(settings.selector)) {
4216				$img_containers = $(this);
4217			} else {
4218				$img_containers = $(this).find(settings.selector);
4219			}
4220            if ($img_containers) {
4221            	$img_containers.each(function(){
4222            		if ($(this).is(':visible')) {
4223            			setup_attributes($(this), false);
4224            		}
4225				});
4226            }
4227		});
4228		
4229	};
4230	
4231	$.fn.tateLazyLoad = function(options) {
4232
4233		var settings = $.extend({
4234            selector: CONTAINER_SELECTOR,
4235            srcDataAttribute: SRC_DATA_ATTRIBUTE,
4236            forceVisible: false
4237        }, options );
4238
4239		var tateWindow = new TateWindow(),
4240	        window_width = tateWindow.width(),
4241	        window_height = tateWindow.height(),
4242	        max_height = Math.max(405, Math.round(0.9 * window_height));
4243
4244        var check_if_image_in_viewport = function (el) {
4245            if (typeof jQuery === "function" && el instanceof jQuery) {
4246                el = el[0];
4247            }
4248
4249            var rect = el.getBoundingClientRect();
4250
4251            return (
4252                rect.top >= 0 - window_height &&
4253                rect.left >= 0 &&
4254                rect.bottom <= 2 * window_height &&
4255                rect.left <= window_width
4256            );
4257        };
4258        
4259        var check_if_image_should_load = function (el) {
4260        	// Handle awkward case of slideshows, where all the slides are 'visible' and
4261        	// stacked in the same XY position, but the current slide has a higher z-index.
4262        	if (el.parent().is('.slide')) {
4263        		var slide = el.closest('.slick-slide');
4264        		if (slide.length && !slide.hasClass('.slick-current'))
4265        			return false;
4266        	}
4267        	return check_if_image_in_viewport(el);
4268        };
4269
4270        var show_image = function(image) {
4271        	if (image.attr(settings.srcDataAttribute) && ( 
4272    			!image.attr('data-visible') || image.attr('src') != image.attr(settings.srcDataAttribute) 
4273    			)) {
4274        		if (settings.forceVisible || check_if_image_should_load(image)) {
4275                	image.attr('src', image.attr(settings.srcDataAttribute));
4276                	image.attr('data-visible', true);
4277                }
4278            }
4279        };
4280		
4281        return this.each(function(){
4282			var $images;
4283			if ($(this).is('img')) {
4284				$images = $(this);
4285			} else if ($(this).is(settings.selector)) {
4286				$images = $(this).find('img');
4287			} else {
4288				$images = $(this).find(settings.selector + ' img');
4289			}
4290			if ($images) {
4291				$images.each(function(){
4292					show_image($(this));
4293				});
4294			}
4295		});
4296
4297	};
4298	
4299   
4300    var TateLazyLoad = function () {
4301        
4302		// Custom event registered on card carousels, so that we can apply the lazy loading to the cards 
4303		// coming into view as the user scrolls through the carousel. 
4304        $('.card--carousel').on('lazyload:carousel', function(evt){
4305        	$(this).tateLazyLoad();
4306        });
4307		// Custom event registered on image containers, so that we can apply the attribute setup 
4308		// to the splash header image from outside this function. 
4309        $('.splash-header__media--inner').on('lazyload:setup', function(evt){
4310        	$(this).tateResponsiveImage();
4311        });
4312        // Custom event registered on slideshow, so that we can apply the lazy loading to the cards 
4313        // coming into view as the user scrolls through the carousel. 
4314        $('.img-display__slideshow').on('lazyload:slideshow', function(evt){
4315        	$(this).tateLazyLoad();
4316        });
4317
4318        var resize_throttle = function(delay) {
4319        	var me = this;
4320        	delay = delay || 100;
4321            clearTimeout(lazyLoad_throttle);
4322            lazyLoad_throttle = setTimeout(function(){
4323            	$('body').tateResponsiveImage({resize: true}).tateLazyLoad();
4324            }, delay);
4325        };
4326        
4327        var setup_events = function () {
4328            $(window).on("resize", resize_throttle);
4329            $(window).on('DOMContentLoaded load scroll', function(){
4330            	$('body').tateLazyLoad();
4331            });
4332        };
4333
4334        var init = function () {
4335            setup_events();
4336        	$('body').tateResponsiveImage().tateLazyLoad();
4337        };        
4338        
4339        $(document).ready(init);
4340    };
4341
4342    return TateLazyLoad;
4343});
4344define('components/TateOpenTimes',['jquery'], function ($) {
4345	var TateOpenTimes = function () {
4346		var me = this;
4347
4348		$(document).ready(function () {
4349			me.init();
4350		});
4351	};
4352
4353	TateOpenTimes.prototype = {
4354		init: function () {
4355
4356			var today = null;
4357			var getToday = function(){
4358				var days = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
4359				var now = new Date(),
4360					date = (now.getDate() < 10) ? '0' + now.getDate() : now.getDate(),
4361					month_index = now.getMonth() + 1,
4362					month = (month_index < 10) ? '0' + month_index : month_index,
4363					year = now.getFullYear();
4364				return {
4365					dateString: (year + "-" + month + "-" + date),
4366					day: days[now.getDay()]
4367				}
4368			}
4369
4370			var getCurrentOpenTimes = function(open_times, closed_dates, default_text){
4371				if (!today) { today = getToday(); }
4372				if (closed_dates) {
4373					var dates = closed_dates.split(",");
4374					for (var i = 0; i < dates.length; i++) {
4375						var date = dates[i];
4376						if (date == today.dateString) {
4377							return "Closed today";
4378						}
4379					}
4380				}
4381				if (open_times) {
4382					var times = open_times.split(",");
4383					for (var i = 0; i < times.length; i++) {
4384						var dayTime = times[i].split(":");
4385						if (dayTime[0] == today.day) {
4386							return "Open today " + dayTime[1];
4387						}
4388					}
4389				}
4390				return default_text;
4391			};
4392			
4393			$('.current-open-times').each(function(){
4394				var newText = getCurrentOpenTimes($(this).attr("data-open-times"), $(this).attr("data-closed-dates"), $(this).text());
4395				$(this).text(newText);
4396			});
4397		}
4398	};
4399	
4400	return TateOpenTimes;
4401});
4402
4403define('components/TateLogin',["jquery"], function ($) {
4404
4405    var TateLogin = function () {
4406
4407        var init = function() {
4408
4409            var dest = window.location.pathname;
4410            if(!dest) {
4411                dest = "/";
4412            }
4413
4414            // remove pesky leading slash as login redirects to 'http://host/' + dest
4415            dest = dest.replace(/^\//,'');
4416
4417            var login_link = function() {
4418                var html_string = [];
4419                html_string.push('<a href="javascript:;" data-href="/user?destination=' + dest + '" class="btn  btn--type__no-bg btn--size__small bt
4419n--style__text" data-gtm-name="Navigation-header">');
4420                html_string.push('<i class="btn__icon icon--user"></i>');
4421                html_string.push('<span class="btn__text">Login<sup>ui</sup></span>');
4422                html_string.push('</a>');
4423
4424                $("#tate-login").html(html_string.join("\n"));
4425                $(".site-menu--handheld").append('<li class="site-menu__item site-menu--handheld__item">' + html_string.join("\n") + '</li>');
4426
4427                // use anon links as we don't have a user
4428                $("a[data-anon-href]").each(function() {
4429                    var anon_url = $(this).attr('data-anon-href');
4430                    if(typeof(anon_url) != 'undefined' && anon_url) {
4431                        $(this).attr('href', anon_url);
4432                    }
4433                });
4434            };
4435
4436            var user_link = function(data) {
4437                var html_string = [];
4438                /*
4439                if(data.user.thumbnail_url) {
4440                    html_string.push('<img src="'+data.user.thumbnail_url+'" style="max-width:50px;height:auto;"></img>');
4441                }
4442                */
4443
4444                // when on www.tate we want admin/editor links to point at auth.tate
4445                base_url = "";
4446                if (window.location.host == "www.tate.org.uk") {
4447                    base_url = "http://auth.tate.org.uk";
4448                }
4449
4450                // note base_url not used
4451                html_string.push('<a href="/user/' + data.user.uid + '" class="btn btn--type__no-bg btn--size__small bt
4451n--style__text" data-gtm-name="Navigation-header">');
4452                html_string.push('<i class="btn__icon icon--user"></i>');
4453                html_string.push('<span class="btn__text">' + data.user.username + '</span>');
4454                html_string.push('</a>');
4455                html_string.push('<ul class="list list--links">');
4456
4457                var links = data.links;
4458
4459                while(l = links.pop()) {
4460                    // user profile should go to www
4461                    if( l.url.startsWith("/user/")) {
4462                        html_string.push('<li class="list__item"><a href="' + l.url + '" class="btn btn--type__no-bg btn--size__small btn--style__text" data-gtm-name="Navigation-header"><span class="btn__text">' + l.label + '</span></a></li>');
4463                    } else {
4464                        html_string.push('<li class="list__item"><a href="' + base_url + l.url + '" class="btn btn--type__no-bg btn--size__small btn--style__text" data-gtm-name="Navigation-header"><span class="btn__text">' + l.label + '</span></a></li>');
4465                    }
4466                }
4467
4468                // e.g. /node/1234
4469                var current_page = null;
4470                $("link[rel=shortlink]").each(function() { current_page = $(this).attr('href') });
4471
4472                if (!current_page) {
4473                    current_page = dest;
4474                }
4475            
4476                if (current_page.indexOf("/node/") == 0 && 
4477                    data.user.roles && (data.user.roles.admin || data.user.roles.editor )) {
4478                    // doesn't guarantee user can actually edit the page
4479                    html_string.push('<li class="list__item"><a href="' + base_url + current_page  + '/edit" class="btn btn--type__no-bg btn--size__small bt
4479n--style__text" data-gtm-name="Navigation-header"><span class="btn__text">Edit page</span></a></li>');
4480                }
4481                
4482                // note base_url not used
4483                html_string.push('<li class="list__item"><a href="/user/logout" class="btn btn--type__no-bg btn--size__small btn--style__text" data-gtm-name="Navigation-header"><i class="btn__icon icon--user"></i><span class="btn__text">Sign out</span></a></li>');
4484                html_string.push('</ul>');
4485
4486                $("#tate-login").html(html_string.join('\n'));
4487                $(".site-menu--handheld").append('<li class="site-menu__item site-menu--handheld__item">' + html_string.join("\n") + '</li>');
4488            };
4489
4490            $.ajax({
4491                url: '/ajax/user',
4492                method: 'GET',
4493                success: function(data, textStatus, xhr) {
4494                    if (xhr.status >= 200 && xhr.status <= 300 && data && data.user) {
4495                        user_link(data);
4496                    } else {
4497                        login_link();
4498                    }
4499                },
4500                error: function(data, textStatus, xhr) {
4501                    login_link();
4502                }
4503            });
4504        };
4505
4506        $(document).ready(function () {
4507            // temporary suspended
4508            // init();
4509        });
4510    };
4511
4512    return TateLogin;
4513});
4514
4515define('components/TateNav',["jquery"], function ($) {
4516    var TateNav = function() {
4517        var $search_overlay_shadow = '<div class="site-search--overlay-shadow" style="z-index: 35; position: fixed; bottom: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.8)"></div>';
4518        var defaultSearchPlaceholder = $('#navigation-search').attr('placeholder');
4519        var setup_sticky_nav = function () {
4520            var nav_height = $('#nav').height();
4521
4522            //var nav_handle_html = '<span class="nav--handle" data-gtm-name="Navigation-burger">Menu</span>';
4523
4524            /**
4525             * Icons classes
4526             * nav--handle__search_burger - search with burger
4527             * nav--handle__lock - show locked in secured pages
4528             * nav--handle__card - show basket
4529             * nav--handle__add - show add icon
4530             *
4531             * more information and icon css classes you can locate
4532             * in _nav-patch.scss
4533             */
4534            var nav_handle_html = '<a href="javascript:void(0)" tabindex="0" class="nav--handle nav--handle__search_burger" data-gtm-name="Navigation-burger">Menu</a>';
4535            var $body = $('body');
4536            $body.prepend(nav_handle_html);
4537
4538            var hide_show_nav = function () {
4539                $('.nav--handle').toggleClass('nav--active');
4540                $('#nav').toggleClass('nav--open');
4541                $('.nav--shadow').unbind().remove();
4542            };
4543
4544            var window_scrolling = function () {
4545                var scroll_top = $(window).scrollTop(),
4546                    $site_menu = $('.site-menu'),
4547                    $nav = $("#nav"),
4548                    nav_height = $nav.height(),
4549                    $nav_handle = $(".nav--handle");
4550
4551                if ($nav.hasClass('tickets-nav')) {
4552                    return;
4553                }
4554
4555                if ($(window).width() < 840) { // Ignore the scroll event on mobile devices
4556                    return;
4557                }
4558
4559                if (scroll_top > (0 + $site_menu.height())) {
4560                    if (!$('.header-menu-close .icon--close').is(':visible')) {
4561
4562                        $nav.removeClass('nav--relaxed');
4563                        $('.member-discount-info').addClass('hidden');
4564                        $nav_handle.removeClass('nav--handle__hidden');
4565                        $('.header-menu .icon--menu').show();
4566
4567                        if ($('.expanded-menu').is(':visible')) {
4568                            $('.expanded-menu').addClass('expanded-menu-up');
4569                        }
4570                    }
4571                } else {
4572                    $nav.addClass('nav--relaxed');
4573                    $('.member-discount-info').removeClass('hidden');
4574                    $nav_handle.addClass('nav--handle__hidden');
4575                    $('.header-menu .icon--menu').hide();
4576
4577                    if (scroll_top == 0) {
4578                        $('.header-menu-close .icon--close').hide();
4579                    }
4580
4581                    $('.expanded-menu').removeClass('expanded-menu-up');
4582                }
4583            };
4584
4585            $('.nav--handle').on('click', function () {
4586                hide_show_nav();
4587                //If nav is active add a shadow and onclick handler
4588                if ($(this).hasClass('nav--active')) {
4589                    $('body').prepend("<div class='nav--shadow'></div>");
4590                    $('.nav--shadow').on('click', function () {
4591                        hide_show_nav();
4592                    });
4593                }
4594            });
4595
4596            $(window).on('scroll', window_scrolling);
4597            window_scrolling();
4598
4599            $(document).ready(function() {
4600                if(navigator.platform == "iPad") {
4601                    // workaround for hover menus not closing on click
4602                    $('body').css('cursor', 'pointer');
4603                }
4604            });
4605
4606            $('.mobile-expand-btn').on('click', function() {
4607
4608                if ($(this).find('.icon--chevron-down').is(':visible')) {
4609
4610                    $(this).find('.icon--chevron-down').hide();
4611                    $(this).find('.icon--chevron-up').show();
4612
4613                    var menuWrapper = $(this).parents('.menu-item-wrapper');
4614                    menuWrapper.find('.expanded-menu').show();
4615                    menuWrapper.find('.site-menu__item').addClass('selected-menu-item');
4616                } else {
4617
4618                    $(this).find('.icon--chevron-down').show();
4619                    $(this).find('.icon--chevron-up').hide();
4620
4621                    var menuWrapper = $(this).parents('.menu-item-wrapper');
4622                    menuWrapper.find('.expanded-menu').hide();
4623                    menuWrapper.find('.site-menu__item').removeClass('selected-menu-item');
4624                }
4625            });
4626
4627            $( window ).on('resize', function() {
4628                var windowWidth = $( window ).width();
4629                if (windowWidth < 840) {
4630                    $('.header-menu .icon--menu').show();
4631                }
4632            });
4633        };
4634
4635        var setup_pager_navigation = function () {
4636            if ($('.artist-letter').length > 0) { //ensuring that the function is running on art prints page
4637                $('.pager__item').on('click', function (e) {
4638                    e.preventDefault();
4639                    var anchorId = $(e.target).attr('href');
4640
4641                    if (typeof anchorId !== 'undefined' && anchorId.trim().length > 0) {
4642                        var anchorOffset = $('.page__header').height() + $('.artist-letter').height(), //total height of fixed properties on top of page and the letter itself
4643                            top = $(anchorId)[0].offsetTop - anchorOffset;
4644
4645                        window.scrollTo(0, top);
4646                    }
4647                });
4648            }
4649        };
4650
4651        var setup_search = function () {
4652
4653            var $search_block_form = $('#search-block-form'), $navigation_search = $('#navigation-search');
4654
4655            $search_block_form.on('submit', function(e) {
4656                if (!$navigation_search.val()) {
4657                    e.preventDefault();
4658                }
4659            });
4660
4661            var close_overlay = function (e) {
4662                $('.site-search--overlay-shadow').unbind().remove();
4663                $search_block_form.css('margin-top' , '-' + $search_block_form.height() + 'px');
4664                $search_block_form.removeClass('site-search--overlay__open');
4665                $search_block_form.attr('aria-hidden','true');
4666                $search_block_form.find('button, input, span[role="button"]').attr('tabindex','-1');
4667            };
4668
4669            var inject_markup_and_style = function () {
4670                var first_span = [
4671                    '<span class="btn btn--style__icon site-search--overlay__close-button" data-gtm-name="Navigation-search-close">',
4672                    '	<i class="btn__icon icon--close"></i>',
4673                    '	<span class="btn__text">Close</span>',
4674                    '</span>'
4675                ].join("\n");
4676
4677                var second_span = [
4678                    '<span class="btn btn--type__no-bg btn--size__small btn--style__text site-search--overlay__open-button" data-gtm-name="Navigation-header">',
4679                    '	<i class="btn__icon icon--search"></i>',
4680                    '	<span class="btn__text">Search<sup>ui</sup></span>',
4681                    '</span>'
4682                ].join("\n");
4683
4684                $search_block_form.addClass('site-search--overlay').css('margin-top' , '-' + $search_block_form.height() + 'px').prepend(first_span);
4685            };
4686
4687            var register_events = function () {
4688                var me = this;
4689
4690                $('.site-search--overlay__close-button').on('click', function (e) {
4691                    close_overlay(e);
4692                });
4693
4694                $('.site-search--overlay__open-button, .member__patron__header .icon--search, .header-search-icon').on('click', function (e) {
4695                    $('body').prepend($search_overlay_shadow);
4696
4697                    /* Is unbound inside close_overlay() */
4698                    $('.site-search--overlay-shadow').on('click', function (e) {
4699                        close_overlay(e);
4700                    });
4701
4702                    $search_block_form.css('margin-top' , '0px');
4703                    $search_block_form.addClass('site-search--overlay__open');
4704                    $search_block_form.attr('aria-hidden','false');
4705                    $search_block_form.find('button, input, span[role="button"]').attr('tabindex','0');
4706                    $('#navigation-search').focus();
4707                });
4708            };
4709
4710            inject_markup_and_style();
4711            register_events();
4712        };
4713
4714        setup_sticky_nav();
4715        setup_search();
4716        setup_pager_navigation();
4717    };
4718
4719    return TateNav;
4720});
4721
4722define('components/TateBreadcrumbs',["jquery"], function ($) {
4723
4724	var TateBreadcrumbs = function() {
4725
4726		var expandHandle = null,
4727			isExpanded = false,
4728            resizeTimeout;
4729
4730		var setup = function() {
4731
4732            if(typeof(window.$) == 'undefined')
4733                return;
4734
4735            var breadcrumbWidth = function() {
4736            	var breadcrumbs = $("#breadcrumb");
4737                if(!breadcrumbs || typeof(breadcrumbs) == 'undefined' || breadcrumbs.length < 1) {
4738                    return;
4739                }
4740                var breadcrumb = $(breadcrumbs[0]),
4741                	expandTrigger = breadcrumb.find('.breadcrumb__handle'),
4742                	container = breadcrumb.find('.breadcrumb__container'),
4743                	bcListWrapper = breadcrumb.find('.breadcrumb__list-outer'),
4744                	bcList = breadcrumb.find('.breadcrumb__list'),
4745                	bcItems = breadcrumb.find('.breadcrumb__item');
4746
4747                // Keep a copy of the expandTrigger as we may remove it and then need it back again
4748                if (!expandHandle && expandTrigger.length) {
4749                    expandHandle = $($('<div>').append(expandTrigger.clone(false)).html());
4750                }
4751
4752                // Quick test of how wide the list really is
4753                container.removeClass('breadcrumb--expanded');
4754                bcItems.css('max-width', 'none');
4755                bcListWidth = bcList.outerWidth();
4756                bcItems.removeAttr('style');
4757
4758                if (bcListWidth > bcListWrapper.width()) {
4759                    if (!expandTrigger.length && expandHandle) {
4760                      expandTrigger = bcListWrapper.before(expandHandle);
4761                    }
4762                	expandTrigger.off('click').on('click', toggleBreadcrumbHeight);
4763                } else if (expandTrigger.length) {
4764                	expandTrigger.remove();
4765                }
4766                if (bcListWidth <= bcListWrapper.width() || isExpanded) {
4767                	container.addClass('breadcrumb--expanded');
4768                }
4769            };
4770
4771            var toggleBreadcrumbHeight = function(event) {
4772            	var trigger = event.target,
4773            		container = $(trigger).closest('.breadcrumb__container'),
4774            		text = container.find('.breadcrumb__handle .btn__text'),
4775            		icon = container.find('.breadcrumb__handle .btn__icon');
4776            	if (text.text() == 'Expand') {
4777            		text.text('Collapse');
4778            		icon.addClass('icon--chevron-up').removeClass('icon--chevron-down');
4779            		container.addClass('breadcrumb--expanded');
4780            		isExpanded = true;
4781            	} else if (text.text() == 'Collapse') {
4782            		text.text('Expand');
4783            		icon.addClass('icon--chevron-down').removeClass('icon--chevron-up');
4784            		container.removeClass('breadcrumb--expanded');
4785            		isExpanded = false;
4786            	}
4787            };
4788
4789            var toggleBreadcrumbs = function() {
4790                var triggers = $('.site-menu');
4791
4792                if(!triggers || typeof(triggers) == 'undefined' || triggers.length < 1) {
4793                    $(window).off("scroll", toggleBreadcrumbs);
4794                    return;
4795                }
4796                var trigger = $(triggers[0]);
4797
4798                var breadcrumbs = $("#breadcrumb");
4799                if(!breadcrumbs || typeof(breadcrumbs) == 'undefined' || breadcrumbs.length < 1) {
4800                    $(window).off("scroll", toggleBreadcrumbs);
4801                    return;
4802                }
4803                var breadcrumb = $(breadcrumbs[0]);
4804                if(typeof(trigger.offset()) == 'undefined') {
4805                    $(window).off("scroll", toggleBreadcrumbs);
4806                    return;
4807                }
4808
4809                var relative_pos = trigger.height();
4810
4811                if($(window).scrollTop() > relative_pos) {
4812                    breadcrumb.css({'top': '66px'});
4813                }
4814                else {
4815                    breadcrumb.css({'top': '0px'});
4816                }
4817            };
4818
4819            var bc_resize_throttle = function(callback, delay){
4820                delay = delay || 100;
4821                clearTimeout(resizeTimeout);
4822                resizeTimeout = setTimeout(callback, delay);
4823            };
4824
4825            $(window).on('resize', function(){
4826              bc_resize_throttle(breadcrumbWidth, 100);
4827            });
4828            breadcrumbWidth();
4829
4830            $(window).on("scroll", toggleBreadcrumbs);
4831            toggleBreadcrumbs();
4832
4833        };
4834
4835        setup();
4836	};
4837
4838	return TateBreadcrumbs;
4839});
4840
4841define('components/TateShareIcons',['jquery', 'components/TateWindow'], function ($, TateWindow) {
4842
4843    var TateShareIcons = function (options) {
4844        options = options || {};
4845        var me = this;
4846
4847        $(document).ready(function () {
4848            me.init();
4849        });
4850    };
4851
4852    TateShareIcons.prototype = {
4853        init: function () {
4854
4855            var tateWindow = new TateWindow(),
4856                shareTimeout = null,
4857                icons = $('.image-strip__share-rollover'),
4858                disableHover = false;
4859
4860            var showIcons = function(){
4861                icons.stop(true, false).fadeIn(150);
4862            };
4863
4864            var hideIcons = function() {
4865                icons.stop(true, false).fadeOut(150);
4866            };
4867
4868            $('.image-strip .rollover-button').on('mouseover', function() {
4869                clearTimeout(shareTimeout);
4870                if (!disableHover) {
4871                    shareTimeout = setTimeout(showIcons, 200);
4872                }
4873            }).on('mouseleave', function(){
4874                disableHover = false;
4875                clearTimeout(shareTimeout);
4876            }).on('click', showIcons);
4877
4878            $('.image-strip .rollover-close').on('click', function(){
4879                // Prevent the icons popping up again as soon as the user moves their mouse
4880                disableHover = true;
4881                hideIcons();
4882            });
4883            $('.image-strip__share-rollover').on('mouseleave', function() {
4884                setTimeout(hideIcons, 100);
4885            });
4886
4887            // TODO: Touchscreen users might click outside of the strip of icons to get rid of it
4888            // This will depend on listening for a tap event, which is not yet generally supported
4889            // within TateUI (see TateSlideshow for example of local support)
4890            /*
4891            if (icons.length) {
4892                $(window).on('tap', function(e){
4893                    // Touchscreen users might click outside of the strip of icons to get rid of it
4894                    if (!$.contains(icons[0], e.target)) {
4895                        hideIcons();
4896                    }
4897                });
4898            }
4899            */
4900
4901            // Make the social share functions open in a new window
4902            $('.image-strip__share-rollover .list--social a').on('click', function(e){
4903                if (tateWindow.width() < 700 || tateWindow.height() < 700)
4904                    return; // Don't try to force a pop-up on a small window.
4905                var href = $(this).attr('href');
4906                if (href.substring(0, 4) != 'http')
4907                    return; // Don't interfere with default behaviour of the 'share by email' link
4908                e.preventDefault();
4909                window.open(href, 'targetWindow', "toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=" 
4909+ 500 + ",height=" + 500);
4910                return false;
4911            });
4912
4913        }
4914    };
4915
4916    return TateShareIcons;
4917});
4918
4919define('components/TateSplash',['jquery'], function ($) {
4920	var TateSplash = function () {
4921		var me = this;
4922
4923		$(document).ready(function () {
4924			me.init();
4925		});
4926	};
4927
4928	TateSplash.prototype = {
4929		init: function () {
4930			var breakpoint = {};
4931			breakpoint.refreshValue = function () {
4932				this.value = window.getComputedStyle(document.querySelector('body'), ':before').getPropertyValue('content').replace(/\"/g, '');
4933			};
4934
4935			var renderSplash = function(){
4936				breakpoint.refreshValue();
4937
4938				if (breakpoint.value == 'screen-wide' ||
4939					breakpoint.value == 'screen-default' ||
4940					breakpoint.value == 'screen-narrow' ||
4941					breakpoint.value == 'tablet' ) {
4942
4943					$('.splash-header--style__splash-image').each(function(){
4944						var splashImg;
4945						$(this).find('.splash-header__media--inner').trigger('lazyload:setup');
4946						splashImg = $(this).find('.splash-header__media img').attr('data-original');
4947
4948						if (!$(this).find('.splash-header__media--background').length) {
4949							$(this).find('.splash-header__media').prepend('<div class="splash-header__media--background"></div>');
4950						}
4951						$(this).find('.splash-header__media--background').css({
4952							backgroundImage: 'url("' + splashImg + '")'
4953						});
4954					});
4955				}
4956			};
4957
4958			renderSplash();
4959
4960			var resize_throttle = function(callback, delay) {
4961				var me = this;
4962				delay = delay || 100;
4963				clearTimeout(me.timeout);
4964				me.timeout = setTimeout(callback, delay);
4965			};
4966
4967			$(window).on('resize', function(){
4968				resize_throttle(renderSplash, 100);
4969			});
4970
4971		}
4972	};
4973
4974	// *************************************************************
4975	// WHAT'S ON SPLASH: load random artwork image on What's On page
4976
4977	var TateWhatsOnSplash = function () {
4978		var me = this;
4979
4980		$(document).ready(function () {
4981			me.init();
4982		});
4983
4984	};
4985
4986	TateWhatsOnSplash.prototype = {
4987		init: function () {
4988			var images = [
4989				'http://www.tate.org.uk/art/images/work/N/N01/N01615_10.jpg',
4990				'http://www.tate.org.uk/art/images/work/N/N01/N01506_10.jpg',
4991				'http://www.tate.org.uk/art/images/work/N/N01/N01981_10.jpg',
4992				'http://www.tate.org.uk/art/images/work/T/T01/T01571_10.jpg',
4993				'http://www.tate.org.uk/art/images/work/N/N01/N01543_10.jpg'
4994			];
4995
4996			$('.container--whats-on__landing.background--image-canvas').css({
4997				'background-image': 'url(' + images[Math.floor(Math.random() * images.length)] + ')'
4998			});
4999		}
5000	};
5001
5002	new TateWhatsOnSplash();
5003
5004	return TateSplash;
5005});
5006
5007define('components/TateAdvancedFilters',['jquery'], function ($) {
5008
5009	var TateAdvancedFilters = function () {
5010		var me = this;
5011
5012		$(document).ready(function () {
5013			me.init();
5014		});
5015	};
5016
5017	TateAdvancedFilters.prototype = {
5018		init: function () {
5019
5020			// Recreate the what's on form to remove junk values that will pollute the URL.
5021			$('form.advanced-filters').on('submit', function(e){
5022				e.preventDefault();
5023				var orig = $(this);
5024				var form = $("<form id=\"dummy-advanced-filters-form\"></form>");
5025				form.attr("action", orig.attr("action"));
5026				form.attr("method", "get");
5027				orig.find('input[type=hidden]').each(function() {
5028					var value = $(this).attr('value');
5029					var name = $(this).attr('name');
5030					if (value === "" || !value || name === "" || !name) {
5031						return;
5032					}
5033					var input = $("<input type=\"hidden\" />");
5034					input.attr("name", name);
5035					input.attr("value", value);
5036					form.append(input);
5037				});
5038				orig.find('select').each(function(){
5039					var value = $(this).find('option:selected').attr('value');
5040					if (value === "" || !value) {
5041						return;
5042					}
5043					var input = $("<input type=\"hidden\" />");
5044					input.attr("name", $(this).attr("name"));
5045					input.attr("value", $(this).find('option:selected').attr('value'));
5046					form.append(input);
5047				});
5048				checkedValues = {};
5049				orig.find('input:checked').each(function(){
5050					var name = $(this).attr("name").replace("[]", "");
5051					var valueList = checkedValues[name] || [];
5052					valueList.push($(this).attr("value"));
5053					checkedValues[name] = valueList;
5054				});
5055				for (name in checkedValues) {
5056					var input = $("<input type=\"hidden\" />");
5057					input.attr("name", name);
5058					input.attr("value", checkedValues[name].join(','));
5059					form.append(input);
5060				}
5061				//TODO: add the values from the advanced filter checkboxes
5062				$("body").append(form);
5063				$("#dummy-advanced-filters-form").submit();
5064				return false;
5065			});
5066
5067			// Show/hide the advanced filter options
5068			$('.form-section--expand h3').on('click', function(){
5069				var section = $(this).closest('.form-section--expand');
5070				var holder = section.find('.form-section__content');
5071				var content = holder.find('.fieldset-grid');
5072				if (section.hasClass('form-section--expand__open')) {
5073					section.removeClass('form-section--expand__open');
5074					holder.stop(false, false).animate({'max-height': '0px'}, 200);
5075				} else {
5076					section.addClass('form-section--expand__open');
5077					holder.stop(false, false).animate({'max-height': content.height() + 'px'}, 200);
5078				}
5079			});
5080
5081		}
5082	};
5083
5084	return TateAdvancedFilters;
5085});
5086
5087define('components/TateTabs',['jquery'], function($) {
5088
5089	var TateTabs = function() {
5090
5091 		/**
5092 		 * Rewrite any local anchor links in a preview of an external text.
5093 		 */ 
5094 		var fixResearchText = function(text){
5095 			var researchLink = text.find('.research-text__canonical-link');
5096 			if (!researchLink.length || !researchLink.attr('href')) { return; }
5097 			var researchPath = researchLink.attr('href');
5098			text.find('a').each(function(){
5099				var link = $(this);
5100				var href = link.attr('href');
5101				if (href && href.length > 1 && href.substring(0,1) == "#") {
5102					link.attr('href', researchPath + href);
5103				}
5104			});
5105 		};
5106
5107 		/**
5108 		Truncates the texts on artist and artwork pages, but only if it's worth it.
5109 		Ensures that the text is never cut off before the bottom of the artwork tombstone
5110 		or the artist portrait image.
5111 		Needs to run on page load, when a research text is loaded via AJAX or when a user clicks on a different tab.
5112 		*/
5113 		var fixTruncation = function(){
5114 			var container = $('.content-truncate-container'),
5115 				texts = $('.artwork__text').add('.artist__text'),
5116 				must_be_visible = $('.content-block--details');
5117
5118			var imageOK = function(img){
5119				return (img.complete && img.naturalWidth != 0);
5120			}
5121 			
5122 			if (container.length && texts.length) {
5123
5124 				if (must_be_visible.length && must_be_visible.find('img').length) {
5125 					// Check that any sidebar image has loaded as this affects our calculations
5126 					var awaitingImage = false;
5127					must_be_visible.find('img').each(function(){
5128						if (!$(this).attr('src') || $(this).attr('src').indexOf('placeholder') > 0 || !imageOK($(this)[0])) {
5129							awaitingImage = true;
5130							return;
5131						}
5132					});
5133					if (awaitingImage) {
5134						// Defer truncation until we know the image height
5135						window.setTimeout(fixTruncation, 250);
5136						return;					
5137					}
5138				}
5139
5140 				// Find which text is visible 
5141 				var visible_text = texts.eq(0);
5142 				if (texts.length > 1) {
5143 					texts.each(function(){
5144 						if ($(this).hasClass('tab-section--active')) {
5145 							visible_text = $(this);
5146 						}
5147 					})
5148 				}
5149 				if (visible_text.attr('data-ajax-url')) {
5150 					// Exit here if the text has not loaded yet.
5151 					return;
5152 				}
5153
5154 				var max_h = 600;
5155 				var text_xy = visible_text.offset(),
5156 					container_xy = container.offset(),
5157 					text_top = text_xy.top - container_xy.top,
5158 					text_h = visible_text.outerHeight() + text_top;
5159 				if (must_be_visible.length) {
5160 					var must_be_visible_xy = must_be_visible.eq(0).offset(),
5161 						must_be_visible_h = must_be_visible.eq(0).outerHeight() + must_be_visible_xy.top - container_xy.top;
5162					if (text_xy.top - must_be_visible_xy.top > 50) { // Mobile layout
5163						max_h = must_be_visible_h + 500;
5164					} else { // Desktop layout
5165						max_h = Math.max(max_h, (must_be_visible_h + 65));
5166					}
5167 				}
5168 				
5169 				// Only apply truncation if there is enough surplus text to warrant hiding it.
5170 				if ((text_h - max_h) > 100) {
5171 					container.addClass('content-truncate-container--active').css({'max-height': max_h + 'px'});
5172 					visible_text.find('.text__read-more').show();
5173 				} else {
5174 					container.removeClass('content-truncate-container--active').css({'max-height': 'none'});
5175 					if (!visible_text.hasClass('research-text')) {
5176 						visible_text.find('.text__read-more').hide();
5177 					}
5178 				}
5179 			}
5180 		};
5181
5182 		var init = function() {
5183
5184        	/**
5185        	 * Enable tabbing between panels within a list of tabs.
5186        	 */
5187            $('.tabs').each(function() {
5188                var $tab = $(this);
5189                if (!$(this).hasClass('tabs--rendered')) {
5190                    $tab.addClass('tabs--rendered');
5191                    // click functionality for each tab link
5192                    $tab.find('.tabs-menu__item a').on('click', function(e) {
5193                        e.preventDefault();
5194                        var aID = $(this).attr('href'),
5195                        	tabBody = $(aID);
5196                        $tab.find('.tabs-menu__item--active').removeClass('tabs-menu__item--active');
5197                        $tab.find('.tab-section--active').removeClass('tab-section--active');
5198                        $(this).parent().addClass('tabs-menu__item--active');
5199                        tabBody.addClass('tab-section--active');
5200                        // Expose a custom event so we can listen for the change
5201                        $tab.trigger('tatetabs:tabchange');
5202                    });
5203                }
5204                $tab.find(".tabs--back-to-menu").on('click', function(e) {
5205                    e.preventDefault();
5206                    $('body').animate({
5207                        'scrollTop': ($tab.offset().top - $('header').height()) + 'px'
5208                    });
5209                });
5210            });
5211
5212            /**
5213             * Display the vertical tab menu on a mobile device
5214             */
5215            $('.tabs--rendered').find('.tabs-menu__item').on('click', function(){
5216                if ($(this).parent('li').hasClass('tabs-menu__item--active')) {
5217                    e.preventDefault();
5218                    $(this).closest('ul').toggleClass("tabs-menu__list--mobile-active"); 
5219                }
5220                else { 
5221                    $(this).closest('ul').toggleClass("tabs-menu__list--mobile-active"); 
5222                }
5223            });
5224
5225     		$('.research-text').each(function(){
5226     			fixResearchText($(this));
5227     		});
5228
5229     		fixTruncation();         		
5230     		
5231            /**
5232             * Very simple bit of Ajax code that does an ajax call for the URL in the
5233             * data-ajax-url attribute and fills that element with the HTML that
5234             * is returned.
5235             */
5236     		$('div[data-ajax-url]').each(function(index) {
5237     			var el = $(this),
5238     				container = el.find('.content-truncate-container');
5239         		el.load(el.attr('data-ajax-url'), function(){
5240         			el.removeAttr('data-ajax-url');
5241             		if (el.hasClass('research-text')) {
5242         				fixResearchText(el);
5243         				fixTruncation();
5244             		}
5245         		});
5246     		});
5247
5248            /**
5249             * Show the full text when user clicks 'Read more' button.
5250             */
5251            $('.content-truncate-container').on('click', '.text__read-more a', function(e){
5252            	if ($(this).attr('href') != '#') { return; }
5253            	e.preventDefault();
5254            	var container = $(this).closest('.content-truncate-container'),
5255            		readMore = $(this).closest('.text__read-more');
5256            	if ($(this).find('.btn__text').text() === 'Read more') {
5257            		container.css({'max-height': 'none'});
5258            		readMore.fadeOut(400);
5259                	$('.content-truncate-container').removeClass('content-truncate-container--active');
5260            	}
5261            	return false;
5262            });         		            
5263        
5264            // Listen for a change in the active tab
5265     		$('.content-truncate-container .tabs').on('tatetabs:tabchange', function(e){
5266     			fixTruncation();
5267     		});
5268            
5269        };
5270
5271        $(document).ready(init);
5272
5273	};
5274
5275	return TateTabs;
5276});
5277
5278define('components/TateReviews',['jquery'], function ($) {
5279	var TateReviews = function () {
5280		var me = this;
5281
5282		$(document).ready(function () {
5283			me.init();
5284		});
5285	};
5286
5287	TateReviews.prototype = {
5288		init: function () {
5289
5290			var reviewSwitcher = null,
5291				fadeDelay = 4000;
5292
5293			var switchReview = function(){
5294				$('.review-block').each(function(){
5295					var block = $(this),
5296						list = block.children('.review');
5297					if (block.hasClass('review-block--paused')) {
5298						return; // Precaution against some odd browser behaviour 
5299					}
5300					var length = list.length,
5301						current = list.filter('.active');
5302					if (current.length == 1) {
5303						var i = list.index(current),
5304							next = (i == length -1) ? list.eq(0) : list.eq(i + 1);
5305						next.css({'z-index': 2, 'display': 'block'});
5306						var read_time = next.attr('data-timeout');
5307						if (!read_time) {
5308							var review_length = next.text().replace(/\W/g, '').length;
5309							read_time = Math.max(review_length * 50, 2000);
5310							next.attr('data-timeout', read_time);
5311						}
5312						current.stop(false, false).fadeOut(800, function(){
5313							current.removeClass('active').css({'z-index': 1});
5314							next.addClass('active').css({'z-index': 3});
5315							start_switching(read_time);
5316						});
5317					}
5318				});
5319			};
5320
5321			var sizeBlocks = function(){
5322				$('.review-block').each(function(){
5323					var block = $(this),
5324						list = block.children('.review');
5325					var tallest = 0;
5326					list.each(function(){
5327						tallest = Math.max(tallest, $(this).outerHeight());
5328					});
5329					block.css({'position': 'relative', 'height': tallest + 'px'});
5330				});
5331			};
5332			
5333			var resize_throttle = function(delay) {
5334				var me = this;
5335				delay = delay || 100;
5336			    clearTimeout(me.timeout);
5337			    stop_switching();
5338			    me.timeout = setTimeout(function(){
5339			    	sizeBlocks();
5340			    	start_switching();
5341			    }, delay);
5342			};
5343			
5344			var start_switching = function(delay){
5345				delay = delay || fadeDelay;
5346				reviewSwitcher = setTimeout(switchReview, delay);
5347			}
5348			
5349			var stop_switching = function(){
5350				clearTimeout(reviewSwitcher);
5351				reviewSwitcher = null;
5352			}
5353			
5354			$(document).ready(function(){
5355
5356				$('.review-block').each(function(){
5357					var block = $(this),
5358						list = block.children('.review');
5359					
5360					if (list.length < 2) { return; }
5361					
5362					// Prepare styles
5363					sizeBlocks();
5364					list.css({
5365						'position': 'absolute',
5366						'display': 'block',
5367						'z-index': 1,
5368						'width': '100%',
5369						'height': '100%',
5370						'box-sizing': 'border-box'
5371						});
5372					list.eq(0).addClass('active').css({'z-index': 3});
5373
5374					// Pause on current review if user hovers
5375					block.on({
5376					    mouseenter: function(){
5377					    	if (list.length > 1) {
5378					    		stop_switching();
5379					    		block.addClass('review-block--paused');
5380					    	}
5381					    },
5382					    mouseleave: function(){
5383					    	if (list.length > 1) {
5384					    		start_switching(1000);
5385					    		block.removeClass('review-block--paused');
5386					    	}
5387					    }
5388					});
5389					
5390				});
5391
5392				if ($('.review-block .active').length) {
5393					$(window).on("resize", resize_throttle);
5394
5395					// Get this show on the road!
5396					start_switching();
5397				}
5398
5399			});
5400			
5401		}
5402	};
5403
5404	return TateReviews;
5405});
5406define('components/TateJPlayer',['jquery'], function ($) {
5407	var TateJPlayer = function () {
5408		var me = this;
5409
5410		$(document).ready(function () {
5411			me.init();
5412		});
5413	};
5414
5415	TateJPlayer.prototype = {
5416		init: function () {
5417
5418            // is there at least one jplayer on this page?
5419            if ($('.jp-jplayer')[0]) {
5420                // create a <script> element to load jplayer library
5421                var jp = document.createElement('script');
5422                $('body').append(jp);
5423                jp.onload = function() {
5424                    // when the jplayer library has loaded, initialise the jplayers on this page
5425                    $(".jp-jplayer").each(function(i) {
5426                        var el = $(this);
5427                        var audio_url = el.attr('data-audio-url');
5428                        if (!audio_url || typeof(audio_url) == 'undefined') {
5429                            console.log("Missing parameter 'data-audio-url'");
5430                            el.remove();
5431                            return;
5432                        }
5433                        el.removeAttr('data-audio-url');
5434                        var title = el.attr('data-title');
5435                        el.removeAttr('data-title');
5436
5437                        var duration = el.attr('data-media-length');
5438                        if (!isNaN(duration)) // if numeric
5439                        {
5440                           // duration seconds to mm:ss
5441                           var minutes = "0" + Math.floor(duration / 60);
5442                           var seconds = "0" + (duration - minutes * 60);
5443                           duration = minutes.substr(-2) + ":" + seconds.substr(-2);
5444
5445                           el.removeAttr('data-media-length');
5446                        }
5447
5448                        // assign unique id to audio wrapper
5449                        el.attr('id','jquery_jplayer_'+i);
5450                        // assign unique id to interface
5451                        el.next('div').attr('id','jp_container_'+i);
5452
5453                        el.jPlayer({
5454                            ready: function () {
5455                                $(this).jPlayer("setMedia", {
5456                                    title: title,
5457                                    mp3: audio_url,
5458                                    duration: (duration == '00:00' || duration == null) ? null : duration,
5459                                });
5460                            },
5461                            play: function() {
5462                                $(".jp-jplayer").not(this).jPlayer("pause");
5463                            },
5464                            cssSelectorAncestor: "#jp_container_"+i, // link audio to interface
5465                            swfPath: "//cdnjs.cloudflare.com/ajax/libs/jplayer/2.9.2/jplayer/",
5466                            useStateClassSkin: true,
5467                            autoBlur: false,
5468                            smoothPlayBar: true,
5469                            keyEnabled: true,
5470                            remainingDuration: false,
5471                            toggleDuration: true,
5472                            preload: "none",
5473                        });
5474                    });
5475                };
5476                jp.src = "//cdnjs.cloudflare.com/ajax/libs/jplayer/2.9.2/jplayer/jquery.jplayer.min.js";
5477            }
5478
5479		}
5480	};
5481
5482	return TateJPlayer;
5483});
5484
5485define('components/TateInfiniteScroll',["jquery"], function ($) {
5486
5487    var TateInfiniteScroll = function() {
5488        
5489        var init = function () {
5490
5491            var lockAjaxCall = false;  // generate a single ajax call from many scroll events
5492            var chillOutScroller = 5;  // avoid processing doInfiniteScroll too many times - a tiny scroll on the wheel of a mouse
5493                                        // generates tons of scroll events
5494            
5495            var doInfiniteScroll = function(force) {
5496                
5497                if(lockAjaxCall)
5498                    return;
5499
5500                if(typeof(force) == 'undefined' || !force) {
5501                    if(chillOutScroller-- > 1)
5502                        return;
5503                    chillOutScroller = 5;
5504                }
5505
5506                var triggers = $("span[data-ajax-type=infinite-scroll]");
5507                if(!triggers || typeof(triggers) == 'undefined' || triggers.length < 1) {
5508                    $(window).off("scroll", doInfiniteScroll);
5509                    return;
5510                }
5511
5512                trigger = $(triggers[0]);
5513
5514                var ajax_url = trigger.attr("data-ajax-url");
5515                if(!ajax_url) {
5516                    $(window).off("scroll", doInfiniteScroll);
5517                    return;
5518                }
5519
5520                var target = trigger.parent("div.card-list");
5521                if(!target || target.length < 1) {
5522                    $(window).off("scroll", doInfiniteScroll);
5523                    return;
5524                }
5525
5526                if(typeof(trigger.offset()) == 'undefined') {
5527                    $(window).off("scroll", doInfiniteScroll);
5528                    return;
5529                }
5530
5531                relative_pos = trigger.offset().top - 300;      // add 300 to fire the loading earlier
5532
5533                if($(window).scrollTop() >= relative_pos - $(window).height()) {
5534                    lockAjaxCall = true;
5535                    $.ajax(ajax_url)
5536                        .done(function(data) {
5537                            if(data) {
5538                                target.append(data);
5539                                trigger.remove();
5540                                if(typeof(TateUI) != 'undefined')
5541                                    TateUI.LazyLoad();  // need to re-active LazyLoad in case some new images were loaded into the DOM
5542                                lockAjaxCall = false;
5543                            }
5544                        });
5545                    }
5546            };
5547            
5548            $(window).on("scroll", doInfiniteScroll);
5549            doInfiniteScroll(true); // also fire on page load
5550        };
5551
5552        init();
5553    };
5554
5555    return TateInfiniteScroll;
5556});
5557
5558define('components/BannerSlider',['jquery', 'vendor/slick'], function ($, Slick) {
5559	var BannerSlider = function () {
5560		this.init();
5561	};
5562
5563	BannerSlider.prototype = {
5564		init: function () {
5565			/** LOCAL CONFIGURATION OBJECTS **/
5566
5567			/** DOM READY SCRIPTS **/
5568			$(document).ready(function () {
5569				$('.main-banner').slick({
5570					dots: true,
5571					arrows: false,
5572					infinite: true,
5573					autoplay: true,
5574					autoplaySpeed: 10000,
5575					pauseOnHover: true,
5576					pauseOnDotsHover: true,
5577					slidesToShow: 1,
5578					slidesToScroll: 1,
5579					draggable: false,
5580					lazyLoad: 'ondemand'
5581				});
5582			});
5583		}
5584	};
5585
5586	return BannerSlider;
5587});
5588
5589define('components/TateModalManager',['jquery'], function ($) {
5590
5591	"use strict";
5592
5593	var TateModalManager = function () {
5594
5595		var self = this;
5596
5597		$(document).ready(function () {
5598			var config = typeof TateModalManagerConfig !== "undefined" ? TateModalManagerConfig : {};
5599			self.init(config);
5600		});
5601	};
5602
5603
5604	TateModalManager.prototype = {
5605
5606		/**
5607		 * Tate Modal Manager ping -> pong game
5608		 */
5609		ping: function () {
5610			return 'pong';
5611		},
5612
5613		loadModal: function (event) {
5614			event.preventDefault();
5615
5616            if (typeof $(this).data('event') !== 'undefined') {
5617                // Push dataLayer for googleTagManager
5618                var $event = $(this).data('event'),
5619                    $label = typeof $(this).data('label') === 'undefined' ? "" : $(this).data('label');
5620
5621                dataLayer.push({
5622                    event : $event,
5623                    label : $label
5624                });
5625            }
5626
5627			// only show modal if user is logged-in - else use default href behaviour
5628			if ($(this).attr('href').indexOf("/user?destination=") !== -1) {
5629				window.location.href = $(this).attr('href');
5630				return true;
5631			}
5632
5633			// need to have either the connected element or the AJAX url callback
5634			if (typeof(event.data) == 'undefined' || (typeof(event.data.ajax_url) == 'undefined' && typeof(event.data.html_element) == 'undefined')) {
5635				return false;
5636			}
5637
5638			var params = {
5639				className: typeof $(this).attr('data-class') !== null ? $(this).attr('data-class') : '',
5640				scrolling: false,
5641				maxWidth: '624px',
5642				width: '90%',
5643				onComplete: function () {
5644					$.fn.colorbox.resize();
5645				}
5646			};
5647
5648			if (event.data.ajax_url) {
5649				var ajax_url = $(this).attr('data-ajax-url');   // data-ajax-url may have been updated by a script
5650                $
5651                .ajax(ajax_url)
5652                .done(function(data) {
5653                    params['html'] = data;
5654                    $.colorbox(params);
5655                    TateUI.Tabs();
5656                });
5657			}
5658			else if (event.data.html_element) {
5659				var content = $("#" + event.data.html_element);
5660				if (content) {
5661					params['html'] = content.prop('innerHTML');
5662					$.colorbox(params);
5663				}
5664			}
5665			return false;
5666		},
5667
5668		/**
5669		 * Init function
5670		 */
5671		init: function (config) {
5672			var self = this;
5673
5674			$.extend(self, {
5675				config: config
5676			});
5677
5678			// observers for modal events
5679			$(window).on("CalendarOnModalAppear", function (event, inst) {
5680				$("a[data-ajax-type=modal]").each(function() {
5681					// test href is set and != data-anon-href
5682					// test data-ajax-url is set, if so, on click: ajax callback, shows the returned HTML in a modal box
5683					var el = $(this);
5684					var href = el.attr('href');
5685					var anon_href = el.attr('data-anon-href');
5686
5687					// hmmm no guarantee that the above (done by TateLogin.js) would be processed before this code hmm hmm
5688					// perhaps it could check that the 'href' doesn't start in /user...?
5689					if (typeof(anon_href) !== 'undefined' && href === anon_href) {
5690						return; // user not logged-in - don't need to show the modal
5691					}
5692
5693					var params = {}
5694					var ajax_url = el.attr('data-ajax-url');    // ajax callback to get the content
5695					var html_element = el.attr('data-ajax-element');   // content is already in the DOM
5696					if (!ajax_url || typeof(ajax_url) === 'undefined') {
5697						if (!html_element || typeof(html_element) === 'undefined') {    // ajax_url has priority over html_element
5698							return;
5699						} else {
5700							params['html_element'] = html_element;
5701						}
5702					} else {
5703						params['ajax_url'] = ajax_url;
5704					}
5705
5706                    el.off('click').on("click", null, params, self.loadModal);
5707				});
5708			});
5709		}
5710	};
5711
5712	return TateModalManager;
5713});
5714
5715define('components/TateAccordionManager',['jquery'], function ($) {
5716
5717	"use strict";
5718
5719	var TateAccordionManager = function () {
5720
5721		var self = this;
5722
5723		$(document).ready(function () {
5724			var config = typeof TateAccordionManagerConfig !== "undefined" ? TateAccordionManagerConfig : {};
5725			self.init(config);
5726		});
5727	};
5728
5729	TateAccordionManager.prototype = {
5730		/**
5731		 * Init function
5732		 */
5733		init: function (config) {
5734			var self = this;
5735
5736			$(window).on("TateAccordionCatch", function (event) {
5737				var $elements = $('.accordion');
5738				if ($elements.length > 0) {
5739					$.each($elements, function (index, item) {
5740						$(item).accordion({
5741							heightStyle: "content"
5742						});
5743					});
5744				}
5745			});
5746
5747			$(window).trigger("TateAccordionCatch");
5748		}
5749	};
5750
5751	return TateAccordionManager;
5752});
5753
5754define('components/TateAccordionPanelManager',['jquery'], function ($) {
5755
5756	"use strict";
5757
5758	var TateAccordionPanelManager = function () {
5759
5760		var self = this;
5761
5762		$(document).ready(function () {
5763			var config = typeof TateAccordionPanelManagerConfig !== "undefined" ? TateAccordionPanelManagerConfig : {};
5764			self.init(config);
5765		});
5766	};
5767
5768	TateAccordionPanelManager.prototype = {
5769		/**
5770		 * Init function
5771		 */
5772		init: function (config) {
5773			var self = this;
5774
5775			$(window).on("TateAccordionPanelCatch", function (event) {
5776				var $elements = $('.banner-panel--accordion');
5777				if ($elements.length > 0 && $elements.size() > 0) {
5778					$.each($elements, function (index, item) {
5779						var id = $(item).data('id');
5780						$('a[data-id=handle]').off('click').on('click', function (e) {
5781							e.preventDefault();
5782							var state = $(item).data('state');
5783							if (state === 'closed') {
5784								$('.icon--chevron-down', $(item)).hide();
5785								$('.icon--chevron-up', $(item)).show();
5786								$(item).data('state', 'opened');
5787								$('#' + id).slideDown();
5788							} else {
5789								$('.icon--chevron-down', $(item)).show();
5790								$('.icon--chevron-up', $(item)).hide();
5791								$(item).data('state', 'closed');
5792								$('#' + id).slideUp();
5793							}
5794						});
5795					});
5796				}
5797			});
5798		}
5799	};
5800
5801	return TateAccordionPanelManager;
5802});
5803
5804define('components/TateUtil',['jquery'], function ($) {
5805
5806	"use strict";
5807
5808	var TateUtil = {
5809		appendParamToURL: function (url, name, value) {
5810			if (url.indexOf(name + '=') !== -1) {
5811				return url;
5812			}
5813			var separator = url.indexOf('?') !== -1 ? '&' : '?';
5814			return url + separator + name + '=' + encodeURIComponent(value);
5815		},
5816		appendParamsToUrl: function (url, params) {
5817			var _url = url;
5818			$.each(params, function (name, value) {
5819				_url = this.appendParamToURL(_url, name, value);
5820			}.bind(this));
5821			return _url;
5822		},
5823		ajaxUrl: function (path) {
5824			return this.appendParamToURL(path, 'format', 'ajax');
5825		},
5826		getDayNumberSuffix: function (day) {
5827			if (day >= 11 && day <= 13) {
5828				return "th";
5829			}
5830			switch (day % 10) {
5831				case 1:
5832					return "st";
5833				case 2:
5834					return "nd";
5835				case 3:
5836					return "rd";
5837				default:
5838					return "th";
5839			}
5840		},
5841        /**
5842         * @function
5843         * @description remove the parameter and its value from the given url and returns the changed url
5844         * @param {String} url the url from which the parameter will be removed
5845         * @param {String} name the name of parameter that will be removed from url
5846         */
5847        removeParamFromURL: function (url, name) {
5848            if (url.indexOf('?') === -1 || url.indexOf(name + '=') === -1) {
5849                return url;
5850            }
5851            var hash;
5852            var params;
5853            var domain = url.split('?')[0];
5854            var paramUrl = url.split('?')[1];
5855            var newParams = [];
5856            // if there is a hash at the end, store the hash
5857            if (paramUrl.indexOf('#') > -1) {
5858                hash = paramUrl.split('#')[1] || '';
5859                paramUrl = paramUrl.split('#')[0];
5860            }
5861            params = paramUrl.split('&');
5862            for (var i = 0; i < params.length; i++) {
5863                // put back param to newParams array if it is not the one to be removed
5864                if (params[i].split('=')[0] !== name) {
5865                    newParams.push(params[i]);
5866                }
5867            }
5868            return domain + '?' + newParams.join('&') + (hash ? '#' + hash : '');
5869        }
5870	};
5871	return TateUtil;
5872});
5873
5874
5875
5876define('components/TatePushStatePageManager',['jquery', 'components/TateUtil'], function ($, TateUtil) {
5877
5878	var TatePushStatePageManager = function () {
5879
5880		var self = this;
5881
5882		$(document).ready(function () {
5883			var config = typeof TatePushStatePageManagerConfig !== "undefined" ? TatePushStatePageManagerConfig : {};
5884			self.init(config);
5885		}.bind(this));
5886	};
5887
5888	TatePushStatePageManager.prototype = {
5889		continuebtn: '.go-continue',
5890		stickyContinueBtnn: '.go-continue-sticky',
5891		continueBtnHiddenClass: 'go-continue-hidden',
5892		stickyContinueInactiveClass: 'go-continue-sticky-inactive',
5893		backbtn: '.go-back',
5894		backBtnHiddenClass: 'go-back-hidden',
5895		stickyBottomCard: '.sticky-bottom-card',
5896		inactiveClass: 'inactive',
5897		tabletBreakpoint: 839,
5898
5899		/**
5900		 * Hides prev button
5901		 */
5902		hidePrev: function () {
5903			$(this.backbtn).addClass(this.backBtnHiddenClass);
5904		},
5905		/**
5906		 * Hides next button
5907		 */
5908		hideNext: function () {
5909			$(this.continuebtn).addClass(this.continueBtnHiddenClass);
5910			$(this.stickyContinueBtnn).addClass(this.stickyContinueInactiveClass);
5911			$(this.stickyBottomCard).addClass(this.inactiveClass);
5912			// Add for ticketing sticky continue
5913			$(this.saveAndContinueStickyBtn).addClass(this.saveAndContinueStickyInactiveClass);
5914		},
5915		/**
5916		 * Show next button
5917		 */
5918		showNext: function () {
5919			// Only show the desktop continue button if the window width is greater than the tablet breakpoint
5920			if ($(window).width() > this.tabletBreakpoint) {
5921				$(this.continuebtn).removeClass(this.continueBtnHiddenClass);
5922			}
5923			$(this.stickyContinueBtnn).removeClass(this.stickyContinueInactiveClass);
5924			$(this.stickyBottomCard).removeClass(this.inactiveClass);
5925			// Remove for ticketing sticky continue
5926			$(this.saveAndContinueStickyBtn).removeClass(this.saveAndContinueStickyInactiveClass);
5927		},
5928		/**
5929		 * Show prev button
5930		 */
5931		showPrev: function () {
5932			$(this.backbtn).removeClass(this.backBtnHiddenClass);
5933        },
5934        /**
5935         * Go to alias location
5936         * @param  {string} where destination alias
5937         */
5938		goTO: function(where) {
5939			var self = this;
5940			$.each(this.config.steps, function (index, value) {
5941				if (value.slug === where) {
5942					var conf = value;
5943					history.pushState({page: conf.slug}, conf.title, conf.url);
5944					this.config.currentIndex = index;
5945					this.load(conf.url);
5946					return false;
5947				}
5948			}.bind(this));
5949		},
5950		/**
5951		 * Return prev url object or null
5952		 * @return {object}
5953		 */
5954		getPrevURL: function () {
5955			if (this.config.currentIndex === 0) {
5956				return null;
5957			}
5958			return this.config.steps[this.config.currentIndex - 1];
5959		},
5960		/**
5961		 * Returns next url object or null
5962		 * @return {object}
5963		 */
5964		getNextURL: function () {
5965			if ((this.config.currentIndex + 1) === this.config.steps.length) {
5966				return null;
5967			}
5968			return this.config.steps[this.config.currentIndex + 1];
5969		},
5970		prevURL: function () {
5971			if (this.config.currentIndex === 0) {
5972				if (typeof this.config.prevURL !== "undefined" && this.config.prevURL !== null) {
5973					document.location = this.config.prevURL;
5974				} else {
5975					window.history.go(-1);
5976				}
5977			} else {
5978				this.config.currentIndex--;
5979				var conf = this.config.steps[this.config.currentIndex];
5980				/** Does it have a prevURL config */
5981				if (this.config.redirectURL !== 'null'
5982					&& this.config.steps[this.config.currentIndex - 1].url.length > 0) {
5983						document.location = this.config.steps[this.config.currentIndex - 1].url;
5984				} else {
5985					if (this.config.steps[this.config.currentIndex + 1].backURL !== false) {
5986						document.location = this.config.steps[this.config.currentIndex + 1].backURL;
5987					}
5988					/** is it disabled */
5989					else if (conf.disabled === false) {
5990						history.pushState({page: conf.slug}, conf.title, conf.url);
5991						this.load(conf.url);
5992					}
5993				}
5994			}
5995		},
5996		nextURL: function (shouldContinue) {
5997			var self = this;
5998			if (shouldContinue) {
5999				if ((this.config.currentIndex + 1) === this.config.steps.length) {
6000					if (typeof this.config.nextURL !== "undefined" && this.config.nextURL !== null) {
6001						document.location = this.config.nextURL;
6002					} else {
6003						window.history.go(1);
6004					}
6005				} else {
6006					this.config.currentIndex++;
6007					var conf = this.config.steps[this.config.currentIndex];
6008					history.pushState({page: conf.slug}, conf.title, conf.url);
6009					this.load(conf.url);
6010				}
6011			}
6012		},
6013		events: function () {
6014			var self = this;
6015
6016			$(this.backbtn).off('click').on('click', function (e) {
6017				e.preventDefault();
6018				if (window.history.length > 1) {
6019					window.history.back();
6020				} else {
6021					self.prevURL();
6022				}
6023			});
6024
6025			$(this.continuebtn).off('click').on('click', function (e) {
6026				e.preventDefault();
6027
6028                // if ($(this).hasClass('members-only-no-go')) {
6029                //     return;
6030                // }
6031
6032				// Check if there is a form that should be submitted on the page
6033				// and prevent going to next step if form is not valid
6034				var $form = $(this).parents('form.validateStep:not(.suppress)'),
6035					shouldContinue = $form.length ? $form.validate().form() : true;
6036				self.nextURL(shouldContinue);
6037			});
6038
6039			// Add handler for sticky continue button
6040			$(this.stickyContinueBtnn).off('click').on('click', function (e) {
6041				e.preventDefault();
6042				var $form = $(this).parents('form.validateStep:not(.suppress)'),
6043					shouldContinue = $form.length ? $form.validate().form() : true;
6044				self.nextURL(shouldContinue);
6045			});
6046
6047			$(window).off('popstate').on('popstate', function(e) {
6048				self.prevURL();
6049			});
6050
6051			$(window).off('TatePushStatePageReplace').on('TatePushStatePageReplace', function(e, id, url) {
6052				$.each(self.config.steps, function (index, value) {
6053					if (value.slug == id) {
6054						value.url = url;
6055					}
6056				});
6057			});
6058
6059			$(window).off('TatePushStateNeedReload').on('TatePushStateNeedReload', function(e) {
6060				self.collectSteps();
6061				self.events();
6062			});
6063
6064			$(window).off('cardHolders').on('cardHolders', function(e) {
6065				self.continuebtn = '.go-continue';
6066				self.events();
6067			});
6068
6069			/**
6070			 * Hides back buttons
6071			 * @return void
6072			 */
6073			$(window).off('TatePushStateManagerHideBackButton').on('TatePushStateManagerHideBackButton', function(e) {
6074				self.hidePrev();
6075			});
6076
6077			/**
6078			 * Check whatever we need to show or hide back buttons
6079			 * @return void
6080			 */
6081			$(window).off('TatePushStateManagerBackButtonCheckState').on('TatePushStateManagerBackButtonCheckState', function(e) {
6082				var prevURL = self.getPrevURL();
6083				if (prevURL !== null && prevURL.disabled === true) {
6084					$(window).trigger('TatePushStateManagerHideBackButton');
6085				}
6086			});
6087		},
6088		/**
6089		 * Load a URL with ajax format
6090		 */
6091		load: function (url) {
6092			var self = this;
6093
6094			$(window).trigger('GTMVirtualPageView', [url]);
6095
6096			$.ajax(TateUtil.ajaxUrl(url)).done(function(data) {
6097				$('.' + self.config.contentClass).html(data);
6098				if (typeof TatePushStatePageManagerConfig !== "undefined") {
6099					$.extend(self.config, TatePushStatePageManagerConfig);
6100				}
6101
6102				if (typeof TatePushStatePageManagerConfig !== 'undefined' && self.config.redirectURL !== 'null') {
6103					window.location.href = self.config.redirectURL;
6104				} else {
6105					self.collectSteps();
6106					self.events();
6107					$(window).trigger('PushStatePageOnLoadComplete', [url, typeof self.config.meta !== "undefined" ? self.config.meta.title : null]);
6108				}
6109			});
6110		},
6111
6112		/**
6113		 *
6114		 */
6115		collectSteps: function () {
6116			var self = this;
6117			this.config.steps = [];
6118			$('.select-steps li[data-href]:not(.skip)').each(function(index, value) {
6119				var o = {
6120					slug: $(this).data('slug'),
6121					url: $(this).data('href'),
6122					disabled: typeof $(this).data('disabled') !== "undefined" ? $(this).data('disabled') : false,
6123					backURL: typeof $(this).data('back-url') !== "undefined" ? $(this).data('back-url') : false
6124				};
6125				$(this).off('click').on('click', 'a', function (e) {
6126					e.preventDefault();
6127					self.goTO($(this).parent().data('slug'));
6128				});
6129				if (o.slug === self.config.currentLocation) {
6130					self.config.currentIndex = index;
6131				}
6132				self.config.steps.push(o);
6133			});
6134		},
6135
6136		init: function (config) {
6137			var self = this;
6138
6139			// deactivate if config not found
6140			if (typeof config.currentLocation === "undefined") {
6141				return;
6142			}
6143			this.config = config;
6144			this.collectSteps();
6145			this.events();
6146		}
6147	};
6148
6149	return TatePushStatePageManager;
6150
6151});
6152define('components/TateCalendarManager',['jquery', 'components/TatePushStatePageManager', 'components/TateUtil'], function ($, TatePushStatePageManager, TateUtil) {
6153
6154	"use strict";
6155
6156    /**
6157     * Replace brief text
6158     * @param  {String} text replacement target
6159     * @return {String} replaced text
6160     */
6161    var replaceBrief = function (text) {
6162        return text.replace('(', '<span class="brief">').replace(')', '</span>');
6163    };
6164
6165	var TateCalendarManager = function () {
6166
6167		var self = this;
6168
6169		$(document).ready(function () {
6170			var config = typeof TateCalendarManagerConfig !== "undefined" ? TateCalendarManagerConfig : {};
6171			self.init(config);
6172		});
6173
6174		// Listen for push state page load complete (AJAX navigation)
6175		$(window).on('PushStatePageOnLoadComplete', function (e, inst) {
6176			var config = typeof TateCalendarManagerConfig !== "undefined" ? TateCalendarManagerConfig : {};
6177			self.init(config);
6178		});
6179	};
6180
6181	TateCalendarManager.prototype = {
6182		toString: function (date) {
6183			return moment(date).format('YYYYMMDD');
6184		},
6185
6186		toDate: function (date) {
6187			return date !== null && date !== "null" ? moment(date, 'YYYYMMDD').toDate() : null;
6188		},
6189
6190		getStartDate: function () {
6191			return this.RecordFilter.startdate;
6192		},
6193
6194		setStartDate: function (year, month, day) {
6195			var month = month + 1;
6196			var date = year + '' + (month < 10 ? "0" + month : month) + '' + day;
6197			this.RecordFilter.startdate = Math.max(this.toDate(date), this.config.today);
6198			this.RecordFilter.enddate = this.toDate(moment(this.RecordFilter.startdate).add(2, 'months'));
6199		},
6200
6201		getEndDate: function () {
6202			return this.RecordFilter.enddate;
6203		},
6204
6205		getToday: function () {
6206			return this.config.today;
6207		},
6208
6209		getOffset: function () {
6210			return this.config.offset;
6211		},
6212
6213		getCount: function () {
6214			if (this.config.isMobileScreen()) {
6215				return this.config.count / 2;
6216			}
6217			return this.config.count;
6218		},
6219
6220		/**
6221		 * Get category id
6222		 */
6223		getCGID: function () {
6224			return this.config.cgid;
6225		},
6226
6227		/**
6228		 * Get product id
6229		 */
6230		getPID: function () {
6231			return this.config.pid;
6232		},
6233
6234		/**
6235		 * Get variant id
6236		 */
6237		getVID: function () {
6238			return this.config.vid;
6239		},
6240
6241		getTimeSlotID: function () {
6242			return this.config.selected.timeSlotId;
6243		},
6244
6245		splitTime: function (time) {
6246			return time.split(':');
6247		},
6248
6249		/**
6250		 * Returns current selected variant
6251		 * stock qty
6252		 */
6253		getCurrentVariantStockQty: function () {
6254			var self = this,
6255				qty = 0;
6256			$.each(self.config.times, function (index, value) {
6257				if (value.timeslot === self.config.selected.time) {
6258					qty = value.stock;
6259					return false;
6260				}
6261			});
6262			if (qty === 0 && !!self.config.selected.vats) {
6263				qty = self.config.selected.vats;
6264			}
6265			return qty;
6266		},
6267
6268		/**
6269		 * Check whatever we need to show the
6270		 * selected tickets pannel
6271         * @param {String} location current location
6272		 */
6273		showSelectedTickets: function (location) {
6274			return ['Donations', 'DonationsGiftAid'].indexOf(location) !== -1;
6275		},
6276
6277		/**
6278		 * Single validation function
6279		 * validation routing by its param
6280		 */
6281		validate: function (what) {
6282			var $time = $('.card-footer-selected-time'),
6283				$date = $('.card-footer-selected-date'),
6284				$tickets = $('.card-footer-selected-tickets');
6285
6286			if (!this.showSelectedTickets(this.config.currentLocation)) {
6287				$tickets.hide();
6288			}
6289
6290			if (what === 'date') {
6291				if (this.config.selected.date === "null") {
6292					this.TatePushStatePageManager.hideNext();
6293					$('.display-selected-date', $date).text('');
6294					$('.display-selected-date-date').text('');
6295					$('.display-selected-date-time').text('');
6296				} else {
6297					$('.display-selected-date', $date).text(moment(this.toDate(this.config.selected.date)).format("ddd DD MMM YYYY"));
6298					$('.display-selected-date-date').text(moment(this.toDate(this.config.selected.date)).format("ddd DD MMMM YYYY"));
6299					this.TatePushStatePageManager.showNext();
6300				}
6301			}
6302
6303			else if (what === 'time') {
6304				if (this.config.selected.time === "null") {
6305					this.TatePushStatePageManager.hideNext();
6306				} else {
6307					this.TatePushStatePageManager.showNext();
6308				}
6309				if (this.config.selected.time !== "null") {
6310					var selectedTime = this.splitTime(this.config.selected.time);
6311
6312					if (selectedTime[0] === Resources.ALL_DAY) {
6313							$('.display-selected-time', $time).text(selectedTime[0]);
6314							$('.display-selected-date-time').text(selectedTime[0]);
6315					} else {
6316							$('.display-selected-time', $time).text(moment(new Date(2017, 11, 8, selectedTime[0], selectedTime[1], 0)).format("H:mm"));
6317							$('.display-selected-date-time').text(" " + moment(new Date(2017, 11, 8, selectedTime[0], selectedTime[1], 0)).format("H:mm").toLowerCase());
6318					}
6319
6320
6321					$time.slideDown();
6322				} else {
6323					$time.slideUp(function () {
6324						$('.display-selected-time', $time).text('');
6325						$('.display-selected-date-time').text('');
6326					});
6327				}
6328			}
6329
6330			else if (what === 'tickets') {
6331
6332                var self = this, reachedSlotLimit = false, slotLimit;
6333
6334                if ('tickets' in this.config.selected && Object.keys(this.config.selected.tickets).length > 0) {
6335
6336                    $.each(this.config.selected.tickets, function (index, value) {
6337
6338                        slotLimit = self.config.tickets[index] && self.config.tickets[index].ticketPurchaseLimit;
6339
6340                        if (slotLimit && value > slotLimit) {
6341                            reachedSlotLimit = true;
6342                            $('input[data-id="' + index + '"]').addClass('item--qty__border--red');
6343                        } else {
6344                            $('input[data-id="' + index + '"]').removeClass('item--qty__border--red');
6345                        }
6346                    });
6347                }
6348
6349                if (reachedSlotLimit) {
6350                    $('.privileged-ticket-purchase-limit-warn-wrap').show();
6351                } else {
6352                    $('.privileged-ticket-purchase-limit-warn-wrap').hide();
6353                }
6354
6355				var total = this.getSelectedTicketsTotal();
6356
6357				// Set the ticket purchase limits
6358				$('.ticket-purchase-limit-warn-wrap span.ticket-purchase-limit').text(this.config.ticketPurchaseLimit);
6359				$('.collective-ticket-purchase-limit-warn-wrap span.ticket-purchase-limit').text(this.config.collectivePurchaseLimit);
6360
6361				if (total > 0) {
6362					this.renderSelectedTickets();
6363					if (this.showSelectedTickets(this.config.currentLocation)) {
6364						$tickets.slideDown();
6365					}
6366				} else {
6367					$tickets.slideUp(function () {
6368						$('.display-selected-tickets', $tickets).text('');
6369					});
6370				}
6371				if (total === 0 || reachedSlotLimit) {
6372					$('.go-save-and-continue').addClass('go-save-and-continue-hidden');
6373					$('.ticket-purchase-limit-warn-wrap').hide();
6374					$('.go-save-and-continue-sticky').addClass('go-save-and-continue-inactive-sticky');
6375					$('.sticky-bottom-card-select').addClass('inactive');
6376
6377				} else if ((this.config.ticketPurchaseLimit && total > this.config.ticketPurchaseLimit) || total > this.getCurrentVariantStockQty() || (this.config.customerIsCollectiveMember && this.getSelectedCollectiveTicketsTotal() > this.config.collectivePurchaseLimit)) {
6378
6379					if (this.config.customerIsCollectiveMember && this.getSelectedCollectiveTicketsTotal() > this.config.collectivePurchaseLimit) {
6380						$('.collective-ticket-purchase-limit-warn-wrap').show();
6381					}
6382
6383					if (this.config.ticketPurchaseLimit && total > this.config.ticketPurchaseLimit) {
6384						$('.ticket-purchase-limit-warn-wrap').show();
6385						$('.ticket-purchase-limit-stock-warn-wrap').hide();
6386						$('.go-save-and-continue-sticky').addClass('go-save-and-continue-inactive-sticky');
6387						$('.sticky-bottom-card-select').addClass('inactive');
6388					} else {
6389						$('.ticket-purchase-limit-warn-wrap').hide();
6390						if (total > this.getCurrentVariantStockQty()) {
6391							$('.ticket-purchase-limit-stock-warn-wrap').show();
6392							$('.go-save-and-continue-sticky').addClass('go-save-and-continue-inactive-sticky');
6393							$('.sticky-bottom-card-select').addClass('inactive');
6394						} else {
6395							$('.ticket-purchase-limit-stock-warn-wrap').hide();
6396							$('.go-save-and-continue-sticky').removeClass('go-save-and-continue-inactive-sticky');
6397    						$('.sticky-bottom-card-select').removeClass('inactive');
6398						}
6399					}
6400					$('.go-save-and-continue').addClass('go-save-and-continue-hidden');
6401					$('.mem-ticket-purchase-limit-warn-wrap').hide();
6402
6403				} else if (this.config.memberPurchaseLimit && ((this.config.onlyMemberPrivilege && total > this.config.memberPurchaseLimit) ||
6404						   this.getSelectedMemTicketsTotal() > this.config.memberPurchaseLimit)) {
6405					$('.mem-ticket-purchase-limit-warn-wrap').show();
6406					$('.ticket-purchase-limit-warn-wrap').hide();
6407					$('.ticket-purchase-limit-stock-warn-wrap').hide();
6408					$('.go-save-and-continue').addClass('go-save-and-continue-hidden');
6409				} else {
6410					var $buttonToBasket = $('.go-save-and-continue.to-basket');
6411					var	$buttonToDonation = $('.go-save-and-continue.to-donation');
6412					var $stickyButton = $('.go-save-and-continue-sticky');
6413					var $stickyBottomCard = $('.sticky-bottom-card-select');
6414					if (this.checkForGiftAidExemptTickets() || this.config.hasExemptTicketsInBasket) {
6415    					$buttonToBasket.removeClass('go-save-and-continue-hidden');
6416    					$buttonToDonation.addClass('go-save-and-continue-hidden');
6417    					$stickyButton.removeClass('go-save-and-continue-inactive-sticky go-save-and-continue-hidden');
6418    					$stickyBottomCard.removeClass('inactive');
6419					} else {
6420    					$buttonToDonation.removeClass('go-save-and-continue-hidden');
6421    					$buttonToBasket.addClass('go-save-and-continue-hidden');
6422    					$stickyButton.removeClass('go-save-and-continue-inactive-sticky go-save-and-continue-hidden');
6423    					$stickyBottomCard.removeClass('inactive');
6424					}
6425
6426					$('.ticket-purchase-limit-warn-wrap').hide();
6427					$('.collective-ticket-purchase-limit-warn-wrap').hide();
6428					$('.ticket-purchase-limit-stock-warn-wrap').hide();
6429					$('.mem-ticket-purchase-limit-warn-wrap').hide();
6430				}
6431			}
6432
6433			else if (what === 'giftAid') {
6434				if (this.config.selected.giftAid !== "null") {
6435					$('.go-save-and-continue').removeClass('go-save-and-continue-hidden');
6436					$('.go-save-and-continue-donation').removeClass('go-save-and-continue-donation-hidden');
6437					$('.go-save-and-continue-sticky').removeClass('go-save-and-continue-inactive-sticky');
6438					$('.sticky-bottom-card-select').removeClass('inactive');
6439					this.TatePushStatePageManager.hideNext();
6440				} else {
6441					$('.go-save-and-continue').addClass('go-save-and-continue-hidden');
6442					$('.go-save-and-continue-donation').addClass('go-save-and-continue-donation-hidden');
6443					$('.go-save-and-continue-sticky').addClass('go-save-and-continue-inactive-sticky');
6444					$('.sticky-bottom-card-select').addClass('inactive');
6445					this.TatePushStatePageManager.hideNext();
6446				}
6447			}
6448
6449			else if (what === 'donation') {
6450
6451			}
6452		},
6453
6454		getPIDFromDate: function (date) {
6455			var pid = null;
6456			$.each(this.config.products, function (index, product) {
6457				if (this.toString(moment(date)) === product.date) {
6458					pid = product.id;
6459					return false;
6460				}
6461			}.bind(this));
6462			return pid;
6463		},
6464
6465		getViewPort: function () {
6466			return this.config.viewport;
6467		},
6468
6469		/**
6470		 * Flag so that resize doesn't trigger
6471		 * the redraw if in same view port
6472		 */
6473		setViewPort: function () {
6474			this.config.viewport = this.config.isMobileScreen() ? "mobile" : "desktop";
6475		},
6476
6477		setSelectDate: function (date) {
6478			if (this.config.selected.date !== this.toString(date)) {
6479				this.config.selected.time = "null";
6480				this.config.selected.tickets = {};
6481			}
6482			this.config.selected.date = this.toString(date);
6483			this.config.selected.month = moment(date).format('MM');
6484			var pid = this.getPIDFromDate(date);
6485			if (pid) {
6486				this.config.pid = pid;
6487				$(document).trigger(
6488					'TatePushStatePageReplace',
6489					[
6490						'SelectEntryTime',
6491						TateUtil.appendParamsToUrl(
6492							this.config.urls.selectEntryTime,
6493							{'prid': pid}
6494						)
6495					]
6496				);
6497				this.hasAdvanceTicket(pid);
6498
6499                // set new day sold out href
6500                var url = TateUtil.removeParamFromURL($('.day-sold-out-trigger').data('ajax-url'), 'variant');
6501                    url = TateUtil.appendParamsToUrl(url, {'variant':pid});
6502
6503                var exhibition = TateUtil.removeParamFromURL($('.day-sold-out-exhibition-trigger').data('ajax-url'), 'variant');
6504                    exhibition = TateUtil.appendParamsToUrl(exhibition, {'variant':pid});
6505
6506                // set new url
6507                $('.day-sold-out-trigger').attr('data-ajax-url', url);
6508                $('.day-sold-out-exhibition-trigger').attr('data-ajax-url', exhibition);
6509
6510                // re-ini modal
6511                $(window).trigger("CalendarOnModalAppear");
6512
6513                //this.loadDaySoldOut(pid);
6514            }
6515			this.validate('time');
6516			this.validate('date');
6517			this.validate('tickets');
6518			this.saveToSession();
6519		},
6520
6521		setSelectedTime: function (id, time) {
6522			var vid = this.getVIDFromID(id);
6523			$(document).trigger(
6524				'TatePushStatePageReplace',
6525				[
6526					'SelectTickets',
6527					TateUtil.appendParamsToUrl(
6528						this.config.urls.selectTickets,
6529						{'variant_pr_id': vid}
6530					)
6531				]
6532			);
6533			if (this.config.selected.time !== time) {
6534				this.config.selected.tickets = {};
6535				this.renderSelectedTickets();
6536			}
6537			this.config.vid = vid;
6538			this.config.selected.timeSlotId = id;
6539			this.config.selected.time = time;
6540			this.validate('time');
6541			this.validate('tickets');
6542			this.saveToSession();
6543		},
6544
6545		selectTime: function (id, time) {
6546			$('div.timeslot--selected').removeClass('timeslot--selected');
6547			$('div[data-id=' + id + ']').addClass('timeslot--selected');
6548			this.setSelectedTime(id, time);
6549		},
6550
6551		getVIDFromID: function (id) {
6552			var vid = null;
6553			$.each(this.config.variants, function (index, value) {
6554				if (value.time === id) {
6555					vid = value.id;
6556					return false;
6557				}
6558			});
6559			return vid;
6560		},
6561
6562        /**
6563         * Check whatever today date is available
6564         * @return {Boolean}
6565         */
6566        isTodayEnabled: function () {
6567            var self = this;
6568            var today = self.getToday(),
6569                anabled = true;
6570            $.each(self.config.products, function (index, product) {
6571                var date = self.toDate(product.date);
6572                if (date.valueOf() == today.valueOf()) {
6573                    anabled = product.show;
6574                    return false;
6575                }
6576            });
6577            return anabled;
6578        },
6579
6580        /**
6581         * Deactivate today calendar cell
6582         */
6583        deactivateTodayCell: function () {
6584            $('.dw-cal-today').removeClass('dw-cal-day-marked dw-cal-e dwb-e dwb-nhl');
6585            $('.dw-cal-today').css('cursor', 'default');
6586            $('.dw-cal-today').on('click', function (e) {
6587                e.stopPropagation();
6588            });
6589        },
6590
6591        setUnavailableTabIndex: function () {
6592            $('.dw-cal-day[aria-disabled="true"], .dw-cal-day[aria-hidden="true"]').attr('tabindex', '-1');
6593        },
6594
6595        setSoldOutDays: function() {
6596
6597            var i, soldOutDate, $dayCell;
6598            var soldOutDates = this.config.soldOutDates;
6599
6600            if (!soldOutDates) {
6601                return;
6602            }
6603
6604            for (i = 0; i < soldOutDates.length; ++i) {
6605
6606                soldOutDate = soldOutDates[i];
6607
6608                $dayCell = $('.dw-cal-day[data-full="' + soldOutDate + '"]');
6609                $dayCell.addClass('dw-cal-sold-out');
6610                $dayCell.on('click touchstart', function (e) {
6611                    e.stopPropagation();
6612                });
6613            }
6614        },
6615
6616        openTicketsModal: function() {
6617            var self = this;
6618
6619			var modalMaxHeight = '';
6620			var headerHeight=0;
6621			if ($(window).width() < 720) {
6622				$(window).height()-66;
6623				modalMaxHeight = $(window).height()-66;
6624				headerHeight=66;
6625			}
6626			if ($(window).width() >= 720 && $(window).width() <= 1024 ) {
6627				modalMaxHeight = $(window).height()-60;
6628				headerHeight=60;
6629			}
6630			if ($(window).width() > 1024) {
6631				modalMaxHeight = $(window).height()-80;
6632				headerHeight=80;
6633			}
6634			modalMaxHeight = modalMaxHeight.toString()+'px';
6635			var desiredHeight='calc(100%-' +headerHeight +'px)';
6636			const modalToUse = !self.config.isBookingNotRequiredEvent ? self.config.urls.memberOnlyEventModal : self.config.urls.memberOrSupporterModal;
6637
6638			$.colorbox({
6639                href: TateUtil.appendParamsToUrl(modalToUse, {'cgid': self.config.cgid}),
6640                className: 'member-or-supporter-modal',
6641                width: '100%',
6642                height: '100%',
6643                maxWidth: desiredHeight,
6644				opacity:0,
6645				top:headerHeight,
6646                maxHeight: modalMaxHeight,
6647                closeButton: false,
6648                overlayClose: false,
6649                onComplete: function() {
6650
6651                    $('.member-or-supporter-continue-button').on('click', function(e) {
6652                        e.preventDefault();
6653                        $.colorbox.close();
6654                    });
6655
6656                    $(document).tooltip({
6657                        items: '.tooltip',
6658                        content: function () {
6659                            return $(this).find('.tooltip-content').html();
6660                        }
6661                    });
6662
6663                    $('.member-or-supporter-email').on('click', function() {
6664                        window.open('mailto:' + $(this).text());
6665                    });
6666                }
6667            });
6668
6669            $('body').css('overflow', 'hidden');
6670            $('body').css('position', 'fixed');
6671            $('body').css('width', '100%');
6672
6673            $(document).bind('cbox_closed', function(){
6674                $('body').css('overflow', 'auto');
6675                $('body').css('position', 'static');
6676            });
6677        },
6678
6679        openExhibitionLunchModal: function() {
6680            var $banner = $('.tickets-admission-lunch-banner').first();
6681            var $lunchModal = $banner.find('.tickets-lunch-modal').first();
6682            var $lunchModalWrapper = $lunchModal.parent('.tickets-exhibition-lunch-modal-wrapper');
6683            var lunchModalWrapperId = 'tickets-exhibition-lunch-modal-inline';
6684
6685            if (!$lunchModal.length) {
6686                $lunchModal = $banner.next('.tickets-lunch-modal');
6687                $lunchModalWrapper = $lunchModal.parent('.tickets-exhibition-lunch-modal-wrapper');
6688            }
6689
6690            if (!$lunchModal.length) {
6691                $lunchModal = $banner.next('.tickets-exhibition-lunch-modal-wrapper').find('.tickets-lunch-modal');
6692                $lunchModalWrapper = $lunchModal.parent('.tickets-exhibition-lunch-modal-wrapper');
6693            }
6694
6695            if (!$lunchModal.length) {
6696                return;
6697            }
6698
6699            if (!$lunchModalWrapper.length) {
6700                $lunchModal.wrap('<div class="tickets-exhibition-lunch-modal-wrapper"></div>');
6701                $lunchModalWrapper = $lunchModal.parent();
6702            }
6703
6704            if (!$lunchModalWrapper.attr('id')) {
6705                $lunchModalWrapper.attr('id', lunchModalWrapperId);
6706            }
6707
6708            $lunchModal.removeClass('d-none');
6709
6710            $.colorbox({
6711                inline: true,
6712                href: '#' + $lunchModalWrapper.attr('id'),
6713                className: 'tickets-lunch-modal-popup',
6714                width: '95%',
6715                maxWidth: '960px',
6716                maxHeight: '90%',
6717                closeButton: true,
6718                onComplete: function() {
6719                    $.fn.colorbox.resize();
6720                },
6721                onCleanup: function() {
6722                    $lunchModal.addClass('d-none');
6723                }
6724            });
6725        },
6726
6727		renderSelectDate: function () {
6728			var self = this;
6729			self.adjustStickyButton();
6730
6731			$(window).trigger('CalendarOnModalAppear', [self]);
6732
6733			// Set viewPort flag (for responsive redraw)
6734            self.setViewPort();
6735
6736
6737            if (!self.config.isLoggedIn && !self.config.isMemberEventsModalOpened) {
6738                self.openTicketsModal();
6739				self.config.isMemberEventsModalOpened = true;
6740            }
6741
6742			// observers for calendar events
6743			$(window).off('CalendarOnDayChange').on("CalendarOnDayChange", function (event, day, inst) {
6744                self.setSelectDate(day.date);
6745			});
6746
6747			$(self.config.renderElement).mobiscroll().calendar({
6748				currentDate: self.getToday(),
6749				sessionTimeOut: '+20m',
6750				display: 'inline',
6751				layout: 'liquid',
6752				navigation: 'month',
6753				markedDisplay: 'bottom',
6754				months: self.ConfigTranslator.getMonths(),
6755				calendarHeight: 300,
6756				minDate: self.ConfigTranslator.getMinDate(),
6757				maxDate: self.ConfigTranslator.getMaxDate(),
6758				markedText: true,
6759				weekDays: 'short',
6760				highlightNow: false,
6761				marked: self.ConfigTranslator.getMarkedDays(),
6762				swipe: self.ConfigTranslator.getSwipe(),
6763				defaultValue: self.ConfigTranslator.getDefaultValue(),
6764				mousewheel: false,
6765                firstDay: 1,
6766				onDayChange: function (day, inst) {
6767
6768                    $(window).trigger('CalendarOnDayChange', [day, inst]);
6769				},
6770				onDestroy: function (inst) {},
6771				onEventSelect: function (e, event, d, inst) {
6772					$(window).trigger('CalendarOnEventSelect', [e, event, d, inst]);
6773				},
6774				onInit: function (inst) {
6775                    $(window).trigger('CalendarOnInit', [self, inst]);
6776                },
6777				onMonthLoaded: function (year, month, inst) {
6778
6779                    // check today calendar state
6780                    // deactivate if it is not available
6781                    if (!self.isTodayEnabled()) {
6782                        self.deactivateTodayCell();
6783                    }
6784                    self.setUnavailableTabIndex();
6785                    self.setSoldOutDays();
6786
6787                    $(window).trigger('CalendarOnMonthLoaded', [year, month, inst]);
6788				},
6789				onMonthChange: function (year, month, inst) {
6790					$(window).trigger('CalendarOnMonthChange', [year, month, inst]);
6791				}
6792			});
6793
6794			$(window).resize(function() {
6795				if (self.config.isMobileScreen() === true && self.getViewPort() !== "mobile") {
6796					self.setViewPort(); //Reset viewport flag
6797					$(self.config.renderElement).mobiscroll('option', {
6798						months: self.ConfigTranslator.getMonths()
6799					});
6800				}
6801
6802				if (self.config.isMobileScreen() === false && self.getViewPort() !== "desktop") {
6803					self.setViewPort(); //Reset viewport flag
6804					$(self.config.renderElement).mobiscroll('option', {
6805						months: self.ConfigTranslator.getMonths()
6806					});
6807				}
6808
6809				if (!self.config.isLoggedIn && !self.config.isMemberEventsModalOpened) {
6810				        if ($("#colorbox.member-or-supporter-modal").css("display") == "block") {
6811				            self.openTicketsModal();
6812				        }
6813				}
6814
6815				setTimeout(function() {
6816					$('.member-or-supporter-modal').colorbox.resize({
6817						height: window.innerHeight,
6818						width: window.innerWidth
6819					});
6820				}, 100);
6821            });
6822
6823		},
6824
6825		/**
6826		 * Checks available Advanced Tickets
6827		 */
6828		hasAdvanceTicket: function (pid) {
6829			if (typeof pid === "undefined") {
6830				return;
6831			}
6832			$.ajax(TateUtil.appendParamsToUrl(this.config.urls.hasAdvanceTicket, {'pid': pid, 'format': 'ajax'})).done(function (data) {
6833				if (!data.success) {
6834					$('#advanceTicket').hide();
6835				} else {
6836					$('#advanceTicket').slideDown();
6837				}
6838			});
6839		},
6840
6841        /**
6842         * Day sold out check
6843		 * @param {string} pid product id
6844		 */
6845		loadDaySoldOut: function (pid) {
6846			var self = this;
6847			if (typeof pid === "undefined") {
6848				return;
6849			}
6850			$.ajax(TateUtil.appendParamsToUrl(this.config.urls.daySoldOut, {'pid': pid, 'format': 'ajax'})).done(function (data) {
6851				if (typeof data.daySoldOut !== undefined && data.daySoldOut === true) {
6852					self.config.selected.date = "null";
6853					self.validate('date');
6854
6855					if (data.isExhibition) {
6856						$('.day-sold-out-exhibition-trigger').trigger('click');
6857					} else {
6858						$('.day-sold-out-trigger').trigger('click');
6859					}
6860				}
6861			});
6862		},
6863
6864		/**
6865		 * Load function
6866		 */
6867		loadDates: function (year, month, inst) {
6868			var self = this;
6869			if (!inst.refreshing) {
6870				self.setStartDate(year, month, '01');
6871
6872				var url = TateUtil.appendParamsToUrl(self.config.urls.categoryJSON, {
6873					startDate: self.toString(self.getStartDate()),
6874					endDate: self.toString(self.getEndDate()),
6875					offset: self.getOffset(),
6876					count: self.getCount(),
6877					cgid: self.getCGID(),
6878					format: 'ajax'
6879				});
6880				$.ajax({
6881					url: url,
6882					dataType: 'json',
6883					beforeSend: function () {
6884						inst.disable();
6885					}
6886				}).done(function (data) {
6887
6888                    if (data.tessituraDown) {
6889                        self.loadServiceDown();
6890                    }
6891
6892					self.config.products = data.products;
6893                    self.config.productsdates = [];
6894                    self.config.soldOutDates = [];
6895
6896					$.each(data.products, function (index, value) {
6897						self.config.productsdates.push(value.date);
6898					});
6899
6900					inst.settings.marked = [];
6901
6902					inst.refreshing = true;
6903					inst.settings.marked = self.ConfigTranslator.getMarkedDays();
6904					inst.settings.invalid = self.ConfigTranslator.getInvalidDays();
6905
6906					inst.redraw();
6907					inst.refreshing = false;
6908
6909					inst.enable();
6910
6911					self.validate('date');
6912
6913				});
6914			}
6915		},
6916
6917        loadServiceDown: function() {
6918
6919            var self = this;
6920
6921            $('.ticket-content-ajax').load(self.config.urls.serviceDown);
6922        },
6923
6924		/**
6925		 * Create a days array from a date period
6926		 * used to determine whatever we have a valid or invalid day
6927		 * if the day exist in the period and not found in ticket list
6928		 * the day is market as invalid
6929		 */
6930		createDaysFromPeriod: function (from, to) {
6931			var days = [],
6932				currentDay = from,
6933				i = 0;
6934			while (currentDay.valueOf() <= to.valueOf()) {
6935				days.push(this.toString(currentDay));
6936				currentDay = moment(currentDay).add(1, 'days');
6937				i++;
6938			}
6939			return days;
6940		},
6941
6942		/**
6943		 * Gets the total selected tickets
6944		 */
6945		getSelectedTicketsTotal: function () {
6946			var total = 0;
6947			if ('tickets' in this.config.selected && Object.keys(this.config.selected.tickets).length > 0) {
6948				$.each(this.config.selected.tickets, function (index, value) {
6949					total += parseInt(value);
6950				});
6951			}
6952			return total;
6953		},
6954
6955		/**
6956		 * Gets the total selected mem tickets
6957		 */
6958		getSelectedMemTicketsTotal: function () {
6959			var self = this, total = 0;
6960
6961			if (Object.keys(this.config.selected.tickets).length > 0) {
6962				$.each(this.config.selected.tickets, function (index, value) {
6963
6964					if (self.config.memOptionsIds.indexOf(index) != -1) {
6965						total += parseInt(value);
6966					}
6967				});
6968			}
6969			return total;
6970		},
6971
6972		/**
6973		 * Gets the total selected collective tickets
6974		 */
6975		getSelectedCollectiveTicketsTotal: function () {
6976			var self = this, total = 0, collectiveOptionIDArr = Resources.COLLECTIVE_OPTION_ID.split(',');
6977
6978			if (Object.keys(this.config.selected.tickets).length > 0) {
6979				$.each(this.config.selected.tickets, function (key, value) {
6980
6981					if (collectiveOptionIDArr.indexOf(key) > -1) {
6982						total += parseInt(value);
6983					}
6984
6985				});
6986			}
6987			return total;
6988		},
6989
6990		// check what need to be checked
6991		locationChange: function (flowStarted) {
6992			var self = this,
6993				cols = this.config.isMobileScreen() ? 1 : 2,
6994				$date = $('.card-footer-selected-date'),
6995				$promo = $('.membership-promo'),
6996				$membershipUpsell = $('.membership-upsell-overlay'),
6997				$heading = $('.select-a-date'),
6998				$time = $('.card-footer-selected-time'),
6999				$continueBtn = $('.start-flow'),
7000				$tickets = $('.card-footer-selected-tickets');
7001
7002			if (TateCalendarManagerConfig.pid !== "null") {
7003				this.config.selected.pid = TateCalendarManagerConfig.pid;
7004			}
7005			if (TateCalendarManagerConfig.selected.date !== "null") {
7006				this.config.selected.date = TateCalendarManagerConfig.selected.date;
7007			}
7008			if (TateCalendarManagerConfig.selected.time !== "null") {
7009				this.config.selected.time = TateCalendarManagerConfig.selected.time;
7010			}
7011			if (TateCalendarManagerConfig.selected.vid !== "null") {
7012				this.config.selected.vid = TateCalendarManagerConfig.selected.vid;
7013			}
7014			if (TateCalendarManagerConfig.selected.timeSlotId !== "null") {
7015				this.config.selected.timeSlotId = TateCalendarManagerConfig.selected.timeSlotId;
7016			}
7017			if (TateCalendarManagerConfig.selected.vats !== "null") {
7018				this.config.selected.vats = TateCalendarManagerConfig.selected.vats;
7019			}
7020			if (TateCalendarManagerConfig.selected.giftAid !== "null") {
7021				this.config.selected.giftAid = TateCalendarManagerConfig.selected.giftAid;
7022			}
7023			/** sets selectDateDisabled from configuration */
7024			if (typeof TateCalendarManagerConfig.selectDateDisabled !== "undefined") {
7025				this.config.selectDateDisabled = TateCalendarManagerConfig.selectDateDisabled;
7026			}
7027			/** sets selectEntryTimeDisabled from configuration */
7028			if (typeof TateCalendarManagerConfig.selectEntryTimeDisabled !== "undefined") {
7029				this.config.selectEntryTimeDisabled = TateCalendarManagerConfig.selectEntryTimeDisabled;
7030			}
7031            /** sets isTicketDonationEnabled from configuration */
7032            if (typeof TateCalendarManagerConfig.isTicketDonationEnabled !== "undefined") {
7033                this.config.isTicketDonationEnabled = TateCalendarManagerConfig.isTicketDonationEnabled;
7034            }
7035
7036			// add event observers
7037			$('.edit-selected-date').off('click').on('click', function (e) {
7038				e.preventDefault();
7039				self.TatePushStatePageManager.goTO('SelectDate');
7040			});
7041
7042			$('.edit-selected-time').off('click').on('click', function (e) {
7043				e.preventDefault();
7044				self.TatePushStatePageManager.goTO('SelectEntryTime');
7045			});
7046
7047			$('.edit-selected-tickets').off('click').on('click', function (e) {
7048				e.preventDefault();
7049				self.TatePushStatePageManager.goTO('SelectTickets');
7050			});
7051
7052			if ($continueBtn.length > 0) {
7053				$continueBtn.off('click').on('click', function(e) {
7054					e.preventDefault();
7055					$membershipUpsell.hide();
7056					self.config.hideCalendar = false;
7057					self.config.selected.hideCalendar = false;
7058					self.locationChange();
7059				});
7060			}
7061
7062			// bind close event
7063			$(document).off('cbox_complete').on('cbox_complete', function () {
7064				$('a.choose-another-time, a.choose-another-date').off('click').on('click', function (e) {
7065					e.preventDefault();
7066					$.colorbox.close();
7067				});
7068			});
7069
7070			this.validate('date');
7071            this.validate('time');
7072            if (!!self.config.times && self.config.times.length) {
7073                this.validate('tickets');
7074            } else {
7075                self.setTimes(function () {
7076                    self.validate('tickets');
7077                });
7078            }
7079			this.validate('giftAid');
7080
7081			if (this.config.selected.date !== "null") {
7082				$date.slideDown();
7083			} else {
7084				$date.slideUp();
7085			}
7086
7087			// show selected time
7088			if (this.config.selected.time !== "null") {
7089				$time.slideDown();
7090			} else {
7091				$time.slideUp();
7092			}
7093
7094			/**
7095			 * show selected tickets if qty > 0 and is it not disabled
7096			 */
7097			if (this.getSelectedTicketsTotal() > 0 &&
7098					this.showSelectedTickets(this.config.currentLocation)) {
7099				$tickets.slideDown();
7100			} else {
7101				$tickets.hide();
7102			}
7103
7104			/**
7105			 * show selected date if it is not on Select Date and is not diabled
7106			 */
7107			if (['SelectDate'].indexOf(this.config.currentLocation) === -1 &&
7108					this.config.selectDateDisabled === false) {
7109				$('.edit-selected-date-wrap', $date).slideDown();
7110			} else {
7111				$('.edit-selected-date-wrap', $date).slideUp();
7112			}
7113
7114			if (['SelectDate', 'SelectEntryTime'].indexOf(this.config.currentLocation) === -1 &&
7115					this.config.selectEntryTimeDisabled === false) {
7116				$('.edit-selected-time-wrap', $time).slideDown();
7117			} else {
7118				$('.edit-selected-time-wrap', $time).slideUp();
7119			}
7120
7121			// selected tickets
7122			if (this.showSelectedTickets(this.config.currentLocation)) {
7123				$('.edit-selected-tickets-wrap', $tickets).slideDown();
7124			} else {
7125				$('.edit-selected-tickets-wrap', $tickets).hide();
7126			}
7127
7128			switch (this.config.currentLocation) {
7129				case 'SelectDate':
7130					if (!this.config.hideCalendar || !self.config.selected.hideCalendar || !$continueBtn.length > 0) {
7131						$heading.show();
7132						this.renderSelectDate();
7133						$membershipUpsell.hide();
7134					}
7135					$(".card-group").addClass("SelectDate");
7136				break;
7137
7138				case 'SelectEntryTime':
7139					this.renderSelectEntryTime();
7140					$(".card-group").addClass("SelectEntryTime");
7141				break;
7142
7143				case 'SelectTickets':
7144					this.renderSelectTickets();
7145					$(".card-group").addClass("SelectTickets");
7146				break;
7147
7148				case 'Donations':
7149					this.setGiftAid("null");
7150
7151					this.renderDonations();
7152				break;
7153
7154				case 'DonationsGiftAid':
7155					this.renderDonationsGiftAid();
7156				break;
7157			}
7158
7159			/**
7160			 * Trigger back button visibility check
7161			 */
7162			$(document).trigger('TatePushStateManagerBackButtonCheckState');
7163
7164            $(window).scrollTop(0);
7165		},
7166
7167		getSelectedTicketQty: function (id) {
7168			return typeof this.config.selected.tickets !== "undefined" && typeof this.config.selected.tickets[id] !== "undefined" ? this.config.selected.tickets[id] : 0;
7169		},
7170
7171		renderSelectEntryTime: function () {
7172			var self = this,
7173				cols = this.config.isMobileScreen() ? 1 : 2;
7174			self.adjustStickyButton();
7175
7176			$(window).trigger('CalendarOnModalAppear', [self]);
7177
7178			if (!self.config.isLoggedIn && !self.config.isMemberEventsModalOpened) {
7179				self.openTicketsModal();
7180				self.config.isMemberEventsModalOpened = true;
7181			}
7182			// load times
7183			$.ajax({
7184				url: TateUtil.appendParamsToUrl(this.config.urls.selectTime, {
7185					pid: this.getPID(),
7186					format: 'ajax'
7187				})
7188			}).done(function(data) {
7189
7190                if (data.tessituraDown) {
7191                    self.loadServiceDown();
7192                    return;
7193                }
7194
7195                if (data.timeslots.length == 0 && data.expiredMembership) {
7196                    self.openMembershipRenewModal(2);
7197                }
7198
7199                var hasSpecialTimeslots = data.hasSpecialTimeslots;
7200
7201				self.config.times = data.timeslots;
7202				self.config.variants = data.variants;
7203
7204				var times = [];
7205
7206				var $html = '<div class="grid grid-outer">';
7207					if (cols === 2) {
7208						$html += '<div class="grid__inner grid-col--50">left</div>';
7209						$html += '<div class="grid__inner grid-col--50">right</div>';
7210					} else {
7211						$html += '<div class="grid__inner">one</div>';
7212					}
7213					$html += '</div>';
7214
7215				var variantsAvailability = {};
7216
7217				for (var variable in data.variants) {
7218					var currentVariant = data.variants[variable];
7219					variantsAvailability[currentVariant.time] = currentVariant.isAvailable;
7220				}
7221
7222				// render times
7223				$.each(self.config.times, function (index, value) {
7224					var cls = value.timeslot === self.config.selected.time && value.active ? 'timeslot--selected' : '';
7225					if ((index + 1) === self.config.times.length) {
7226						cls += ' last-child';
7227                    }
7228
7229                    if (hasSpecialTimeslots) {
7230                        cls += ' bigger-timeslot';
7231                    }
7232
7233                    var lunchMessage = '';
7234                    var lunchCls = '';
7235
7236                    if (value.lunchMessage) {
7237                        lunchMessage = value.lunchMessage + '<br>';
7238                        lunchCls = ' plus-lunch-msg';
7239                    }
7240
7241                    if (variantsAvailability[value.vid] && value.active) {
7242                        value.html = '<div role="button" tabindex="0" data-action="selectTime" data-id="' + value.vid + '" data-time="' + value.timeslot + '" class="timeslot ' + cls + '">';
7243                    } else {
7244                        value.html = '<div class="timeslot timeslot--disabled ' + cls + '">';
7245                    }
7246                    if (value.passholdersOnly) {
7247
7248                        if (value.stockMessage) {
7249                            value.html += '<p class="passholders-only-msg passholders-only-plus-stock' + lunchCls + '"> ' + lunchMessage + value.passholdersOnlyMsg + '<br>' + value.stockMessage + '</p>';
7250                        } else {
7251                            value.html += '<p class="passholders-only-msg' + lunchCls + '"> ' + lunchMessage + value.passholdersOnlyMsg + '</p>';
7252                        }
7253                    } else if (value.stockMessage) {
7254                        value.html += '<p class="stock--msg stock--msg__' + value.stockMessageType + lunchCls + '"> ' + lunchMessage + value.stockMessage + '</p>';
7255                    } else if (lunchMessage) {
7256                        value.html += '<p class="lunch-msg"> ' + value.lunchMessage + '</p>';
7257                    }
7258					var timeslot = self.splitTime(value.timeslot);
7259					value.html += '<p><i class="icon icon--tick"></i> ' + (moment(new Date(2017, 11, 8, timeslot[0], timeslot[1], 0)).format("h:mmA")).toLowerCase() + '</p>';
7260					if (value.briefMessage !== '') {
7261						var text = $('<div />').html(value.briefMessage).html().replace('-ajax-url-', self.config.urls.pageShowMembershipBenefits);
7262						value.html += '<div class="brief-msg">
7262' + text + '</div>';
7263					}
7264					value.html += '</div>';
7265					times.push(value);
7266				});
7267
7268				if (cols === 2) {
7269					var leftHTML = "",
7270						rightHTML = "";
7271
7272					$.each(times.splice(0, Math.ceil(times.length / 2)), function (index, value) {
7273						leftHTML += value.html;
7274					});
7275
7276					$.each(times, function (index, value) {
7277						rightHTML += value.html;
7278					});
7279
7280					$html = $html.replace('left', leftHTML);
7281					$html = $html.replace('right', rightHTML);
7282				} else {
7283					var oneHTML = '';
7284					$.each(times, function (index, value) {
7285						oneHTML += value.html;
7286					});
7287					$html = $html.replace('one', oneHTML);
7288				}
7289
7290				// inject
7291				$('#entryTime').html($html);
7292
7293				// attack events
7294				$('#entryTime div[data-action=selectTime]').off('click').on('click', function (e) {
7295					e.preventDefault();
7296					var id = $(this).attr('data-id'),
7297						time = $(this).attr('data-time');
7298					self.selectTime(id, time);
7299				});
7300
7301				// dispatch CalendarOnModalAppear event
7302				// to notify the modal manager
7303				// that we need assistance
7304				$(window).trigger('CalendarOnModalAppear', [self]);
7305
7306				// dispatch TateAccordionPanelCatch event
7307				// to notify the accordion panel manager
7308				// that we need assistance
7309				$(document).trigger('TateAccordionPanelCatch');
7310
7311                self.validate('time');
7312
7313                if (window.location.search.indexOf('qrcode=') > -1 && !data.hasAvailableTickets) {
7314
7315                    $.colorbox({
7316                        href: TateUtil.appendParamsToUrl(self.config.urls.soldOutModal, {'cgid': self.config.cgid}),
7317                        className: 'today-sold-out-modal',
7318                        width: '95%',
7319                        maxWidth: '670px',
7320                        closeButton: false
7321                    });
7322
7323                    $('body').css('overflow', 'hidden');
7324                    $('body').css('position', 'fixed');
7325                    $('body').css('width', '100%');
7326
7327                    $(document).bind('cbox_closed', function(){
7328                        $('body').css('overflow', 'auto');
7329                        $('body').css('position', 'static');
7330                    });
7331                }
7332
7333
7334			}.bind(this));
7335		},
7336
7337        decrementIncrementEvents: function() {
7338            var self = this;
7339
7340            // decrement event handler
7341            $('.option-item button.btn--action_decrement').off('click').on('click', function (e) {
7342                e.preventDefault();
7343                var id = $(this).data('id');
7344                self.decrementTicket(id);
7345            });
7346
7347            // increment event handler
7348            $('.option-item button.btn--action_increment').off('click').on('click', function (e) {
7349                e.preventDefault();
7350                var id = $(this).data('id');
7351                self.incrementTicket(id);
7352            });
7353        },
7354
7355        promoAjaxCall: function() {
7356            var self = this;
7357            var pcdCode = $('[data-id="promo"]').val();
7358
7359            $.ajax(TateUtil.appendParamsToUrl(self.config.urls.checkPromoCode, {
7360                pcdCode: pcdCode,
7361                pid: self.config.selected.vid
7362
7363            })).done(function(data) {
7364                if (data.success) {
7365                    var priceHolderText = '<span class="price-holder">' + data.priceText + '</span>',
7366                        id = data.id;
7367
7368                    // Create html for recieved ticket.
7369                    var $pcdTicket = [
7370                        '<div data-id="' + id + '" class="grid grid-outer option-item green-class">',
7371                        '    <div class="grid__inner grid-col--40 option-item-desc">',
7372                        '        <span class="desc promo-ticket">' + replaceBrief(data.description) + '</span>',
7373                                 priceHolderText,
7374                        '    </div>',
7375                        '    <div class="grid__inner grid-col--40 option-item-price">',
7376                                 priceHolderText,
7377                        '    </div>',
7378                        '    <div class="grid__inner grid-col--20 option-item-form">',
7379                        '        <div class="btn-row">',
7380                        '            <button aria-label="' + Resources.QUANTITY + ' ' + Resources.MINUS + '" class="btn btn--float__left btn--size__small bt
7380n--style__text btn--type__outline btn--type__outline_grey btn--action_decrement" data-id="' + id + '">',
7381                        '                <span class="btn__text">-</span>',
7382                        '            </button>',
7383                        '            <div class="form-item form-item--number">',
7384                        '                <label for="inputqty' + id + '" class="h-e">-a-</label>',
7385                        '                <input id="inputqty' + id + '" value="0" class="form-item__input form-item__input--number input--qty " data-id="' + id + '" type="input" />',
7386                        '            </div>',
7387                        '            <button aria-label="' + Resources.QUANTITY + ' ' + Resources.PLUS + '" class="btn btn--float__left btn--size__small btn--style__text btn--type__outline btn--type__outline_grey btn--action_increment" data-id="' + id + '">',
7388                        '                <span class="btn__text">+</span>',
7389                        '            </button>',
7390                        '        </div>',
7391                        '    </div>',
7392                        '</div>'
7393                    ].join("\n");
7394
7395                    $('#entryTickets').append($pcdTicket);
7396
7397                    self.config.tickets[id] = {
7398                        id: id,
7399                        description: data.description,
7400                        priceText: data.priceText
7401                    };
7402
7403                    //initialize events for +/- buttons//initialize events for +/- buttons
7404                    self.decrementIncrementEvents();
7405
7406                    //show success message
7407                    $('#ticketCoupon').find('.coupon-error').addClass('hidden');
7408                    $('#ticketCoupon').find('.coupon-applied').removeClass('hidden');
7409                } else {
7410                    //show error message
7411                    $('#ticketCoupon').find('.coupon-error').removeClass('hidden');
7412                    $('#ticketCoupon').find('.coupon-applied').addClass('hidden');
7413                }
7414
7415            });
7416
7417        },
7418
7419		/**
7420		 * Renders Select Tickets panel
7421		 */
7422			renderSelectTickets: function () {
7423
7424			var self = this;
7425			var ticketMemberBannerEventsContainer = $('.ticket-member-banner-events-container');
7426			self.adjustStickyButton();
7427
7428			// dispatch CalendarOnModalAppear event
7429			// to notify the modal manager that we need assistance
7430			$(window).trigger('CalendarOnModalAppear', [self]);
7431
7432				if (!self.config.isLoggedIn && !self.config.isMemberEventsModalOpened) {
7433					self.openTicketsModal();
7434				}
7435
7436                $(document).off('click.exhibitionLunchModal').on('click.exhibitionLunchModal', '.tickets-admission-lunch-banner .tickets-lunch-banner-menu a', function (e) {
7437                    e.preventDefault();
7438                    self.openExhibitionLunchModal();
7439                });
7440
7441			// replace stock qty
7442			$('.ticket-purchase-limit-stock-warn').text(self.getCurrentVariantStockQty());
7443
7444			var params = {};
7445			params['cgid'] = this.getCGID();
7446			params['pid'] = this.getPID();
7447			params['dwvar_' + this.getPID() + '_TicketTimeslotVariantAttribute'] = this.getTimeSlotID();
7448			params['format'] = 'ajax';
7449
7450
7451			$.ajax(TateUtil.appendParamsToUrl(this.config.urls.optionsJSON, params)).done(function(data) {
7452
7453                if (data.options.length == 0 && data.expiredMembership) {
7454                    self.openMembershipRenewModal(3);
7455                }
7456
7457				self.config.options = data.options;
7458				self.config.ticketPurchaseLimit = data.purchaseLimit;
7459				self.config.collectivePurchaseLimit = data.collectivePurchaseLimit;
7460				self.config.customerIsCollectiveMember = window.isCollectiveActive;
7461                self.config.customerIsMember = window.customerIsMember;
7462				self.config.onlyMemberPrivilege = data.onlyMemberPrivilege;
7463				self.config.memberPurchaseLimit = data.memberPurchaseLimit;
7464				self.config.hasExemptTicketsInBasket = data.hasExemptTicketsInBasket;
7465				self.config.collectiveOptions = data.collectiveOptions;
7466                self.config.memberOptions = data.memberOptions;
7467				self.config.memOptionsIds = [];
7468				var $html = '',
7469				    $htmlAccordion = '',
7470				    ticketMoreOptions = SitePreferences.TICKET_MORE_OPTIONS,
7471					collectiveOptionIDArr = Resources.COLLECTIVE_OPTION_ID.split(','),
7472                    memberOptionIDArr = Resources.MEMBER_OPTION_ID.split(','),
7473					ticketSuppression = SitePreferences.TICKET_SUPPRESSION,
7474					removeMemberBannerForEvents = true;
7475
7476				/**
7477				* Determines whether a regular collective option should be displayed based on the option's ID and customer membership status.
7478				* @param {Object} opt - The option object to evaluate, expected to have an 'id' property.
7479				* @returns {boolean} - Returns true if the option should be shown; false otherwise.
7480				*/
7481				function showRegularCollectiveOption(opt) {
7482					var isCollectiveById = collectiveOptionIDArr.indexOf(opt.id) > -1;
7483					var showOption = true;
7484
7485					if (self.config.customerIsCollectiveMember && isCollectiveById) {
7486						showOption = false;
7487					}
7488
7489					return showOption;
7490				}
7491
7492                /**
7493                * Determines whether a member option should be displayed based on its ID.
7494                * Checks if the provided option's ID exists in the global memberOptionIDArr array.
7495                * If the ID is found, the option will not be shown.
7496                *
7497                * @param {Object} opt - The option object to evaluate, expected to have an 'id' property.
7498                * @returns {boolean} - Returns true if the option should be shown; false otherwise.
7499                */
7500                function showRegularMemberOption(opt) {
7501                    var isMemberById = memberOptionIDArr.indexOf(opt.id) > -1;
7502                    var showOption = true;
7503
7504                    if (isMemberById) {
7505                        showOption = false;
7506                    }
7507
7508                    return showOption;
7509                }
7510
7511				$.each(self.config.options, function (index, value) {
7512					self.config.tickets[value.id] = value;
7513					var id = value.id;
7514					var qty = self.getSelectedTicketQty(id);
7515					var memOptionsClass = '';
7516
7517					if (value.isMemOption) {
7518						memOptionsClass = ' memOption';
7519						self.config.memOptionsIds.push(id);
7520					}
7521
7522					/**
7523					 * two ticket option IDs commes from SitePreference.
7524					 * the two and all tickets listed between within the accordion.
7525					 * If nothing set in SitePreference all tickets are shown.
7526					 */
7527                    var priceHolderText = '';
7528                    if (typeof value.promoName !== 'undefined') {
7529                        priceHolderText = [
7530                            '<div class="price-holder-wrapper-with-discount">',
7531                            '   <span class="price-holder">' + value.promoOptionPriceText + '</span>',
7532                            '   <span class="price-holder-was">' + value.priceText + '</span>',
7533                            '</div>'
7534                        ].join('');
7535                    } else {
7536                        priceHolderText = '<span class="price-holder">' + value.priceText + '</span>';
7537                    }
7538
7539                    var ajaxSuccess = '';
7540                    if (value.description.indexOf(Resources.SPECIAL_PREFIX) !== -1) {
7541                        ajaxSuccess = 'promo-ticket';
7542                    }
7543
7544                    var desc = replaceBrief(value.description.replace(Resources.SPECIAL_PREFIX, ''));
7545                    var optionBannerClass = value.bannerClass ? ' ' + value.bannerClass : '';
7546
7547					if (!self.config.customerIsCollectiveMember && collectiveOptionIDArr.indexOf(id) > -1) {
7548						/**
7549						* If customer hasn't active Collective membership
7550						* don't render this product option
7551						*/
7552					} else if (ticketMoreOptions.indexOf(id) != -1 && showRegularMemberOption(value) && showRegularCollectiveOption(value)) {
7553						$htmlAccordion += [
7554                                '<div data-id="' + id + '" class="grid grid-outer option-item' + memOptionsClass + optionBannerClass + '">',
7555							'    <div class="grid__inner grid-col--40 option-item-desc">',
7556							'        <span class="desc ' + ajaxSuccess + '">' + desc + '</span>',
7557							         priceHolderText,
7558							'    </div>',
7559							'    <div class="grid__inner grid-col--40 option-item-price">',
7560							         priceHolderText,
7561							'    </div>',
7562							'    <div class="grid__inner grid-col--20 option-item-form">',
7563							'        <div class="btn-row">',
7564							'            <button aria-label="' + Resources.QUANTITY + ' ' + Resources.MINUS + '" class="btn btn--float__left btn--size__small bt
7564n--style__text btn--type__outline btn--type__outline_grey btn--action_decrement" data-id="' + id + '">',
7565							'                <span class="btn__text">-</span>',
7566							'            </button>',
7567							'            <div class="form-item form-item--number">',
7568                            '                <label for="inputqty' + id + '" class="h-e">' + Resources.ENTER_TICKETS_QTY_FOR + ' ' + desc + '</label>',
7569							'                <input id="inputqty' + id + '" value="' + qty + '" class="form-item__input form-item__input--number input--qty ' + (qty > 0 ? 'item--qty__border--green' : '') + '" data-id="' + id + '" type="input" />',
7570							'            </div>',
7571							'            <button aria-label="' + Resources.QUANTITY + ' ' + Resources.PLUS + '" class="btn btn--float__left btn--size__small btn--style__text btn--type__outline btn--type__outline_grey btn--action_increment" data-id="' + id + '">',
7572							'                <span class="btn__text">+</span>',
7573							'            </button>',
7574							'        </div>',
7575							'    </div>',
7576							'</div>'
7577						].join("\n");
7578
7579					} else if (ticketSuppression.indexOf(id) != -1) {
7580						// don't print these tickets
7581					} else if (showRegularMemberOption(value) && showRegularCollectiveOption(value)) {
7582						$html += [
7583                                '<div data-id="' + id + '" class="grid grid-outer option-item' + memOptionsClass + optionBannerClass + '">',
7584							'    <div class="grid__inner grid-col--40 option-item-desc">',
7585							'        <span class="desc ' + ajaxSuccess + '">' + desc + '</span>',
7586							         priceHolderText,
7587							'    </div>',
7588							'    <div class="grid__inner grid-col--40 option-item-price">',
7589							         priceHolderText,
7590							'    </div>',
7591							'    <div class="grid__inner grid-col--20 option-item-form">',
7592							'        <div class="btn-row">',
7593							'            <button aria-label="' + Resources.QUANTITY + ' ' + Resources.MINUS + '" class="btn btn--float__left btn--size__small bt
7593n--style__text btn--type__outline btn--type__outline_grey btn--action_decrement" data-id="' + id + '">',
7594							'                <span class="btn__text">-</span>',
7595							'            </button>',
7596							'            <div class="form-item form-item--number">',
7597                            '                <label for="inputqty' + id + '" class="h-e">' + Resources.ENTER_TICKETS_QTY_FOR + ' ' + desc + '</label>',
7598							'                <input id="inputqty' + id + '" value="' + qty + '" class="form-item__input form-item__input--number input--qty ' + (qty > 0 ? 'item--qty__border--green' : '') + '" data-id="' + id + '" type="input" />',
7599							'            </div>',
7600							'            <button aria-label="' + Resources.QUANTITY + ' ' + Resources.PLUS + '" class="btn btn--float__left btn--size__small btn--style__text btn--type__outline btn--type__outline_grey btn--action_increment" data-id="' + id + '">',
7601							'                <span class="btn__text">+</span>',
7602							'            </button>',
7603							'        </div>',
7604							'    </div>',
7605							'</div>'
7606						].join("\n");
7607					}
7608                });
7609
7610                // Member Banner
7611                if (self.config.memberOptions && self.config.memberOptions.length > 0) {
7612                    var memberOptions = self.config.memberOptions;
7613
7614                    if (memberOptions.some(opt => opt.priceText && opt.priceText.trim() !== '')) {
7615                        removeMemberBannerForEvents = false;
7616                    }
7617
7618                    function buildQtyControls(option) {
7619                        var id = option.id;
7620                        var qty = self.getSelectedTicketQty(id)
7621                        var memOptionsClass = qty > 0 ? ' has-qty' : '';
7622
7623                        return '' +
7624                            '<div class="grid grid-outer option-item' + memOptionsClass + '" data-id="' + id + '">' +
7625                            '  <div class="btn-row">' +
7626                            '    <button aria-label="' + Resources.QUANTITY + ' ' + Resources.MINUS + '" class="btn btn--float__left btn--size__small bt
7626n--style__text btn--type__outline btn--type__outline_grey btn--action_decrement" data-id="' + id + '">' +
7627                            '      <span class="btn__text">-</span>' +
7628                            '    </button>' +
7629                            '    <div class="form-item form-item--number">' +
7630                            '      <label class="h-e" for="inputqty' + id + '">' + Resources.ENTER_TICKETS_QTY_FOR + ' ' + option.description + '</label>' +
7631                            '      <input id="inputqty' + id + '" value="' + qty + '" class="form-item__input form-item__input--number input--qty ' + (qty > 0 ? 'item--qty__border--green' : '') + '" data-id="' + id + '" type="input" />' +
7632                            '    </div>' +
7633                            '    <button aria-label="' + Resources.QUANTITY + ' ' + Resources.PLUS + '" class="btn btn--float__left btn--size__small btn--style__text btn--type__outline btn--type__outline_grey btn--action_increment" data-id="' + id + '">' +
7634                            '      <span class="btn__text">+</span>' +
7635                            '    </button>' +
7636                            '  </div>' +
7637                            '</div>';
7638                    }
7639
7640                    $(document).ready(function() {
7641                        var $template = $('.tate-member-banner').first();
7642                        if (!$template.length) return;
7643
7644                        $('[data-mb-clone="1"]').remove();
7645
7646                        if (!self.config.customerIsMember) {
7647                            var option = memberOptions[0];
7648                            var priceHolderText = '<span class="price-holder">' + option.priceText + '</span>';
7649                            $template.find($('#memberBannerTicketOption')).html(buildQtyControls(option));
7650
7651                            // Several member ticket types displayed for non-members (only one banner is displayed with Discount Tickets Available message)
7652                            if (memberOptions.length > 1) {
7653                                $template.addClass('multiple-member-tickets');
7654                                $template.find($('.ticket-banner-title')).append('<span class="tate-member-tickets-available">' + Resources.DISCOUNT_TICKETS_AVAILABLE + '</span>');
7655                            } else {
7656                                // One member ticket type displayed for non-members (display actual ticket description as a name)
7657                                $template.find('.ticket-banner-title').text(option.description);
7658                                $template.find($('#memberBannerPrice')).html(priceHolderText);
7659                                $template.find($('#memberBannerPriceDesktop')).html(priceHolderText);
7660                            }
7661                        } else {
7662                            $template.hide();
7663                            for (var i = 0; i < memberOptions.length; i++) {
7664                                var opt = memberOptions[i];
7665                                var priceHTML = opt.priceText ? '<span class="price-holder">' + opt.priceText + '</span>' : '';
7666                                var $clone = $template.clone(true, true);
7667
7668                                $clone.attr('data-mb-clone', '1').css('display', 'flex');
7669                                $clone.find('.ticket-banner-title').first().text(opt.description);
7670                                $clone.find('.member-banner-price').first().html(priceHTML);
7671                                $clone.find('.member-banner-price-desktop').first().html(priceHTML);
7672                                $clone.find('.member-banner-ticket-option').first().html(buildQtyControls(opt));
7673                                $clone.attr('data-option-id', opt.id);
7674                                $template.after($clone);
7675                            }
7676                        }
7677                        self.decrementIncrementEvents();
7678                    });
7679                }
7680
7681                if ($htmlAccordion !== '') {
7682                    $('#entryTicketsAccordion').html($htmlAccordion);
7683                    // toggle ticket accordion for more options
7684                    $('.ticket-accordion-wrapper .accordion-header').on('click', function (e) {
7685                        e.preventDefault();
7686                        $('#entryTicketsAccordion').toggle();
7687                        $('.accordion-header').toggleClass('gray-background');
7688                        $('.accordion-header i.icon').toggle();
7689                    });
7690                    $('.ticket-accordion-wrapper').show();
7691                }
7692
7693                if (removeMemberBannerForEvents && ticketMemberBannerEventsContainer.length) {
7694                    ticketMemberBannerEventsContainer.html('');
7695                } else {
7696                    $('.tate-member-banner').css('display', 'flex');
7697                }
7698
7699                $('#entryTickets').html($html);
7700
7701                // Collective Banner
7702                if (self.config.collectiveOptions && self.config.collectiveOptions.length > 0) {
7703                    var collectiveOptions = self.config.collectiveOptions;
7704
7705                    function buildQtyControls(option) {
7706                        var optionQty = self.getSelectedTicketQty(option.id);
7707
7708                        return '' +
7709                            '<div class="grid grid-outer option-item no-padding" data-id="' + option.id + '">' +
7710                            '  <div class="btn-row">' +
7711                            '    <button aria-label="' + Resources.QUANTITY + ' ' + Resources.MINUS + '" class="btn btn--float__left btn--size__small btn--style__text btn--type__outline btn--type__outline_grey btn--action_decrement" data-id="' + option.id + '">' +
7712                            '      <span class="btn__text">-</span>' +
7713                            '    </button>' +
7714                            '    <div class="form-item form-item--number">' +
7715                            '      <label class="h-e" for="inputqty' + option.id + '">' + Resources.ENTER_TICKETS_QTY_FOR + '</label>' +
7716                            '      <input id="inputqty' + option.id + '" value="' + optionQty + '" class="form-item__input form-item__input--number input--qty ' + (optionQty > 0 ? 'item--qty__border--green' : '') + '" data-id="' + option.id + '" type="input" />' +
7717                            '    </div>' +
7718                            '    <button aria-label="' + Resources.QUANTITY + ' ' + Resources.PLUS + '" class="btn btn--float__left btn--size__small bt
7718n--style__text btn--type__outline btn--type__outline_grey btn--action_increment" data-id="' + option.id + '">' +
7719                            '      <span class="btn__text">+</span>' +
7720                            '    </button>' +
7721                            '  </div>' +
7722                            '</div>';
7723                    }
7724
7725                    $(document).ready(function () {
7726                        var $template = $('.tate-collective-banner').first();
7727                        if (!$template.length) return;
7728
7729                        $('[data-tc-clone="1"]').remove();
7730
7731                        if (!self.config.customerIsCollectiveMember) {
7732                            var option = collectiveOptions[0];
7733                            var priceHolderText = '<span class="price-holder">' + option.priceText + '</span>';
7734                            $template.find($('#tateCollectiveTicketOption')).html(buildQtyControls(option));
7735
7736                            // Several collective ticket types displayed for non collective members (only one banner is displayed with Discount Tickets Available message)
7737                            if (collectiveOptions.length > 1) {
7738                                $template.addClass('multiple-collective-tickets');
7739                                $template.find($('.ticket-banner-info')).append('<span class="tate-collective-tickets-available">' + Resources.DISCOUNT_TICKETS_AVAILABLE + '</span>');
7740                            } else {
7741                                // One collective ticket type displayed for non collective members (display actual ticket description as a name)
7742                                $template.find('.ticket-banner-title').text(option.description);
7743                                $template.find($('#tateCollectivePrice')).html(priceHolderText);
7744                                $template.find($('#tateCollectivePriceDesktop')).html(priceHolderText);
7745                            }
7746                        } else {
7747                            $template.hide();
7748                            for (var i = 0; i < collectiveOptions.length; i++) {
7749                                var opt = collectiveOptions[i];
7750                                var priceHTML = '<span class="price-holder">' + opt.priceText + '</span>';
7751                                var $clone = $template.clone(true, true);
7752
7753                                $clone.attr('data-tc-clone', '1');
7754                                $clone.find('.ticket-banner-title').first().text(opt.description);
7755                                $clone.find('.ticket-price-holder').first().html(priceHTML);
7756                                $clone.find('.ticket-price-holder-desktop').first().html(priceHTML);
7757                                $clone.find('.ticket-banner-controls-holder').first().html(buildQtyControls(opt));
7758                                $clone.attr('data-option-id', opt.id).css('display', 'flex');
7759                                $template.after($clone);
7760                            }
7761                        }
7762                        self.decrementIncrementEvents();
7763                    });
7764
7765                    var $tateCollectiveBanner = $('.tate-collective-banner').first();
7766
7767                    if (!self.config.customerIsCollectiveMember) {
7768                        var $optionItems = $('#entryTickets .option-item');
7769                        if ($tateCollectiveBanner.length) {
7770                            $tateCollectiveBanner.addClass('no-margin-top');
7771                            if ($optionItems.length >= 2) {
7772                                $optionItems.eq(1).after($tateCollectiveBanner);
7773                            } else if ($optionItems.length === 1) {
7774                                $optionItems.eq(0).after($tateCollectiveBanner);
7775                            } else {
7776                                $('#entryTickets').append($tateCollectiveBanner);
7777                            }
7778                        }
7779                    }
7780
7781                    if ($tateCollectiveBanner.length) {
7782                        $tateCollectiveBanner.css('display', 'flex');
7783                    }
7784                }
7785
7786                // add promo code input
7787                var $ticketCoupon = '';
7788                $ticketCoupon += [
7789                    '        <div class="ticket-promo-wrapper">',
7790                    '            <div class="form-item form-item--number promo-input-wrapper">',
7791                    '                <label for="applycoupon" class="h-e">' + Resources.APPLY_COUPON + '</label>',
7792                    '                <input id="applycoupon" class="form-item__input form-item__input--number input--promo " data-id="promo" type="input" placeholder="'+ Resources.ENTE
7792R_PROMO_CODE +'" />',
7793                    '            </div>',
7794                    '            <div class="btn-row coupon-button-wrapper">',
7795                    '                <button class="btn btn--submit btn--action_promo">',
7796                    '                    <span class="btn__text">' + Resources.APPLY_COUPON + '</span>',
7797                    '                </button>',
7798                    '            </div>',
7799                    '            <div class="coupon-applied hidden">',
7800                    '               <span class="promo-applied-heading"><i class="icon--tick"></i><span>' + Resources.COUPON_APPLIED + '</span></span>',
7801                    '             </div>',
7802                    '            <div class="error coupon-error hidden">',
7803                    '               <span class="promo-error-heading"><i class="icon--chevron-up"></i><span class="message">'+ Resources.COUPON_ERROR + '</span></span>',
7804                    '            </div>',
7805                    '            <div class="promo-message-wrapper">',
7806                    '                <div class="promo-applied-message">' + Resources.COUPON_MESSAGE + '</div>',
7807                    '            </div>',
7808                    '        </div>'
7809                ].join("\n");
7810
7811                // inject coupons
7812                $('#ticketCoupon').html($ticketCoupon);
7813
7814                // if event is only for members/collective dissable control buttons
7815                if ($('.ticket-purchase-only-members-warn').length > 0 || $('.ticket-purchase-only-collective-warn').length > 0 || $('.event-ticket-has-past-warn-wrap').length > 0) {
7816                    $('#entryTickets .btn-row, #entryTicketsAccordion .btn-row').css('opacity', 0.5);
7817                    $('#entryTickets .btn-row button, #entryTickets .btn-row input, #entryTicketsAccordion .btn-row button, #entryTicketsAccordion .btn-row input').attr("disabled", true);
7818                }
7819
7820				// if the user is not member disable mem options
7821				if (self.config.memberPurchaseLimit == 0) {
7822					$('.memOption .btn-row').css('opacity', 0.5);
7823                    $('.memOption .btn-row button, .memOption .btn-row input').attr('disabled', true);
7824				}
7825
7826                //initialize events for +/- buttons
7827				self.decrementIncrementEvents();
7828
7829				/** check for valid number entry and reset field value upon validation fail */
7830				$('.option-item input.input--qty').off('keyup').on('keyup', function (e) {
7831					e.preventDefault();
7832
7833					var qty = $(this).val();
7834					if (['', 0].indexOf(qty) === -1) {
7835						$(this).val(isNaN(parseInt(qty)) ? '' : parseInt(qty));
7836					}
7837				});
7838
7839				/** check for valid number entry and sets zero upon validation fail */
7840				$('.option-item input.input--qty').off('change').on('change', function (e) {
7841					e.preventDefault();
7842
7843					var id = $(this).attr('data-id'),
7844						qty = $(this).val();
7845
7846					/** validate current qty */
7847					if (isNaN(parseInt(qty))) {
7848						// set value to zero if qty is not a number
7849						$(this).val(0);
7850					} else {
7851                        // parse in int and set value to selected array
7852                        self.setTicketQty(id, parseInt(qty));
7853					}
7854				});
7855
7856				/** ticket puchase limit close button event handler */
7857				$('.ticket-purchase-limit-warn-wrap .closebtn').off('click').on('click', function (e) {
7858					e.preventDefault();
7859					$('.ticket-purchase-limit-warn-wrap').hide();
7860				});
7861
7862				$('.collective-ticket-purchase-limit-warn-wrap .closebtn').off('click').on('click', function (e) {
7863					e.preventDefault();
7864					$('.collective-ticket-purchase-limit-warn-wrap').hide();
7865				});
7866
7867				$('.mem-ticket-purchase-limit-warn-wrap .closebtn').off('click').on('click', function (e) {
7868					e.preventDefault();
7869					$('.mem-ticket-purchase-limit-warn-wrap').hide();
7870				});
7871
7872				$('.event-ticket-has-past-warn-wrap .closebtn').off('click').on('click', function (e) {
7873					e.preventDefault();
7874					$('.event-ticket-has-past-warn-wrap').hide();
7875				});
7876
7877				/** save selected tickets and then continue */
7878				$('.go-save-and-continue').off('click').on('click', function (e) {
7879					e.preventDefault();
7880					var total = self.getSelectedTicketsTotal();
7881					if (total > 0) {
7882						self.saveToSession(function (data) {
7883                            if (typeof data.status !== "undefined" && data.status === true) {
7884                                // if customer has gift aid exempt tickets skip donaton step
7885								if (self.config.isTicketDonationEnabled && (self.checkForGiftAidExemptTickets() || self.config.hasExemptTicketsInBasket)) {
7886									$('.add-to-basket-without-donation').trigger('submit');
7887								} else if (self.config.isTicketDonationEnabled) {
7888                                    // check donation enabled trigger next step
7889                                    $('.go-continue').trigger('click');
7890                                } else {
7891                                    // submit add to basket without donation
7892                                    $('.add-to-basket-without-donation').trigger('submit');
7893                                }
7894							}
7895						});
7896					}
7897				});
7898
7899				// Add handler for sticky save-and-continue button
7900				$('.go-save-and-continue-sticky').off('click').on('click', function (e) {
7901					e.preventDefault();
7902					var total = self.getSelectedTicketsTotal();
7903					if (total > 0) {
7904						self.saveToSession(function (data) {
7905							if (typeof data.status !== "undefined" && data.status === true) {
7906								if (self.config.isTicketDonationEnabled && (self.checkForGiftAidExemptTickets() || self.config.hasExemptTicketsInBasket)) {
7907									$('.add-to-basket-without-donation').trigger('submit');
7908								} else if (self.config.isTicketDonationEnabled) {
7909									$('.go-continue').trigger('click');
7910								} else {
7911									$('.add-to-basket-without-donation').trigger('submit');
7912								}
7913							}
7914						});
7915					}
7916				});
7917
7918                //promo code submit event handler
7919                $('.btn--action_promo').off('click').on('click', function (e) {
7920                    e.preventDefault();
7921                    self.promoAjaxCall();
7922                });
7923
7924                $('[data-id="promo"]').off('keyup').on('keyup', function (e) {
7925                    e.preventDefault();
7926                    if (e.keyCode === 13) {
7927                        self.promoAjaxCall();
7928                    }
7929                });
7930
7931				// trigger accordion panel
7932				$(document).trigger('TateAccordionPanelCatch');
7933
7934                if (self.config.times.length) {
7935                    self.validate('tickets');
7936                } else {
7937                    self.setTimes(function () {
7938                        self.validate('tickets');
7939                    });
7940                }
7941			});
7942		},
7943
7944		/**
7945		 * Check is there added gift aid exempt ticket options
7946		 */
7947		checkForGiftAidExemptTickets: function () {
7948			var self = this, hasExemptTickets = false, giftAidExemptArray = SitePreferences.TICKET_GIFT_AID_EXEMPT;
7949
7950			if (Object.keys(this.config.selected.tickets).length > 0) {
7951				$.each(this.config.selected.tickets, function (key, value) {
7952
7953					if (giftAidExemptArray.indexOf(key) > -1 && value > 0) {
7954						hasExemptTickets = true;
7955						return false;
7956					}
7957
7958				});
7959			}
7960			return hasExemptTickets;
7961		},
7962
7963        /**
7964         * Save to session selected ticket data
7965         * @param  {Function} done   callback
7966         * @param  {Function} fail   callback
7967         * @param  {Function} always callback
7968         */
7969		saveToSession: function (done, fail, always) {
7970			var self = this;
7971			$.ajax({
7972				url: TateUtil.ajaxUrl(self.config.urls.saveToSession),
7973				type: 'POST',
7974				cache: false,
7975				contentType: 'application/json',
7976				dataType: "json",
7977				data: JSON.stringify($.extend(self.config.selected, {
7978					cgid: self.getCGID(),
7979					pid: self.getPID(),
7980					vid: self.getVID()
7981				}))
7982			}).done(function (data) {
7983				if (typeof done !== "undefined") {
7984					done(data);
7985				}
7986			});
7987		},
7988
7989		decrementTicket:function (id) {
7990			if (typeof this.config.selected.tickets[id] === "undefined") {
7991				this.config.selected.tickets[id] = 0;
7992			}
7993
7994			var $input = $('.option-item[data-id=' + id + '] input'),
7995				qty = Math.max(this.config.selected.tickets[id] - 1, 0);
7996
7997			this.config.selected.tickets[id] = qty;
7998			$input.val(qty);
7999			if (qty > 0) {
8000				$input.addClass('item--qty__border--green')
8001			} else {
8002				$input.removeClass('item--qty__border--green');
8003			}
8004			this.validate('tickets');
8005			this.renderSelectedTickets();
8006		},
8007
8008		incrementTicket: function (id) {
8009			if (typeof this.config.selected.tickets[id] === "undefined") {
8010				this.config.selected.tickets[id] = 0;
8011			}
8012
8013            var $input = $('.option-item[data-id=' + id + '] input'),
8014                qty = this.config.selected.tickets[id] + 1;
8015
8016			this.config.selected.tickets[id] = qty;
8017			$input.val(qty);
8018			if (qty > 0) {
8019				$input.addClass('item--qty__border--green')
8020			} else {
8021				$input.removeClass('item--qty__border--green');
8022			}
8023			this.validate('tickets');
8024			this.renderSelectedTickets();
8025		},
8026
8027        setTimes: function(callback) {
8028            var self = this;
8029            $.ajax({
8030                url: TateUtil.appendParamsToUrl(self.config.urls.selectTime, {
8031                    pid: self.getPID(),
8032                    format: 'ajax'
8033                })
8034            }).done(function(data) {
8035
8036                self.config.times = data.timeslots;
8037                callback();
8038            });
8039        },
8040
8041		setTicketQty: function (id, qty) {
8042			if (typeof this.config.selected.tickets[id] === "undefined") {
8043				this.config.selected.tickets[id] = 0;
8044			}
8045			var $input = $('.option-item[data-id=' + id + '] input'),
8046				qty = Math.max(0, qty)
8047			this.config.selected.tickets[id] = qty;
8048			$input.val(qty);
8049			if (qty > 0) {
8050				$input.addClass('item--qty__border--green')
8051			} else {
8052				$input.removeClass('item--qty__border--green');
8053			}
8054			this.validate('tickets');
8055			this.renderSelectedTickets();
8056		},
8057
8058		/**
8059		 * Renders currect selected tickers to selected tickets html
8060		 */
8061		renderSelectedTickets: function () {
8062			var self = this,
8063				$tickets = $('.card-footer-selected-tickets'),
8064				html = "";
8065
8066			$.each(this.config.selected.tickets, function (index, value) {
8067				   if (value > 0 && typeof self.config.tickets[index] !== "undefined") {
8068					var ticket = self.config.tickets[index];
8069					html += [
8070						'<div class="grid grid grid-outer">',
8071						'	<div class="grid__inner grid-col--60" style="padding-right: 0; padding-left: 7px;">',
8072						'		<span class="ticket-qty">' + value + ' x ' + '</span><span class="ticket-name">' + (ticket.description.replace('(', '</span><span class="h-e brief">').replace(')', '</span>').replace(Resources.SPECIAL_PREFIX, '')),
8073						'	</div>',
8074						'	<div class="grid__inner grid-col--40" style="padding-left: 4px">',
8075						'		<span>' + ticket.priceText + ' each</span>',
8076						'	</div>',
8077						'</div>'
8078					].join("\n");
8079				}
8080			});
8081
8082            html += '<div class="grid grid grid-outer">';
8083
8084            // Pluralized ticket count message
8085            var totalTickets = this.getSelectedTicketsTotal();
8086            var pluralText = totalTickets === 1 ? 'ticket' : 'tickets';
8087            // $('.selected-tickets-summary').text('You have selected ' + totalTickets + ' ' + pluralText + '.');
8088
8089            // Update button text based on ticket count
8090            var btnText = '';
8091            if (totalTickets === 0) {
8092                btnText = Resources.ADD_TICKET;
8093            } else if (totalTickets === 1) {
8094                btnText = Resources.ADD_TICKET;
8095            } else {
8096                btnText = Resources.ADD_TICKET_PLURAL;
8097            }
8098            $('.go-save-and-continue, .go-save-and-continue-sticky').find('.btn__text').text(btnText);
8099
8100            $('.display-selected-tickets', $tickets).html(html);
8101
8102            // write ticket to add to cart without donation form
8103            $('input[data-name=ticket_tickets]').val(JSON.stringify(this.config.selected.tickets));
8104
8105		},
8106
8107		/**
8108		 * Set the selected donation value to configuration
8109		 */
8110		setDonationValue: function (donation) {
8111			var selected = $('.donation-item-selected').data('value'),
8112				$ticketGiftAidBox = $('.ticket-gift-aid-box');
8113
8114			// clean all items
8115			$('.donation-item').removeClass('donation-item-selected');
8116
8117			// if current selected donation is the current
8118			// selected donations, deactivate and return panel
8119			// to default state
8120			if (selected == donation) {
8121				donation = 0;
8122			}
8123
8124			// write donation to config
8125			this.config.selected.donation = donation;
8126
8127			// write donation to form
8128			$('input[data-id=donation]').val(donation);
8129
8130			// select current donation element
8131			var donationElement = $('.donation-item[data-value=' + donation + ']');
8132			if (donationElement.length > 0) {
8133				$('.donation-item[data-value=' + donation + ']').addClass('donation-item-selected');
8134
8135				// hide no thanks button holder
8136				$('.nothanks-wrap').hide();
8137
8138				// show gift aid dialog
8139				$('.gift-aid-dialog-wrap').show();
8140			} else {
8141				$('.nothanks-wrap').show();
8142				$('.gift-aid-dialog-wrap').hide();
8143				$('.go-save-and-continue-donation').addClass('go-save-and-continue-donation-hidden');
8144				$('input[data-name=giftAid]').removeAttr('checked');
8145			}
8146
8147			// scroll to ticket gift aid box
8148			if ($ticketGiftAidBox.length > 0) {
8149				$('html, body').animate({
8150					scrollTop: $ticketGiftAidBox.offset().top - 120
8151				}, 500);
8152			}
8153		},
8154
8155		/**
8156		 *
8157		 */
8158		getDonationValue: function () {
8159			if (this.config.selected.donation !== null) {
8160				return this.config.selected.donation;
8161			}
8162			return 0;
8163		},
8164
8165		renderDonations: function () {
8166			var self = this,
8167				$donationItemsTicket = $('.donation-items-ticket');
8168
8169			// events
8170			$('.go-continue-with-donation').off('click').on('click', function (e) {
8171                e.preventDefault();
8172
8173                const $this = $(this);
8174                if ($this.hasClass('disabled')) {
8175                    return;
8176                }
8177
8178				$this.addClass('disabled');
8179				$this.siblings('a').addClass('disabled');
8180
8181				self.TatePushStatePageManager.goTO('DonationsGiftAid');
8182			});
8183
8184			$('.go-continue-add-to-basket-without-donation').off('click').on('click', function (e) {
8185                e.preventDefault();
8186
8187                const $this = $(this);
8188                if ($this.hasClass('disabled')) {
8189                    return;
8190                }
8191
8192                $this.addClass('disabled');
8193                $this.siblings('a').addClass('disabled');
8194
8195				$('.add-to-basket-without-donation').trigger('submit');
8196			});
8197
8198			if ($donationItemsTicket.length > 0) {
8199
8200				var previousDonation = self.getDonationValue();
8201				if (previousDonation) {
8202					self.setDonationValue(previousDonation);
8203				}
8204
8205				$('.donation-item', $donationItemsTicket).off('click').on('click', function (e) {
8206					e.preventDefault();
8207					var id = $(this).data('id'),
8208						value = $(this).data('value');
8209
8210					$('.go-save-and-continue-donation .btn__text').text(Resources.ADD_TO_BASKET);
8211
8212					// set donation value
8213					self.setDonationValue(value);
8214				});
8215			}
8216
8217			// event handler for no thanks button
8218			$('a[data-action=nothanks]').off('click').on('click', function (e) {
8219				e.preventDefault();
8220				$('.go-save-and-continue-donation .btn__text').text(Resources.ADD_TO_BASKET_WITHOUT_DONATION);
8221				$('.go-save-and-continue-donation').removeClass('go-save-and-continue-donation-hidden');
8222				self.setDonationValue(0);
8223				self.setGiftAid(false);
8224			});
8225
8226			// event handler for gift aid selection
8227			$('input[data-name=giftAid]').off('change').on('change', function (e) {
8228				e.preventDefault();
8229				self.setGiftAid($(this).val());
8230			});
8231
8232			// save selected tickets and then continue
8233			$('.go-save-and-continue-donation').off('click').on('click', function (e) {
8234                e.preventDefault();
8235
8236                const $this = $(this);
8237                if ($this.hasClass('disabled')) {
8238                    return;
8239                }
8240
8241                $this.addClass('disabled');
8242                $this.siblings('a').addClass('disabled');
8243
8244				self.saveToSession(function (data) {
8245					if (typeof data.status !== "undefined" && data.status === true) {
8246						$('.add-to-basket').trigger('submit');
8247					}
8248				});
8249			});
8250
8251			$.ajax(TateUtil.appendParamsToUrl(this.config.urls.donationsJSON, {
8252				format: 'ajax'
8253			})).done(function(data) {
8254
8255				$.each(data.tickets, function (index, value) {
8256					self.config.tickets[value.id] = value;
8257					self.config.selected.tickets[value.id] = value.quantity;
8258				});
8259
8260				self.validate('tickets');
8261			});
8262		},
8263
8264		setGiftAid: function (giftAid) {
8265			var self = this;
8266
8267			self.config.selected.giftAid = giftAid;
8268
8269			self.validate('giftAid');
8270		},
8271
8272		/**
8273		 *
8274		 */
8275		   renderDonationsGiftAid: function () {
8276			   var self = this;
8277			   var $firstParagraphYes = $('.ticket-gift-aid-bottom-yes:not
8277(.additional)');
8278			   var $additionalParagraph = $('.ticket-gift-aid-bottom-yes.additional');
8279
8280			   // Ensure ticket definitions are loaded before rendering summary
8281			   if (!self.config.tickets || Object.keys(self.config.tickets).length === 0) {
8282				   // Fetch ticket definitions from donationsJSON endpoint
8283				   $.ajax(TateUtil.appendParamsToUrl(self.config.urls.donationsJSON, { format: 'ajax' }))
8284					   .done(function(data) {
8285						   // Populate ticket definitions
8286						   $.each(data.tickets, function (index, value) {
8287							   self.config.tickets[value.id] = value;
8288							   // Optionally update selected.tickets quantities if needed
8289							   if (typeof self.config.selected.tickets[value.id] === 'undefined') {
8290								   self.config.selected.tickets[value.id] = value.quantity;
8291							   }
8292						   });
8293						   self.renderSelectedTickets();
8294					   });
8295			   } else {
8296				   self.renderSelectedTickets();
8297			   }
8298
8299			   $('input[data-name=giftAid]').off('change').on('change', function (e) {
8300				   e.preventDefault();
8301				   var giftAid = $(this).val();
8302				   self.setGiftAid(giftAid);
8303
8304				   $('.ticket-gift-aid-bottom').hide();
8305
8306				   if (giftAid === "true") {
8307					   $firstParagraphYes.show();
8308				   } else {
8309					   $firstParagraphYes.hide();
8310					   $additionalParagraph.hide();
8311					   $firstParagraphYes.find('.read-more').show();
8312					   $firstParagraphYes.find('.hide').hide();
8313				   }
8314			   });
8315
8316			   $firstParagraphYes.find('.read-more, .hide').on('click', function () {
8317				   $firstParagraphYes.find('.read-more, .hide').toggle();
8318				   $additionalParagraph.toggle();
8319			   });
8320
8321			   $('.go-save-and-continue').off('click').on('click', function (e) {
8322					e.preventDefault();
8323
8324					const $this = $(this);
8325					if ($this.hasClass('disabled')) {
8326						return;
8327					}
8328
8329					$this.addClass('disabled');
8330					$this.siblings('a').addClass('disabled');
8331
8332				   $('.add-to-basket-with-donation').trigger('submit');
8333			   });
8334
8335			   $('.go-save-and-continue-sticky').off('click').on('click', function (e) {
8336					e.preventDefault();
8337
8338					const $this = $(this);
8339					if ($this.hasClass('disabled')) {
8340						return;
8341					}
8342
8343					$this.addClass('disabled');
8344					$this.siblings('a').addClass('disabled');
8345
8346				   $('.add-to-basket-with-donation').trigger('submit');
8347			   });
8348
8349			   self.validate('giftAid');
8350			},
8351
8352        openMembershipRenewModal: function(step) {
8353
8354            var self = this;
8355
8356            var modalMaxHeight = '510px';
8357
8358            if ($(window).width() < 720) {
8359                modalMaxHeight = '580px';
8360            }
8361
8362            $.colorbox({
8363                href: TateUtil.appendParamsToUrl(self.config.urls.renewMembershipModal, {'step': step, 'event': self.config.cgid}),
8364                className: 'renew-membership-modal',
8365                width: '95%',
8366                height: '92%',
8367                maxWidth: '620px',
8368                maxHeight: modalMaxHeight,
8369                closeButton: true
8370            });
8371
8372            $('body').css('overflow', 'hidden');
8373            $('body').css('position', 'fixed');
8374            $('body').css('width', '100%');
8375
8376            $(document).bind('cbox_closed', function(){
8377                $('body').css('overflow', 'auto');
8378                $('body').css('position', 'static');
8379            });
8380        },
8381		adjustStickyButton: function() {
8382			var stickyButtons = [
8383				'.sticky-bottom-card.mobile-only',
8384				'.sticky-bottom-card-select'
8385			];
8386			var pageContent = $('.page__content');
8387			var tabletBreakpoint = 840
8388			var mobileBreakpoint = 720
8389
8390			// Check if the window width is between 720px and 840px
8391			if ($(window).width() >= mobileBreakpoint && $(window).width() <= tabletBreakpoint) {
8392				var totalStickyHeight = 0;
8393
8394				stickyButtons.forEach(function(selector) {
8395					var stickyButton = $(selector);
8396					if (stickyButton.length) {
8397						var stickyHeight = stickyButton.outerHeight(true);
8398						totalStickyHeight += stickyHeight;
8399					}
8400				});
8401
8402				if (pageContent.length) {
8403					pageContent.css('padding-bottom', totalStickyHeight + 'px');
8404				}
8405			} else {
8406				if (pageContent.length) {
8407					pageContent.css('padding-bottom', '');
8408				}
8409			}
8410		},
8411		/**
8412		 * Init function
8413		 */
8414		init: function (config) {
8415			var self = this;
8416
8417			// stop all execution if not on the right place
8418			if (typeof config.currentLocation === "undefined") {
8419				return;
8420			}
8421
8422			$.extend(config, {
8423				renderElement: '#calendar',
8424				isMobileScreen: function () {
8425					return $(window).width() < self.config.mobileBreakPoint ? true : false;
8426				},
8427				isSwipe: function () {
8428					return Modernizr.touchevents ? true : false;
8429				}
8430			});
8431
8432			// make startdate till enddate key array
8433			config.startdate = self.toDate(config.startdate);
8434			config.enddate = self.toDate(config.enddate);
8435			config.today = self.toDate(config.today);
8436
8437			config.alldays = self.createDaysFromPeriod(config.startdate, config.enddate);
8438
8439			config.productsdates = [];
8440
8441			$.extend(self, {
8442				RecordFilter: {
8443					startdate: null,
8444					enddate: null,
8445					count: 62,
8446					offset: 0
8447
8448				},
8449				config: config,
8450				ConfigTranslator: {
8451					getDefaultValue: function (from) {
8452						return self.config.selected.date !== null && self.config.selected.date !== "null" ? self.toDate(self.config.selected.date) : null;
8453					},
8454					getMonths: function () {
8455						return self.config.isMobileScreen() ? 1 : 2;
8456					},
8457                    /**
8458                     * Get calendar min date
8459
8460
8461
8462                     * @return {date} start date of calendar rendering
8463                     */
8464                    getMinDate: function () {
8465                        // check whatever event start date is in the future
8466                        return config.startdate > self.getToday() ? config.startdate : self.getToday();
8467                    },
8468					getMaxDate: function () {
8469						return self.config.enddate;
8470					},
8471                    /**
8472
8473                     * Get all invalid days
8474                     * @return {Array} all invalid days
8475                     */
8476                    getInvalidDays: function () {
8477                        var days = [];
8478                        $.each(self.config.alldays, function(index, value) {
8479                            if (self.config.productsdates.indexOf(value) === -1) {
8480                                days.push(self.toDate(value));
8481                            }
8482                        });
8483                        return days;
8484                    },
8485                    /**
8486                     * Get all market days
8487                     * @return {Array} array of days objects
8488                     */
8489					getMarkedDays: function () {
8490						var days = [],
8491							today = self.getToday();
8492						$.each(self.config.products, function (index, product) {
8493
8494                            var date = self.toDate(product.date);
8495                            var text = '';
8496
8497                            if (product.soldOut) {
8498
8499                                text = 'SOLD OUT';
8500                                self.config.soldOutDates.push(date.getFullYear() + '-' + date.getMonth() + '-' + date.getDate());
8501
8502                            } else if (date.valueOf() == today.valueOf()) {
8503                                text = 'TODAY';
8504                            }
8505
8506							days.push({
8507								id: product.id,
8508								d: date,
8509								text: text,
8510								color: 'transparent'
8511							});
8512						});
8513						return days;
8514					},
8515					getSwipe: function () {
8516						return self.config.isSwipe();
8517					}
8518				}
8519			});
8520
8521			self.TatePushStatePageManager = new TatePushStatePageManager();
8522
8523			$(window).on("CalendarOnInit", function () {
8524                self.validate();
8525			});
8526
8527			$(window).on("CalendarOnMonthLoaded", function (event, year, month, inst) {
8528				var $headers = $('.dw-cal-btnw-m'),
8529					$wrap = $('.mbsc-w-p.dw-cal-c');
8530				if ($headers.length=== 2) {
8531					$wrap.prepend('<div class="calendar-vertical-line" />');
8532				}
8533				self.loadDates(year, month, inst);
8534
8535				inst.currentDate = {"year" : year, "month" : month};
8536			});
8537
8538			$(window).on("CalendarOnMonthChange", function (event, year, month, inst) {
8539				// Moving through the calendar, left or right, GTM data
8540				if ((inst.currentDate.year > year || inst.currentDate.year === year) && inst.currentDate.month > month) {
8541					if (dataLayer.length > 0) {
8542						dataLayer.push({
8543							event: "dateLeft",
8544							label: ""
8545						});
8546					}
8547				} else {
8548					if (dataLayer.length > 0) {
8549						dataLayer.push({
8550							event: "dateRight",
8551							label: ""
8552						});
8553					}
8554				}
8555			});
8556
8557			$(window).on('CalendarOnEventSelect', function (e, event, d, inst) {});
8558
8559			self.locationChange();
8560		}
8561	};
8562
8563	return TateCalendarManager;
8564});
8565
8566define('components/TateStepNavigationManager',['jquery', 'components/TatePushStatePageManager', 'components/TateUtil'], function ($, TatePushStatePageManager, TateUtil) {
8567
8568	"use strict";
8569
8570	var TateStepNavigationManager = function () {
8571
8572		var self = this;
8573
8574		$(document).ready(function () {
8575			var config = typeof TateStepNavigationManagerConfig !== "undefined" ? TateStepNavigationManagerConfig : {};
8576			self.init(config);
8577		});
8578	};
8579
8580	TateStepNavigationManager.prototype = {
8581		init: function (config) {
8582			var self = this;
8583
8584			$.extend(self, {
8585				config: config
8586			});
8587		}
8588	};
8589	
8590	return TateStepNavigationManager;
8591});
8592
8593define('components/TateMetaManager',['jquery', 'components/TateUtil'], function ($, TateUtil) {
8594
8595    "use strict";
8596
8597    var TateMetaManager = function () {
8598
8599        var self = this;
8600
8601        $(document).ready(function () {
8602            var config = typeof TateMetaManagerConfig !== "undefined" ? TateMetaManagerConfig : {};
8603            self.init(config);
8604        }.bind(this));
8605    };
8606
8607    TateMetaManager.prototype = {
8608        /**
8609         * Set document title
8610         * @param {string} title
8611         */
8612        setTitle: function (title) {
8613            if (title) {
8614                $(document).prop('title', title);
8615            }
8616        },
8617        /**
8618         * Attach events handler
8619         */
8620        events: function () {
8621            var self = this;
8622
8623            $(window).on('PushStatePageOnLoadComplete', function(e, url, title) {
8624                if (title) {
8625                    self.setTitle(title);
8626                }
8627            });
8628        },
8629        /**
8630         * Init handler
8631         * @param  {object} config
8632         */
8633        init: function (config) {
8634            var self = this;
8635            this.config = config;
8636            this.events();
8637        }
8638    };
8639
8640    return TateMetaManager;
8641});
8642
8643define('vanilla/tate-jquery',['jquery'], function ($) {
8644    var TatejQueryUILib = function () {
8645        /*
8646         *
8647         * Tate JavaScript API
8648         *
8649         */
8650
8651        // Tate's global object
8652        var $tate = $tate || {};
8653
8654        (function($){
8655
8656            //
8657            // Internal variables
8658            //
8659            $tate._vars = {}
8660
8661            // allow lazy-loading of jQuery selectors
8662            // why? cos if you load main.js before the <body> then the selectors 
8663            // will be empty cos the page hasn't loaded any elements yet
8664            $tate._select = function(selector) {
8665                return $(selector);
8666            }
8667
8668            //
8669            // Current page
8670            //
8671            page = {
8672                promos: function() { return $tate._select('.promo-list') },
8673                search_box: function() { return $tate._select('input[name=q]') }
8674            };
8675
8676            $tate.page = page;
8677
8678            //
8679            // Equal Height methods and properties
8680            //
8681
8682            // internal switch not to perform the equal height refresh "too often" (when
8683            // the user resizes their browser window)
8684            $tate._vars.doEqualHeight = [];
8685
8686            // exported method to do equal height promo items (the method below only contains
8687            // the logic)
8688            $tate.equalHeightElement = function(selector) {
8689
8690                var varname = selector ? selector : '<global>';
8691                var elements = selector ? $(selector) : $tate.page.promos();
8692
8693                // are we already doing equal height or have done it super very recently?
8694                if(varname in $tate._vars.doEqualHeight && 
8695                    !$tate._vars.doEqualHeight[varname]) 
8696                    return;
8697              
8698                // current element or children elements?
8699                if(!elements.hasClass('promo-list--equal-height'))
8700                    elements = elements.find('.promo-list--equal-height');
8701
8702               // reset height on selected elements to let the browser render the box'es
8703                elements.each(function(){
8704                    $(this).removeAttr("data-original-height");
8705                    $(this).find('.promo-item--inner').css('height' , '');
8706                });
8707
8708                $tate._vars.doEqualHeight[varname] = false;
8709                $tate._applyEqualHeightElement(selector);
8710                // do not call equalHeight more than once every 100 msecs 
8711                setTimeout(function() { $tate._vars.doEqualHeight[varname] = true }, 100);
8712            }
8713
8714            // internal method - actually does the equal height transformation
8715            $tate._applyEqualHeightElement = function(selector) {
8716                
8717                var elements = selector ? $(selector) : $tate.page.promos();
8718
8719                // current element or children elements?
8720                if(!elements.hasClass('promo-list--equal-height'))
8721                    elements = elements.find('.promo-list--equal-height');
8722
8723                elements.each(function(){
8724                    var t = $(this);
8725                    var h = 0;
8726                    
8727                    t.find('.promo-item--inner').each(function(){
8728                        th = $(this).outerHeight();
8729                        $(this).attr("data-original-height", th);
8730                        if (th > h){
8731                            h = th;
8732                        }
8733                    }).css('height' , h + 'px'); // 16 is the baseline px
8734                })
8735            }
8736
8737            // reset promo items' height, optionally starting from $(selector)
8738            $tate.resetHeight = function(selector) {
8739
8740                var elements = selector ? $(selector) : $tate.page.promos();
8741
8742                // current element or children elements?
8743                if(!elements.hasClass('promo-list--equal-height'))
8744                    elements = elements.find('.promo-list--equal-height')
8745
8746                elements.each(function(){
8747                    $(this).find('.promo-item--inner').css('height' , 'auto');
8748                })
8749            }
8750
8751            // show promo items after the list has been built
8752            $tate.showPromos = function() {
8753                $tate.page.promos().find('div.promo-hidden-on-load').each(function() {
8754                    $(this).removeClass('promo-hidden-on-load')
8755                });
8756            }
8757
8758            //
8759            // Hamburger menu toggling - suitable for vegetarians
8760            //
8761
8762            // current selected hamburger menu
8763            $tate._vars.hamburgerMenu = null;
8764
8765            $tate.initHamburgerMenu = function() {
8766                $('*[data-role=hamburger-switch]').each(function() {
8767
8768                    var el = $(this);
8769                    var switch_for = el.attr('data-switch-for');
8770                    if(!switch_for)
8771                        return
8772
8773                    $tate.registerHamburger(el, switch_for);
8774                });
8775            }
8776
8777            $tate.registerHamburger = function(el, switch_for) {
8778
8779                el.click(function(event) {
8780                    event.preventDefault();
8781
8782                    var current = $tate._vars.hamburgerMenu;
8783                    if(current)
8784                    {
8785                        if(current == switch_for)
8786                        {
8787                            $('#'+current).slideUp();
8788                            $tate._vars.hamburgerMenu = null;
8789                            return;
8790                        }
8791
8792                        $('#'+current).hide();
8793                    }
8794
8795                    $tate._vars.hamburgerMenu = switch_for;
8796
8797                    $('#'+switch_for).slideDown();
8798                });
8799
8800            }
8801           
8802            //
8803            // Footer menu toggling (only enabled on small screens)
8804            //
8805            // similar to the above Hamburger functions
8806            // but it's simpler for the footer columns (no toggling involved)
8807
8808            $tate.initFooter = function() {
8809            
8810                // Hides the footer columns when width is too little
8811                // and user can reveal them by clicking on the 
8812                // column's title
8813                $("footer h2.block-title").each(function() {
8814                    var title = $(this);
8815                    title.on("click", function(event) {
8816                        event.preventDefault();
8817                        var magic_attr = title.css('cursor');
8818
8819                        // the 'cursor' css is set via @media queries (when width is small)
8820                        if(typeof(magic_attr) == 'undefined' || magic_attr == null 
8821                            || magic_attr != 'pointer')
8822                            return;
8823
8824                        title.siblings("div.content").each(function() {
8825                            var content = $(this);
8826                            if(content.is(":visible"))
8827                            {
8828                                content.slideUp();
8829                                title.removeClass("footer-block--open");
8830                            }
8831                            else
8832                            {
8833                                content.slideDown();
8834                                title.addClass("footer-block--open");
8835                            }
8836                        });
8837                    });
8838                });
8839            };
8840
8841            //
8842            // Dirty Elements
8843            // 
8844            // Used to flag elements (usually form elements) whose data or state 
8845            // has changed (e.g. user entered their email address to a form)
8846            // and the form hasn't been submitted (let's say).
8847            // 
8848            // When such scenario occurs and the users are ready to leave the
8849            // page - a warning will be displayed to the users asking to confirm
8850            //
8851
8852            $tate._vars.dirty = {};
8853
8854            // $tate.flagAsDirty(tc_name, tc_msg);
8855            $tate.flagAsDirty = function(name, msg) {
8856                $tate._vars.dirty[name] = msg;
8857            };
8858            
8859            $tate.unflagAsDirty = function(name) {
8860                $tate._vars.dirty[name] = false;
8861            };
8862            
8863            $tate.dirtyElements = function() {
8864
8865                els = [];
8866
8867                for(d in $tate._vars.dirty)
8868                {
8869                    if(!$tate._vars.dirty[d])
8870                        continue
8871                    els.push({
8872                        'name': d,
8873                        'msg': $tate._vars.dirty[d]
8874                    });
8875                }
8876
8877                return els;
8878            };
8879
8880            $tate.resetDirtyElements = function() {
8881                $tate._vars.dirty = {};
8882            }
8883
8884            $tate.isDirty = function(name) {
8885                if(!name)
8886                    return false;
8887                if($tate._vars.dirty[name])
8888                    return true;
8889                return false;
8890            }
8891
8892            $(window).on("beforeunload", function(){
8893
8894                var els = $tate.dirtyElements();
8895                if(!els || typeof(els) == "undefined" || els.length < 1)
8896                    return;
8897
8898                // only show the first 'available' error msg
8899                for(i in els)
8900                {
8901                    if(typeof(els[i].msg) == "undefined")
8902                        continue
8903                    return els[i].msg;
8904                }
8905            });
8906
8907        })(jQuery);
8908
8909
8910        /*
8911         *
8912         * Tate UI JS Components (note: they are jQuery plug-ins)
8913         *
8914         * method names are prefixed with 'tateUi' to avoid name clashing eg. jQueryUI provides
8915         * a .tabs() method - so ours is called .tateUiTabs()
8916         *
8917         */
8918
8919        (function($){
8920
8921            // Tate Modal Box Form
8922            $.fn.tateUiModalForm = function(options){
8923                var target = $(this);
8924
8925                if (!target) return 0; // Impossible?
8926                if (!options) throw new Error("tateUiModalForm - No options specified...");
8927                if (!options.html) throw new Error("tateUiModalForm - No HTML for lightbox specified...");
8928
8929                var settings = $.extend({
8930                    'onSubmit': function(){
8931                        return 0;
8932                    }
8933                });
8934
8935                var form_destination = options.html.attr('action'),
8936                    form_method = options.html.attr('method'),
8937                    form_original_button_state = options.html.find('button[data-action=submit]').attr('class');
8938
8939                // Button fires modal window. 
8940                target.on('click', function(e){
8941                    e.preventDefault();
8942
8943                    var form_html = options.html.wrap('<div>').parent().html();
8944                    options.html.unwrap();
8945
8946                    var html = [];
8947
8948                    var modal_header = '<div class="modal-box"><div class="modal-box--content">',
8949                        modal_footer = '</div></div>';
8950
8951                    html.push(modal_header);
8952                    html.push(form_html);
8953                    html.push(modal_footer);
8954
8955                    $.colorbox({
8956                        html: html.join("\n"),
8957                        closeButton: false,
8958                        onClosed: function() { $.colorbox.remove(); }
8959                    });
8960
8961                    $('.modal-box--content form').tateUiForm({});
8962                });
8963
8964                // Submit button inside form
8965                $(document).on('submit', options.html, function(e){
8966                    e.preventDefault();
8967
8968                    // Javascript scope ¯\(°_o)/¯
8969                    var data = {},
8970                        $submitBtn = $('.modal-box--content form button[data-action=submit]');
8971
8972                    $submitBtn.tateUiIcon('fa-spinner fa-spin');
8973
8974                    // Set up data for ajax
8975                    $('.modal-box--content form input').each(function(){
8976                        var $me = $(this);
8977                        data[$me.attr('name')] = $me.val();
8978                    });
8979
8980                    // Fire off ajax to end point specified in form
8981                    $.ajax({
8982                        data: data,
8983                        url: form_destination,
8984                        method: form_method,
8985                        success: function(data, textStatus, xhr){
8986                            if (xhr.status >= 200 && xhr.status <= 300){
8987                                // Perfec'
8988                                $submitBtn.tateUiIcon('fa-check');
8989                                if (typeof options.onSuccess == "function") options.onSuccess(data, xhr);
8990                                setTimeout(function(){
8991                                    $.colorbox.remove();   
8992                                }, 250);
8993                            } else {
8994                                // Oops?
8995                                $submitBtn.tateUiIcon('fa-times');
8996                                if (typeof options.onError == "function") options.onError(data, xhr);
8997                            }
8998                        },
8999                        error: function(data, textStatus, xhr){
9000                            // Definite oops.
9001                            $submitBtn.tateUiIcon('fa-times');
9002
9003                            $('.modal-box--content').html("");
9004                            $('.modal-box--content').html('<div class="patternui--typography-set-inner"><p class="standfirst">Oops...</p> <p>We seem to be having some trouble on our end. Please try again later.</p></div><div class="btn-row"><div class="btn-group btn-group--right"><a href="#" id="closeModelBtn" class="btn btn--medium btn--icon-r btn--shamrock"><i class="icon fa fa-close"></i><span class="btn-text">Okay</span></a></div></div>');
9005                            
9006                            // Set bind for close model button
9007                            $(document).on('click', '#closeModelBtn', function(e){
9008                                e.preventDefault();
9009                                $.colorbox.remove();
9010                            });
9011
9012                            if (typeof options.onError == "function") options.onError(data, xhr);
9013                        }
9014                    });
9015                });
9016            };
9017
9018            // Tate Confirm Box Form
9019            $.fn.tateUiConfirm = function(options){
9020                var target = $(this);
9021
9022                if(!target)
9023                    return;
9024
9025                var settings = $.extend({
9026                    'title': 'Confirm',
9027                    'message': '',
9028                    'onConfirm': function() {}
9029                }, options);
9030
9031                var message = settings.message;
9032                var title = settings.title;
9033                    
9034                target.on('click', function(event) {
9035                    event.preventDefault();
9036
9037                    var confirm_title = '<div class="modal-box--title">' + title + '</div>';
9038
9039                    var confirm_content = '<div class="modal-box--content">' + message + '</div>';
9040
9041                    var confirm_buttons = '<div class="btn-row">' +
9042                    '<div class="btn-group btn-group--center">' +
9043                    '<button class="btn btn--small btn--text-link" data-role="button" data-value="0">' +
9044                    '<span class="btn-text">Cancel</span>' +
9045                    '</button>' +
9046                    '<button class="btn btn--small btn--vermilion" data-role="button" data-value="1">' +
9047                    '<span class="btn-text">Confirm</span>' +
9048                    '</button>' +
9049                    '</div>' +
9050                    '</div>';
9051
9052                    var confirm_box = $('<div class="confirm modal-box">' + confirm_title + confirm_content + confirm_buttons + '</div>');
9053                    $('body').append(confirm_box);
9054                    $('.confirm [data-role=button]').on('click', function(event){
9055                    
9056                        event.preventDefault();
9057
9058                        $.colorbox.remove();
9059                        $('.confirm').remove();
9060                        if ($(this).attr('data-value') == "1") {
9061                            settings.onConfirm();
9062                        }
9063                        return false;
9064                    });
9065
9066                    $.colorbox({
9067                        inline:true, 
9068                        href:".confirm",
9069                        closeButton: false,
9070                        onClosed: function() { $.colorbox.remove(); $('.confirm').remove(); }
9071                    });
9072
9073                    return false;
9074                });
9075            };
9076
9077            // Tate focus search box <input>
9078            $.fn.tateUiFocusTextToEnd = function() {
9079                this.focus();
9080                var $thisVal = this.val();
9081                this.val('').val($thisVal);
9082                return this;
9083            };
9084
9085            // when called with a <button>, it replaces the button's icon style
9086            // eg. $(mybutton).icon('fa-trash-o')
9087            // $(mybutton).icon('fa-spinner fa-spin')
9088            $.fn.tateUiIcon = function(classes) {
9089                if(!classes) classes = '';
9090                var button = $(this);
9091                var icon = button.find('i.icon');
9092                if(icon.length == 0) {
9093                    // add the icon if it doesn't exist
9094                    button
9095                        .find('span.btn-text')
9096                        .before('<i class="icon fa '+classes+'"></i>');
9097                }
9098                else {
9099                    icon
9100                        .removeClass()
9101                        .addClass('icon fa ' + classes);
9102                }
9103                return this;
9104            };
9105
9106            // Tate Tabs
9107            // params:
9108            // 'onTabChange': function(tabid) { your_code_here; } (optional)
9109            $.fn.tateUiTabs = function(options) {
9110
9111                var settings = $.extend({}, options);
9112
9113                var ti = $(this);
9114
9115                ti.find('.tabbed--content-item').addClass('tabbed--content--hidden');
9116                ti.find('.tabbed--content_title').hide();
9117
9118                ti.find('.tabbed-menu--item').each(function(){
9119
9120                    var t = $(this);
9121                    var a = t.find('a');
9122
9123                    if(t.hasClass('active')){
9124                        var i = t.index();
9125                        var tc = t.closest('.tabbed-container').find('.tabbed--content-item');
9126                        tc.eq(i).removeClass('tabbed--content--hidden');
9127                    }
9128
9129                    a.on('click' , function(event, i){
9130                        event.preventDefault();
9131                        ti.find('.tabbed-menu--item').removeClass('active');
9132
9133                        var link = $(this);
9134                        link.parent().addClass('active');
9135
9136                        var i = link.parent().index();
9137
9138                        ti.find('.tabbed--content-item').addClass('tabbed--content--hidden');
9139                        ti.find('.tabbed--content-item').eq(i).removeClass('tabbed--content--hidden');
9140
9141                        if(settings.onTabChange)
9142                            settings.onTabChange(i)
9143                    });
9144                });
9145            }
9146
9147            //
9148            // Tate Infinite Scroll
9149            //
9150
9151            $.fn.tateUiInfiniteScroll = function(params) {
9152
9153                var pages = params.pages;
9154                var url = params.url;
9155
9156                if(typeof(pages) == "undefined" || typeof(url) == "undefined")
9157                {
9158                    return;
9159                }
9160
9161                // which element triggers fetching the next page
9162                // default to "window" ie when the user scrolls
9163                // all the way down
9164                if(typeof(params.trigger) != "undefined")
9165                    trigger = params.trigger
9166                else
9167                    trigger = false
9168
9169                if(typeof(params.html_label) != "undefined")
9170                    html_label = params.html_label
9171                else
9172                    html_label = "<p>Loading...</p>"
9173
9174                var name;
9175                if(typeof(params.name) != "undefined")
9176                    name = params.name.replace(/ /g, "_")
9177                else
9178                    name = params.url.replace(/\//g, "_")
9179              
9180                if(typeof(params.back_to_top) != "undefined" && params.back_to_top)
9181                    var back_to_top = '<div id="'+name+'_b2t" class="link--back-to-top" style="display:none;"><a href="#page" class="btn btn--small btn--icon-r btn--square"><i class="icon fa fa-angle-double-up"></i><span class="btn-text">Back to top</span></a>';
9182                    $("footer.canvas--section").prepend(back_to_top);
9183
9184                var marker_id = name + "_marker";
9185                var b2t = $("#"+name+"_b2t");
9186
9187                // extra url (GET) parameters
9188                var args = params.args;
9189                if(typeof(args) == "undefined")
9190                    args = {}
9191
9192                var current_page = 1;
9193                var initial_offset = 0;
9194                if(typeof(params.current_page) != "undefined")
9195                    current_page = params.current_page;
9196                    if(current_page == 0)
9197                        initial_offset = 1
9198
9199                if( typeof($tate._vars.infinite_scroll) == "undefined" )
9200                    $tate._vars.infinite_scroll = {}
9201                
9202                $tate._vars.infinite_scroll[name] = {
9203                    'pages': pages,
9204                    'current_page': current_page,
9205                    'pending': false,
9206                    'b2t': "hidden"         // any animation in-progress? 
9207                                            // one 'hidden', 'animating', 'shown'
9208                };
9209                
9210                var container = $(this);
9211                
9212                // the marker <div> ie the <div> that will receive the next page of data
9213                var marker = "<div id='"+marker_id+"' style='display:none'>"+html_label+"</div>";
9214                container.append(marker);
9215
9216                $(window).scroll(function()
9217                {
9218                    if(trigger)
9219                        relative_pos = $("#"+trigger).offset().top
9220                    else
9221                        relative_pos = $(document).height()
9222
9223                    // show/hide the "back to top" button depending on the scrolling position
9224                    if($(window).scrollTop() < 500) 
9225                    {
9226                        if($tate._vars.infinite_scroll[name]['b2t'] != 'hidden') {
9227                            $tate._vars.infinite_scroll[name]['b2t'] = 'animating';
9228                            b2t.animate(
9229                                { marginTop:'-60px' }, 
9230                                250, 
9231                                function() {
9232                                    b2t.hide();
9233                                    $tate._vars.infinite_scroll[name]['b2t'] = 'hidden';
9234                                }
9235                            );
9236                        }
9237                    }
9238                    else 
9239                    {
9240                        if($tate._vars.infinite_scroll[name]['b2t'] != 'shown') {
9241
9242                            $tate._vars.infinite_scroll[name]['b2t'] = 'animating';
9243                            b2t.show().animate(
9244                                { marginTop:'0px' }, 
9245                                250, 
9246                                function() {
9247                                    $tate._vars.infinite_scroll[name]['b2t'] = 'shown';
9248                                }
9249                            );
9250                        }
9251                    }
9252
9253                    if($(window).scrollTop() >= relative_pos - $(window).height())
9254                    {
9255                        if($('div#'+marker_id).length == 0)
9256                            return;
9257                        
9258                        if($tate._vars.infinite_scroll[name]['pages'] <=
9259                            $tate._vars.infinite_scroll[name]['current_page'] + initial_offset)
9260                            // we're done here
9261                            return;
9262
9263                        $('div#'+marker_id).show();
9264                        var next_page = $tate._vars.infinite_scroll[name]['current_page'] + 1;
9265
9266                        // don't run the same ajax query
9267                        if($tate._vars.infinite_scroll[name]['pending'])
9268                            return;
9269                        $tate._vars.infinite_scroll[name]['pending'] = true;
9270
9271                        new_data_url = url + "?";
9272                        var url_args = [];
9273                        for(var k in args)
9274                            if(args[k])
9275                                url_args.push(k + "=" + args[k]);
9276
9277                        url_args.push("page=" + next_page);
9278                        new_data_url += url_args.join("&");
9279
9280                        $.ajax({
9281                            url: new_data_url,
9282                            success: function(html)
9283                            {
9284                                $('div#'+marker_id).remove();
9285                                if(html)
9286                                {
9287                                    container.append(html);
9288                                    container.append(marker);
9289
9290                                    $(document).trigger("tate-scroll-page-loaded", [name, next_page, pages]);
9291                                }
9292
9293                                $tate._vars.infinite_scroll[name]['pending'] = false;
9294                                $tate._vars.infinite_scroll[name]['current_page']++;
9295
9296                                if($tate._vars.infinite_scroll[name]['b2t'] != 'hidden')
9297                                    return;
9298
9299                                $tate._vars.infinite_scroll[name]['b2t'] = 'animating';
9300                                b2t.css('marginTop', '-60px').show().animate(
9301                                    { marginTop:'0px' }, 
9302                                    250, 
9303                                    function() {
9304                                        $tate._vars.infinite_scroll[name]['b2t'] = 'shown';
9305                                    }
9306                                );
9307                            }
9308                        });
9309                    }
9310                });
9311
9312            }
9313
9314
9315            //
9316            // Tate Forms
9317            //
9318
9319            // reset all error messages
9320            $.fn.tateUiResetForm = function() {
9321                $(this)
9322                    .find('span.form-input--error-text')
9323                    .closest('div.form-field').removeClass('form-field--error');
9324            }
9325
9326            // turn the selected <form> into a managed form, with validation
9327            // rules and UI-lib custom error classes
9328            // relies on jquery-validate - http://jqueryvalidation.org/
9329            $.fn.tateUiForm = function(options) {
9330
9331                var form = $(this);
9332
9333                // auto-add the required asterisk for required fields
9334                form.find('.form-field--required input').each(function() {
9335                    var input_id = $(this).attr('id');
9336                    if(input_id) {
9337                        // front-end
9338                        form.find('label[for='+input_id+']').append('<abbr title="Required">*</abbr>');
9339                        // also need that for jquery-validate
9340                        $(this).attr('required', 'required');
9341                    }
9342                });
9343
9344                // enable the custom submit button, if any
9345                form.find('button[data-action=submit]').on('click', function(event) {
9346                    event.preventDefault();
9347                    form.submit();
9348                });
9349
9350                // built-in validation methods
9351                // cf. http://jqueryvalidation.org/
9352                rules = {};
9353                rules_len = 0;
9354                valid_rules = {
9355                    'email': true,
9356                    'url': true
9357                };
9358
9359                // fixes attributes - UI lib conventions vs. jQuery validation conventions
9360                form.find('.form-field--input').each(function() {
9361                    var div = $(this);
9362                    if(div.hasClass('form-field--valid-url'))
9363                    {
9364                        div.find('input').attr('data-validate', 'url');
9365                    }
9366                    else if(div.hasClass('form-field--valid-email'))
9367                    {
9368                        div.find('input').attr('data-validate', 'email');
9369                    }
9370                });
9371                
9372                form.find('input[data-validate]').each(function() {
9373                    input = $(this);
9374                    name = input.attr('name');
9375                    validator = input.attr('data-validate');
9376                    if(name && validator && valid_rules[validator])
9377                    {
9378                        rules[name] = {};
9379                        rules[name][validator] = true;
9380                        rules_len++;
9381                    }
9382                });
9383
9384                validateOptions = {
9385                    'showErrors': function(errorMap, errorList) {
9386
9387                        // reset state
9388                        form.tateUiResetForm();
9389
9390                        // fixes error message 'top' property
9391                        for(err in errorMap) {
9392                            if(err){
9393                                var err_el = form.find('span[data-error-for='+err+']');
9394                                if(err_el) {
9395                                    var th = err_el.siblings('label').outerHeight();
9396                                    if(th)
9397                                        err_el.css('top', th + 'px')
9398                                    err_el
9399                                        .closest('div.form-field').addClass('form-field--error');
9400                                }
9401                                break;
9402                            }
9403                        }
9404                    },
9405                    'onfocusout': function(element, event) {
9406                        // reset state
9407                        form.tateUiResetForm();
9408                    }
9409                }
9410
9411                if(rules && rules_len > 0)
9412                    validateOptions.rules = rules;
9413
9414                if(options != null && options.submitHandler)
9415                    validateOptions.submitHandler = options.submitHandler;
9416
9417                // $.validate() is provided by the jquery-validate plug-in
9418                form.validate(validateOptions);
9419
9420                // track changes across the form
9421                // and message users when they're leaving the
9422                // page with unsaved changes
9423
9424                if(typeof(options.trackChanges) == "undefined")
9425                    return;
9426
9427                tc = options.trackChanges;
9428
9429                // optional option
9430                tc_name = tc.name;
9431                if(typeof(tc_name) == "undefined")
9432                    tc_name = "trackelt_" + new Date().valueOf() + "_" + Math.floor((Math.random() * 10000));
9433
9434                tc_msg = tc.msg;
9435                if(typeof(tc_msg) == "undefined")
9436                {
9437                    return;
9438                }
9439
9440                // tags  to track (other tags are not supported: https://api.jquery.com/change/)
9441                tags = ['input', 'select', 'textarea'];
9442
9443                for(i in tags)
9444                    form.find(tags[i]).change(function() {
9445                        $tate.flagAsDirty(tc_name, tc_msg);
9446                    });
9447
9448                form.submit(function() {
9449                    $tate.unflagAsDirty(tc_name);
9450                });
9451
9452            }
9453
9454        }(jQuery));
9455
9456
9457        // READ MORE FUNCTION
9458
9459        $(document).ready(function(){
9460            var rmCount = 0;
9461               
9462            $('.read-more--hidden').each(function(){
9463                $('.read-more--active p').css('margin-bottom' , '0');
9464                var t = $(this);
9465                $(this).after('<span class="read-more--handle rm--' + rmCount + '">Read more</span>');
9466                $(this).hide();
9467                $('.rm--'+ rmCount).on('click' , function(){
9468                    if ($(this).hasClass('rm--open')) {
9469                        $(this).text('Read more').removeClass('rm--open');
9470                        t.slideUp();
9471                    }
9472                    else {
9473                        t.slideDown();
9474                        $(this).text('Read less').addClass('rm--open');
9475                    }
9476                   
9477                })
9478                rmCount++;
9479               
9480            });
9481            
9482            $('.jump-menu__select-list').on('change', function(){
9483                var selection = $(this).find('option:selected');
9484                if (selection && selection.attr('value')) {
9485                    window.location = selection.attr('value');
9486                }
9487            });
9488            
9489            // Make the social share functions open in a new window
9490            $('.splash-header__share .list__item--social a').on('click', function(e){
9491                var href = $(this).attr('href');
9492                if (href.substring(0, 4) != 'http')
9493                    return; // Don't interfere with default behaviour of the 'share by email' link
9494                e.preventDefault();
9495                window.open(href, 'targetWindow', "toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=" 
9495+ 500 + ",height=" + 500);
9496                return false;
9497            });
9498
9499        });
9500    };
9501
9502    return new TatejQueryUILib();
9503});
9504/*
9505*	Our library is built here. Require all components and insert them into one object.
9506*	The object is exported through AMD and exposed globally in the build process.
9507*
9508*
9509*	@notice IT IS VERY IMPORTANT TO MAINTAIN THE ORDER OF THE IMPORTS AND FUNCTION PARAMETERS
9510*	FAILING THAT WILL IMPORT THE WRONG CLASS UNDER THE WRONG OBJECT.
9511*/
9512
9513define('TateUI',[
9514    'components/TatePolyfill',
9515    'components/TateWindow',
9516    'components/TateStorage',
9517    'components/TateNotification',
9518    'components/TateGOTO',
9519    'components/TateReady',
9520    'components/TateBTT',
9521    'components/TateLazyLoad',
9522    'components/TateOpenTimes',
9523    'components/TateLogin',
9524    'components/TateNav',
9525    'components/TateBreadcrumbs',
9526    'components/TateShareIcons',
9527    'components/TateSplash',
9528    'components/TateAdvancedFilters',
9529    'components/TateTabs',
9530    'components/TateReviews',
9531    'components/TateJPlayer',
9532    'components/TateInfiniteScroll',
9533    'components/BannerSlider',
9534
9535    'components/TateModalManager',
9536    'components/TateAccordionManager',
9537    'components/TateAccordionPanelManager',
9538    'components/TatePushStatePageManager',
9539    'components/TateCalendarManager',
9540    'components/TateUtil',
9541    'components/TateStepNavigationManager',
9542    'components/TateMetaManager',
9543
9544    'vanilla/tate-jquery'
9545], function (
9546    TatePolyfill,
9547    TateWindow,
9548    TateStorage,
9549    TateNotification,
9550    TateGOTO,
9551    TateReady,
9552    TateBTT,
9553    TateLazyLoad,
9554    TateOpenTimes,
9555    TateLogin,
9556    TateNav,
9557    TateBreadcrumbs,
9558    TateShareIcons,
9559    TateSplash,
9560    TateAdvancedFilters,
9561    TateTabs,
9562    TateReviews,
9563    TateJPlayer,
9564    TateInfiniteScroll,
9565    BannerSlider,
9566
9567    TateModalManager,
9568    TateAccordionManager,
9569    TateAccordionPanelManager,
9570    TatePushStatePageManager,
9571    TateCalendarManager,
9572    TateUtil,
9573    TateStepNavigationManager,
9574    TateMetaManager,
9575    
9576    TateJQUERY
9577) {
9578
9579var tate_ready = new TateReady(),
9580    tate_polyfill = new TatePolyfill(),
9581    tate_back_to_top = new TateBTT(),
9582    tate_lazy_load = new TateLazyLoad(),
9583    tate_open_times = new TateOpenTimes(),
9584    tate_login = new TateLogin(),
9585    tate_nav = new TateNav(),
9586    tate_bc = new TateBreadcrumbs(),
9587    tate_share_icons = new TateShareIcons(),
9588    tate_splash = new TateSplash(),
9589    tate_advanced_filters = new TateAdvancedFilters(),
9590    tate_tabs = new TateTabs(),
9591    tate_reviews = new TateReviews(),
9592    tate_jplayer = new TateJPlayer(),
9593    tate_infinite_scroll = new TateInfiniteScroll(),
9594    tate_banner_slider = new BannerSlider(),
9595
9596    tate_modal_manager = new TateModalManager(),
9597    tate_accordion_manager = new TateAccordionManager(),
9598    tate_accordion_panel_manager = new TateAccordionPanelManager(),
9599    tate_push_state_page_manager = new TatePushStatePageManager(),
9600    tate_calendar_manager = new TateCalendarManager(),
9601    
9602    tate_step_navigation_manager = new TateStepNavigationManager(),
9603    tate_meta_manager = new TateMetaManager();
9604
9605return {
9606    get: function (what) {
9607        return what;
9608    },
9609    "Polyfill": TatePolyfill,
9610    "Window": TateWindow,
9611    "Storage": TateStorage,
9612    "Notification": TateNotification,
9613    "GOTO": TateGOTO,
9614    "Ready": TateReady,
9615    "BTT": TateBTT,
9616    "LazyLoad": TateLazyLoad,
9617    "OpenTimes": TateOpenTimes,
9618    "Login": TateLogin,
9619    "Nav": TateNav,
9620    "Breadcrumbs": TateBreadcrumbs,
9621    "ShareIcons": TateShareIcons,
9622    "Splash": TateSplash,
9623    "AdvancedFilters": TateAdvancedFilters,
9624    "Tabs": TateTabs,
9625    "Reviews": TateReviews,
9626    "JPlayer": TateJPlayer,
9627    "InfiniteScroll": TateInfiniteScroll,
9628    "BannerSlider": BannerSlider,
9629    "TateModalManager": TateModalManager,
9630    "TateAccordionManager": TateAccordionManager,
9631    "TateAccordionPanelManager": TateAccordionPanelManager,
9632    "TatePushStatePageManager": TatePushStatePageManager,
9633    "TateCalendarManager": TateCalendarManager,
9634    "TateUtil": TateUtil,
9635    "TateStepNavigationManager": TateStepNavigationManager
9636};
9637});
9638
9639	return require('TateUI');
9640}));

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.