PageSourceSearch

https://www.hotelceilidh-donia.co.uk/bundles/eviivo/instantweb-pag…QkULmdFafUUmwze-oE3uiUNuMhPgS6Re1IQJ9JbfJA1

js hotelceilidh-donia.co.uk collected 2026-09-26 01:23:36 UTC 234,971 bytes, 5,509 lines download raw bytes

1/*! Picturefill - v2.1.0 - 2014-07-25
2* http://scottjehl.github.io/picturefill
3* Copyright (c) 2014 https://github.com/scottjehl/picturefill/blob/master/Authors.txt; Licensed MIT */
4/*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas, David Knight. Dual MIT/BSD license */
5
6window.matchMedia || (window.matchMedia = function () {
7    "use strict";
8
9    // For browsers that support matchMedium api such as IE 9 and webkit
10    var styleMedia = (window.styleMedia || window.media);
11
12    // For those that don't support matchMedium
13    if (!styleMedia) {
14        var style = document.createElement('style'),
15            script = document.getElementsByTagName('script')[0],
16            info = null;
17
18        style.type = 'text/css';
19        style.id = 'matchmediajs-test';
20
21        script.parentNode.insertBefore(style, script);
22
23        // 'style.currentStyle' is used by IE <= 8 and 'window.getComputedStyle' for all other browsers
24        info = ('getComputedStyle' in window) && window.getComputedStyle(style, null) || style.currentStyle;
25
26        styleMedia = {
27            matchMedium: function (media) {
28                var text = '@media ' + media + '{ #matchmediajs-test { width: 1px; } }';
29
30                // 'style.styleSheet' is used by IE <= 8 and 'style.textContent' for all other browsers
31                if (style.styleSheet) {
32                    style.styleSheet.cssText = text;
33                } else {
34                    style.textContent = text;
35                }
36
37                // Test if media query is true or false
38                return info.width === '1px';
39            }
40        };
41    }
42
43    return function (media) {
44        return {
45            matches: styleMedia.matchMedium(media || 'all'),
46            media: media || 'all'
47        };
48    };
49}());
50/*! Picturefill - Responsive Images that work today.
51*  Author: Scott Jehl, Filament Group, 2012 ( new proposal implemented by Shawn Jansepar )
52*  License: MIT/GPLv2
53*  Spec: http://picture.responsiveimages.org/
54*/
55(function (w, doc) {
56    // Enable strict mode
57    "use strict";
58
59    // If picture is supported, well, that's awesome. Let's get outta here...
60    if (w.HTMLPictureElement) {
61        w.picturefill = function () { };
62        return;
63    }
64
65    // HTML shim|v it for old IE (IE9 will still need the HTML video tag workaround)
66    doc.createElement("picture");
67
68    // local object for method references and testing exposure
69    var pf = {};
70
71    // namespace
72    pf.ns = "picturefill";
73
74    // srcset support test
75    pf.srcsetSupported = "srcset" in doc.createElement("img");
76    pf.sizesSupported = w.HTMLImageElement.sizes;
77
78    // just a string trim workaround
79    pf.trim = function (str) {
80        return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, "");
81    };
82
83    // just a string endsWith workaround
84    pf.endsWith = function (str, suffix) {
85        return str.endsWith ? str.endsWith(suffix) : str.indexOf(suffix, str.length - suffix.length) !== -1;
86    };
87
88	/**
89	 * Shortcut method for matchMedia ( for easy overriding in tests )
90	 */
91    pf.matchesMedia = function (media) {
92        return w.matchMedia && w.matchMedia(media).matches;
93    };
94
95	/**
96	 * Shortcut method for `devicePixelRatio` ( for easy overriding in tests )
97	 */
98    pf.getDpr = function () {
99        return (w.devicePixelRatio || 1);
100    };
101
102	/**
103	 * Get width in css pixel value from a "length" value
104	 * http://dev.w3.org/csswg/css-values-3/#length-value
105	 */
106    pf.getWidthFromLength = function (length) {
107        // If no length was specified, or it is 0 or negative, default to `100vw` (per the spec).
108        length = length && (parseFloat(length) > 0 || length.indexOf("calc(") > -1) ? length : "100vw";
109
110		/**
111		* If length is specified in  `vw` units, use `%` instead since the div we�re measuring
112		* is injected at the top of the document.
113		*
114		* TODO: maybe we should put this behind a feature test for `vw`?
115		*/
116        length = length.replace("vw", "%");
117
118        // Create a cached element for getting length value widths
119        if (!pf.lengthEl) {
120            pf.lengthEl = doc.createElement("div");
121            doc.documentElement.insertBefore(pf.lengthEl, doc.documentElement.firstChild);
122        }
123
124        // Positioning styles help prevent padding/margin/width on `html` from throwing calculations off.
125        pf.lengthEl.style.cssText = "position: absolute; left: 0; width: " + length + ";";
126
127        if (pf.lengthEl.offsetWidth <= 0) {
128            // Something has gone wrong. `calc()` is in use and unsupported, most likely. Default to `100vw` (`100%`, for broader support.):
129            pf.lengthEl.style.cssText = "width: 100%;";
130        }
131
132        return pf.lengthEl.offsetWidth;
133    };
134
135    // container of supported mime types that one might need to qualify before using
136    pf.types = {};
137
138    // Add support for standard mime types.
139    pf.types["image/jpeg"] = true;
140    pf.types["image/gif"] = true;
141    pf.types["image/png"] = true;
142
143    // test svg support
144    pf.types["image/svg+xml"] = doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image", "1.1");
145
146    // test webp support, only when the markup calls for it
147    pf.types["image/webp"] = function () {
148        // based on Modernizr's lossless img-webp test
149        // note: asynchronous
150        var img = new w.Image(),
151            type = "image/webp";
152
153        img.onerror = function () {
154            pf.types[type] = false;
155            picturefill();
156        };
157        img.onload = function () {
158            pf.types[type] = img.width === 1;
159            picturefill();
160        };
161        img.src = "data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAAAAAAfQ//73v/+BiOh/AAA=";
162    };
163
164	/**
165	 * Takes a source element and checks if its type attribute is present and if so, supported
166	 * Note: for type tests that require a async logic,
167	 * you can define them as a function that'll run only if that type needs to be tested. Just make the test function call picturefill again when it is complete.
168	 * see the async webp test above for example
169	 */
170    pf.verifyTypeSupport = function (source) {
171        var type = source.getAttribute("type");
172        // if type attribute exists, return test result, otherwise return true
173        if (type === null || type === "") {
174            return true;
175        } else {
176            // if the type test is a function, run it and return "pending" status. The function will rerun picturefill on pending elements once finished.
177            if (typeof (pf.types[type]) === "function") {
178                pf.types[type]();
179                return "pending";
180            } else {
181                return pf.types[type];
182            }
183        }
184    };
185
186	/**
187	* Parses an individual `size` and returns the length, and optional media query
188	*/
189    pf.parseSize = function (sourceSizeStr) {
190        var match = /(\([^)]+\))?\s*(.+)/g.exec(sourceSizeStr);
191        return {
192            media: match && match[1],
193            length: match && match[2]
194        };
195    };
196
197	/**
198	 * Takes a string of sizes and returns the width in pixels as a number
199	 */
200    pf.findWidthFromSourceSize = function (sourceSizeListStr) {
201        // Split up source size list, ie ( max-width: 30em ) 100%, ( max-width: 50em ) 50%, 33%
202        //                            or (min-width:30em) calc(30% - 15px)
203        var sourceSizeList = pf.trim(sourceSizeListStr).split(/\s*,\s*/),
204            winningLength;
205
206        for (var i = 0, len = sourceSizeList.length; i < len; i++) {
207            // Match <media-condition>? length, ie ( min-width: 50em ) 100%
208            var sourceSize = sourceSizeList[i],
209                // Split "( min-width: 50em ) 100%" into separate strings
210                parsedSize = pf.parseSize(sourceSize),
211                length = parsedSize.length,
212                media = parsedSize.media;
213
214            if (!length) {
215                continue;
216            }
217            if (!media || pf.matchesMedia(media)) {
218                // if there is no media query or it matches, choose this as our winning length
219                // and end algorithm
220                winningLength = length;
221                break;
222            }
223        }
224
225        // pass the length to a method that can properly determine length
226        // in pixels based on these formats: http://dev.w3.org/csswg/css-values-3/#length-value
227        return pf.getWidthFromLength(winningLength);
228    };
229
230    pf.parseSrcset = function (srcset) {
231		/**
232		* A lot of this was pulled from Boris Smus� parser for the now-defunct WHATWG `srcset`
233		* https://github.com/borismus/srcset-polyfill/blob/master/js/srcset-info.js
234		*
235		* 1. Let input (`srcset`) be the value passed to this algorithm.
236		* 2. Let position be a pointer into input, initially pointing at the start of the string.
237		* 3. Let raw candidates be an initially empty ordered list of URLs with associated 
238		*    unparsed descriptors. The order of entries in the list is the order in which entries 
239		*    are added to the list.
240		*/
241        var candidates = [];
242
243        while (srcset !== "") {
244            srcset = srcset.replace(/^\s+/g, "");
245
246            // 5. Collect a sequence of characters that are not space characters, and let that be url.
247            var pos = srcset.search(/\s/g),
248                url, descriptor = null;
249
250            if (pos !== -1) {
251                url = srcset.slice(0, pos);
252
253                var last = url[url.length - 1];
254
255                // 6. If url ends with a U+002C COMMA character (,), remove that character from url
256                // and let descriptors be the empty string. Otherwise, follow these substeps
257                // 6.1. If url is empty, then jump to the step labeled descriptor parser.
258
259                if (last === "," || url === "") {
260                    url = url.replace(/,+$/, "");
261                    descriptor = "";
262                }
263                srcset = srcset.slice(pos + 1);
264
265                // 6.2. Collect a sequence of characters that are not U+002C COMMA characters (,), and 
266                // let that be descriptors.
267                if (descriptor === null) {
268                    var descpos = srcset.indexOf(",");
269                    if (descpos !== -1) {
270                        descriptor = srcset.slice(0, descpos);
271                        srcset = srcset.slice(descpos + 1);
272                    } else {
273                        descriptor = srcset;
274                        srcset = "";
275                    }
276                }
277            } else {
278                url = srcset;
279                srcset = "";
280            }
281
282            // 7. Add url to raw candidates, associated with descriptors.
283            if (url || descriptor) {
284                candidates.push({
285                    url: url,
286                    descriptor: descriptor
287                });
288            }
289        }
290        return candidates;
291    };
292
293    pf.parseDescriptor = function (descriptor, sizesattr) {
294        // 11. Descriptor parser: Let candidates be an initially empty source set. The order of entries in the list 
295        // is the order in which entries are added to the list.
296        var sizes = sizesattr || "100vw",
297            sizeDescriptor = descriptor && descriptor.replace(/(^\s+|\s+$)/g, ""),
298            widthInCssPixels = pf.findWidthFromSourceSize(sizes),
299            resCandidate;
300
301        if (sizeDescriptor) {
302            var splitDescriptor = sizeDescriptor.split(" ");
303
304            for (var i = splitDescriptor.length + 1; i >= 0; i--) {
305                if (splitDescriptor[i] !== undefined) {
306                    var curr = splitDescriptor[i],
307                        lastchar = curr && curr.slice(curr.length - 1);
308
309                    if ((lastchar === "h" || lastchar === "w") && !pf.sizesSupported) {
310                        resCandidate = parseFloat((parseInt(curr, 10) / widthInCssPixels));
311                    } else if (lastchar === "x") {
312                        var res = curr && parseFloat(curr, 10);
313                        resCandidate = res && !isNaN(res) ? res : 1;
314                    }
315                }
316            }
317        }
318        return resCandidate || 1;
319    };
320
321	/**
322	 * Takes a srcset in the form of url/
323	 * ex. "images/pic-medium.png 1x, images/pic-medium-2x.png 2x" or
324	 *     "images/pic-medium.png 400w, images/pic-medium-2x.png 800w" or
325	 *     "images/pic-small.png"
326	 * Get an array of image candidates in the form of
327	 *      {url: "/foo/bar.png", resolution: 1}
328	 * where resolution is http://dev.w3.org/csswg/css-values-3/#resolution-value
329	 * If sizes is specified, resolution is calculated
330	 */
331    pf.getCandidatesFromSourceSet = function (srcset, sizes) {
332        var candidates = pf.parseSrcset(srcset),
333            formattedCandidates = [];
334
335        for (var i = 0, len = candidates.length; i < len; i++) {
336            var candidate = candidates[i];
337
338            formattedCandidates.push({
339                url: candidate.url,
340                resolution: pf.parseDescriptor(candidate.descriptor, sizes)
341            });
342        }
343        return formattedCandidates;
344    };
345
346	/*
347	 * if it's an img element and it has a srcset property,
348	 * we need to remove the attribute so we can manipulate src
349	 * (the property's existence infers native srcset support, and a srcset-supporting browser will prioritize srcset's value over our winning picture candidate)
350	 * this moves srcset's value to memory for later use and removes the attr
351	 */
352    pf.dodgeSrcset = function (img) {
353        if (img.srcset) {
354            img[pf.ns].srcset = img.srcset;
355            img.removeAttribute("srcset");
356        }
357    };
358
359	/*
360	 * Accept a source or img element and process its srcset and sizes attrs
361	 */
362    pf.processSourceSet = function (el) {
363        var srcset = el.getAttribute("srcset"),
364            sizes = el.getAttribute("sizes"),
365            candidates = [];
366
367        // if it's an img element, use the cached srcset property (defined or not)
368        if (el.nodeName.toUpperCase() === "IMG" && el[pf.ns] && el[pf.ns].srcset) {
369            srcset = el[pf.ns].srcset;
370        }
371
372        if (srcset) {
373            candidates = pf.getCandidatesFromSourceSet(srcset, sizes);
374        }
375        return candidates;
376    };
377
378    pf.applyBestCandidate = function (candidates, picImg) {
379        var candidate,
380            length,
381            bestCandidate;
382
383        candidates.sort(pf.ascendingSort);
384
385        length = candidates.length;
386        bestCandidate = candidates[length - 1];
387
388        for (var i = 0; i < length; i++) {
389            candidate = candidates[i];
390            if (candidate.resolution >= pf.getDpr()) {
391                bestCandidate = candidate;
392                break;
393            }
394        }
395
396        if (bestCandidate && !pf.endsWith(picImg.src, bestCandidate.url)) {
397            picImg.src = bestCandidate.url;
398            // currentSrc attribute and property to match
399            // http://picture.responsiveimages.org/#the-img-element
400            picImg.currentSrc = picImg.src;
401        }
402    };
403
404    pf.ascendingSort = function (a, b) {
405        return a.resolution - b.resolution;
406    };
407
408	/*
409	 * In IE9, <source> elements get removed if they aren't children of
410	 * video elements. Thus, we conditionally wrap source elements
411	 * using <!--[if IE 9]><video style="display: none;"><![endif]-->
412	 * and must account for that here by moving those source elements
413	 * back into the picture element.
414	 */
415    pf.removeVideoShim = function (picture) {
416        var videos = picture.getElementsByTagName("video");
417        if (videos.length) {
418            var video = videos[0],
419                vsources = video.getElementsByTagName("source");
420            while (vsources.length) {
421                picture.insertBefore(vsources[0], video);
422            }
423            // Remove the video element once we're finished removing its children
424            video.parentNode.removeChild(video);
425        }
426    };
427
428	/*
429	 * Find all `img` elements, and add them to the candidate list if they have
430	 * a `picture` parent, a `sizes` attribute in basic `srcset` supporting browsers,
431	 * a `srcset` attribute at all, and they haven�t been evaluated already.
432	 */
433    pf.getAllElements = function () {
434        var elems = [],
435            imgs = doc.getElementsByTagName("img");
436
437        for (var h = 0, len = imgs.length; h < len; h++) {
438            var currImg = imgs[h];
439
440            if (currImg.parentNode.nodeName.toUpperCase() === "PICTURE" ||
441                (currImg.getAttribute("srcset") !== null) || currImg[pf.ns] && currImg[pf.ns].srcset !== null) {
442                elems.push(currImg);
443            }
444        }
445        return elems;
446    };
447
448    pf.getMatch = function (img, picture) {
449        var sources = picture.childNodes,
450            match;
451
452        // Go through each child, and if they have media queries, evaluate them
453        for (var j = 0, slen = sources.length; j < slen; j++) {
454            var source = sources[j];
455
456            // ignore non-element nodes
457            if (source.nodeType !== 1) {
458                continue;
459            }
460
461            // Hitting the `img` element that started everything stops the search for `sources`.
462            // If no previous `source` matches, the `img` itself is evaluated later.
463            if (source === img) {
464                return match;
465            }
466
467            // ignore non-`source` nodes
468            if (source.nodeName.toUpperCase() !== "SOURCE") {
469                continue;
470            }
471            // if it's a source element that has the `src` property set, throw a warning in the console
472            if (source.getAttribute("src") !== null && typeof console !== undefined) {
473                console.warn("The `src` attribute is invalid on `picture` `source` element; instead, use `srcset`.");
474            }
475
476            var media = source.getAttribute("media");
477
478            // if source does not have a srcset attribute, skip
479            if (!source.getAttribute("srcset")) {
480                continue;
481            }
482
483            // if there's no media specified, OR w.matchMedia is supported
484            if ((!media || pf.matchesMedia(media))) {
485                var typeSupported = pf.verifyTypeSupport(source);
486
487                if (typeSupported === true) {
488                    match = source;
489                    break;
490                } else if (typeSupported === "pending") {
491                    return false;
492                }
493            }
494        }
495
496        return match;
497    };
498
499    function picturefill(opt) {
500        var elements,
501            element,
502            parent,
503            firstMatch,
504            candidates,
505
506            options = opt || {};
507        elements = options.elements || pf.getAllElements();
508
509        // Loop through all elements
510        for (var i = 0, plen = elements.length; i < plen; i++) {
511            element = elements[i];
512            parent = element.parentNode;
513            firstMatch = undefined;
514            candidates = undefined;
515
516            // expando for caching data on the img
517            if (!element[pf.ns]) {
518                element[pf.ns] = {};
519            }
520
521            // if the element has already been evaluated, skip it
522            // unless `options.force` is set to true ( this, for example,
523            // is set to true when running `picturefill` on `resize` ).
524            if (!options.reevaluate && element[pf.ns].evaluated) {
525                continue;
526            }
527
528            // if `img` is in a `picture` element
529            if (parent.nodeName.toUpperCase() === "PICTURE") {
530
531                // IE9 video workaround
532                pf.removeVideoShim(parent);
533
534                // return the first match which might undefined
535                // returns false if there is a pending source
536                // TODO the return type here is brutal, cleanup
537                firstMatch = pf.getMatch(element, parent);
538
539                // if any sources are pending in this picture due to async type test(s)
540                // remove the evaluated attr and skip for now ( the pending test will
541                // rerun picturefill on this element when complete)
542                if (firstMatch === false) {
543                    continue;
544                }
545            } else {
546                firstMatch = undefined;
547            }
548
549            // Cache and remove `srcset` if present and we�re going to be doing `picture`/`srcset`/`sizes` polyfilling to it.
550            if (parent.nodeName.toUpperCase() === "PICTURE" ||
551                (element.srcset && !pf.srcsetSupported) ||
552                (!pf.sizesSupported && (element.srcset && element.srcset.indexOf("w") > -1))) {
553                pf.dodgeSrcset(element);
554            }
555
556            if (firstMatch) {
557                candidates = pf.processSourceSet(firstMatch);
558                pf.applyBestCandidate(candidates, element);
559            } else {
560                // No sources matched, so we�re down to processing the inner `img` as a source.
561                candidates = pf.processSourceSet(element);
562
563                if (element.srcset === undefined || element[pf.ns].srcset) {
564                    // Either `srcset` is completely unsupported, or we need to polyfill `sizes` functionality.
565                    pf.applyBestCandidate(candidates, element);
566                } // Else, resolution-only `srcset` is supported natively.
567            }
568
569            // set evaluated to true to avoid unnecessary reparsing
570            element[pf.ns].evaluated = true;
571        }
572    }
573
574	/**
575	 * Sets up picture polyfill by polling the document and running
576	 * the polyfill every 250ms until the document is ready.
577	 * Also attaches picturefill on resize
578	 */
579    function runPicturefill() {
580        picturefill();
581        var intervalId = setInterval(function () {
582            // When the document has finished loading, stop checking for new images
583            // https://github.com/ded/domready/blob/master/ready.js#L15
584            picturefill();
585            if (/^loaded|^i|^c/.test(doc.readyState)) {
586                clearInterval(intervalId);
587                return;
588            }
589        }, 250);
590        if (w.addEventListener) {
591            var resizeThrottle;
592            w.addEventListener("resize", function () {
593                if (!w._picturefillWorking) {
594                    w._picturefillWorking = true;
595                    w.clearTimeout(resizeThrottle);
596                    resizeThrottle = w.setTimeout(function () {
597                        picturefill({ reevaluate: true });
598                        w._picturefillWorking = false;
599                    }, 60);
600                }
601            }, false);
602        }
603    }
604
605    runPicturefill();
606
607    /* expose methods for testing */
608    picturefill._ = pf;
609
610    /* expose picturefill */
611    if (typeof module === "object" && typeof module.exports === "object") {
612        // CommonJS, just export
613        module.exports = picturefill;
614    } else if (typeof define === "function" && define.amd) {
615        // AMD support
616        define(function () { return picturefill; });
617    } else if (typeof w === "object") {
618        // If no AMD and we are in the browser, attach to window
619        w.picturefill = picturefill;
620    }
621
622})(this, this.document);
623
624/*! lazysizes - v1.1.3 -  Licensed MIT */
625!function (a, b) { var c = b(a, a.document); a.lazySizes = c, "object" == typeof module && module.exports ? module.exports = c : "function" == typeof define && define.amd && define(c) }(window, function (a, b) { "use strict"; if (b.getElementsByClassName) { var c, d = b.documentElement, e = a.addEventListener, f = a.setTimeout, g = a.requestAnimationFrame || f, h = /^picture$/i, i = ["load", "error", "lazyincluded", "_lazyloaded"], j = function (a, b) { var c = new RegExp("(\\s|^)" + b + "(\\s|$)"); return a.className.match(c) && c }, k = function (a, b) { j(a, b) || (a.className += " " + b) }, l = function (a, b) { var c; (c = j(a, b)) && (a.className = a.className.replace(c, " ")) }, m = function (a, b, c) { var d = c ? "addEventListener" : "removeEventListener";
625 c && m(a, b), i.forEach(function (c) { a[d](c, b) }) }, n = function (a, c, d, e, f) { var g = b.createEvent("CustomEvent"); return g.initCustomEvent(c, !e, !f, d || {}), g.details = g.detail, a.dispatchEvent(g), g }, o = function (b, d) { var e; a.HTMLPictureElement || ((e = a.picturefill || a.respimage || c.pf) ? e({ reevaluate: !0, elements: [b] }) : d && d.src && (b.src = d.src)) }, p = function (a, b) { return getComputedStyle(a, null)[b] }, q = function (a, b, d) { for (d = d || a.offsetWidth; d < c.minSize && b && !a._lazysizesWidth;) d = b.offsetWidth, b = b.parentNode; return d }, r = function (b) { var d, e = 0, h = a.Date, i = function () { d = !1, e = h.now(), b() }, j = function () { f(i) }, k = function () { g(j) }; return function () { if (!d) { var a = c.throttle - (h.now() - e); d = !0, 9 > a && (a = 9), f(k, a) } } }, s = function () { var i, q, s, u, v, w, x, y, z, A, B, C, D, E = /^img$/i, F = /^iframe$/i, G = "onscroll" in a && !/glebot/.test(navigator.userAgent), H = 0, I = 0, J = 0, K = 1, L = function (a) { J--, a && a.target && m(a.target, L), (!a || 0 > J || !a.target) && (J = 0) }, M = function (a, b) { var c, d = a, e = "hidden" != p(a, "visibility"); for (y -= b, B += b, z -= b, A += b; e && (d = d.offsetParent) ;) e = (p(d, "opacity") || 1) > 0, e && "visible" != p(d, "overflow") && (c = d.getBoundingClientRect(), e = A > c.left && z < c.right && B > c.top - 1 && y < c.bottom + 1); return e }, N = function () { var a, b, d, e, f, g, h, j, k; if ((v = c.loadMode) && 8 > J && (a = i.length)) { for (b = 0, K++, D > I && 1 > J && K > 3 && v > 2 ? (I = D, K = 0) : I = I != C && v > 1 && K > 2 && 6 > J ? C : H; a > b; b++) i[b] && !i[b]._lazyRace && (G ? ((j = i[b].getAttribute("data-expand")) && (g = 1 * j) || (g = I), k !== g && (w = innerWidth + g, x = innerHeight + g, h = -1 * g, k = g), d = i[b].getBoundingClientRect(), (B = d.bottom) >= h && (y = d.top) <= x && (A = d.right) >= h && (z = d.left) <= w && (B || A || z || y) && (s && 3 > J && !j && (3 > v || 4 > K) || M(i[b], g)) ? (S(i[b], d.width), f = !0) : !f && s && !e && 3 > J && 4 > K && v > 2 && (q[0] || c.preloadAfterLoad) && (q[0] || !j && (B || A || z || y || "auto" != i[b].getAttribute(c.sizesAttr))) && (e = q[0] || i[b])) : S(i[b])); e && !f && S(e) } }, O = r(N), P = function (a) { k(a.target, c.loadedClass), l(a.target, c.loadingClass), m(a.target, P) }
625, Q = function (a, b) { try { a.contentWindow.location.replace(b) } catch (c) { a.setAttribute("src", b) } }, R = function () { var a, b = [], c = function () { for (; b.length;) b.shift()(); a = !1 }; return function (d) { b.push(d), a || (a = !0, g(c)) } }(), S = function (a, b) { var d, e, g, i, p, q, r, v, w, x, y, z = E.test(a.nodeName), A = a.getAttribute(c.sizesAttr) || a.getAttribute("sizes"), B = "auto" == A; (!B && s || !z || !a.src && !a.srcset || a.complete || j(a, c.errorClass)) && (a._lazyRace = !0, J++, R(function () { if (a._lazyRace && delete a._lazyRace, l(a, c.lazyClass), !(w = n(a, "lazybeforeunveil")).defaultPrevented) { if (A && (B ? (t.updateElem(a, !0, b), k(a, c.autosizesClass)) : a.setAttribute("sizes", A)), q = a.getAttribute(c.srcsetAttr), p = a.getAttribute(c.srcAttr), z && (r = a.parentNode, v = r && h.test(r.nodeName || "")), x = w.detail.firesLoad || "src" in a && (q || p || v), w = { target: a }, x && (m(a, L, !0), clearTimeout(u), u = f(L, 2500), k(a, c.loadingClass), m(a, P, !0)), v) for (d = r.getElementsByTagName("source"), e = 0, g = d.length; g > e; e++) (y = c.customMedia[d[e].getAttribute("data-media") || d[e].getAttribute("media")]) && d[e].setAttribute("media", y), i = d[e].getAttribute(c.srcsetAttr), i && d[e].setAttribute("srcset", i); q ? a.setAttribute("srcset", q) : p && (F.test(a.nodeName) ? Q(a, p) : a.setAttribute("src", p)), (q || v) && o(a, { src: p }) } (!x || a.complete) && (x ? L(w) : J--, P(w)) })) }, T = function () { var a, b = function () { c.loadMode = 3, O() }; s = !0, K += 8, c.loadMode = 3, e("scroll", function () { 3 == c.loadMode && (c.loadMode = 2), clearTimeout(a), a = f(b, 99) }, !0) }; return { _: function () { i = b.getElementsByClassName(c.lazyClass), q = b.getElementsByClassName(c.lazyClass + " " + c.preloadClass), C = c.expand, D = Math.round(C * c.expFactor), e("scroll", O, !0), e("resize", O, !0), a.MutationObserver ? new MutationObserver(O).observe(d, { childList: !0, subtree: !0, attributes: !0 }) : (d.addEventListener("DOMNodeInserted", O, !0), d.addEventListener("DOMAttrModified", O, !0), setInterval(O, 999)), e("hashchange", O, !0), ["focus", "mouseover", "click", "load", "transitionend", "animationend", "webkitAnimationEnd"].forEach(function (a) { b.addEventListener(a, O, !0) }), (s = /d$|^c/.test(b.readyState)) ? T() : (e("load", T), b.addEventListener("DOMContentLoaded", O)), O() }, checkElems: O, unveil: S } }(), t = function () { var a, d = function (a, b, c) { var d, e, f, g, i = a.parentNode; if (i && (c = q(a, i, c), g = n(a, "lazybeforesizes", { width: c, dataAttr: !!b }), !g.defaultPrevented && (c = g.detail.width, c && c !== a._lazysizesWidth))) { if (a._lazysizesWidth = c, c += "px", a.setAttribute("sizes", c), h.test(i.nodeName || "")) for (d = i.getElementsByTagName("source"), e = 0, f = d.length; f > e; e++) d[e].setAttribute("sizes", c); g.detail.dataAttr || o(a, g.detail) } }, f = function () { var b, c = a.length; if (c) for (b = 0; c > b; b++) d(a[b]) }, g = r(f); return { _: function () { a = b.getElementsByClassName(c.autosizesClass), e("resize", g) }, checkElems: g, updateElem: d } }(), u = function () { u.i || (u.i = !0, t._(), s._()) }; return function () { var b, d = { lazyClass: "lazyload", loadedClass: "lazyloaded", loadingClass: "lazyloading", preloadClass: "lazypreload", errorClass: "lazyerror", autosizesClass: "lazyautosizes", srcAttr: "data-src", srcsetAttr: "data-srcset", sizesAttr: "data-sizes", minSize: 40, customMedia: {}, init: !0, expFactor: 2, expand: 359, loadMode: 2, throttle: 125 }; c = a.lazySizesConfig || a.lazysizesConfig || {}; for (b in d) b in c || (c[b] = d[b]); a.lazySizesConfig = c, f(function () { c.init && u() }) }(), { cfg: c, autoSizer: t, loader: s, init: u, uP: o, aC: k, rC: l, hC: j, fire: n, gW: q } } });
626/*! jCarousel - v0.3.9 - 2018-07-30
627* http://sorgalla.com/jcarousel/
628* Copyright (c) 2006-2018 Jan Sorgalla; Licensed MIT */
629
630!function (t) { "use strict"; var i = t.jCarousel = {}; i.version = "0.3.9"; var s = /^([+\-]=)?(.+)$/; i.parseTarget = function (t) { var i = !1, e = "object" != typeof t ? s.exec(t) : null; return e ? (t = parseInt(e[2], 10) || 0, e[1] && (i = !0, "-=" === e[1] && (t *= -1))) : "object" != typeof t && (t = parseInt(t, 10) || 0), { target: t, relative: i } }, i.detectCarousel = function (t) { for (var i; t.length > 0;
vendor: 10,372 bytes, line 630
630) { if ((i = t.filter("[data-jcarousel]")).length > 0) return i; if ((i = t.find("[data-jcarousel]")).length > 0) return i; t = t.parent() } return null }, i.base = function (s) { return { version: i.version, _options: {}, _element: null, _carousel: null, _init: t.noop, _create: t.noop, _destroy: t.noop, _reload: t.noop, create: function () { return this._element.attr("data-" + s.toLowerCase(), !0).data(s, this), !1 === this._trigger("create") ? this : (this._create(), this._trigger("createend"), this) }, destroy: function () { return !1 === this._trigger("destroy") ? this : (this._destroy(), this._trigger("destroyend"), this._element.removeData(s).removeAttr("data-" + s.toLowerCase()), this) }, reload: function (t) { return !1 === this._trigger("reload") ? this : (t && this.options(t), this._reload(), this._trigger("reloadend"), this) }, element: function () { return this._element }, options: function (i, s) { if (0 === arguments.length) return t.extend({}, this._options); if ("string" == typeof i) { if (void 0 === s) return void 0 === this._options[i] ? null : this._options[i]; this._options[i] = s } else this._options = t.extend({}, this._options, i); return this }, carousel: function () { return this._carousel || (this._carousel = i.detectCarousel(this.options("carousel") || this._element), this._carousel || t.error('Could not detect carousel for plugin "' + s + '"')), this._carousel }, _trigger: function (i, e, r) { var n, o = !1; return r = [this].concat(r || []), (e || this._element).each(function () { n = t.Event((s + ":" + i).toLowerCase()), t(this).trigger(n, r), n.isDefaultPrevented() && (o = !0) }), !o } } }, i.plugin = function (s, e) { var r = t[s] = function (i, s) { this._element = t(i), this.options(s), this._init(), this.create() }; return r.fn = r.prototype = t.extend({}, i.base(s), e), t.fn[s] = function (i) { var e = Array.prototype.slice.call(arguments, 1), n = this; return "string" == typeof i ? this.each(function () { var r = t(this).data(s); if (!r) return t.error("Cannot call methods on " + s + ' prior to initialization; attempted to call method "' + i + '"'); if (!t.isFunction(r[i]) || "_" === i.charAt(0)) return t.error('No such method "' + i + '" for ' + s + " instance"); var o = r[i].apply(r, e); return o !== r && void 0 !== o ? (n = o, !1) : void 0 }) : this.each(function () { var e = t(this).data(s); e instanceof r ? e.reload(i) : new r(this, i) }), n }, r } }(jQuery), function (t, i) { "use strict"; var s = t(i), e = function (t) { return parseFloat(t) || 0 }; t.jCarousel.plugin("jcarousel", { animating: !1, tail: 0, inTail: !1, resizeState: null, resizeTimer: null, lt: null, vertical: !1, rtl: !1, circular: !1, underflow: !1, relative: !1, _options: { list: function () { return this.element().children().eq(0) }, items: function () { return this.list().children() }, animation: 400, transitions: !1, wrap: null, vertical: null, rtl: null, center: !1 }, _list: null, _items: null, _target: t(), _first: t(), _last: t(), _visible: t(), _fullyvisible: t(), _init: function () { var t = this; return t.resizeState = s.width() + "x" + s.height(), this.onWindowResize = function () { t.resizeTimer && clearTimeout(t.resizeTimer), t.resizeTimer = setTimeout(function () { var i = s.width() + "x" + s.height(); i !== t.resizeState && (t.resizeState = i, t.reload()) }, 100) }, this }, _create: function () { this._reload(), s.on("resize.jcarousel", this.onWindowResize) }, _destroy: function () { s.off("resize.jcarousel", this.onWindowResize) }, _reload: function () { this.vertical = this.options("vertical"), null == this.vertical && (this.vertical = e(this.list().height()) > e(this.list().width())), this.rtl = this.options("rtl"), null == this.rtl && (this.rtl = function (i) { if ("rtl" === ("" + i.attr("dir")).toLowerCase()) return !0; var s = !1; return i.parents("[dir]").each(function () { if (/rtl/i.test(t(this).attr("dir"))) return s = !0, !1 }), s }(this._element)), this.lt = this.vertical ? "top" : "left", this.relative = "relative" === this.list().css("position"), this._list = null, this._items = null; var i = this.index(this._target) >= 0 ? this._target : this.closest(); this.circular = "circular" === this.options("wrap"), this.underflow = !1; var s = { left: 0, top: 0 }; return i.length > 0 && (this._prepare(i), this.list().find("[data-jcarousel-clone]").remove(), this._items = null, this.underflow = this._fullyvisible.length >= this.items().length, this.circular = this.circular && !this.underflow, s[this.lt] = this._position(i) + "px"), this.move(s), this }, list: function () { if (null === this._list) { var i = this.options("list"); this._list = t.isFunction(i) ? i.call(this) : this._element.find(i) } return this._list }, items: function () { if (null === this._items) { var i = this.options("items"); this._items = (t.isFunction(i) ? i.call(this) : this.list().find(i)).not("[data-jcarousel-clone]") } return this._items }, index: function (t) { return this.items().index(t) }, closest: function () { var i, s = this, r = this.list().position()[this.lt], n = t(), o = !1, l = this.vertical ? "bottom" : this.rtl && !this.relative ? "left" : "right"; return this.rtl && this.relative && !this.vertical && (r += e(this.list().width()) - this.clipping()), this.items().each(function () { if (n = t(this), o) return !1; var a = s.dimension(n); if ((r += a) >= 0) { if (i = a - e(n.css("margin-" + l)), !(Math.abs(r) - a + i / 2 <= 0)) return !1; o = !0 } }), n }, target: function () { return this._target }, first: function () { return this._first }, last: function () { return this._last }, visible: function () { return this._visible }, fullyvisible: function () { return this._fullyvisible }, hasNext: function () { if (!1 === this._trigger("hasnext")) return !0; var t = this.options("wrap"), i = this.items().length - 1, s = this.options("center") ? this._target : this._last; return !!(i >= 0 && !this.underflow && (t && "first" !== t || this.index(s) < i || this.tail && !this.inTail)) }, hasPrev: function () { if (!1 === this._trigger("hasprev")) return !0; var t = this.options("wrap"); return !!(this.items().length > 0 && !this.underflow && (t && "last" !== t || this.index(this._first) > 0 || this.tail && this.inTail)) }, clipping: function () { return e(this._element["inner" + (this.vertical ? "Height" : "Width")]()) }, dimension: function (t) { return e(t["outer" + (this.vertical ? "Height" : "Width")](!0)) }, scroll: function (i, s, e) { if (this.animating) return this; if (!1 === this._trigger("scroll", null, [i, s])) return this; t.isFunction(s) && (e = s, s = !0); var r = t.jCarousel.parseTarget(i); if (r.relative) { var n, o, l, a, h, u, c, f, d = this.items().length - 1, _ = Math.abs(r.target), p = this.options("wrap"); if (r.target > 0) { var g = this.index(this._last); if (g >= d && this.tail) this.inTail ? "both" === p || "last" === p ? this._scroll(0, s, e) : t.isFunction(e) && e.call(this, !1) : this._scrollTail(s, e); else if (n = this.index(this._target), this.underflow && n === d && ("circular" === p || "both" === p || "last" === p) || !this.underflow && g === d && ("both" === p || "last" === p)) this._scroll(0, s, e); else if (l = n + _, this.circular && l > d) { for (f = d, h = this.items().get(-1); f++ < l;)h = this.items().eq(0), (u = this._visible.index(h) >= 0) && h.after(h.clone(!0).attr("data-jcarousel-clone", !0)), this.list().append(h), u || ((c = {})[this.lt] = this.dimension(h), this.moveBy(c)), this._items = null; this._scroll(h, s, e) } else this._scroll(Math.min(l, d), s, e) } else if (this.inTail) this._scroll(Math.max(this.index(this._first) - _ + 1, 0), s, e); else if (o = this.index(this._first), n = this.index(this._target), l = (a = this.underflow ? n : o) - _, a <= 0 && (this.underflow && "circular" === p || "both" === p || "first" === p)) this._scroll(d, s, e); else if (this.circular && l < 0) { for (f = l, h = this.items().get(0); f++ < 0;) { h = this.items().eq(-1), (u = this._visible.index(h) >= 0) && h.after(h.clone(!0).attr("data-jcarousel-clone", !0)), this.list().prepend(h), this._items = null; var m = this.dimension(h); (c = {})[this.lt] = -m, this.moveBy(c) } this._scroll(h, s, e) } else this._scroll(Math.max(l, 0), s, e) } else this._scroll(r.target, s, e); return this._trigger("scrollend"), this }, moveBy: function (t, i) { var s = this.list().position(), r = 1, n = 0; return this.rtl && !this.vertical && (r = -1, this.relative && (n = e(this.list().width()) - this.clipping())), t.left && (t.left = e(s.left) + n + e(t.left) * r + "px"), t.top && (t.top = e(s.top) + n + e(t.top) * r + "px"), this.move(t, i) }, move: function (i, s) { s = s || {}; var e = this.options("transitions"), r = !!e, n = !!e.transforms, o = !!e.transforms3d, l = s.duration || 0, a = this.list(); if (!r && l > 0) a.animate(i, s); else { var h = s.complete || t.noop, u = {}; if (r) { var c = { transitionDuration: a.css("transitionDuration"), transitionTimingFunction: a.css("transitionTimingFunction"), transitionProperty: a.css("transitionProperty") }, f = h; h = function () { t(this).css(c), f.call(this) }, u = { transitionDuration: (l > 0 ? l / 1e3 : 0) + "s", transitionTimingFunction: e.easing || s.easing, transitionProperty: l > 0 ? n || o ? "all" : i.left ? "left" : "top" : "none", transform: "none" } } o ? u.transform = "translate3d(" + (i.left || 0) + "," + (i.top || 0) + ",0)" : n ? u.transform = "translate(" + (i.left || 0) + "," + (i.top || 0) + ")" : t.extend(u, i), r && l > 0 && a.one("transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd", h), a.css(u), l <= 0 && a.each(function () { h.call(this) }) } }, _scroll: function (i, s, r) { if (this.animating) return t.isFunction(r) && r.call(this, !1), this; if ("object" != typeof i ? i = this.items().eq(i) : void 0 === i.jquery && (i = t(i)), 0 === i.length) return t.isFunction(r) && r.call(this, !1), this; this.inTail = !1, this._prepare(i); var n = this._position(i); if (n === e(this.list().position()[this.lt])) return t.isFunction(r) && r.call(this, !1), this; var o = {}; return o[this.lt] = n + "px", this._animate(o, s, r), this }, _scrollTail: function (i, s) { if (this.animating || !this.tail) return t.isFunction(s) && s.call(this, !1), this; var r = this.list().position()[this.lt]; this.rtl && this.relative && !this.vertical && (r += e(this.list().width()) 
vendor: 6,180 bytes, line 630
630- this.clipping()), this.rtl && !this.vertical ? r += this.tail : r -= this.tail, this.inTail = !0; var n = {}; return n[this.lt] = r + "px", this._update({ target: this._target.next(), fullyvisible: this._fullyvisible.slice(1).add(this._visible.last()) }), this._animate(n, i, s), this }, _animate: function (i, s, e) { if (e = e || t.noop, !1 === this._trigger("animate")) return e.call(this, !1), this; this.animating = !0; var r = this.options("animation"), n = t.proxy(function () { this.animating = !1; var t = this.list().find("[data-jcarousel-clone]"); t.length > 0 && (t.remove(), this._reload()), this._trigger("animateend"), e.call(this, !0) }, this), o = "object" == typeof r ? t.extend({}, r) : { duration: r }, l = o.complete || t.noop; return !1 === s ? o.duration = 0 : void 0 !== t.fx.speeds[o.duration] && (o.duration = t.fx.speeds[o.duration]), o.complete = function () { n(), l.call(this) }, this.move(i, o), this }, _prepare: function (i) { var s, r, n, o = this.index(i), l = o, a = this.dimension(i), h = this.clipping(), u = this.vertical ? "bottom" : this.rtl ? "left" : "right", c = this.options("center"), f = { target: i, first: i, last: i, visible: i, fullyvisible: a <= h ? i : t() }; if (c && (a /= 2, h /= 2), a < h) for (; ;) { if (0 === (s = this.items().eq(++l)).length) { if (!this.circular) break; if (s = this.items().eq(0), i.get(0) === s.get(0)) break; if ((r = this._visible.index(s) >= 0) && s.after(s.clone(!0).attr("data-jcarousel-clone", !0)), this.list().append(s), !r) { var d = {}; d[this.lt] = this.dimension(s), this.moveBy(d) } this._items = null } if (0 === (n = this.dimension(s))) break; if (a += n, f.last = s, f.visible = f.visible.add(s), a - e(s.css("margin-" + u)) <= h && (f.fullyvisible = f.fullyvisible.add(s)), a >= h) break } if (!this.circular && !c && a < h) for (l = o; !(--l < 0 || 0 === (s = this.items().eq(l)).length || 0 === (n = this.dimension(s)) || (a += n, f.first = s, f.visible = f.visible.add(s), a - e(s.css("margin-" + u)) <= h && (f.fullyvisible = f.fullyvisible.add(s)), a >= h));); return this._update(f), this.tail = 0, c || "circular" === this.options("wrap") || "custom" === this.options("wrap") || this.index(f.last) !== this.items().length - 1 || (a -= e(f.last.css("margin-" + u))) > h && (this.tail = a - h), this }, _position: function (t) { var i = this._first, s = e(i.position()[this.lt]), r = this.options("center"), n = r ? this.clipping() / 2 - this.dimension(i) / 2 : 0; return this.rtl && !this.vertical ? (this.relative ? s -= e(this.list().width()) - this.dimension(i) : s -= this.clipping() - this.dimension(i), s += n) : s -= n, !r && (this.index(t) > this.index(i) || this.inTail) && this.tail ? (s = this.rtl && !this.vertical ? s - this.tail : s + this.tail, this.inTail = !0) : this.inTail = !1, -s }, _update: function (i) { var s, e = this, r = { target: this._target, first: this._first, last: this._last, visible: this._visible, fullyvisible: this._fullyvisible }, n = this.index(i.first || r.first) < this.index(r.first), o = function (s) { var o = [], l = []; i[s].each(function () { r[s].index(this) < 0 && o.push(this) }), r[s].each(function () { i[s].index(this) < 0 && l.push(this) }), n ? o = o.reverse() : l = l.reverse(), e._trigger(s + "in", t(o)), e._trigger(s + "out", t(l)), e["_" + s] = i[s] }; for (s in i) o(s); return this } }) }(jQuery, window), function (t) { "use strict"; t.jcarousel.fn.scrollIntoView = function (i, s, e) { var r, n = t.jCarousel.parseTarget(i), o = this.index(this._fullyvisible.first()), l = this.index(this._fullyvisible.last()); if ((r = n.relative ? n.target < 0 ? Math.max(0, o + n.target) : l + n.target : "object" != typeof n.target ? n.target : this.index(n.target)) < o) return this.scroll(r, s, e); if (r >= o && r <= l) return t.isFunction(e) && e.call(this, !1), this; for (var a, h = this.items(), u = this.clipping(), c = this.vertical ? "bottom" : this.rtl ? "left" : "right", f = 0; 0 !== (a = h.eq(r)).length;) { if ((f += this.dimension(a)) >= u) { f - (parseFloat(a.css("margin-" + c)) || 0) !== u && r++; break } if (r <= 0) break; r-- } return this.scroll(r, s, e) } }(jQuery), function (t) { "use strict"; t.jCarousel.plugin("jcarouselControl", { _options: { target: "+=1", event: "click", method: "scroll" }, _active: null, _init: function () { this.onDestroy = t.proxy(function () { this._destroy(), this.carousel().one("jcarousel:createend", t.proxy(this._create, this)) }, this), this.onReload = t.proxy(this._reload, this), this.onEvent = t.proxy(function (i) { i.preventDefault(); var s = this.options("method"); t.isFunction(s) ? s.call(this) : this.carousel().jcarousel(this.options("method"), this.options("target")) }, this) }, _create: function () { this.carousel().one("jcarousel:destroy", this.onDestroy).on("jcarousel:reloadend jcarousel:scrollend", this.onReload), this._element.on(this.options("event") + ".jcarouselcontrol", this.onEvent), this._reload() }, _destroy: function () { this._element.off(".jcarouselcontrol", this.onEvent), this.carousel().off("jcarousel:destroy", this.onDestroy).off("jcarousel:reloadend jcarousel:scrollend", this.onReload) }, _reload: function () { var i, s = t.jCarousel.parseTarget(this.options("target")), e = this.carousel(); if (s.relative) i = e.jcarousel(s.target > 0 ? "hasNext" : "hasPrev"); else { var r = "object" != typeof s.target ? e.jcarousel("items").eq(s.target) : s.target; i = e.jcarousel("target").index(r) >= 0 } return this._active !== i && (this._trigger(i ? "active" : "inactive"), this._active = i), this } }) }(jQuery), function (t) { "use strict"; t.jCarousel.plugin("jcarouselPagination", { _options: { perPage: null, item: function (t) { return '<a href="#' + t + '">' + t + "</a>" }, event: "click", method: "scroll" }, _carouselItems: null, _pages: {}, _items: {}, _currentPage: null, _init: function () { this.onDestroy = t.proxy(function () { this._destroy(), this.carousel().one("jcarousel:createend", t.proxy(this._create, this)) }, this), this.onReload = t.proxy(this._reload, this), this.onScroll = t.proxy(this._update, this) }, _create: function () { this.carousel().one("jcarousel:destroy", this.onDestroy).on("jcarou
630sel:reloadend", this.onReload).on("jcarousel:scrollend", this.onScroll), this._reload() }, _destroy: function () { this._clear(), this.carousel().off("jcarousel:destroy", this.onDestroy).off("jcarousel:reloadend", this.onReload).off("jcarousel:scrollend", this.onScroll), this._carouselItems = null }, _reload: function () { var i = this.options("perPage"); if (this._pages = {}, this._items = {}, t.isFunction(i) && (i = i.call(this)), null == i) this._pages = this._calculatePages(); else for (var s, e = parseInt(i, 10) || 0, r = this._getCarouselItems(), n = 1, o = 0; 0 !== (s = r.eq(o++)).length;)this._pages[n] ? this._pages[n] = this._pages[n].add(s) : this._pages[n] = s, o % e == 0 && n++; this._clear(); var l = this, a = this.carousel().data("jcarousel"), h = this._element, u = this.options("item"), c = this._getCarouselItems().length; t.each(this._pages, function (i, s) { var e = l._items[i] = t(u.call(l, i, s)); e.on(l.options("event") + ".jcarouselpagination", t.proxy(function () { var t = s.eq(0); if (a.circular) { var e = a.index(a.target()), r = a.index(t); parseFloat(i) > parseFloat(l._currentPage) ? r < e && (t = "+=" + (c - e + r)) : r > e && (t = "-=" + (e + (c - r))) } a[this.options("method")](t) }, l)), h.append(e) }), this._update() }, _update: function () { var i, s = this.carousel().jcarousel("target"); t.each(this._pages, function (t, e) { if (e.each(function () { if (s.is(this)) return i = t, !1 }), i) return !1 }), this._currentPage !== i && (this._trigger("inactive", this._items[this._currentPage]), this._trigger("active", this._items[i])), this._currentPage = i }, items: function () { return this._items }, reloadCarouselItems: function () { return this._carouselItems = null, this }, _clear: function () { this._element.empty(), this._currentPage = null }, _calculatePages: function () { for (var t, i, s = this.carousel().data("jcarousel"), e = this._getCarouselItems(), r = s.clipping(), n = 0, o = 0, l = 1, a = {}; 0 !== (t = e.eq(o++)).length;)n + (i = s.dimension(t)) > r && (l++ , n = 0), n += i, a[l] ? a[l] = a[l].add(t) : a[l] = t; return a }, _getCarouselItems: function () { return this._carouselItems || (this._carouselItems = this.carousel().jcarousel("items")), this._carouselItems } }) }(jQuery), function (t, i) { "use strict"; var s, e; t.each({ hidden: "visibilitychange", mozHidden: "mozvisibilitychange", msHidden: "msvisibilitychange", webkitHidden: "webkitvisibilitychange" }, function (t, r) { if (void 0 !== i[t]) return s = t, e = r, !1 }), t.jCarousel.plugin("jcarouselAutoscroll", { _options: { target: "+=1", interval: 3e3, autostart: !0, method: "scroll" }, _timer: null, _started: !1, _init: function () { this.onDestroy = t.proxy(function () { this._destroy(), this.carousel().one("jcarousel:createend", t.proxy(this._create, this)) }, this), this.onAnimateEnd = t.proxy(this._start, this), this.onVisibilityChange = t.proxy(function () { i[s] ? this._stop() : this._start() }, this) }, _create: function () { this.carousel().one("jcarousel:destroy", this.onDestroy), t(i).on(e, this.onVisibilityChange), this.options("autostart") && this.start() }, _destroy: function () { this._stop(), this.carousel().off("jcarousel:destroy", this.onDestroy), t(i).off(e, this.onVisibilityChange) }, _start: function () { if (this._stop(), this._started) return this.carousel().one("jcarousel:animateend", this.onAnimateEnd), this._timer = setTimeout(t.proxy(function () { this.carousel().jcarousel(this.options("method"), this.options("target")) }, this), this.options("interval")), this }, _stop: function () { return this._timer && (this._timer = clearTimeout(this._timer)), this.carousel().off("jcarousel:animateend", this.onAnimateEnd), this }, start: function () { return this._started = !0, this._start(), this }, stop: function () { return this._started = !1, this._stop(), this } }) }(jQuery, document);
631/** ==========================================================
632
633* jquery lightGallery.js v1.1.4
634* http://sachinchoolur.github.io/lightGallery/
635* Released under the MIT License - http://opensource.org/licenses/mit-license.html  ---- FREE ----
636
637=========================================================/**/
638;
639(function ($) {
640    "use strict";
641    $.fn.lightGallery = function (options) {
642        var defaults = {
643            mode: 'slide',
644            useCSS: true,
645            cssEasing: 'ease', //'cubic-bezier(0.25, 0, 0.25, 1)',//
646            easing: 'linear', //'for jquery animation',//
647            speed: 600,
648            addClass: '',
649
650            closable: true,
651            loop: false,
652            auto: false,
653            pause: 4000,
654            escKey: true,
655            controls: true,
656            hideControlOnEnd: false,
657
658            preload: 1, //number of preload slides. will exicute only after the current slide is fully loaded. ex:// you clicked on 4th image and if preload = 1 then 3rd slide and 5th slide will be loaded in the background after the 4th slide is fully loaded.. if preload is 2 then 2nd 3rd 5th 6th slides will be preloaded.. ... ...
659            showAfterLoad: true,
660            selector: null,
661            index: false,
662
663            lang: {
664                allPhotos: 'All photos'
665            },
666            counter: false,
667
668            exThumbImage: false,
669            thumbnail: true,
670            showThumbByDefault: false,
671            animateThumb: true,
672            currentPagerPosition: 'middle',
673            thumbWidth: 100,
674            thumbMargin: 5,
675
676
677            mobileSrc: false,
678            mobileSrcMaxWidth: 640,
679            swipeThreshold: 50,
680            enableTouch: true,
681            enableDrag: true,
682
683            vimeoColor: 'CCCCCC',
684            videoAutoplay: true,
685            videoMaxWidth: '855px',
686
687            dynamic: false,
688            dynamicEl: [],
689            //callbacks
690
691            onOpen: function (plugin) { },
692            onSlideBefore: function (plugin) { },
693            onSlideAfter: function (plugin) { },
694            onSlideNext: function (plugin) { },
695            onSlidePrev: function (plugin) { },
696            onBeforeClose: function (plugin) { },
697            onCloseAfter: function (plugin) { }
698        },
699            el = $(this),
700            plugin = this,
701            $children = null,
702            index = 0,
703            isActive = false,
704            lightGalleryOn = false,
705            isTouch = document.createTouch !== undefined || ('ontouchstart' in window) || ('onmsgesturechange' in window) || navigator.msMaxTouchPoints,
706            $gallery, $galleryCont, $slider, $slide, $prev, $next, prevIndex, $thumb_cont, $thumb, windowWidth, interval, usingThumb = false,
707            aTiming = false,
708            aSpeed = false;
709        var settings = $.extend(true, {}, defaults, options);
710        var lightGallery = {
711            init: function () {
712                el.each(function () {
713                    var $this = $(this);
714                    if (settings.dynamic) {
715                        $children = settings.dynamicEl;
716                        index = 0;
717                        prevIndex = index;
718                        setUp.init(index);
719                    } else {
720                        if (settings.selector !== null) {
721                            $children = $(settings.selector);
722                        } else {
723                            $children = $this.children();
724                        }
725                        $children.on('click', function (e) {
726                            if (settings.selector !== null) {
727                                $children = $(settings.selector);
728                            } else {
729                                $children = $this.children();
730                            }
731                            e.preventDefault();
732                            e.stopPropagation();
733                            index = $children.index(this);
734                            prevIndex = index;
735                            setUp.init(index);
736                        });
737                    }
738                });
739            }
740        };
741        var setUp = {
742            init: function () {
743                isActive = true;
744                this.structure();
745                this.getWidth();
746                this.closeSlide();
747                this.autoStart();
748                this.counter();
749                this.slideTo();
750                this.buildThumbnail();
751                this.keyPress();
752                if (settings.index) {
753                    this.slide(settings.index);
754                    this.animateThumb(settings.index);
755                } else {
756                    this.slide(index);
757                    this.animateThumb(index);
758                }
759                if (settings.enableDrag) {
760                    this.touch();
761                };
762                if (settings.enableTouch) {
763                    this.enableTouch();
764                }
765
766                setTimeout(function () {
767                    $gallery.addClass('opacity');
768                }, 50);
769            },
770            structure: function () {
771                $('body').append('<div id="lightGallery-outer" class="' + settings.addClass + '"><div id="lightGallery-Gallery"><div id="lightGallery-slider"></div><a id="lightGallery-close" class="close"></a></div></div>').addClass('lightGallery');
772                $galleryCont = $('#lightGallery-outer');
773                $gallery = $('#lightGallery-Gallery');
774                if (settings.showAfterLoad === true) {
775                    $gallery.addClass('showAfterLoad');
776                } 
777                
778                $slider = $gallery.find('#lightGallery-slider');
779                var slideList = '';
780                if (settings.dynamic) {
781                    for (var i = 0; i < settings.dynamicEl.length; i++) {
782                        slideList += '<div class="lightGallery-slide"></div>';
783                    }
784                } else {
785                    $children.each(function () {
786                        slideList += '<div class="lightGallery-slide"></div>';
787                    });
788                }
789                $slider.append(slideList);
790                $slide = $gallery.find('.lightGallery-slide');
791            },
792            closeSlide: function () {
793                var $this = this;
794                if (settings.closable) {
795                    $('#lightGallery-outer')
796                        .on('click', function (event) {
797                            if ($(event.target).is('.lightGallery-slide')) {
798                                plugin.destroy(false);
799                            }
800                        });
801                }
802                $('#lightGallery-close').bind('click touchend', function () {
803                    plugin.destroy(false);
804                });
805            },
806            getWidth: function () {
807                var resizeWindow = function () {
808                    windowWidth = $(window).width();
809                };
810                $(window).bind('resize.lightGallery', resizeWindow());
811            },
812            doCss: function () {
813                var support = function () {
814                    var transition = ['transition', 'MozTransition', 'WebkitTransition', 'OTransition', 'msTransition', 'KhtmlTransition'];
815                    var root = document.documentElement;
816                    for (var i = 0; i < transition.length; i++) {
817                        if (transition[i] in root.style) {
818                            return true;
819                        }
820                    }
821                };
822                if (settings.useCSS && support()) {
823                    return true;
824                }
825                return false;
826            },
827            enableTouch: function () {
828                var $this = this;
829                if (isTouch) {
830                    var startCoords = {},
831                        endCoords = {};
832                    $('body').on('touchstart.lightGallery', function (e) {
833                        endCoords = e.originalEvent.targetTouches[0];
834                        startCoords.pageX = e.originalEvent.targetTouches[0].pageX;
835                        startCoords.pageY = e.originalEvent.targetTouches[0].pageY;
836                    });
837                    $('body').on('touchmove.lightGallery', function (e) {
838                        var orig = e.originalEvent;
839                        endCoords = orig.targetTouches[0];
840                        e.preventDefault();
841                    });
842                    $('body').on('touchend.lightGallery', function (e) {
843                        var distance = endCoords.pageX - startCoords.pageX,
844                            swipeThreshold = settings.swipeThreshold;
845                        if (distance >= swipeThreshold) {
846                            $this.prevSlide();
847                            clearInterval(interval);
848                        } else if (distance <= -swipeThreshold) {
849                            $this.nextSlide();
850                            clearInterval(interval);
851                        }
852                    });
853                }
854            },
855            touch: function () {
856                var xStart, xEnd;
857                var $this = this;
858                $('.lightGallery').bind('mousedown', function (e) {
859                    e.stopPropagation();
860                    e.preventDefault();
861                    xStart = e.pageX;
862                });
863                $('.lightGallery').bind('mouseup', function (e) {
864                    e.stopPropagation();
865                    e.preventDefault();
866                    xEnd = e.pageX;
867                    if (xEnd - xStart > 20) {
868                        $this.prevSlide();
869                    } else if (xStart - xEnd > 20) {
870                        $this.nextSlide();
871                    }
872                });
873            },
874            isVideo: function (src, index) {
875                var youtube = src.match(/\/\/(?:www\.)?youtu(?:\.be|be\.com)\/(?:watch\?v=|embed\/)?([a-z0-9_\-]+)/i);
876                var vimeo = src.match(/\/\/(?:www\.)?vimeo.com\/([0-9a-z\-_]+)/i);
877                var iframe = false;
878                if (settings.dynamic) {
879                    if (settings.dynamicEl[index].iframe == 'true') {
880                        iframe = true;
881                    }
882                } else {
883                    if ($children.eq(index).attr('data-iframe') == 'true') {
884                        iframe = true;
885                    }
886                }
887                if (youtube || vimeo || iframe) {
888                    return true;
889                }
890            },
891            loadVideo: function (src, _id) {
892                var youtube = src.match(/\/\/(?:www\.)?youtu(?:\.be|be\.com)\/(?:watch\?v=|embed\/)?([a-z0-9_\-]+)/i);
893                var vimeo = src.match(/\/\/(?:www\.)?vimeo.com\/([0-9a-z\-_]+)/i);
894                var video = '';
895                var a = '';
896                if (youtube) {
897                    if (settings.videoAutoplay === true && lightGalleryOn === false) {
898                        a = '?autoplay=1&rel=0&wmode=opaque';
899                    } else {
900                        a = '?wmode=opaque';
901                    }
902                    video = '<iframe class="object" width="560" height="315" src="//www.youtube.com/embed/' + youtube[1] + a + '" frameborder="0" allowfullscreen></iframe>';
903                } else if (vimeo) {
904                    if (settings.videoAutoplay === true && lightGalleryOn === false) {
905                        a = 'autoplay=1&amp;';
906                    } else {
907                        a = '';
908                    }
909                    video = '<iframe class="object" id="video' + _id + '" width="560" height="315"  src="//player.vimeo.com/video/' + vimeo[1] + '?' + a + 'byline=0&amp;portrait=0&amp;color=' + settings.vimeoColor + '" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>';
910                } else {
911                    video = '<iframe class="object" frameborder="0" src="' + src + '"  allowfullscreen="true"></iframe>';
912                }
913                return '<div class="video_cont" style="max-width:' + settings.videoMaxWidth + ' !important;"><div class="video">' + video + '</div></div>';
914            },
915            addHtml: function (index) {
916                var dataSubHtml = null;
917                if (settings.dynamic) {
918                    dataSubHtml = settings.dynamicEl[index]['sub-html'];
919                } else {
920                    dataSubHtml = $children.eq(index).attr('data-sub-html');
921                }
922                if (typeof dataSubHtml !== 'undefined' && dataSubHtml !== null) {
923                    var fL = dataSubHtml.substring(0, 1);
924                    if (fL == '.' || fL == '#') {
925                        dataSubHtml = $(dataSubHtml).html();
926                    } else {
927                        dataSubHtml = dataSubHtml;
928                    }
929                    $slide.eq(index).append(dataSubHtml);
930                }
931            },
932            preload: function (index) {
933                var newIndex = index;
934                for (var k = 0; k <= settings.preload; k++) {
935                    if (k >= $children.length - index) {
936                        break;
937                    }
938                    this.loadContent(newIndex + k, true);
939                }
940                for (var h = 0; h <= settings.preload; h++) {
941                    if (newIndex - h < 0) {
942                        break;
943                    }
944                    this.loadContent(newIndex - h, true);
945                }
946            },
947            loadObj: function (r, index) {
948                var $this = this;
949                $slide.eq(index).find('.object').on('load error', function () {
950                    $slide.eq(index).addClass('complete');
951                });
952                if (r === false) {
953                    if (!$slide.eq(index).hasClass('complete')) {
954                        $slide.eq(index).find('.object').on('load error', function () {
955                            $this.preload(index);
956                        });
957                    } else {
958                        $this.preload(index);
959                    }
960                }
961            },
962            loadContent: function (index, rec) {
963                var $this = this;
964                var i, j, l = $children.length - index;
965                var src;
966
967                if (settings.preload > $children.length) {
968                    settings.preload = $children.length;
969                }
970                if (settings.mobileSrc === true && windowWidth <= settings.mobileSrcMaxWidth) {
971                    if (settings.dynamic) {
972                        src = settings.dynamicEl[index].mobileSrc;
973                    } else {
974                        src = $children.eq(index).attr('data-responsive-src');
975                    }
976                } else {
977                    if (settings.dynamic) {
978                        src = settings.dynamicEl[index].src;
979                    } else {
980                        src = $children.eq(index).attr('data-src');
981                    }
982                }
983                var time = 0;
984                if (rec === true) {
985                    time = settings.speed + 400;
986                }
987
988
989
990
991                if (typeof src !== 'undefined' && src !== '') {
992                    if (!$this.isVideo(src, index)) {
993                        setTimeout(function () {
994                            if (!$slide.eq(index).hasClass('loaded')) {
995                                $slide.eq(index).prepend('<img class="object" src="' + src + '" />');
996                                $this.addHtml(index);
997                                $slide.eq(index).addClass('loaded');
998                            }
999                            $this.loadObj(rec, index);
1000                        }, time);
1001                    } else {
1002                        setTimeout(function () {
1003                            if (!$slide.eq(index).hasClass('loaded')) {
1004                                $slide.eq(index).prepend($this.loadVideo(src, index));
1005                                $this.addHtml(index);
1006                                $slide.eq(index).addClass('loaded');
1007
1008                                if (settings.auto && settings.videoAutoplay === true) {
1009                                    clearInterval(interval);
1010                                }
1011                            }
1012                            $this.loadObj(rec, index);
1013                        }, time);
1014
1015                    }
1016                } else {
1017                    setTimeout(function () {
1018                        if (!$slide.eq(index).hasClass('loaded')) {
1019                            var dataHtml = null;
1020                            if (settings.dynamic) {
1021                                dataHtml = settings.dynamicEl[index].html;
1022                            } else {
1023                                dataHtml = $children.eq(index).attr('data-html');
1024                            }
1025                            if (typeof dataHtml !== 'undefined' && dataHtml !== null) {
1026                                var fL = dataHtml.substring(0, 1);
1027                                if (fL == '.' || fL == '#') {
1028                                    dataHtml = $(dataHtml).html();
1029                                } else {
1030                                    dataHtml = dataHtml;
1031                                }
1032                            }
1033                            if (typeof dataHtml !== 'undefined' && dataHtml !== null) {
1034                                $slide.eq(index).append('<div class="video_cont" style="max-width:' + settings.videoMaxWidth + ' !important;"><div class="video">' + dataHtml + '</div></div>');
1035                            }
1036                            $this.addHtml(index);
1037                            $slide.eq(index).addClass('loaded complete');
1038
1039                            if (settings.auto && settings.videoAutoplay === true) {
1040                                clearInterval(interval);
1041                            }
1042                        }
1043                        $this.loadObj(rec, index);
1044                    }, time);
1045                }
1046
1047            },
1048            counter: function () {
1049                if (settings.counter === true) {
1050                    var slideCount = $("#lightGallery-slider > div").length;
1051                    $gallery.append("<div id='lightGallery_counter'><span id='lightGallery_counter_current'></span> / <span id='lightGallery_counter_all'>" + slideCount + "</span></div>");
1052                }
1053            },
1054            buildThumbnail: function () {
1055                if (settings.thumbnail === true && $children.length > 1) {
1056                    var $this = this,
1057                        $close = '<span class="close ib"><i class="bUi-iCn-rMv-16" aria-hidden="true"></i></span>';
1058                    if (settings.showThumbByDefault) {
1059                        $close = '<span class="close ib"><i class="bUi-iCn-rMv-16" aria-hidden="true"></i></span>';
1060                    }
1061                    $gallery.append('<div class="thumb_cont"><div class="thumb_info">' + $close + '</div><div class="thumb_inner"></div></div>');
1062                    $thumb_cont = $gallery.find('.thumb_cont');
1063                    $prev.after('<a class="cLthumb"></a>');
1064                    $prev.parent().addClass('hasThumb');
1065                    $gallery.find('.cLthumb').bind('click touchend', function () {
1066                        $gallery.addClass('open');
1067                        if ($this.doCss() && settings.mode === 'slide') {
1068                            $slide.eq(index).prevAll().removeClass('nextSlide').addClass('prevSlide');
1069                            $slide.eq(index).nextAll().removeClass('prevSlide').addClass('nextSlide');
1070                        }
1071                    });
1072                    $gallery.find('.thumb_cont .close').bind('click touchend', function () {
1073                        $gallery.removeClass('open');
1074                    });
1075                    var thumbInfo = $gallery.find('.thumb_info');
1076                    var $thumb_inner = $gallery.find('.thumb_inner');
1077                    var thumbList = '';
1078                    var thumbImg;
1079                    if (settings.dynamic) {
1080                        for (var i = 0; i < settings.dynamicEl.length; i++) {
1081                            thumbImg = settings.dynamicEl[i].thumb;
1082                            thumbList += '<div class="thumb"><img src="' + thumbImg + '" /></div>';
1083                        }
1084                    } else {
1085                        $children.each(function () {
1086                            if (settings.exThumbImage === false || typeof $(this).attr(settings.exThumbImage) == 'undefined' || $(this).attr(settings.exThumbImage) === null) {
1087                                thumbImg = $(this).find('img').attr('src');
1088                            } else {
1089                                thumbImg = $(this).attr(settings.exThumbImage);
1090                            }
1091                            thumbList += '<div class="thumb"><img src="' + thumbImg + '" /></div>';
1092                        });
1093                    }
1094                    $thumb_inner.append(thumbList);
1095                    $thumb = $thumb_inner.find('.thumb');
1096                    $thumb.css({
1097                        'margin-right': settings.thumbMargin + 'px',
1098                        'width': settings.thumbWidth + 'px'
1099                    });
1100                    if (settings.animateThumb === true) {
1101                        var width = ($children.length * (settings.thumbWidth + settings.thumbMargin));
1102                        $gallery.find('.thumb_inner').css({
1103                            'width': width + 'px',
1104                            'position': 'relative',
1105                            'transition-duration': settings.speed + 'ms'
1106                        });
1107                    }
1108                    $thumb.bind('click touchend', function () {
1109                        usingThumb = true;
1110                        var index = $(this).index();
1111                        $thumb.removeClass('active');
1112                        $(this).addClass('active');
1113                        $this.slide(index);
1114                        $this.animateThumb(index);
1115                        clearInterval(interval);
1116                    });
1117                    thumbInfo.prepend('<span class="ib count">' + settings.lang.allPhotos + ' (' + $thumb.length + ')</span>');
1118                    if (settings.showThumbByDefault) {
1119                        $gallery.addClass('open');
1120                    }
1121                }
1122            },
1123            animateThumb: function (index) {
1124                if (settings.animateThumb === true) {
1125                    var thumb_contW = $gallery.find('.thumb_cont').width();
1126                    var position;
1127                    switch (settings.currentPagerPosition) {
1128                        case 'left':
1129                            position = 0;
1130                            break;
1131                        case 'middle':
1132                            position = (thumb_contW / 2) - (settings.thumbWidth / 2);
1133                            break;
1134                        case 'right':
1135                            position = thumb_contW - settings.thumbWidth;
1136                    }
1137                    var left = ((settings.thumbWidth + settings.thumbMargin) * index - 1) - position;
1138                    var width = ($children.length * (settings.thumbWidth + settings.thumbMargin));
1139                    if (left > (width - thumb_contW)) {
1140                        left = width - thumb_contW;
1141                    }
1142                    if (left < 0) {
1143                        left = 0;
1144                    }
1145                    if (this.doCss()) {
1146                        $gallery.find('.thumb_inner').css('transform', 'translate3d(-' + left + 'px, 0px, 0px)');
1147                    } else {
1148                        $gallery.find('.thumb_inner').animate({
1149                            left: -left + "px"
1150                        }, settings.speed);
1151                    }
1152                }
1153            },
1154            slideTo: function () {
1155                var $this = this;
1156                if (settings.controls === true && $children.length > 1) {
1157                    $gallery.append('<div id="lightGallery-action"><a id="lightGallery-prev"></a><a id="lightGallery-next"></a></div>');
1158                    $prev = $gallery.find('#lightGallery-prev');
1159                    $next = $gallery.find('#lightGallery-next');
1160                    $prev.bind('click', function () {
1161                        $this.prevSlide();
1162             
1162           clearInterval(interval);
1163                    });
1164                    $next.bind('click', function () {
1165                        $this.nextSlide();
1166                        clearInterval(interval);
1167                    });
1168                }
1169            },
1170            autoStart: function () {
1171                var $this = this;
1172                if (settings.auto === true) {
1173                    interval = setInterval(function () {
1174                        if (index + 1 < $children.length) {
1175                            index = index;
1176                        } else {
1177                            index = -1;
1178                        }
1179                        index++;
1180                        $this.slide(index);
1181                    }, settings.pause);
1182                }
1183            },
1184            keyPress: function () {
1185                var $this = this;
1186                $(window).bind('keyup.lightGallery', function (e) {
1187                    e.preventDefault();
1188                    e.stopPropagation();
1189                    if (e.keyCode === 37) {
1190                        $this.prevSlide();
1191                        clearInterval(interval);
1192                    }
1193                    if (e.keyCode === 38 && settings.thumbnail === true && $children.length > 1) {
1194                        if (!$gallery.hasClass('open')) {
1195                            if ($this.doCss() && settings.mode === 'slide') {
1196                                $slide.eq(index).prevAll().removeClass('nextSlide').addClass('prevSlide');
1197                                $slide.eq(index).nextAll().removeClass('prevSlide').addClass('nextSlide');
1198                            }
1199                            $gallery.addClass('open');
1200                        }
1201                    } else if (e.keyCode === 39) {
1202                        $this.nextSlide();
1203                        clearInterval(interval);
1204                    }
1205                    if (e.keyCode === 40 && settings.thumbnail === true && $children.length > 1 && !settings.showThumbByDefault) {
1206                        if ($gallery.hasClass('open')) {
1207                            $gallery.removeClass('open');
1208                        }
1209                    } else if (settings.escKey === true && e.keyCode === 27) {
1210                        if (!settings.showThumbByDefault && $gallery.hasClass('open')) {
1211                            $gallery.removeClass('open');
1212                        } else {
1213                            plugin.destroy(false);
1214                        }
1215                    }
1216                });
1217            },
1218            nextSlide: function () {
1219                var $this = this;
1220                index = $slide.index($slide.eq(prevIndex));
1221                if (index + 1 < $children.length) {
1222                    index++;
1223                    $this.slide(index);
1224                } else {
1225                    if (settings.loop) {
1226                        index = 0;
1227                        $this.slide(index);
1228                    } else if (settings.thumbnail === true && $children.length > 1 && !settings.showThumbByDefault) {
1229                        $gallery.addClass('open');
1230                    } else {
1231                        $slide.eq(index).find('.object').addClass('rightEnd');
1232                        setTimeout(function () {
1233                            $slide.find('.object').removeClass('rightEnd');
1234                        }, 400);
1235                    }
1236                }
1237                $this.animateThumb(index);
1238                settings.onSlideNext.call(this, plugin);
1239            },
1240            prevSlide: function () {
1241                var $this = this;
1242                index = $slide.index($slide.eq(prevIndex));
1243                if (index > 0) {
1244                    index--;
1245                    $this.slide(index);
1246                } else {
1247                    if (settings.loop) {
1248                        index = $children.length - 1;
1249                        $this.slide(index);
1250                    } else if (settings.thumbnail === true && $children.length >
1250 1 && !settings.showThumbByDefault) {
1251                        $gallery.addClass('open');
1252                    } else {
1253                        $slide.eq(index).find('.object').addClass('leftEnd');
1254                        setTimeout(function () {
1255                            $slide.find('.object').removeClass('leftEnd');
1256                        }, 400);
1257                    }
1258                }
1259                $this.animateThumb(index);
1260                settings.onSlidePrev.call(this, plugin);
1261            },
1262            slide: function (index) {
1263                var $this = this;
1264                if (lightGalleryOn) {
1265                    setTimeout(function () {
1266                        $this.loadContent(index, false);
1267                    }, settings.speed + 400);
1268                    if (!$slider.hasClass('on')) {
1269                        $slider.addClass('on');
1270                    }
1271                    if (this.doCss() && settings.speed !== '') {
1272                        if (!$slider.hasClass('speed')) {
1273                            $slider.addClass('speed');
1274                        }
1275                        if (aSpeed === false) {
1276                            $slider.css('transition-duration', settings.speed + 'ms');
1277                            aSpeed = true;
1278                        }
1279                    }
1280                    if (this.doCss() && settings.cssEasing !== '') {
1281                        if (!$slider.hasClass('timing')) {
1282                            $slider.addClass('timing');
1283                        }
1284                        if (aTiming === false) {
1285                            $slider.css('transition-timing-function', settings.cssEasing);
1286                            aTiming = true;
1287                        }
1288                    }
1289                    settings.onSlideBefore.call(this, plugin);
1290                } else {
1291                    $this.loadContent(index, false);
1292                }
1293                if (settings.mode === 'slide') {
1294                    var isiPad = navigator.userAgent.match(/iPad/i) !== null;
1295                    if (this.doCss() && !$slider.hasClass('slide') && !isiPad) {
1296                        $slider.addClass('slide');
1297                    } else if (this.doCss() && !$slider.hasClass('useLeft') && isiPad) {
1298                        $slider.addClass('useLeft');
1299                    }
1300                    /*                  if(this.doCss()){
1301                        $slider.css({ 'transform' : 'translate3d('+(-index*100)+'%, 0px, 0px)' });
1302                    }*/
1303                    if (!this.doCss() && !lightGalleryOn) {
1304                        $slider.css({
1305                            left: (-index * 100) + '%'
1306                        });
1307                        //$slide.eq(index).css('transition','none');
1308                    } else if (!this.doCss() && lightGalleryOn) {
1309                        $slider.animate({
1310                            left: (-index * 100) + '%'
1311                        }, settings.speed, settings.easing);
1312                    }
1313                } else if (settings.mode === 'fade') {
1314                    if (this.doCss() && !$slider.hasClass('fadeM')) {
1315                        $slider.addClass('fadeM');
1316                    } else if (!this.doCss() && !$slider.hasClass('animate')) {
1317                        $slider.addClass('animate');
1318                    }
1319                    if (!this.doCss() && !lightGalleryOn) {
1320                        $slide.fadeOut(100);
1321                        $slide.eq(index).fadeIn(100);
1322                    } else if (!this.doCss() && lightGalleryOn) {
1323                        $slide.eq(prevIndex).fadeOut(settings.speed, settings.easing);
1324                        $slide.eq(index).fadeIn(settings.speed, settings.easing);
1325                    }
1326                }
1327                if (index + 1 >= $children.length && settings.auto && settings.loop === false) {
1328                    clearInterval(interval);
1329                }
1330                $slide.eq(prevIndex).removeClass('current');
1331                $slide.eq(index).addClass('current');
1332                if (this.doCss() && settings.mode === 'slide') {
1333                    if (usingThumb === false) {
1334                        $('.prevSlide').removeClass('prevSlide');
1335                        $('.nextSlide').removeClass('nextSlide');
1336                        $slide.eq(index - 1).addClass('prevSlide');
1337                        $slide.eq(index + 1).addClass('nextSlide');
1338                    } else {
1339                        $slide.eq(index).prevAll().removeClass('nextSlide').addClass('prevSlide');
1340                        $slide.eq(index).nextAll().removeClass('prevSlide').addClass('nextSlide');
1341                    }
1342                }
1343                if (settings.thumbnail === true && $children.length > 1) {
1344                    $thumb.removeClass('active');
1345                    $thumb.eq(index).addClass('active');
1346                }
1347                if (settings.controls && settings.hideControlOnEnd && settings.loop === false && $children.length > 1) {
1348                    var l = $children.length;
1349                    l = parseInt(l) - 1;
1350                    if (index === 0) {
1351                        $prev.addClass('disabled');
1352                        $next.removeClass('disabled');
1353                    } else if (index === l) {
1354                        $prev.removeClass('disabled');
1355                        $next.addClass('disabled');
1356                    } else {
1357                        $prev.add($next).removeClass('disabled');
1358                    }
1359                }
1360                prevIndex = index;
1361                lightGalleryOn === false ? settings.onOpen.call(this, plugin) : settings.onSlideAfter.call(this, plugin);
1362                setTimeout(function () {
1363                    lightGalleryOn = true;
1364                });
1365                usingThumb = false;
1366                if (settings.counter) {
1367                    $("#lightGallery_counter_current").text(index + 1);
1368                }
1369                $(window).bind('resize.lightGallery', function () {
1370                    setTimeout(function () {
1371                        $this.animateThumb(index);
1372                    }, 200);
1373                });
1374            }
1375        };
1376        plugin.isActive = function () {
1377            if (isActive === true) {
1378                return true;
1379            } else {
1380                return false;
1381            }
1382
1383        };
1384        plugin.destroy = function (d) {
1385            isActive = false;
1386            d = typeof d !== 'undefined' ? false : true;
1387            settings.onBeforeClose.call(this, plugin);
1388            var lightGalleryOnT = lightGalleryOn;
1389            lightGalleryOn = false;
1390            aTiming = false;
1391            aSpeed = false;
1392            usingThumb = false;
1393            clearInterval(interval);
1394            if (d === true) {
1395                $children.off('click touch touchstart');
1396            }
1397            $('.lightGallery').off('mousedown mouseup');
1398            $('body').off('touchstart.lightGallery touchmove.lightGallery touchend.lightGallery');
1399            $(window).off('resize.lightGallery keyup.lightGallery');
1400            if (lightGalleryOnT === true) {
1401                $gallery.addClass('fadeM');
1402                setTimeout(function () {
1403                    $galleryCont.remove();
1404                    $('body').removeClass('lightGallery');
1405                }, 500);
1406            }
1407            settings.onCloseAfter.call(this, plugin);
1408        };
1409        lightGallery.init();
1410        return this;
1411    };
1412}(jQuery));
1413window.eviivo = window.eviivo ? window.eviivo : {};
1414window.eviivo.mapView = window.eviivo.mapView ? window.eviivo.mapView : {};
1415
1416window.eviivo.mapView = function ($) {
1417
1418    var defaultOptions = {
1419        googleMapsApiUrl: null,
1420        lat: null,
1421        lng: null,
1422        resources: {
1423            switchToMap: "Switch to Map View",
1424            switchToImage: "Switch back to Image View"
1425        },
1426        showMapByDefault: true
1427    };
1428    var options;
1429
1430	var isMapScriptLoaded = false;
1431	var $mapContainer;
1432    var $mapView;
1433    var map;
1434    
1435    ///<summary>Main init method of the eviivo.mapView object</summary>
1436    ///<param name="settings">external settings</param>
1437	function init(settings) {
1438	    //extend internal settings with external ones
1439	    options = $.extend(defaultOptions, settings);
1440	    $mapContainer = $("#map-canvas");
1441	    $mapView = $("#view-address");
1442	    
1443	    if (options.showMapByDefault) {
1444	        showMap();
1445	    } else {
1446	        $mapView.on("click", function() {
1447	            eviivo.popup.display({
1448	                html: "<div id='map-canvas'></div>",
1449	                bodyClass: "dialogue-map"
1450	            });
1451
1452                $mapContainer = $(".mod-dialogueBox > #map-canvas");
1453
1454	            showMap();
1455	        });
1456	    }
1457	}
1458
1459    ///<summary>
1459Callback method from google maps once the google maps api script is loaded</summary>
1460	function loadMap() {
1461	    if (typeof (google.maps.Map) === "function") {
1462	        //only add geo location if we have coordinates
1463	        if (options.lat != null && options.lng != null) {
1464	            var position = new google.maps.LatLng(options.lat, options.lng);
1465	            var mapOptions = {
1466	                zoom: 13,
1467	                center: position,
1468	                disableDoubleClickZoom: true,
1469	                scrollwheel: false,
1470	                mapTypeControl: false,
1471	                panControl: false
1472	                //zoomControl: false,
1473	                //streetViewControl: false
1474	            };
1475	            
1476	            map = new google.maps.Map($mapContainer[0], mapOptions);
1477	            var pin = new google.maps.Marker({ position: position, clickable: false, map: map, animation: google.maps.Animation.DROP });
1478	            google.maps.event.trigger(map, 'resize');
1479	            google.maps.event.addDomListener(window, 'resize', resizeGoogleMap);
1480
1481	        } else {
1482	            console.log('eviivo warning: no location data.');
1483	        }
1484	    }
1485	}
1486
1487	///<summary>Loading the google maps script</summary>
1488	function loadMapScript() {
1489	    if (options.googleMapsApiUrl != "" && options.googleMapsApiUrl != null) {
1490	        //use script tag appender (much safer and much simpler)
1491	        $(document.body).append($("<script src=\"" + options.googleMapsApiUrl + "window.eviivo.mapView.loadMap\" type=\"text/javascript\" />"));
1492	        isMapScriptLoaded = true;
1493	    }
1494	}
1495
1496    function resizeGoogleMap(){
1497        var center = map.getCenter();
1498        google.maps.event.trigger(map, 'resize');
1499        map.setCenter(center);
1500    }
1501
1502    ///<summary>Show map handler</summary>
1503	function showMap() {
1504
1505        //load map and script if not done already; otherwise, just load map
1506        if (!isMapScriptLoaded) {
1507            loadMapScript();
1508        } else {
1509            loadMap();
1510        }
1511    }
1512
1513	///<summary>Expose the internal "private" methods for external use (make them public)</summary>
1514	return {
1515	    init: init,
1516	    loadMap: loadMap
1517	};
1518}(jQuery);
1519window.eviivo = window.eviivo ? window.eviivo : {};
1520window.eviivo.stickyContainer = window.eviivo.stickyContainer ? window.eviivo.stickyContainer : {};
1521
1522window.eviivo.stickyContainer = function ($) {
1523
1524    var defaultOptions = {
1525        resources: {
1526            room: "Room",
1527            found: "found"
1528        }
1529    };
1530    var options;
1531
1532    var $resultContainer;
1533    var $groupPriceLabel;
1534    var $groupContent;
1535
1536    ///<summary>Main init method of the eviivo.stickyContainer object</summary>
1537    ///<param name="settings">external settings</param>
1538	function init(settings) {
1539	    //extend internal settings with external ones
1540	    options = $.extend(defaultOptions, settings);
1541
1542	    $resultContainer = $("#mod-results");
1543	    $groupPriceLabel = $(".results-price label");
1544	    $groupContent = $(".group-content");
1545
1546	    if ($(".radio-buttons").length > 0) {
1547	        showFoundRooms();
1548	    }
1549	}
1550    
1551    ///<summary>Shou found rooms</summary>
1552    function showFoundRooms() {
1553        var rooms = $groupContent.length;
1554        //clean all existing room group links
1555        $("div.mod-sticky-inner div.column a").remove();
1556        //loop through all rooms and autogenerate the group links in the header container
1557        for (var i = 1; i <= rooms; i++) {
1558            var roomsFound = $("#group-" + i + "-content section").length;
1559            $('<a/>', {
1560                id: ("group-" + i),
1561                href: ("#group-room-" + i),
1562                class: "target-scroll selected"
1563            }).html(options.resources.room + " " + i + " <span>(<i>" + roomsFound + "</i> " + options.resources.found + ")</span>").appendTo("div.mod-sticky-inner div.column:first-child");
1564        }
1565    }
1566  
1567	///<summary>Expose the internal "private" methods for external use (make them public)</summary>
1568	return {
1569	    init: init
1570	};
1571}(jQuery);
1572window.eviivo = window.eviivo ? window.eviivo : {};
1573window.eviivo.smoothScrolling = window.eviivo.smoothScrolling ? window.eviivo.smoothScrolling : {};
1574
1575window.eviivo.smoothScrolling = function ($) {
1576
1577    var defaultOptions = {
1578        resources: {
1579        }
1580    };
1581    var options;
1582    var bodyAnimation;
1583
1584    ///<summary>Main init method of the eviivo.smoothScrolling object</summary>
1585    ///<param name="settings">external settings</param>
1586    function init(settings) {
1587        //extend internal settings with external ones
1588        options = $.extend(defaultOptions, settings);
1589
1590        $("body div.main").on("click", "a.target-scroll, #cheapestPriceCta", smoothScrollHandler);
1591        $("body div.main").on("click", "a.target-scroll, #view-address", smoothScrollHandler);
1592        $(document).on("scroll", toggleScrollTopLink);
1593        $("a[href='#top']").on("click", scrollToTop);
1594    }
1595
1596    ///<summary>the "smooth scroll" action handler with header offset</summary>
1597    function smoothScrollHandler(e, callback, obj) {
1598        e.preventDefault();
1599        if (obj == null) {
1600            obj = this;
1601        }
1602
1603        var hash = obj.hash ? obj.hash : $(obj).data("anchor");
1604        var $target = $(hash);
1605
1606        if ($target.length > 0) {
1607            var targetOffset = $target.offset().top;
1608
1609            // Get sticky header height dynamically
1610            var headerHeight = $('.sticky-header').outerHeight() || 0;
1611
1612            // Calculate scroll destination, minus header
1613            var delta = $(document).height() - $(window).height();
1614            var dest = targetOffset - headerHeight;
1615            dest = dest > delta ? delta : dest;
1616
1617            if (callback == null) {
1618                $('html,body').animate({ scrollTop: dest }, 500, 'swing');
1619            } else {
1620                //go to destination
1621                $('html,body').animate({ scrollTop: dest }, 500, 'swing', callback);
1622            }
1623
1624            // Update the URL hash without jumping
1625            if (obj.hash) {
1626                history.pushState(null, null, hash);
1627            }
1628        }
1629
1630        return false;
1631    }
1632
1633    ///<summary>toggle scroll to top link display</summary>
1634    function toggleScrollTopLink() {
1635        var scroll = $(window).scrollTop();
1636        if (scroll >= 500) {
1637            $("html").removeClass("not-scrolled");
1638        } else {
1639            $("html").addClass("not-scrolled");
1640        }
1641    }
1642
1643    ///<summary>scroll to top handler</summary>
1644    function scrollToTop() {
1645        $("html, body").animate({ scrollTop: 0 }, "slow");
1646        return false;
1647    }
1648
1649    ///<summary>Expose the internal "private" methods for external use (make them public)</summary>
1650    return {
1651        init: init,
1652        smoothScrollHandler: smoothScrollHandler
1653    };
1654}(jQuery);
1655window.eviivo = window.eviivo ? window.eviivo : {};
1656window.eviivo.bookingFlow = window.eviivo.bookingFlow ? window.eviivo.bookingFlow : {};
1657
1658window.eviivo.bookingFlow = function ($) {
1659    var defaultOptions = {
1660    };
1661    var options;
1662
1663    var $bookButtons;
1664    var $submitForm;
1665    var $bookButtonMultiple;
1666    var $radioButtons;
1667    var $radioButtonLabels;
1668    var $radioButtonsInputs;
1669    var $stickyContainer;
1670    var $stickyContainerButton;
1671    var $showPricesButtons;
1672    var $viewportWidth;
1673
1674    /**
1675     * Initialize booking flow module with settings
1676     * Caches DOM elements, attaches event handlers, and sets up radio button controls
1677     * @param {Object} settings - Configuration settings to extend default options
1678     */
1679    function init(settings) {
1680        //extend internal settings with external ones
1681        options = $.extend(defaultOptions, settings);
1682
1683        $bookButtons = $("#mod-results div.results-price button.cp-button-book");
1684        $bookButtonMultyRoom = $("div.mod-sticky-inner button.cp-button");
1685        $bookButtonMultiple = $("#mod-results div.mod-sticky-inner");
1686        $submitForm = $("#submit-reservation");
1687        $radioButtons = $(".radio-buttons");
1688        $radioButtonLabels = $(".radio-buttons label");
1689        $radioButtonsInputs = $(".radio-buttons input");
1690        $stickyContainer = $("#mod-results .mod-sticky");
1691        $stickyContainerButton = $(".mod-sticky-inner button");
1692        $showPricesButtons = $("#mod-results div.results-price button.cp-button-showprices");
1693        $viewportWidth = $(window).width();
1694
1695        $bookButtons.on("click", submitOrder);
1696        $bookButtonMultyRoom.on("click", submitMultiRoomOrder);
1697
1698        // Track rate plan selection from more rates accordion
1699        $(document).on("click", ".cp-button-rates", trackRatePlanSelection);
1700
1701        if ($bookButtonMultiple.length > 0) {
1702            $bookButtonMultiple.on("click", selectMultipleOrder);
1703            $stickyContainerButton.on("click", submitForm);
1704            $radioButtonsInputs.on("change", groupPriceClickHandler);
1705
1706
1707            if ($radioButtons.length > 0) {
1708                $radioButtons.controlgroup();
1709                $radioButtonLabels.on("click", selectorClickHandler);
1710                $radioButtonLabels.each(function () {
1711                    selectorClickHandler(this);
1712                });
1713            }
1714            $stickyContainer.fadeIn(600);
1715            groupPriceClickHandler();
1716        }
1717
1718        $showPricesButtons.on("click", showStartDateCalendar);
1719    }
1720
1721    /**
1722     * Handle room selection in group availability results
1723     * Marks selected room item with visual indicator class and removes it from siblings
1724     * @param {Event|Element} e - Click event or DOM element triggering the selection
1725     */
1726    function selectorClickHandler(e) {
1727        var elem = e != null ? e : this;
1728        var roomItem = $(elem).parents("section.results-item");
1729        roomItem.siblings().removeClass("results-item-gallery-selected");
1730        roomItem.addClass("results-item-gallery-selected");
1731    }
1732
1733    /**
1734     * Validate multiple room selections and enable/disable form submission
1735     * Checks if all product fields have values and updates submit button state accordingly
1736     */
1737    function groupPriceClickHandler() {
1738        selectMultipleOrder();
1739
1740        var allProducts = $submitForm.find("input[name^='product']");
1741        var selectedProducts = $submitForm.find("input[name^='product'][value]");
1742        if (allProducts.length == selectedProducts.length) {
1743            $stickyContainerButton.removeAttr("disabled");
1744        } else {
1745            $stickyContainerButton.attr("disabled", "disabled");
1746        }
1747    }
1748
1749
1750    /**
1751     * Send Google Analytics tracking event when user clicks book
1752     * Tracks both primary and property-specific GA trackers if available
1753     */
1754    function trackPropertyViewBookEvent() {
1755        if (typeof ga != "undefined") {
1756            ga("send", "event", "ViewProperty", "Book");
1757            if (typeof ga.getByName == "function") {
1758                var propertyTracker = ga.getByName('propertyTracker');
1759                if (typeof propertyTracker != "undefined") {
1760                    ga("propertyTracker.send", "event", "ViewProperty", "Book");
1761                }
1762            }
1763
1764        }
1765    }
1766
1767    /**
1768     * Handle single room booking submission with Google Analytics 4 tracking
1769     * Extracts selected product ID, detects iframe context, and submits reservation form
1770     * @this {Element} The clicked book button element
1771     */
1772    function submitOrder() {
1773        trackPropertyViewBookEvent();
1774        var isInIframe = (window.location != window.parent.location) ? true : false;
1775        var productId = $(this).parents(".results-price").find("input[type='hidden']");
1776
1777        if (isInIframe) {
1778            $submitForm.attr("target", "_top");
1779        }
1780        $submitForm.find("input[name^='product']").val(productId.val());
1781        submitForm();
1782    }
1783
1784
1785    /**
1786     * Handle multi-room booking submission with Google Analytics 4 tracking
1787     * Detects iframe context and submits entire reservation form with all selected rooms
1788     * @this {Element} The clicked submit button element
1789     */
1790    function submitMultiRoomOrder() {
1791        trackPropertyViewBookEvent();
1792        var isInIframe = (window.location != window.parent.location) ? true : false;
1793        if (isInIframe) {
1794            $submitForm.attr("target", "_top");
1795        }
1796        submitForm();
1797    }
1798
1799    /**
1800     * Populate form with selected room and occupancy data
1801     * Iterates checked radio buttons and maps product IDs, occupant counts, and occupancy info to form fields
1802     */
1803    function selectMultipleOrder() {
1804        var selectedRoom = $("#mod-results").find("input[type='radio']:checked");
1805        selectedRoom.each(function (i, e) {
1806            var index = i + 1;
1807            var productId = $(e).parents(".results-price").find("input[type='hidden']");
1808            $submitForm.find("input[name^='product-" + index + "']").val(productId.val());
1809            var adults = $(e).parents(".group-content").find("input[name='adults']");
1810            $submitForm.find("input[name^='adults-" + index + "']").val(adults.val());
1811            var children = $(e).parents(".group-content").find("input[name='children']");
1812            $submitForm.find("input[name^='children-" + index + "']").val(children.val());
1813            var occupancyId = $(e).parents(".group-content").find("input[name='occupancy-id']");
1814            $submitForm.find("input[name^='occupancy-id-" + index + "']").val(occupancyId.val());
1815        });
1816    }
1817
1818    /**
1819     * Submit the reservation form to the current page URL
1820     * Sets form action to current location and triggers form submission
1821     */
1822    function submitForm() {
1823        $submitForm.attr("action", window.location.href);
1824        $submitForm.trigger("submit");
1825    }
1826
1827    /**
1828     * Handle show prices button click and smooth scroll behavior
1829     * Triggers smooth scrolling only on smaller viewports (< 768px, iPad width)
1830     * @param {Event} e - The click event from show prices button
1831     */
1832    function showStartDateCalendar(e) {
1833        if (!$viewportWidth < 768) { //smaller than ipad
1834            eviivo.smoothScrolling.smoothScrollHandler(e, function () {
1835                //$startDate.datepicker("show");
1836            }, this);
1837        }
1838    }
1839
1840      /**
1841       * Tracks rate plan selection for Google Analytics 4 ecommerce events
1842       * Fires both 'select_item' and 'add_to_cart' events when user selects a unit rate
1843       * Reads unit data from DOM and property metadata from mod-results container
1844       * 
1845       * GA4 Events Fired:
1846       * - select_item: Tracks which unit/rate was selected from the available list
1847       * - add_to_cart: Indicates user has started the booking process
1848       * 
1849       * @param {Event} e - Click event from rate plan button (main rates or more rates accordion)
1850       * @returns {void}
1851       */
1852      function trackRatePlanSelection(e) {
1853          if (!window.eviivo.googleAnalytics || !window.eviivo.googleAnalytics.isGtagAvailable()) {
1854              return;
1855          }
1856
1857          const $button = $(e.currentTarget);
1858
1859          // Try to find data on parent containers (works for both main rates and more rates)
1860          const $dataContainer = $button.closest(".mod-rateItem, .results-price-wrapper");
1861
1862          // Get property info from the mod-results container
1863          const $modResults = $("#mod-results");
1864          const propertyName = $modResults.data("property-name") ?? "";
1865          const currencyCode = $modResults.data("currency-code") ?? "";
1866          const pointOfSaleName = $modResults.data("point-of-sale-name") ?? "";
1867          const propertyShortName = $modResults.data("property-shortname") ?? $modResults.data("property-short-name") ?? "";
1868          const propertyType = $modResults.data("property-type") ?? options?.constants?.accommodation;
1869          const checkInDate = $modResults.data("check-in-date") ?? "";
1870          const checkOutDate = $modResults.data("check-out-date") ?? "";
1871
1872           // Build item using centralized Google Analytics 4 helper for consistency
1873           const itemData = window.eviivo.googleAnalytics.buildItemFromDOM($dataContainer, {
1874               propertyName: propertyName,
1875               itemCategory: options?.singularRoomTypeTranslation ?? options?.constants?.unit,
1876               itemCategory2: propertyType,
1877               itemAffiliation: pointOfSaleName,
1878               checkInDate: checkInDate,
1879               checkOutDate: checkOutDate
1880           });
1881
1882         if (!itemData || Object.entries(itemData).length === 0) {
1883             console.warn('GA4 Tracking: Unable to build item data from DOM');
1884             return;
1885         }
1886
1887          // Track the selection
1888          const roomListId = options?.constants?.room_list;
1889          const roomListName = options?.constants?.available_rooms;
1890          const selectItemData = {
1891              item_list_id: roomListId,
1892              item_list_name: roomListName,
1893              currency: currencyCode,
1894              value: itemData.price,
1895              items: [itemData]
1896          };
1897
1898          if (checkInDate?.trim()) {
1899              selectItemData.check_in_date = checkInDate;
1900          }
1901
1902          if (checkOutDate?.trim()) {
1903              selectItemData.check_out_date = checkOutDate;
1904          }
1905
1906          window.eviivo.googleAnalytics.sendEvent('select_item', selectItemData);
1907
1908          // Track the "Add to Cart" (The moment they start the booking)
1909          const addToCartData = {
1910              currency: currencyCode,
1911              value: itemData.price,
1912              items: [itemData]
1913          };
1914
1915          if (checkInDate?.trim()) {
1916              addToCartData.check_in_date = checkInDate;
1917          }
1918
1919          if (checkOutDate?.trim()) {
1920              addToCartData.check_out_date = checkOutDate;
1921          }
1922
1923          window.eviivo.googleAnalytics.sendEvent('add_to_cart', addToCartData);
1924     }
1925
1926    /**
1927     * Public API - Expose internal methods for external use
1928     * @type {Object}
1929     * @property {Function} init - Initialize booking flow module
1930     */
1931    return {
1932        init: init
1933    };
1934}(jQuery);
1935/*! Magnific Popup - v1.1.0 - 2016-02-20
vendor: 59,032 bytes, lines 1936-3690
1936* http://dimsemenov.com/plugins/magnific-popup/
1937* Copyright (c) 2016 Dmitry Semenov; */
1938; (function (factory) {
1939    if (typeof define === 'function' && define.amd) {
1940        // AMD. Register as an anonymous module. 
1941        define(['jquery'], factory);
1942    } else if (typeof exports === 'object') {
1943        // Node/CommonJS 
1944        factory(require('jquery'));
1945    } else {
1946        // Browser globals 
1947        factory(window.jQuery || window.Zepto);
1948    }
1949}(function ($) {
1950
1951    /*>>core*/
1952    /**
1953     * 
1954     * Magnific Popup Core JS file
1955     * 
1956     */
1957
1958
1959    /**
1960     * Private static constants
1961     */
1962    var CLOSE_EVENT = 'Close',
1963        BEFORE_CLOSE_EVENT = 'BeforeClose',
1964        AFTER_CLOSE_EVENT = 'AfterClose',
1965        BEFORE_APPEND_EVENT = 'BeforeAppend',
1966        MARKUP_PARSE_EVENT = 'MarkupParse',
1967        OPEN_EVENT = 'Open',
1968        CHANGE_EVENT = 'Change',
1969        NS = 'mfp',
1970        EVENT_NS = '.' + NS,
1971        READY_CLASS = 'mfp-ready',
1972        REMOVING_CLASS = 'mfp-removing',
1973        PREVENT_CLOSE_CLASS = 'mfp-prevent-close';
1974
1975
1976    /**
1977     * Private vars 
1978     */
1979    /*jshint -W079 */
1980    var mfp, // As we have only one instance of MagnificPopup object, we define it locally to not to use 'this'
1981        MagnificPopup = function () { },
1982        _isJQ = !!(window.jQuery),
1983        _prevStatus,
1984        _window = $(window),
1985        _document,
1986        _prevContentType,
1987        _wrapClasses,
1988        _currPopupType;
1989
1990
1991    /**
1992     * Private functions
1993     */
1994    var _mfpOn = function (name, f) {
1995        mfp.ev.on(NS + name + EVENT_NS, f);
1996    },
1997        _getEl = function (className, appendTo, html, raw) {
1998            var el = document.createElement('div');
1999            el.className = 'mfp-' + className;
2000            if (html) {
2001                el.innerHTML = html;
2002            }
2003            if (!raw) {
2004                el = $(el);
2005                if (appendTo) {
2006                    el.appendTo(appendTo);
2007                }
2008            } else if (appendTo) {
2009                appendTo.appendChild(el);
2010            }
2011            return el;
2012        },
2013        _mfpTrigger = function (e, data) {
2014            mfp.ev.triggerHandler(NS + e, data);
2015
2016            if (mfp.st.callbacks) {
2017                // converts "mfpEventName" to "eventName" callback and triggers it if it's present
2018                e = e.charAt(0).toLowerCase() + e.slice(1);
2019                if (mfp.st.callbacks[e]) {
2020                    mfp.st.callbacks[e].apply(mfp, $.isArray(data) ? data : [data]);
2021                }
2022            }
2023        },
2024        _getCloseBtn = function (type) {
2025            if (type !== _currPopupType || !mfp.currTemplate.closeBtn) {
2026                mfp.currTemplate.closeBtn = $(mfp.st.closeMarkup.replace('%title%', mfp.st.tClose));
2027                _currPopupType = type;
2028            }
2029            return mfp.currTemplate.closeBtn;
2030        },
2031        // Initialize Magnific Popup only when called at least once
2032        _checkInstance = function () {
2033            if (!$.magnificPopup.instance) {
2034                /*jshint -W020 */
2035                mfp = new MagnificPopup();
2036                mfp.init();
2037                $.magnificPopup.instance = mfp;
2038            }
2039        },
2040        // CSS transition detection, http://stackoverflow.com/questions/7264899/detect-css-transitions-using-javascript-and-without-modernizr
2041        supportsTransitions = function () {
2042            var s = document.createElement('p').style, // 's' for style. better to create an element if body yet to exist
2043                v = ['ms', 'O', 'Moz', 'Webkit']; // 'v' for vendor
2044
2045            if (s['transition'] !== undefined) {
2046                return true;
2047            }
2048
2049            while (v.length) {
2050                if (v.pop() + 'Transition' in s) {
2051                    return true;
2052                }
2053            }
2054
2055            return false;
2056        };
2057
2058
2059
2060    /**
2061     * Public functions
2062     */
2063    MagnificPopup.prototype = {
2064
2065        constructor: MagnificPopup,
2066
2067        /**
2068         * Initializes Magnific Popup plugin. 
2069         * This function is triggered only once when $.fn.magnificPopup or $.magnificPopup is executed
2070         */
2071        init: function () {
2072            var appVersion = navigator.appVersion;
2073            mfp.isLowIE = mfp.isIE8 = document.all && !document.addEventListener;
2074            mfp.isAndroid = (/android/gi).test(appVersion);
2075            mfp.isIOS = (/iphone|ipad|ipod/gi).test(appVersion);
2076            mfp.supportsTransition = supportsTransitions();
2077
2078            // We disable fixed positioned lightbox on devices that don't handle it nicely.
2079            // If you know a better way of detecting this - let me know.
2080            mfp.probablyMobile = (mfp.isAndroid || mfp.isIOS || /(Opera Mini)|Kindle|webOS|BlackBerry|(Opera Mobi)|(Windows Phone)|IEMobile/i.test(navigator.userAgent));
2081            _document = $(document);
2082
2083            mfp.popupsCache = {};
2084        },
2085
2086        /**
2087         * Opens popup
2088         * @param  data [description]
2089         */
2090        open: function (data) {
2091
2092            var i;
2093
2094            if (data.isObj === false) {
2095                // convert jQuery collection to array to avoid conflicts later
2096                mfp.items = data.items.toArray();
2097
2098                mfp.index = 0;
2099                var items = data.items,
2100                    item;
2101                for (i = 0; i < items.length; i++) {
2102                    item = items[i];
2103                    if (item.parsed) {
2104                        item = item.el[0];
2105                    }
2106                    if (item === data.el[0]) {
2107                        mfp.index = i;
2108                        break;
2109                    }
2110                }
2111            } else {
2112                mfp.items = $.isArray(data.items) ? data.items : [data.items];
2113                mfp.index = data.index || 0;
2114            }
2115
2116            // if popup is already opened - we just update the content
2117            if (mfp.isOpen) {
2118                mfp.updateItemHTML();
2119                return;
2120            }
2121
2122            mfp.types = [];
2123            _wrapClasses = '';
2124            if (data.mainEl && data.mainEl.length) {
2125                mfp.ev = data.mainEl.eq(0);
2126            } else {
2127                mfp.ev = _document;
2128            }
2129
2130            if (data.key) {
2131                if (!mfp.popupsCache[data.key]) {
2132                    mfp.popupsCache[data.key] = {};
2133                }
2134                mfp.currTemplate = mfp.popupsCache[data.key];
2135            } else {
2136                mfp.currTemplate = {};
2137            }
2138
2139
2140
2141            mfp.st = $.extend(true, {}, $.magnificPopup.defaults, data);
2142            mfp.fixedContentPos = mfp.st.fixedContentPos === 'auto' ? !mfp.probablyMobile : mfp.st.fixedContentPos;
2143
2144            if (mfp.st.modal) {
2145                mfp.st.closeOnContentClick = false;
2146                mfp.st.closeOnBgClick = false;
2147                mfp.st.showCloseBtn = false;
2148                mfp.st.enableEscapeKey = false;
2149            }
2150
2151
2152            // Building markup
2153            // main containers are created only once
2154            if (!mfp.bgOverlay) {
2155
2156                // Dark overlay
2157                mfp.bgOverlay = _getEl('bg').on('click' + EVENT_NS, function () {
2158                    mfp.close();
2159                });
2160
2161                mfp.wrap = _getEl('wrap').attr('tabindex', -1).on('click' + EVENT_NS, function (e) {
2162                    if (mfp._checkIfClose(e.target)) {
2163                        mfp.close();
2164                    }
2165                });
2166
2167                mfp.container = _getEl('container', mfp.wrap);
2168            }
2169
2170            mfp.contentContainer = _getEl('content');
2171            if (mfp.st.preloader) {
2172                mfp.preloader = _getEl('preloader', mfp.container, mfp.st.tLoading);
2173            }
2174
2175
2176            // Initializing modules
2177            var modules = $.magnificPopup.modules;
2178            for (i = 0; i < modules.length; i++) {
2179                var n = modules[i];
2180                n = n.charAt(0).toUpperCase() + n.slice(1);
2181                mfp['init' + n].call(mfp);
2182            }
2183            _mfpTrigger('BeforeOpen');
2184
2185
2186            if (mfp.st.showCloseBtn) {
2187                // Close button
2188                if (!mfp.st.closeBtnInside) {
2189                    mfp.wrap.append(_getCloseBtn());
2190                } else {
2191                    _mfpOn(MARKUP_PARSE_EVENT, function (e, template, values, item) {
2192                        values.close_replaceWith = _getCloseBtn(item.type);
2193                    });
2194                    _wrapClasses += ' mfp-close-btn-in';
2195                }
2196            }
2197
2198            if (mfp.st.alignTop) {
2199                _wrapClasses += ' mfp-align-top';
2200            }
2201
2202
2203
2204            if (mfp.fixedContentPos) {
2205                mfp.wrap.css({
2206                    overflow: mfp.st.overflowY,
2207                    overflowX: 'hidden',
2208                    overflowY: mfp.st.overflowY
2209                });
2210            } else {
2211                mfp.wrap.css({
2212                    top: _window.scrollTop(),
2213                    position: 'absolute'
2214                });
2215            }
2216            if (mfp.st.fixedBgPos === false || (mfp.st.fixedBgPos === 'auto' && !mfp.fixedContentPos)) {
2217                mfp.bgOverlay.css({
2218                    height: _document.height(),
2219                    position: 'absolute'
2220                });
2221            }
2222
2223
2224
2225            if (mfp.st.enableEscapeKey) {
2226                // Close on ESC key
2227                _document.on('keyup' + EVENT_NS, function (e) {
2228                    if (e.keyCode === 27) {
2229                        mfp.close();
2230                    }
2231                });
2232            }
2233
2234            _window.on('resize' + EVENT_NS, function () {
2235                mfp.updateSize();
2236            });
2237
2238
2239            if (!mfp.st.closeOnContentClick) {
2240                _wrapClasses += ' mfp-auto-cursor';
2241            }
2242
2243            if (_wrapClasses)
2244                mfp.wrap.addClass(_wrapClasses);
2245
2246
2247            // this triggers recalculation of layout, so we get it once to not to trigger twice
2248            var windowHeight = mfp.wH = _window.height();
2249
2250
2251            var windowStyles = {};
2252
2253            if (mfp.fixedContentPos) {
2254                if (mfp._hasScrollBar(windowHeight)) {
2255                    var s = mfp._getScrollbarSize();
2256                    if (s) {
2257                        windowStyles.marginRight = s;
2258                    }
2259                }
2260            }
2261
2262            if (mfp.fixedContentPos) {
2263                if (!mfp.isIE7) {
2264                    windowStyles.overflow = 'hidden';
2265                } else {
2266                    // ie7 double-scroll bug
2267                    $('body, html').css('overflow', 'hidden');
2268                }
2269            }
2270
2271
2272
2273            var classesToadd = mfp.st.mainClass;
2274            if (mfp.isIE7) {
2275                classesToadd += ' mfp-ie7';
2276            }
2277            if (classesToadd) {
2278                mfp._addClassToMFP(classesToadd);
2279            }
2280
2281            // add content
2282            mfp.updateItemHTML();
2283
2284            _mfpTrigger('BuildControls');
2285
2286            // remove scrollbar, add margin e.t.c
2287            $('html').css(windowStyles);
2288
2289            // add everything to DOM
2290            mfp.bgOverlay.add(mfp.wrap).prependTo(mfp.st.prependTo || $(document.body));
2291
2292            // Save last focused element
2293            mfp._lastFocusedEl = document.activeElement;
2294
2295            // Wait for next cycle to allow CSS transition
2296            setTimeout(function () {
2297
2298                if (mfp.content) {
2299                    mfp._addClassToMFP(READY_CLASS);
2300                    mfp._setFocus();
2301                } else {
2302                    // if content is not defined (not loaded e.t.c) we add class only for BG
2303                    mfp.bgOverlay.addClass(READY_CLASS);
2304                }
2305
2306                // Trap the focus in popup
2307                _document.on('focusin' + EVENT_NS, mfp._onFocusIn);
2308
2309            }, 16);
2310
2311            mfp.isOpen = true;
2312            mfp.updateSize(windowHeight);
2313            _mfpTrigger(OPEN_EVENT);
2314
2315            return data;
2316        },
2317
2318        /**
2319         * Closes the popup
2320         */
2321        close: function () {
2322            if (!mfp.isOpen) return;
2323            _mfpTrigger(BEFORE_CLOSE_EVENT);
2324
2325            mfp.isOpen = false;
2326            // for CSS3 animation
2327            if (mfp.st.removalDelay && !mfp.isLowIE && mfp.supportsTransition) {
2328                mfp._addClassToMFP(REMOVING_CLASS);
2329                setTimeout(function () {
2330                    mfp._close();
2331                }, mfp.st.removalDelay);
2332            } else {
2333                mfp._close();
2334            }
2335        },
2336
2337        /**
2338         * Helper for close() function
2339         */
2340        _close: function () {
2341            _mfpTrigger(CLOSE_EVENT);
2342
2343            var classesToRemove = REMOVING_CLASS + ' ' + READY_CLASS + ' ';
2344
2345            mfp.bgOverlay.detach();
2346            mfp.wrap.detach();
2347            mfp.container.empty();
2348
2349            if (mfp.st.mainClass) {
2350                classesToRemove += mfp.st.mainClass + ' ';
2351            }
2352
2353            mfp._removeClassFromMFP(classesToRemove);
2354
2355            if (mfp.fixedContentPos) {
2356                var windowStyles = { marginRight: '' };
2357                if (mfp.isIE7) {
2358                    $('body, html').css('overflow', '');
2359                } else {
2360                    windowStyles.overflow = '';
2361                }
2362                $('html').css(windowStyles);
2363            }
2364
2365            _document.off('keyup' + EVENT_NS + ' focusin' + EVENT_NS);
2366            mfp.ev.off(EVENT_NS);
2367
2368            // clean up DOM elements that aren't removed
2369            mfp.wrap.attr('class', 'mfp-wrap').removeAttr('style');
2370            mfp.bgOverlay.attr('class', 'mfp-bg');
2371            mfp.container.attr('class', 'mfp-container');
2372
2373            // remove close button from target element
2374            if (mfp.st.showCloseBtn &&
2375                (!mfp.st.closeBtnInside || mfp.currTemplate[mfp.currItem.type] === true)) {
2376                if (mfp.currTemplate.closeBtn)
2377                    mfp.currTemplate.closeBtn.detach();
2378            }
2379
2380
2381            if (mfp.st.autoFocusLast && mfp._lastFocusedEl) {
2382                $(mfp._lastFocusedEl).focus(); // put tab focus back
2383            }
2384            mfp.currItem = null;
2385            mfp.content = null;
2386            mfp.currTemplate = null;
2387            mfp.prevHeight = 0;
2388
2389            _mfpTrigger(AFTER_CLOSE_EVENT);
2390        },
2391
2392        updateSize: function (winHeight) {
2393
2394            if (mfp.isIOS) {
2395                // fixes iOS nav bars https://github.com/dimsemenov/Magnific-Popup/issues/2
2396                var zoomLevel = document.documentElement.clientWidth / window.innerWidth;
2397                var height = window.innerHeight * zoomLevel;
2398                mfp.wrap.css('height', height);
2399                mfp.wH = height;
2400            } else {
2401                mfp.wH = winHeight || _window.height();
2402            }
2403            // Fixes #84: popup incorrectly positioned with position:relative on body
2404            if (!mfp.fixedContentPos) {
2405                mfp.wrap.css('height', mfp.wH);
2406            }
2407
2408            _mfpTrigger('Resize');
2409
2410        },
2411
2412        /**
2413         * Set content of popup based on current index
2414         */
2415        updateItemHTML: function () {
2416            var item = mfp.items[mfp.index];
2417
2418            // Detach and perform modifications
2419            mfp.contentContainer.detach();
2420
2421            if (mfp.content)
2422                mfp.content.detach();
2423
2424            if (!item.parsed) {
2425                item = mfp.parseEl(mfp.index);
2426            }
2427
2428            var type = item.type;
2429
2430            _mfpTrigger('BeforeChange', [mfp.currItem ? mfp.currItem.type : '', type]);
2431            // BeforeChange event works like so:
2432            // _mfpOn('BeforeChange', function(e, prevType, newType) { });
2433
2434            mfp.currItem = item;
2435
2436            if (!mfp.currTemplate[type]) {
2437                var markup = mfp.st[type] ? mfp.st[type].markup : false;
2438
2439                // allows to modify markup
2440                _mfpTrigger('FirstMarkupParse', markup);
2441
2442                if (markup) {
2443                    mfp.currTemplate[type] = $(markup);
2444                } else {
2445                    // if there is no markup found we just define that template is parsed
2446                    mfp.currTemplate[type] = true;
2447                }
2448            }
2449
2450            if (_prevContentType && _prevContentType !== item.type) {
2451                mfp.container.removeClass('mfp-' + _prevContentType + '-holder');
2452            }
2453
2454            var newContent = mfp['get' + type.charAt(0).toUpperCase() + type.slice(1)](item, mfp.currTemplate[type]);
2455            mfp.appendContent(newContent, type);
2456
2457            item.preloaded = true;
2458
2459            _mfpTrigger(CHANGE_EVENT, item);
2460            _prevContentType = item.type;
2461
2462            // Append container back after its content changed
2463            mfp.container.prepend(mfp.contentContainer);
2464
2465            _mfpTrigger('AfterChange');
2466        },
2467
2468
2469        /**
2470         * Set HTML content of popup
2471         */
2472        appendContent: function (newContent, type) {
2473            mfp.content = newContent;
2474
2475            if (newContent) {
2476                if (mfp.st.showCloseBtn && mfp.st.closeBtnInside &&
2477                    mfp.currTemplate[type] === true) {
2478                    // if there is no markup, we just append close button element inside
2479                    if (!mfp.content.find('.mfp-close').length) {
2480                        mfp.content.append(_getCloseBtn());
2481                    }
2482                } else {
2483                    mfp.content = newContent;
2484                }
2485            } else {
2486                mfp.content = '';
2487            }
2488
2489            _mfpTrigger(BEFORE_APPEND_EVENT);
2490            mfp.container.addClass('mfp-' + type + '-holder');
2491
2492            mfp.contentContainer.append(mfp.content);
2493        },
2494
2495
2496        /**
2497         * Creates Magnific Popup data object based on given data
2498         * @param  {int} index Index of item to parse
2499         */
2500        parseEl: function (index) {
2501            var item = mfp.items[index],
2502                type;
2503
2504            if (item.tagName) {
2505                item = { el: $(item) };
2506            } else {
2507                type = item.type;
2508                item = { data: item, src: item.src };
2509            }
2510
2511            if (item.el) {
2512                var types = mfp.types;
2513
2514                // check for 'mfp-TYPE' class
2515                for (var i = 0; i < types.length; i++) {
2516                    if (item.el.hasClass('mfp-' + types[i])) {
2517                        type = types[i];
2518                        break;
2519                    }
2520                }
2521
2522                item.src = item.el.attr('data-mfp-src');
2523                if (!item.src) {
2524                    item.src = item.el.attr('href');
2525                }
2526            }
2527
2528            item.type = type || mfp.st.type || 'inline';
2529            item.index = index;
2530            item.parsed = true;
2531            mfp.items[index] = item;
2532            _mfpTrigger('ElementParse', item);
2533
2534            return mfp.items[index];
2535        },
2536
2537
2538        /**
2539         * Initializes single popup or a group of popups
2540         */
2541        addGroup: function (el, options) {
2542            var eHandler = function (e) {
2543                e.mfpEl = this;
2544                mfp._openClick(e, el, options);
2545            };
2546
2547            if (!options) {
2548                options = {};
2549            }
2550
2551            var eName = 'click.magnificPopup';
2552            options.mainEl = el;
2553
2554            if (options.items) {
2555                options.isObj = true;
2556                el.off(eName).on(eName, eHandler);
2557            } else {
2558                options.isObj = false;
2559                if (options.delegate) {
2560                    el.off(eName).on(eName, options.delegate, eHandler);
2561                } else {
2562                    options.items = el;
2563                    el.off(eName).on(eName, eHandler);
2564                }
2565            }
2566        },
2567        _openClick: function (e, el, options) {
2568            var midClick = options.midClick !== undefined ? options.midClick : $.magnificPopup.defaults.midClick;
2569
2570
2571            if (!midClick && (e.which === 2 || e.ctrlKey || e.metaKey || e.altKey || e.shiftKey)) {
2572                return;
2573            }
2574
2575            var disableOn = options.disableOn !== undefined ? options.disableOn : $.magnificPopup.defaults.disableOn;
2576
2577            if (disableOn) {
2578                if ($.isFunction(disableOn)) {
2579                    if (!disableOn.call(mfp)) {
2580                        return true;
2581                    }
2582                } else { // else it's number
2583                    if (_window.width() < disableOn) {
2584                        return true;
2585                    }
2586                }
2587            }
2588
2589            if (e.type) {
2590                e.preventDefault();
2591
2592                // This will prevent popup from closing if element is inside and popup is already opened
2593                if (mfp.isOpen) {
2594                    e.stopPropagation();
2595                }
2596            }
2597
2598            options.el = $(e.mfpEl);
2599            if (options.delegate) {
2600                options.items = el.find(options.delegate);
2601            }
2602            mfp.open(options);
2603        },
2604
2605
2606        /**
2607         * Updates text on preloader
2608         */
2609        updateStatus: function (status, text) {
2610
2611            if (mfp.preloader) {
2612                if (_prevStatus !== status) {
2613                    mfp.container.removeClass('mfp-s-' + _prevStatus);
2614                }
2615
2616                if (!text && status === 'loading') {
2617                    text = mfp.st.tLoading;
2618                }
2619
2620                var data = {
2621                    status: status,
2622                    text: text
2623                };
2624                // allows to modify status
2625                _mfpTrigger('UpdateStatus', data);
2626
2627                status = data.status;
2628                text = data.text;
2629
2630                mfp.preloader.html(text);
2631
2632                mfp.preloader.find('a').on('click', function (e) {
2633                    e.stopImmediatePropagation();
2634                });
2635
2636                mfp.container.addClass('mfp-s-' + status);
2637                _prevStatus = status;
2638            }
2639        },
2640
2641
2642        /*
2643            "Private" helpers that aren't private at all
2644         */
2645        // Check to close popup or not
2646        // "target" is an element that was clicked
2647        _checkIfClose: function (target) {
2648
2649            if ($(target).hasClass(PREVENT_CLOSE_CLASS)) {
2650                return;
2651            }
2652
2653            var closeOnContent = mfp.st.closeOnContentClick;
2654            var closeOnBg = mfp.st.closeOnBgClick;
2655
2656            if (closeOnContent && closeOnBg) {
2657                return true;
2658            } else {
2659
2660                // We close the popup if click is on close button or on preloader. Or if there is no content.
2661                if (!mfp.content || $(target).hasClass('mfp-close') || (mfp.preloader && target === mfp.preloader[0])) {
2662                    return true;
2663                }
2664
2665                // if click is outside the content
2666                if ((target !== mfp.content[0] && !$.contains(mfp.content[0], target))) {
2667                    if (closeOnBg) {
2668                        // last check, if the clicked element is in DOM, (in case it's removed onclick)
2669                        if ($.contains(document, target)) {
2670                            return true;
2671                        }
2672                    }
2673                } else if (closeOnContent) {
2674                    return true;
2675                }
2676
2677            }
2678            return false;
2679        },
2680        _addClassToMFP: function (cName) {
2681            mfp.bgOverlay.addClass(cName);
2682            mfp.wrap.addClass(cName);
2683        },
2684        _removeClassFromMFP: function (cName) {
2685            this.bgOverlay.removeClass(cName);
2686            mfp.wrap.removeClass(cName);
2687        },
2688        _hasScrollBar: function (winHeight) {
2689            return ((mfp.isIE7 ? _document.height() : document.body.scrollHeight) > (winHeight || _window.height()));
2690        },
2691        _setFocus: function () {
2692            (mfp.st.focus ? mfp.content.find(mfp.st.focus).eq(0) : mfp.wrap).focus();
2693        },
2694        _onFocusIn: function (e) {
2695            if (e.target !== mfp.wrap[0] && !$.contains(mfp.wrap[0], e.target)) {
2696                mfp._setFocus();
2697                return false;
2698            }
2699        },
2700        _parseMarkup: function (template, values, item) {
2701            var arr;
2702            if (item.data) {
2703                values = $.extend(item.data, values);
2704            }
2705            _mfpTrigger(MARKUP_PARSE_EVENT, [template, values, item]);
2706
2707            $.each(values, function (key, value) {
2708                if (value === undefined || value === false) {
2709                    return true;
2710                }
2711                arr = key.split('_');
2712                if (arr.length > 1) {
2713                    var el = template.find(EVENT_NS + '-' + arr[0]);
2714
2715                    if (el.length > 0) {
2716                        var attr = arr[1];
2717                        if (attr === 'replaceWith') {
2718                            if (el[0] !== value[0]) {
2719                                el.replaceWith(value);
2720                            }
2721                        } else if (attr === 'img') {
2722                            if (el.is('img')) {
2723                                el.attr('src', value);
2724                            } else {
2725                                el.replaceWith($('<img>').attr('src', value).attr('class', el.attr('class')));
2726                            }
2727                        } else {
2728                            el.attr(arr[1], value);
2729                        }
2730                    }
2731
2732                } else {
2733                    template.find(EVENT_NS + '-' + key).html(value);
2734                }
2735            });
2736        },
2737
2738        _getScrollbarSize: function () {
2739            // thx David
2740            if (mfp.scrollbarSize === undefined) {
2741                var scrollDiv = document.createElement("div");
2742                scrollDiv.style.cssText = 'width: 99px; height: 99px; overflow: scroll; position: absolute; top: -9999px;';
2743                document.body.appendChild(scrollDiv);
2744                mfp.scrollbarSize = scrollDiv.offsetWidth - scrollDiv.clientWidth;
2745                document.body.removeChild(scrollDiv);
2746            }
2747            return mfp.scrollbarSize;
2748        }
2749
2750    }; /* MagnificPopup core prototype end */
2751
2752
2753
2754
2755    /**
2756     * Public static functions
2757     */
2758    $.magnificPopup = {
2759        instance: null,
2760        proto: MagnificPopup.prototype,
2761        modules: [],
2762
2763        open: function (options, index) {
2764            _checkInstance();
2765
2766            if (!options) {
2767                options = {};
2768            } else {
2769                options = $.extend(true, {}, options);
2770            }
2771
2772            options.isObj = true;
2773            options.index = index || 0;
2774            return this.instance.open(options);
2775        },
2776
2777        close: function () {
2778            return $.magnificPopup.instance && $.magnificPopup.instance.close();
2779        },
2780
2781        registerModule: function (name, module) {
2782            if (module.options) {
2783                $.magnificPopup.defaults[name] = module.options;
2784            }
2785            $.extend(this.proto, module.proto);
2786            this.modules.push(name);
2787        },
2788
2789        defaults: {
2790
2791            // Info about options is in docs:
2792            // http://dimsemenov.com/plugins/magnific-popup/documentation.html#options
2793
2794            disableOn: 0,
2795
2796            key: null,
2797
2798            midClick: false,
2799
2800            mainClass: '',
2801
2802            preloader: true,
2803
2804            focus: '', // CSS selector of input to focus after popup is opened
2805
2806            closeOnContentClick: false,
2807
2808            closeOnBgClick: true,
2809
2810            closeBtnInside: true,
2811
2812            showCloseBtn: true,
2813
2814            enableEscapeKey: true,
2815
2816            modal: false,
2817
2818            alignTop: false,
2819
2820            removalDelay: 0,
2821
2822            prependTo: null,
2823
2824            fixedContentPos: 'auto',
2825
2826            fixedBgPos: 'auto',
2827
2828            overflowY: 'auto',
2829
2830            closeMarkup: '<button title="%title%" type="button" class="mfp-close">&#215;</button>',
2831
2832            tClose: 'Close (Esc)',
2833
2834            tLoading: 'Loading...',
2835
2836            autoFocusLast: true
2837
2838        }
2839    };
2840
2841
2842
2843    $.fn.magnificPopup = function (options) {
2844        _checkInstance();
2845
2846        var jqEl = $(this);
2847
2848        // We call some API method of first param is a string
2849        if (typeof options === "string") {
2850
2851            if (options === 'open') {
2852                var items,
2853                    itemOpts = _isJQ ? jqEl.data('magnificPopup') : jqEl[0].magnificPopup,
2854                    index = parseInt(arguments[1], 10) || 0;
2855
2856                if (itemOpts.items) {
2857                    items = itemOpts.items[index];
2858                } else {
2859                    items = jqEl;
2860                    if (itemOpts.delegate) {
2861                        items = items.find(itemOpts.delegate);
2862                    }
2863                    items = items.eq(index);
2864                }
2865                mfp._openClick({ mfpEl: items }, jqEl, itemOpts);
2866            } else {
2867                if (mfp.isOpen)
2868                    mfp[options].apply(mfp, Array.prototype.slice.call(arguments, 1));
2869            }
2870
2871        } else {
2872            // clone options obj
2873            options = $.extend(true, {}, options);
2874
2875            /*
2876             * As Zepto doesn't support .data() method for objects
2877             * and it works only in normal browsers
2878             * we assign "options" object directly to the DOM element. FTW!
2879             */
2880            if (_isJQ) {
2881                jqEl.data('magnificPopup', options);
2882            } else {
2883                jqEl[0].magnificPopup = options;
2884            }
2885
2886            mfp.addGroup(jqEl, options);
2887
2888        }
2889        return jqEl;
2890    };
2891
2892    /*>>core*/
2893
2894    /*>>inline*/
2895
2896    var INLINE_NS = 'inline',
2897        _hiddenClass,
2898        _inlinePlaceholder,
2899        _lastInlineElement,
2900        _putInlineElementsBack = function () {
2901            if (_lastInlineElement) {
2902                _inlinePlaceholder.after(_lastInlineElement.addClass(_hiddenClass)).detach();
2903                _lastInlineElement = null;
2904            }
2905        };
2906
2907    $.magnificPopup.registerModule(INLINE_NS, {
2908        options: {
2909            hiddenClass: 'hide', // will be appended with `mfp-` prefix
2910            markup: '',
2911            tNotFound: 'Content not found'
2912        },
2913        proto: {
2914
2915            initInline: function () {
2916                mfp.types.push(INLINE_NS);
2917
2918                _mfpOn(CLOSE_EVENT + '.' + INLINE_NS, function () {
2919                    _putInlineElementsBack();
2920                });
2921            },
2922
2923            getInline: function (item, template) {
2924
2925                _putInlineElementsBack();
2926
2927                if (item.src) {
2928                    var inlineSt = mfp.st.inline,
2929                        el = $(item.src);
2930
2931                    if (el.length) {
2932
2933                        // If target element has parent - we replace it with placeholder and put it back after popup is closed
2934                        var parent = el[0].parentNode;
2935                        if (parent && parent.tagName) {
2936                            if (!_inlinePlaceholder) {
2937                                _hiddenClass = inlineSt.hiddenClass;
2938                                _inlinePlaceholder = _getEl(_hiddenClass);
2939                                _hiddenClass = 'mfp-' + _hiddenClass;
2940                            }
2941                            // replace target inline element with placeholder
2942                            _lastInlineElement = el.after(_inlinePlaceholder).detach().removeClass(_hiddenClass);
2943                        }
2944
2945                        mfp.updateStatus('ready');
2946                    } else {
2947                        mfp.updateStatus('error', inlineSt.tNotFound);
2948                        el = $('<div>');
2949                    }
2950
2951                    item.inlineElement = el;
2952                    return el;
2953                }
2954
2955                mfp.updateStatus('ready');
2956                mfp._parseMarkup(template, {}, item);
2957                return template;
2958            }
2959        }
2960    });
2961
2962    /*>>inline*/
2963
2964    /*>>ajax*/
2965    var AJAX_NS = 'ajax',
2966        _ajaxCur,
2967        _removeAjaxCursor = function () {
2968            if (_ajaxCur) {
2969                $(document.body).removeClass(_ajaxCur);
2970            }
2971        },
2972        _destroyAjaxRequest = function () {
2973            _removeAjaxCursor();
2974            if (mfp.req) {
2975                mfp.req.abort();
2976            }
2977        };
2978
2979    $.magnificPopup.registerModule(AJAX_NS, {
2980
2981        options: {
2982            settings: null,
2983            cursor: 'mfp-ajax-cur',
2984            tError: '<a href="%url%">The content</a> could not be loaded.'
2985        },
2986
2987        proto: {
2988            initAjax: function () {
2989                mfp.types.push(AJAX_NS);
2990                _ajaxCur = mfp.st.ajax.cursor;
2991
2992                _mfpOn(CLOSE_EVENT + '.' + AJAX_NS, _destroyAjaxRequest);
2993                _mfpOn('BeforeChange.' + AJAX_NS, _destroyAjaxRequest);
2994            },
2995            getAjax: function (item) {
2996
2997                if (_ajaxCur) {
2998                    $(document.body).addClass(_ajaxCur);
2999                }
3000
3001                mfp.updateStatus('loading');
3002
3003                var opts = $.extend({
3004                    url: item.src,
3005                    success: function (data, textStatus, jqXHR) {
3006                        var temp = {
3007                            data: data,
3008                            xhr: jqXHR
3009                        };
3010
3011                        _mfpTrigger('ParseAjax', temp);
3012
3013                        mfp.appendContent($(temp.data), AJAX_NS);
3014
3015                        item.finished = true;
3016
3017                        _removeAjaxCursor();
3018
3019                        mfp._setFocus();
3020
3021                        setTimeout(function () {
3022                            mfp.wrap.addClass(READY_CLASS);
3023                        }, 16);
3024
3025                        mfp.updateStatus('ready');
3026
3027                        _mfpTrigger('AjaxContentAdded');
3028                    },
3029                    error: function () {
3030                        _removeAjaxCursor();
3031                        item.finished = item.loadError = true;
3032                        mfp.updateStatus('error', mfp.st.ajax.tError.replace('%url%', item.src));
3033                    }
3034                }, mfp.st.ajax.settings);
3035
3036                mfp.req = $.ajax(opts);
3037
3038                return '';
3039            }
3040        }
3041    });
3042
3043    /*>>ajax*/
3044
3045    /*>>image*/
3046    var _imgInterval,
3047        _getTitle = function (item) {
3048            if (item.data && item.data.title !== undefined)
3049                return item.data.title;
3050
3051            var src = mfp.st.image.titleSrc;
3052
3053            if (src) {
3054                if ($.isFunction(src)) {
3055                    return src.call(mfp, item);
3056                } else if (item.el) {
3057                    return item.el.attr(src) || '';
3058                }
3059            }
3060            return '';
3061        };
3062
3063    $.magnificPopup.registerModule('image', {
3064
3065        options: {
3066            markup: '<div class="mfp-figure">' +
3067                '<div class="mfp-close"></div>' +
3068                '<figure>' +
3069                '<div class="mfp-img"></div>' +
3070                '<figcaption>' +
3071                '<div class="mfp-bottom-bar">' +
3072                '<div class="mfp-title"></div>' +
3073                '<div class="mfp-counter"></div>' +
3074                '</div>' +
3075                '</figcaption>' +
3076                '</figure>' +
3077                '</div>',
3078            cursor: 'mfp-zoom-out-cur',
3079            titleSrc: 'title',
3080            verticalFit: true,
3081            tError: '<a href="%url%">The image</a> could not be loaded.'
3082        },
3083
3084        proto: {
3085            initImage: function () {
3086                var imgSt = mfp.st.image,
3087                    ns = '.image';
3088
3089                mfp.types.push('image');
3090
3091                _mfpOn(OPEN_EVENT + ns, function () {
3092                    if (mfp.currItem.type === 'image' && imgSt.cursor) {
3093                        $(document.body).addClass(imgSt.cursor);
3094                    }
3095                });
3096
3097                _mfpOn(CLOSE_EVENT + ns, function () {
3098                    if (imgSt.cursor) {
3099                        $(document.body).removeClass(imgSt.cursor);
3100                    }
3101                    _window.off('resize' + EVENT_NS);
3102                });
3103
3104                _mfpOn('Resize' + ns, mfp.resizeImage);
3105                if (mfp.isLowIE) {
3106                    _mfpOn('AfterChange', mfp.resizeImage);
3107                }
3108            },
3109            resizeImage: function () {
3110                var item = mfp.currItem;
3111                if (!item || !item.img) return;
3112
3113                if (mfp.st.image.verticalFit) {
3114                    var decr = 0;
3115                    // fix box-sizing in ie7/8
3116                    if (mfp.isLowIE) {
3117                        decr = parseInt(item.img.css('padding-top'), 10) + parseInt(item.img.css('padding-bottom'), 10);
3118                    }
3119                    item.img.css('max-height', mfp.wH - decr);
3120                }
3121            },
3122            _onImageHasSize: function (item) {
3123                if (item.img) {
3124
3125                    item.hasSize = true;
3126
3127                    if (_imgInterval) {
3128                        clearInterval(_imgInterval);
3129                    }
3130
3131                    item.isCheckingImgSize = false;
3132
3133                    _mfpTrigger('ImageHasSize', item);
3134
3135                    if (item.imgHidden) {
3136                        if (mfp.content)
3137                            mfp.content.removeClass('mfp-loading');
3138
3139                        item.imgHidden = false;
3140                    }
3141
3142                }
3143            },
3144
3145            /**
3146             * Function that loops until the image has size to display elements that rely on it asap
3147             */
3148            findImageSize: function (item) {
3149
3150                var counter = 0,
3151                    img = item.img[0],
3152                    mfpSetInterval = function (delay) {
3153
3154                        if (_imgInterval) {
3155                            clearInterval(_imgInterval);
3156                        }
3157                        // decelerating interval that checks for size of an image
3158                        _imgInterval = setInterval(function () {
3159                            if (img.naturalWidth > 0) {
3160                                mfp._onImageHasSize(item);
3161                                return;
3162                            }
3163
3164                            if (counter > 200) {
3165                                clearInterval(_imgInterval);
3166                            }
3167
3168                            counter++;
3169                            if (counter === 3) {
3170                                mfpSetInterval(10);
3171                            } else if (counter === 40) {
3172                                mfpSetInterval(50);
3173                            } else if (counter === 100) {
3174                                mfpSetInterval(500);
3175                            }
3176                        }, delay);
3177                    };
3178
3179                mfpSetInterval(1);
3180            },
3181
3182            getImage: function (item, template) {
3183
3184                var guard = 0,
3185
3186                    // image load complete handler
3187                    onLoadComplete = function () {
3188                        if (item) {
3189                            if (item.img[0].complete) {
3190                                item.img.off('.mfploader');
3191
3192                                if (item === mfp.currItem) {
3193                                    mfp._onImageHasSize(item);
3194
3195                                    mfp.updateStatus('ready');
3196                                }
3197
3198                                item.hasSize = true;
3199                                item.loaded = true;
3200
3201                                _mfpTrigger('ImageLoadComplete');
3202
3203                            }
3204                            else {
3205                                // if image complete check fails 200 times (20 sec), we assume that there was an error.
3206                                guard++;
3207                                if (guard < 200) {
3208                                    setTimeout(onLoadComplete, 100);
3209                                } else {
3210                                    onLoadError();
3211                                }
3212                            }
3213                        }
3214                    },
3215
3216                    // image error handler
3217                    onLoadError = function () {
3218                        if (item) {
3219                            item.img.off('.mfploader');
3220                            if (item === mfp.currItem) {
3221                                mfp._onImageHasSize(item);
3222                                mfp.updateStatus('error', imgSt.tError.replace('%url%', item.src));
3223                            }
3224
3225                            item.hasSize = true;
3226                            item.loaded = true;
3227                            item.loadError = true;
3228                        }
3229                    },
3230                    imgSt = mfp.st.image;
3231
3232
3233                var el = template.find('.mfp-img');
3234                if (el.length) {
3235                    var img = document.createElement('img');
3236                    img.className = 'mfp-img';
3237                    if (item.el && item.el.find('img').length) {
3238                        img.alt = item.el.find('img').attr('alt');
3239                    }
3240                    item.img = $(img).on('load.mfploader', onLoadComplete).on('error.mfploader', onLoadError);
3241                    img.src = item.src;
3242
3243                    // without clone() "error" event is not firing when IMG is replaced by new IMG
3244                    // TODO: find a way to avoid such cloning
3245                    if (el.is('img')) {
3246                        item.img = item.img.clone();
3247                    }
3248
3249                    img = item.img[0];
3250                    if (img.naturalWidth > 0) {
3251                        item.hasSize = true;
3252                    } else if (!img.width) {
3253                        item.hasSize = false;
3254                    }
3255                }
3256
3257                mfp._parseMarkup(template, {
3258                    title: _getTitle(item),
3259                    img_replaceWith: item.img
3260                }, item);
3261
3262                mfp.resizeImage();
3263
3264                if (item.hasSize) {
3265                    if (_imgInterval) clearInterval(_imgInterval);
3266
3267                    if (item.loadError) {
3268                        template.addClass('mfp-loading');
3269                        mfp.updateStatus('error', imgSt.tError.replace('%url%', item.src));
3270                    } else {
3271                        template.removeClass('mfp-loading');
3272                        mfp.updateStatus('ready');
3273                    }
3274                    return template;
3275                }
3276
3277                mfp.updateStatus('loading');
3278                item.loading = true;
3279
3280                if (!item.hasSize) {
3281                    item.imgHidden = true;
3282                    template.addClass('mfp-loading');
3283                    mfp.findImageSize(item);
3284                }
3285
3286                return template;
3287            }
3288        }
3289    });
3290
3291    /*>>image*/
3292
3293    /*>>zoom*/
3294    var hasMozTransform,
3295        getHasMozTransform = function () {
3296            if (hasMozTransform === undefined) {
3297                hasMozTransform = document.createElement('p').style.MozTransform !== undefined;
3298            }
3299            return hasMozTransform;
3300        };
3301
3302    $.magnificPopup.registerModule('zoom', {
3303
3304        options: {
3305            enabled: false,
3306            easing: 'ease-in-out',
3307            duration: 300,
3308            opener: function (element) {
3309                return element.is('img') ? element : element.find('img');
3310            }
3311        },
3312
3313        proto: {
3314
3315            initZoom: function () {
3316                var zoomSt = mfp.st.zoom,
3317                    ns = '.zoom',
3318                    image;
3319
3320                if (!zoomSt.enabled || !mfp.supportsTransition) {
3321                    return;
3322                }
3323
3324                var duration = zoomSt.duration,
3325                    getElToAnimate = function (image) {
3326                        var newImg = image.clone().removeAttr('style').removeAttr('class').addClass('mfp-animated-image'),
3327                            transition = 'all ' + (zoomSt.duration / 1000) + 's ' + zoomSt.easing,
3328                            cssObj = {
3329                                position: 'fixed',
3330                                zIndex: 9999,
3331                                left: 0,
3332                                top: 0,
3333                                '-webkit-backface-visibility': 'hidden'
3334                            },
3335                            t = 'transition';
3336
3337                        cssObj['-webkit-' + t] = cssObj['-moz-' + t] = cssObj['-o-' + t] = cssObj[t] = transition;
3338
3339                        newImg.css(cssObj);
3340                        return newImg;
3341                    },
3342                    showMainContent = function () {
3343                        mfp.content.css('visibility', 'visible');
3344                    },
3345                    openTimeout,
3346                    animatedImg;
3347
3348                _mfpOn('BuildControls' + ns, function () {
3349                    if (mfp._allowZoom()) {
3350
3351                        clearTimeout(openTimeout);
3352                        mfp.content.css('visibility', 'hidden');
3353
3354                        // Basically, all code below does is clones existing image, puts in on top of the current one and animated it
3355
3356                        image = mfp._getItemToZoom();
3357
3358                        if (!image) {
3359                            showMainContent();
3360                            return;
3361                        }
3362
3363                        animatedImg = getElToAnimate(image);
3364
3365                        animatedImg.css(mfp._getOffset());
3366
3367                        mfp.wrap.append(animatedImg);
3368
3369                        openTimeout = setTimeout(function () {
3370                            animatedImg.css(mfp._getOffset(true));
3371                            openTimeout = setTimeout(function () {
3372
3373                                showMainContent();
3374
3375                                setTimeout(function () {
3376                                    animatedImg.remove();
3377                                    image = animatedImg = null;
3378                                    _mfpTrigger('ZoomAnimationEnded');
3379                                }, 16); // avoid blink when switching images
3380
3381                            }, duration); // this timeout equals animation duration
3382
3383                        }, 16); // by adding this timeout we avoid short glitch at the beginning of animation
3384
3385
3386                        // Lots of timeouts...
3387                    }
3388                });
3389                _mfpOn(BEFORE_CLOSE_EVENT + ns, function () {
3390                    if (mfp._allowZoom()) {
3391
3392                        clearTimeout(openTimeout);
3393
3394                        mfp.st.removalDelay = duration;
3395
3396                        if (!image) {
3397                            image = mfp._getItemToZoom();
3398                            if (!image) {
3399                                return;
3400                            }
3401                            animatedImg = getElToAnimate(image);
3402                        }
3403
3404                        animatedImg.css(mfp._getOffset(true));
3405                        mfp.wrap.append(animatedImg);
3406                        mfp.content.css('visibility', 'hidden');
3407
3408                        setTimeout(function () {
3409                            animatedImg.css(mfp._getOffset());
3410                        }, 16);
3411                    }
3412
3413                });
3414
3415                _mfpOn(CLOSE_EVENT + ns, function () {
3416                    if (mfp._allowZoom()) {
3417                        showMainContent();
3418                        if (animatedImg) {
3419                            animatedImg.remove();
3420                        }
3421                        image = null;
3422                    }
3423                });
3424            },
3425
3426            _allowZoom: function () {
3427                return mfp.currItem.type === 'image';
3428            },
3429
3430            _getItemToZoom: function () {
3431                if (mfp.currItem.hasSize) {
3432                    return mfp.currItem.img;
3433                } else {
3434                    return false;
3435                }
3436            },
3437
3438            // Get element postion relative to viewport
3439            _getOffset: function (isLarge) {
3440                var el;
3441                if (isLarge) {
3442                    el = mfp.currItem.img;
3443                } else {
3444                    el = mfp.st.zoom.opener(mfp.currItem.el || mfp.currItem);
3445                }
3446
3447                var offset = el.offset();
3448                var paddingTop = parseInt(el.css('padding-top'), 10);
3449                var paddingBottom = parseInt(el.css('padding-bottom'), 10);
3450                offset.top -= ($(window).scrollTop() - paddingTop);
3451
3452
3453                /*
3454    
3455                Animating left + top + width/height looks glitchy in Firefox, but perfect in Chrome. And vice-versa.
3456    
3457                 */
3458                var obj = {
3459                    width: el.width(),
3460                    // fix Zepto height+padding issue
3461                    height: (_isJQ ? el.innerHeight() : el[0].offsetHeight) - paddingBottom - paddingTop
3462                };
3463
3464                // I hate to do this, but there is no another option
3465                if (getHasMozTransform()) {
3466                    obj['-moz-transform'] = obj['transform'] = 'translate(' + offset.left + 'px,' + offset.top + 'px)';
3467                } else {
3468                    obj.left = offset.left;
3469                    obj.top = offset.top;
3470                }
3471                return obj;
3472            }
3473
3474        }
3475    });
3476
3477
3478
3479    /*>>zoom*/
3480
3481    /*>>iframe*/
3482
3483    var IFRAME_NS = 'iframe',
3484        _emptyPage = '//about:blank',
3485
3486        _fixIframeBugs = function (isShowing) {
3487            if (mfp.currTemplate[IFRAME_NS]) {
3488                var el = mfp.currTemplate[IFRAME_NS].find('iframe');
3489                if (el.length) {
3490                    // reset src after the popup is closed to avoid "video keeps playing after popup is closed" bug
3491                    if (!isShowing) {
3492                        el[0].src = _emptyPage;
3493                    }
3494
3495                    // IE8 black screen bug fix
3496                    if (mfp.isIE8) {
3497                        el.css('display', isShowing ? 'block' : 'none');
3498                    }
3499                }
3500            }
3501        };
3502
3503    $.magnificPopup.registerModule(IFRAME_NS, {
3504
3505        options: {
3506            markup: '<div class="mfp-iframe-scaler">' +
3507                '<div class="mfp-close"></div>' +
3508                '<iframe class="mfp-iframe" src="//about:blank" frameborder="0" allowfullscreen></iframe>' +
3509                '</div>',
3510
3511            srcAction: 'iframe_src',
3512
3513            // we don't care and support only one default type of URL by default
3514            patterns: {
3515                youtube: {
3516                    index: 'youtube.com',
3517                    id: 'v=',
3518                    src: '//www.youtube.com/embed/%id%?autoplay=1'
3519                },
3520                vimeo: {
3521                    index: 'vimeo.com/',
3522                    id: '/',
3523                    src: '//player.vimeo.com/video/%id%?autoplay=1'
3524                },
3525                gmaps: {
3526                    index: '//maps.google.',
3527                    src: '%id%&output=embed'
3528                }
3529            }
3530        },
3531
3532        proto: {
3533            initIframe: function () {
3534                mfp.types.push(IFRAME_NS);
3535
3536                _mfpOn('BeforeChange', function (e, prevType, newType) {
3537                    if (prevType !== newType) {
3538                        if (prevType === IFRAME_NS) {
3539                            _fixIframeBugs(); // iframe if removed
3540                        } else if (newType === IFRAME_NS) {
3541                            _fixIframeBugs(true); // iframe is showing
3542                        }
3543                    }// else {
3544                    // iframe source is switched, don't do anything
3545                    //}
3546                });
3547
3548                _mfpOn(CLOSE_EVENT + '.' + IFRAME_NS, function () {
3549                    _fixIframeBugs();
3550                });
3551            },
3552
3553            getIframe: function (item, template) {
3554                var embedSrc = item.src;
3555                var iframeSt = mfp.st.iframe;
3556
3557                $.each(iframeSt.patterns, function () {
3558                    if (embedSrc.indexOf(this.index) > -1) {
3559                        if (this.id) {
3560                            if (typeof this.id === 'string') {
3561                                embedSrc = embedSrc.substr(embedSrc.lastIndexOf(this.id) + this.id.length, embedSrc.length);
3562                            } else {
3563                                embedSrc = this.id.call(this, embedSrc);
3564                            }
3565                        }
3566                        embedSrc = this.src.replace('%id%', embedSrc);
3567                        return false; // break;
3568                    }
3569                });
3570
3571                var dataObj = {};
3572                if (iframeSt.srcAction) {
3573                    dataObj[iframeSt.srcAction] = embedSrc;
3574                }
3575                mfp._parseMarkup(template, dataObj, item);
3576
3577                mfp.updateStatus('ready');
3578
3579                return template;
3580            }
3581        }
3582    });
3583
3584
3585
3586    /*>>iframe*/
3587
3588    /*>>gallery*/
3589    /**
3590     * Get looped index depending on number of slides
3591     */
3592    var _getLoopedId = function (index) {
3593        var numSlides = mfp.items.length;
3594        if (index > numSlides - 1) {
3595            return index - numSlides;
3596        } else if (index < 0) {
3597            return numSlides + index;
3598        }
3599        return index;
3600    },
3601        _replaceCurrTotal = function (text, curr, total) {
3602            return text.replace(/%curr%/gi, curr + 1).replace(/%total%/gi, total);
3603        };
3604
3605    $.magnificPopup.registerModule('gallery', {
3606
3607        options: {
3608            enabled: false,
3609            arrowMarkup: '<button title="%title%" type="button" class="mfp-arrow mfp-arrow-%dir%"></button>',
3610            preload: [0, 2],
3611            navigateByImgClick: true,
3612            arrows: true,
3613
3614            tPrev: 'Previous (Left arrow key)',
3615            tNext: 'Next (Right arrow key)',
3616            tCounter: '%curr% of %total%'
3617        },
3618
3619        proto: {
3620            initGallery: function () {
3621
3622                var gSt = mfp.st.gallery,
3623                    ns = '.mfp-gallery';
3624
3625                mfp.direction = true; // true - next, false - prev
3626
3627                if (!gSt || !gSt.enabled) return false;
3628
3629                _wrapClasses += ' mfp-gallery';
3630
3631                _mfpOn(OPEN_EVENT + ns, function () {
3632
3633                    if (gSt.navigateByImgClick) {
3634                        mfp.wrap.on('click' + ns, '.mfp-img', function () {
3635                            if (mfp.items.length > 1) {
3636                                mfp.next();
3637                                return false;
3638                            }
3639                        });
3640                    }
3641
3642                    _document.on('keydown' + ns, function (e) {
3643                        if (e.keyCode === 37) {
3644                            mfp.prev();
3645                        } else if (e.keyCode === 39) {
3646                            mfp.next();
3647                        }
3648                    });
3649                });
3650
3651                _mfpOn('UpdateStatus' + ns, function (e, data) {
3652                    if (data.text) {
3653                        data.text = _replaceCurrTotal(data.text, mfp.currItem.index, mfp.items.length);
3654                    }
3655                });
3656
3657                _mfpOn(MARKUP_PARSE_EVENT + ns, function (e, element, values, item) {
3658                    var l = mfp.items.length;
3659                    values.counter = l > 1 ? _replaceCurrTotal(gSt.tCounter, item.index, l) : '';
3660                });
3661
3662                _mfpOn('BuildControls' + ns, function () {
3663                    if (mfp.items.length > 1 && gSt.arrows && !mfp.arrowLeft) {
3664                        var markup = gSt.arrowMarkup,
3665                            arrowLeft = mfp.arrowLeft = $(markup.replace(/%title%/gi, gSt.tPrev).replace(/%dir%/gi, 'left')).addClass(PREVENT_CLOSE_CLASS),
3666                            arrowRight = mfp.arrowRight = $(markup.replace(/%title%/gi, gSt.tNext).replace(/%dir%/gi, 'right')).addClass(PREVENT_CLOSE_CLASS);
3667
3668                        arrowLeft.click(function () {
3669                            mfp.prev();
3670                        });
3671                        arrowRight.click(function () {
3672                            mfp.next();
3673                        });
3674
3675                        mfp.container.append(arrowLeft.add(arrowRight));
3676                    }
3677                });
3678
3679                _mfpOn(CHANGE_EVENT + ns, function () {
3680                    if (mfp._preloadTimeout) clearTimeout(mfp._preloadTimeout);
3681
3682                    mfp._preloadTimeout = setTimeout(function () {
3683                        mfp.preloadNearbyImages();
3684                        mfp._preloadTimeout = null;
3685                    }, 16);
3686                });
3687
3688
3689                _mfpOn(CLOSE_EVENT + ns, function () {
3690                    _document.off(ns);
3691                    mfp.wrap.off('click' + ns);
3692                    mfp.arrowRight = mfp.arrowLeft = null;
3693                });
3694
3695            },
3696            next: function () {
3697                mfp.direction = true;
3698                mfp.index = _getLoopedId(mfp.index + 1);
3699                mfp.updateItemHTML();
3700            },
3701            prev: function () {
3702                mfp.direction = false;
3703                mfp.index = _getLoopedId(mfp.index - 1);
3704                mfp.updateItemHTML();
3705            },
3706            goTo: function (newIndex) {
3707                mfp.direction = (newIndex >= mfp.index);
3708                mfp.index = newIndex;
3709                mfp.updateItemHTML();
3710            },
3711            preloadNearbyImages: function () {
3712                var p = mfp.st.gallery.preload,
3713                    preloadBefore = Math.min(p[0], mfp.items.length),
3714                    preloadAfter = Math.min(p[1], mfp.items.length),
3715                    i;
3716
3717                for (i = 1; i <= (mfp.direction ? preloadAfter : preloadBefore); i++) {
3718                    mfp._preloadItem(mfp.index + i);
3719                }
3720                for (i = 1; i <= (mfp.direction ? preloadBefore : preloadAfter); i++) {
3721                    mfp._preloadItem(mfp.index - i);
3722                }
3723            },
3724            _preloadItem: function (index) {
3725                index = _getLoopedId(index);
3726
3727                if (mfp.items[index].preloaded) {
3728                    return;
3729                }
3730
3731                var item = mfp.items[index];
3732                if (!item.parsed) {
3733                    item = mfp.parseEl(index);
3734                }
3735
3736                _mfpTrigger('LazyLoad', item);
3737
3738                if (item.type === 'image') {
3739                    item.img = $('<img class="mfp-img" />').on('load.mfploader', function () {
3740                        item.hasSize = true;
3741                    }).on('error.mfploader', function () {
3742                        item.hasSize = true;
3743                        item.loadError = true;
3744                        _mfpTrigger('LazyLoadError', item);
3745                    }).attr('src', item.src);
3746                }
3747
3748
3749                item.preloaded = true;
3750            }
3751        }
3752    });
3753
3754    /*>>gallery*/
3755
3756    /*>>retina*/
3757
3758    var RETINA_NS = 'retina';
3759
3760    $.magnificPopup.registerModule(RETINA_NS, {
3761        options: {
3762            replaceSrc: function (item) {
3763                return item.src.replace(/\.\w+$/, function (m) { return '@2x' + m; });
3764            },
3765            ratio: 1 // Function or number.  Set to 1 to disable.
3766        },
3767        proto: {
3768            initRetina: function () {
3769                if (window.devicePixelRatio > 1) {
3770
3771                    var st = mfp.st.retina,
3772                        ratio = st.ratio;
3773
3774                    ratio = !isNaN(ratio) ? ratio : ratio();
3775
3776                    if (ratio > 1) {
3777                        _mfpOn('ImageHasSize' + '.' + RETINA_NS, function (e, item) {
3778                            item.img.css({
3779                                'max-width': item.img[0].naturalWidth / ratio,
3780                                'width': '100%'
3781                            });
3782                        });
3783                        _mfpOn('ElementParse' + '.' + RETINA_NS, function (e, item) {
3784                            item.src = st.replaceSrc(item, ratio);
3785                        });
3786                    }
3787                }
3788
3789            }
3790        }
3791    });
3792
3793    /*>>retina*/
3794    _checkInstance();
3795}));
3796window.eviivo = window.eviivo ? window.eviivo : {};
3797window.eviivo.imageMainGallery = window.eviivo.imageMainGallery ? window.eviivo.imageMainGallery : {};
3798
3799window.eviivo.imageMainGallery = function ($) {
3800
3801    var defaultOptions = {
3802        initializeGalleryByDefault: true,
3803        initializeGalleryOnClick: true,
3804        initializeGalleryForRooms: true,
3805        trigger: '.trigger',
3806        heroSlide: '#hero-slider',
3807        heroNav: '#hero-nav',
3808        thumbSlider: "#hero-nav-single"
3809    };
3810
3811    var options;
3812    var trigger;
3813    var thumbSlider;
3814    var virtualTourButton;
3815    var galleryButton;
3816    var imageGalleryContainer;
3817    var closeVTButton;
3818
3819    function init(settings) {
3820        //extend internal settings with external ones
3821        options = $.extend(defaultOptions, settings);
3822
3823        trigger = $(options.trigger);
3824        thumbSlider = $(options.thumbSlider);
3825
3826        virtualTourButton = $(".s-vt-virtual-tour");
3827        galleryButton = $(".s-vt-image-gallery");
3828
3829        imageGalleryContainer = $(".mfp-arrow,.mfp-content");
3830        virtualTourContainer = $(".s-vt-container");
3831        closeVTButton = $(".s-vt-close");
3832
3833        if (options.initializeGalleryByDefault === true && options.initializeGalleryOnClick === true) {
3834            throw "Initializing the Gallery with both options Initialize By Default and OnClick will generate conflicts as the classes are shared.";
3835        }
3836
3837        // This condition should be used for scenarios where we want to load the gallery by DEFAULT on the page. e.g.: via pages that load the gallery on load
3838        if (options.initializeGalleryByDefault === true) {
3839            initDummyGallery();
3840        }
3841
3842        // This function should be used for scenarios where we want to load the gallery only when a CLICK EVENT occurs. e.g.: toprooms/partners property pages
3843        if (options.initializeGalleryOnClick === true) {
3844            trigger.on('click', function () {
3845                initMainGallery();
3846            });
3847        }
3848    }
3849
3850    $('.results-trigger').on('click', function () {
3851        $(this).next().magnificPopup('open');
3852    });
3853
3854    // Click on Virtual tour icon on Homepage & Photos, except for Fullscape templates
3855    $('.page-type-1 .gallery-details .virtual-tour-icon, .page-type-5 .gallery-details .virtual-tour-icon').on('click', function () {
3856        if (!$("body").hasClass("template-13", "template-14")) {
3857            var iFrameUrl = $(this).parent().parent("a").data('iframe-url');
3858
3859            $("body").addClass("mfp-hidden");
3860            virtualTourButton.addClass('active');
3861            galleryButton.removeClass('active');
3862            virtualTourContainer.removeClass("hidden");
3863            // Pass Virtual tour URL into an iFrame
3864            vtIframe.attr("src", iFrameUrl);
3865        }
3866    });
3867    // Click on Virtual tour icon on RoomType
3868    $('.page-type-2 .gallery-details .virtual-tour-icon').on('click', function () {
3869        $(this).closest('.lightbox-gallery-link').next().magnificPopup('open');
3870        $('.s-vt-virtual-tour.cp-button').trigger('click');
3871    });
3872
3873    var vtIframe = $("#s-vt-iframe");
3874
3875    $('.lightbox-gallery').each(function () {
3876        $(this).magnificPopup({
3877            delegate: 'a',
3878            type: 'image',
3879            gallery: {
3880                enabled: true,
3881                navigateByImgClick: true
3882            },
3883            fixedContentPos: true,
3884            callbacks: {
3885                open: function () {
3886                    launchGallery();
3887                },
3888                close: function () {
3889                    // Will fire when popup gallery is closed
3890                    hideVirtualTour();
3891                }
3892            }
3893        });
3894    });
3895
3896    function virtualTourToggle() {
3897
3898        virtualTourButton.on("click", function () {
3899            galleryButton.removeClass("active");
3900            virtualTourButton.addClass("active");
3901            $("body").addClass("mfp-hidden");
3902
3903            virtualTourContainer.removeClass("hidden");
3904            imageGalleryContainer.hide();
3905        });
3906
3907        galleryButton.on("click", function () {
3908            galleryButton.addClass("active");
3909            virtualTourButton.removeClass("active");
3910            $("body").removeClass("mfp-hidden");
3911
3912            virtualTourContainer.addClass("hidden");
3913            imageGalleryContainer.show();
3914        });
3915
3916        closeVTButton.on("click", function () {
3917            $.magnificPopup.close(); // Close popup that is currently opened (shorthand)
3918        });
3919    }
3920
3921    /*=============================
3922    toprooms Main gallery
3923    =============================*/
3924
3925    function initMainGallery() {
3926        $('.lightbox-gallery').magnificPopup({
3927            delegate: 'a',
3928            type: 'image',
3929            gallery: {
3930                enabled: true,
3931                navigateByImgClick: true
3932            },
3933            fixedContentPos: true,
3934            callbacks: {
3935                open: function () {
3936                    launchGallery();
3937                },
3938                close: function () {
3939                    // Will fire when popup gallery is closed
3940                    hideVirtualTour();
3941                }
3942            }
3943        });
3944    }
3945
3946    /*=========================================
3947    Dummy her gallery with thumbnail slider
3948    =========================================*/
3949    // THIS INITIATES SLIDERS FOR ONLY TEMPLATES 7 AND 12
3950    function initDummyGallery() {
3951        thumbSlider.jcarousel({
3952            wrap: 'last'
3953        });
3954
3955        $('.slide-prev').on("click", function () {
3956            if (window.innerWidth > 768) {
3957                thumbSlider.jcarousel('scroll', '-=3');
3958            }
3959            // if on small screen, scroll by a sigle image
3960            else {
3961                thumbSlider.jcarousel('scroll', '-=1');
3962            }
3963        });
3964
3965        $('.slide-next').on("click", function () {
3966            
3967            if (window.innerWidth > 768) {
3968                thumbSlider.jcarousel('scroll', '+=3');
3969            }
3970            // if on small screen, scroll by a sigle image
3971            else {
3972                thumbSlider.jcarousel('scroll', '+=1');
3973            }
3974        });
3975
3976        $(".lightbox-trigger-container").magnificPopup({
3977            delegate: 'a',
3978            type: 'image',
3979            gallery: {
3980                enabled: true,
3981                navigateByImgClick: true
3982            },
3983            fixedContentPos: true,
3984            callbacks: {
3985                open: function () {
3986                    launchGallery();
3987                },
3988                close: function () {
3989                    // Will fire when popup gallery is closed
3990                    hideVirtualTour();
3991                }
3992            }
3993        });
3994    }
3995    // IW template 13-14 or for inline galleries without an overlay
3996    $(".s-virtual-tour-launch").on("click", function () {
3997            $("body").addClass("virtual-tour-enabled");
3998
3999            virtualTourContainer.removeClass("hidden");
4000
4001            closeVTButton.on("click", function () {
4002                hideVirtualTour();
4003            });
4004    });
4005
4006    $(document).on('keyup', function (e) {
4007        if (e.key === "Escape") hideVirtualTour();
4008    });
4009
4010    function launchGallery() {
4011        var popUpGallery = $.magnificPopup.instance,
4012            firstImage = $(popUpGallery.currItem.el);
4013
4014        var iFrameUrl = firstImage.data('iframe-url');
4015
4016        if (iFrameUrl != null) {
4017            $("body").addClass("virtual-tour-enabled");
4018
4019            virtualTourToggle();
4020
4021            // Pass Virtual tour URL into an iFrame
4022            vtIframe.attr("src", iFrameUrl)
4023        }
4024    }
4025
4026    function hideVirtualTour() {
4027        // Will fire when popup gallery is closed
4028        $("body").removeClass("virtual-tour-enabled mfp-hidden");
4029        $(".s-vt-container").addClass("hidden");
4030
4031        // Reset buttons to a default state
4032        $(".s-vt-image-gallery").addClass("active");
4033        $(".s-vt-virtual-tour").removeClass("active");
4034        if (!$("body").hasClass("template-13", "template-14")) {
4035            // Clear iFrame's src
4036            vtIframe.attr("src", "");
4037        }
4038
4039    }
4040
4041    return {
4042        init: init
4043    };
4044}(jQuery);
4045window.eviivo = window.eviivo ? window.eviivo : {};
4046window.eviivo.reviews = window.eviivo.reviews ? window.eviivo.reviews : {};
4047
4048window.eviivo.reviews = function ($, ajaxHelper) {
4049
4050    var defaultOptions = {
4051        propertyReviewsUrl: "",
4052        resources: {
4053            ReadMore: "Read more",
4054            ReadLess: "Read less"
4055        }
4056    };
4057    var ajaxRequestId = "reviewsAjaxRequest";
4058    var options;
4059    var currentPage = 1;
4060    var pages;
4061    var isNextDisabled;
4062    var isPreviousDisabled;
4063    var $firstArrow;
4064    var $lastArrow;
4065    var $lastArrowLink;
4066    var $firstArrowLink;
4067    var $reviewsSection;
4068    ///<summary>Main init method of the eviivo.reviews object</summary>
4069    ///<param name="settings">external settings</param>
4070    function init(settings) {
4071        //extend internal settings with external ones
4072        options = $.extend(defaultOptions, settings);
4073        $reviewsSection = $(".reviews-section");
4074
4075        if (!$reviewsSection.length) { //In cases when comments are disabled.
4076            return;
4077        }
4078
4079        pages = $reviewsSection.data("numberofpages");
4080
4081        ajaxHelper.init(ajaxRequestId, options.ajaxLoaderResources, $reviewsSection, updatePageContent, loadPageError);
4082
4083        loadReviewWhenReviewsSectionIsVisible();
4084    }
4085
4086    function loadReviewWhenReviewsSectionIsVisible() {
4087        if (isReviewsSectionVisible()) {
4088            navigateToPage(1);
4089        } else {
4090            $(window).scroll(function () {
4091                if (isReviewsSectionVisible()) {
4092                    $(window).unbind('scroll');
4093                    navigateToPage(1);
4094                }
4095            });
4096        }
4097    }
4098
4099    function isReviewsSectionVisible() {
4100        return $(window).scrollTop() + $(window).height() + 70 >= $reviewsSection.offset().top;
4101    }
4102
4103    function navigatePrevious() {
4104        if (currentPage - 1 > 0) {
4105            navigateToPage(currentPage - 1);
4106            return true;
4107        }
4108        return false;
4109    }
4110
4111    function navigateNext() {
4112        if (currentPage + 1 <= pages) {
4113            navigateToPage(currentPage + 1);
4114            return true;
4115        }
4116        return false;
4117    }
4118
4119    function updatePagination() {
4120        $firstArrow = $(".mod-paginate li.arrow:first-child");
4121        $lastArrow = $(".mod-paginate li.arrow:last-child");
4122        $firstArrowLink = $firstArrow.find("a");
4123        $lastArrowLink = $lastArrow.find("a");
4124
4125        $firstArrowLink.on("click", navigatePrevious);
4126        $lastArrowLink.on("click", navigateNext);
4127
4128        //$('.mod-paginate li[class!="arrow"]').remove();
4129
4130        var currentPageHtml = $('<li class="current"><a class="link-bg" href="javascript:;">' + currentPage + '</a></li>');
4131        currentPageHtml.insertAfter($firstArrow);
4132
4133        var pageBefore = currentPage - 1;
4134        if (pageBefore > 0) {
4135            $firstArrow.removeAttr("disabled");
4136            $('<li><a href="javascript:eviivo.reviews.navigateToPage(' + pageBefore + ');">' + pageBefore + '</a></li>').insertAfter($firstArrow);
4137
4138            if (pageBefore - 2 >= 0) {
4139                $('<li><a href="javascript:;">&hellip;</a></li>').insertAfter($firstArrow);
4140                $('<li><a href="javascript:eviivo.reviews.navigateToPage(1);">1</a></li>').insertAfter($firstArrow);
4141            }
4142        }
4143
4144        var pageAfter = currentPage + 1;
4145        if (pageAfter <= pages) {
4146            $('<li><a href="javascript:eviivo.reviews.navigateToPage(' + pageAfter + ');">' + pageAfter + '</a></li>').insertBefore($lastArrow);
4147        }
4148
4149        if (currentPage + 2 <= pages) {
4150            $('<li><a href="javascript:;">&hellip;</a></li>').insertBefore($lastArrow);
4151            $('<li><a href="javascript:eviivo.reviews.navigateToPage(' + pages + ');">' + pages + '</a></li>').insertBefore($lastArrow);
4152        }
4153
4154        if (currentPage == 1) {
4155            $firstArrowLink.off("click");
4156            isPreviousDisabled = true;
4157        }
4158        else if (isPreviousDisabled) {
4159            $firstArrowLink.on("click", navigatePrevious);
4160            isPreviousDisabled = false;
4161        }
4162
4163        if (currentPage == pages) {
4164            $lastArrowLink.off("click");
4165            isNextDisabled = true;
4166        }
4167        else if (isNextDisabled) {
4168            $lastArrowLink.on("click", navigateNext);
4169            isNextDisabled = false;
4170        }
4171    }
4172
4173    function loadPageError(jqXhr, textStatus, errorThrown) {
4174        console.log(textStatus);
4175        console.log(errorThrown);
4176    }
4177
4178    function updatePageContent(data) {
4179        $reviewsSection.html(data);
4180
4181        updatePagination();
4182
4183        //reviews read more action
4184
4185        $("#reviews-list .column-review-content p a").on("click", function () {
4186            $(this).parent().siblings(".review-item-details").slideToggle();
4187            $(this).parent().parent().toggleClass("active");
4188            var textDefault = options.resources.ReadMore;
4189            var textExpanded = options.resources.ReadLess;
4190            $(this).text(function (i, text) {
4191                return text === textExpanded ? textDefault : textExpanded;
4192            });
4193        });
4194    }
4195
4196    function loadPage() {
4197        ajaxHelper.executeAjax({
4198            requestId: ajaxRequestId,
4199            url: options.propertyReviewsUrl + "/" + currentPage,
4200            method: "GET",
4201            cache: false,
4202            dataType: "html"
4203        });
4204    }
4205
4206    function navigateToPage(page) {
4207        currentPage = page;
4208        loadPage();
4209    }
4210
4211    ///<summary>Expose the internal "private" methods for external use (make them public)</summary>
4212    return {
4213        init: init,
4214        navigateToPage: navigateToPage
4215    };
4216}(jQuery, eviivo.utils.ajaxHelper);
4217window.eviivo = window.eviivo ? window.eviivo : {};
4218window.eviivo.popup = window.eviivo.popup ? window.eviivo.popup : {};
4219
4220window.eviivo.popup = function ($) {
4221
4222    var defaultOptions = {
4223        resources: {
4224        },
4225        bypassClose: false,
4226        bypassBodyHtml: false,
4227        popupBox: null,
4228        bypassHeaderHtml: false
4229    };
4230    var options;
4231    var $popupBox;
4232    var $popupContent;
4233    var $popupClose;
4234    var $popupHeaderTitle;
4235
4236    ///<summary>Main init method of the eviivo.popup object</summary>
4237    ///<param name="settings">external settings</param>
4238    function init(settings) {
4239        //extend internal settings with external ones
4240        options = $.extend(defaultOptions, settings);
4241        $popupBox = $(".dialogue-outer");
4242        $popupContent = $popupBox.find(".mod-dialogueBox");
4243        $popupHeaderTitle = $popupBox.find(".dialogue-header h3");
4244        $popupClose = $popupBox.find(".cp-link");
4245        $popupHeaderTitle = $popupBox.find(".dialogue-header h3");
4246        $popupClose.on("click", close);
4247        $(document).on("keydown", close);
4248        popUpVisibility();
4249    }
4250
4251    function display(showOptions) {
4252        if (showOptions.popupBox != null && showOptions.popupBox != "") {
4253            $popupBox = $(showOptions.popupBox);
4254            $popupContent = $popupBox.find(".mod-dialogueBox");
4255            $popupHeaderTitle = $popupBox.find(".dialogue-header h3");
4256            $popupClose = $popupBox.find(".cp-link");
4257            $popupHeaderTitle = $popupBox.find(".dialogue-header h3");
4258        }
4259
4260        if (showOptions.bypassClose) {
4261            $popupClose.hide();
4262            $popupClose.off("click", close);
4263            $(document).off("keydown", close);
4264        } else {
4265            $popupClose.show();
4266            $popupClose.on("click", close);
4267            $(document).on("keydown", close);
4268        }
4269
4270        if (showOptions.bypassBodyHtml === false || showOptions.bypassBodyHtml == undefined) {
4271            $popupContent.html(showOptions.html);
4272        }
4273
4274        if (showOptions.bypassHeaderHtml === false || showOptions.bypassHeaderHtml == undefined) {
4275            $popupHeaderTitle.html(showOptions.headerTitle).text();
4276        }
4277
4278        if (showOptions.bodyClass) {
4279            $popupBox.removeClass().addClass('dialogue-outer').addClass(showOptions.bodyClass);
4280        }
4281
4282        if (typeof showOptions.popupCloseCallBack === "function") {
4283            options.popupCloseCallBack = showOptions.popupCloseCallBack;
4284        }
4285
4286        $popupBox.css("display", "table");
4287    }
4288
4289    function close(e) {
4290        if (e.which === 1 || e.keyCode === 27) {
4291            $popupContent.html("");
4292            $popupBox.css("display", "none");
4293            $popupHeaderTitle.text("");
4294            if (typeof options.popupCloseCallBack === "function") {
4295                options.popupCloseCallBack();
4296            }
4297
4298            if ($popupBox.hasClass('mod_announcement-popup')) {
4299                window.eviivo.webcore.cookieManager.setCookie("announcement_popup", "announcement_" + $('#announcement-popup_business_shortname').val() + "=1", 1);
4300                $popupBox.remove('.mod_announcement-popup');
4301            }
4302            $("body").removeClass("overflow-hidden");
4303        }
4304    }
4305
4306    function popUpVisibility() {
4307        if ($(".mod_announcement-popup").is(':visible')) {
4308            $("body").addClass("overflow-hidden");
4309        }
4310    }
4311
4312    ///<summary>Expose the internal "private" methods for external use (make them public)</summary>
4313    return {
4314        init: init,
4315        display: display
4316    };
4317}(jQuery);
4318document.addEventListener("DOMContentLoaded", function () {
4319    // Open modal
4320    function openModal(event) {
4321        event.preventDefault();
4322        var targetId = event.currentTarget.getAttribute('id').replace('openM
4322odal-', 'moreInfoModal-');
4323        var modal = document.getElementById(targetId);
4324        if (modal) {
4325            modal.style.display = "block";
4326            modal.classList.add("modal-open");
4327            document.body.classList.add("modal-open");
4328        }
4329    }
4330
4331    // Close modal
4332    function closeModal(event) {
4333        event.preventDefault();
4334        var targetId = event.currentTarget.getAttribute('data-target').replace('#', '');
4335        var modal = document.getElementById(targetId);
4336        if (modal) {
4337            modal.style.display = "none";
4338            modal.classList.remove("modal-open");
4339            document.body.classList.remove("modal-open");
4340        }
4341    }
4342
4343    // Close modal by modal element
4344    function closeModalByElement(modal) {
4345        if (modal) {
4346            modal.style.display = "none";
4347            modal.classList.remove("modal-open");
4348            document.body.classList.remove("modal-open");
4349        }
4350    }
4351
4352    // Attach event listeners to open modal links
4353    var openModalLinks = document.querySelectorAll('.modal-rooms-toggle');
4354    openModalLinks.forEach(function (link) {
4355        link.addEventListener('click', openModal);
4356    });
4357
4358    // Attach event listeners to close modal links
4359    var closeModalLinks = document.querySelectorAll('.modal-close');
4360    closeModalLinks.forEach(function (link) {
4361        link.addEventListener('click', closeModal);
4362    });
4363
4364    // Close modal when clicking outside of it
4365    window.addEventListener('click', function (event) {
4366        if (event.target.classList.contains('modal')) {
4367            closeModalByElement(event.target);
4368        }
4369    });
4370
4371    // Close modal when pressing the Escape key
4372    document.addEventListener('keydown', function (event) {
4373        if (event.key === "Escape") {
4374            var openModals = document.querySelectorAll('.modal.modal-open');
4375            openModals.forEach(function (modal) {
4376                closeModalByElement(modal);
4377            });
4378        }
4379    });
4380});
4381
4382document.addEventListener("DOMContentLoaded", function () {
4383    $(".accordion-toggle").on("click", function () {
4384        var target = $(this).data("target");
4385        $("#" + target).slideToggle("slow");
4386        $(this).toggleClass("active");
4387    });
4388});
4389window.eviivo = window.eviivo ? window.eviivo : {};
4390window.eviivo.optionSelector = window.eviivo.optionSelector ? window.eviivo.optionSelector : {};
4391
4392window.eviivo.optionSelector = function ($) {
4393    var defaultOptions = {
4394    };
4395
4396    var options;
4397
4398
4399    ///<summary>Main init method of the eviivo.optionSelector object</summary>
4400    ///<param name="settings">external settings</param>
4401    function init(settings) {
4402        //extend internal settings with external ones
4403        options = $.extend(defaultOptions, settings);
4404
4405    }
4406
4407
4408
4409    //Header language selector
4410    var $expandableSelector = $("header .cp-expandable");
4411    var $headerControls = $(".cp-expandable");
4412    var $currencyContainer = $(".currency-inner");
4413    var $languageContainer = $(".language-inner");
4414    var $headerTooltip = $("header .cp-tooltip");
4415
4416
4417    $expandableSelector.on("click", function () {
4418        $(this).toggleClass("active");
4419        $(this).siblings(".cp-tooltip").toggle();
4420    });
4421
4422    $headerControls.on("click", function () {
4423        if ($(this).hasClass("active")) {
4424            $(this).parent().siblings().find(".cp-expandable").removeClass("active");
4425            $(this).parent().siblings().find(".cp-tooltip").hide();
4426        }
4427    });
4428
4429    /* Hide */
4430    $(document).on("click", function (e) {
4431
4432        if ((!$(e.target).hasClass("cp-expandable") || !$(e.target).hasClass("active")) && $(e.target).parents("header .mod-selectors").length == 0) {
4433            $currencyContainer.hide();
4434            $languageContainer.hide();
4435            $expandableSelector.removeClass("active");
4436        }
4437
4438    });
4439
4440    $currencyContainer.on("click", function (evt) {
4441        evt.stopPropagation();
4442    });
4443
4444    $languageContainer.on("click", function (evt) {
4445        evt.stopPropagation();
4446    });
4447
4448    $(document).on("keyup",function (e) {
4449        if (e.keyCode == 27) {
4450
4451            if (!$(e.target).hasClass(".cp-expandable.cp-expandable")) {
4452                $headerTooltip.hide();
4453                $expandableSelector.removeClass("active");
4454
4455            }
4456
4457        }
4458    });
4459    //Header language selector END
4460
4461
4462
4463
4464    ///<summary>Expose the internal "private" methods for external use (make them public)</summary>
4465    return {
4466        init: init
4467    };
4468
4469}(jQuery);
4470window.eviivo = window.eviivo ? window.eviivo : {};
4471window.eviivo.roomImages = window.eviivo.roomImages ? window.eviivo.roomImages : {};
4472
4473window.eviivo.roomImages = function ($) {
4474
4475    var defaultOptions = {
4476    };
4477    
4478    var options;
4479    var clickedImageSrc;
4480    
4481    ///<summary>Main init method of the eviivo.roomImages object</summary>
4482    ///<param name="settings">external settings</param>
4483    function init(settings) {
4484        //extend internal settings with external ones
4485        options = $.extend(defaultOptions, settings);
4486
4487        $("div.mod-results div.results-gallery-thumbs li:first-child a").addClass("active");
4488
4489        // Results thumbs click functionality
4490        $(".results-gallery-thumbs a").on("click", thumbnailClickHandler);
4491    }
4492    
4493    ///<summary></summary>
4494    function thumbnailClickHandler() {
4495        var $obj = $(this);
4496        if (!$obj.hasClass("active")) {
4497            $obj.parents(".results-gallery-thumbs").find("a").removeClass("active");
4498            $obj.addClass("active");
4499            var $picture = $obj.parents("div.results-gallery").find("div.results-gallery-single picture");
4500            if ($picture) {
4501                var src = $obj.find("img").attr("srcset");
4502                clickedImageSrc = extractUrl(src, false);
4503                $picture.find("img, source").each(changeSourceSet);
4504            }
4505        }
4506    }
4507    
4508    ///<summary></summary>
4509    function extractUrl(url, extensionOnly) {
4510        var index = url.lastIndexOf("-");
4511        var str = url.substr(index);
4512        if (str.length > 6) {
4513            index = url.lastIndexOf(".");
4514            return extensionOnly ? url.substr(index) : url.substr(0, index);
4515        }
4516        return extensionOnly ? str : url.substr(0, index);
4517    }
4518    
4519    ///<summary></summary>
4520    function changeSourceSet(i, e) {
4521        var srcExtension = $(e).attr("srcset");
4522        srcExtension = extractUrl(srcExtension, true);
4523        if ($(e).attr("src")) {
4524            $(e).attr("src", clickedImageSrc + srcExtension);
4525        }
4526        $(e).attr("srcset", clickedImageSrc + srcExtension);
4527    }
4528
4529    ///<summary>Expose the internal "private" methods for external use (make them public)</summary>
4530    return {
4531        init: init
4532    };
4533}(jQuery);
4534window.eviivo = window.eviivo ? window.eviivo : {};
4535window.eviivo.instantWebMenu = window.eviivo.instantWebMenu ? window.eviivo.instantWebMenu : {};
4536
4537window.eviivo.instantWebMenu = function ($) {
4538
4539    var defaultOptions = {
4540        navigation: '.menu',
4541        menuOuter: '.menu-toggle ul.outer-menu',
4542        menuItems: '.menu li a',
4543        iconRight: '.icon-arrow-right',
4544        iconLeft: '.icon-arrow-left',
4545        menuControls: '.nav-controls',
4546        menuCta: '.menu-toggle .cp-button',
4547        searchBar: '.mod-search',
4548        menuButton: '.cp-menuIcon',
4549        iconMenuContainer: '.menu-icon',
4550        iconMenuDefault: '≡',
4551        iconMenuClose: '×',
4552        subMenu: '.sub-menu',
4553        hasChildren: '.has-children'
4554    };
4555    var options;
4556    var totalWidth;
4557
4558    var $navigation;
4559    var $menuOuter;
4560    var $menuItems;
4561    var $iconRight;
4562    var $iconLeft;
4563    var $menuControls;
4564    var $menuCta;
4565    var $searchBar;
4566    // Responsive menu toggle
4567    var $menuButton;
4568    var $iconMenuContainer;
4569    var iconMenuDefault;
4570    var iconMenuClose;
4571    var $subMenu;
4572    var $hasChildren;
4573
4574    ///<summary>Main init method of the eviivo.availabilitySearch object</summary>
4575    ///<param name="settings">external settings</param>
4576    function init(settings) {
4577
4578        //extend internal settings with external ones
4579        options = $.extend(defaultOptions, settings);
4580
4581        $navigation = $(options.navigation);
4582        $menuOuter = $(options.menuOuter);
4583        $menuItems = $(options.menuItems);
4584        $iconRight = $(options.iconRight);
4585        $iconLeft = $(options.iconLeft);
4586        $menuControls = $(options.menuControls);
4587        // header book button
4588        $menuCta = $(options.menuCta);
4589        $searchBar = $(options.searchBar);
4590        // Responsive menu toggle
4591        $menuButton = $(options.menuButton);
4592        iconMenuContainer = options.iconMenuContainer;
4593        iconMenuDefault = options.iconMenuDefault;
4594        iconMenuClose = options.iconMenuClose;
4595        $hasChildren = $(options.hasChildren);
4596        $subMenu = $(options.subMenu);
4597
4598        totalWidth = 0;
4599
4600        $iconRight.on('click', clickScrollRight);
4601        $iconLeft.on('click', clickScrollLeft);
4602
4603        menuLengthCheck();
4604        positionSubMenus();
4605
4606        $menuCta.on("click", function () {
4607            $(this).toggleClass("active");
4608            $searchBar.toggleClass('open');
4609
4610            if ($menuButton.parent().hasClass("active")) {
4611                toggleBookButtonIcon($menuButton);
4612            }
4613        });
4614        
4615        $menuButton.on("click", function () {
4616            toggleBookButtonIcon($(this));
4617
4618            if ($menuCta.hasClass('active')) {
4619                closeSearch();
4620            }
4621        });
4622
4623        $navigation.on('scroll', positionSubMenus);
4624
4625        // Toggle submenu on click (mobile or burger menu), exclude desktop horizontal menu
4626        $hasChildren.on('click', function (e) {
4627            var $navMenuToggle = $(this).closest('nav .menu-toggle');
4628            var isDesktopHorizontalMenu = ($(window).width() >= 1024) && $navMenuToggle.length;
4629
4630            if (!isDesktopHorizontalMenu) {
4631                e.preventDefault();
4632                var $subMenu = $(this).find('.sub-menu');
4633
4634                // Close all other submenus
4635                $hasChildren.not(this).find('.sub-menu').slideUp(200);
4636
4637                // Toggle current submenu
4638                $subMenu.slideToggle(200);
4639            }
4640        });
4641
4642        $(window).on('resize', function () {
4643            // Reset submenus when switching to desktop (only for horizontal menu)
4644            if ($(window).width() >= 1024) {
4645                $('nav .menu-toggle .sub-menu').css('display', '');
4646            }
4647            menuLengthCheck();
4648            positionSubMenus();
4649        });
4650    }
4651
4652    // Position submenus horizontally relative to their parent items
4653    function positionSubMenus() {
4654        $hasChildren.each(function () {
4655            var $parent = $(this); // get the parent li
4656            var $subMenu = $parent.find('.sub-menu'); // get the sub menu
4657
4658            if ($subMenu.length) {
4659                // Get position relative to the scrollable container
4660                var parentOffset = $parent.position().left;
4661
4662                // Position relative to parent with proper scoping
4663                $subMenu.css({
4664                    'left': parentOffset + 'px',
4665                });
4666            }
4667        });
4668    }
4669
4670    function closeSearch() {
4671        $menuCta.removeClass("active");
4672        $searchBar.removeClass('open');
4673    }
4674    
4675    function initSearchBar() {
4676        $menuCta.toggleClass('active');
4677        $searchBar.toggleClass('open');
4678    }
4679
4680    function toggleBookButtonIcon(object) {
4681        $(object).parent().toggleClass("active");
4682
4683        if ($menuButton.parent().hasClass('active')) {
4684            $(iconMenuContainer).text(iconMenuClose);
4685        }
4686        else {
4687            $(iconMenuContainer).text(iconMenuDefault);
4688        }
4689    }
4690
4691    function menuLengthCheck() {
4692        var menuContainer = $('nav .menu').outerWidth(true);
4693
4694        totalWidth = 0;
4695        $menuItems.each(function (i, e) {
4696            totalWidth += $(e).outerWidth(true); // adds its width to the total
4697        });
4698
4699        if ($(window).width() > 1023) {
4700
4701            $menuOuter.css('width', (totalWidth + 1)); // I give it extra 1 pixels for to accommodate different browser rendering issues.
4702
4703            if (totalWidth > menuContainer) {
4704                menuShow();
4705            } else {
4706                menuHide();
4707            }
4708        } else {
4709            menuHide();
4710        }
4711    }
4712
4713    function menuHide() {
4714        $menuOuter.removeAttr("style");
4715        $menuControls.hide();
4716    }
4717
4718    function menuShow() {
4719        $menuControls.show();
4720    }
4721
4722    function clickScrollRight() {
4723        var scrollX = $navigation.scrollLeft();
4724        $navigation.animate({ scrollLeft: scrollX + 200 }, 300);
4725    }
4726
4727    function clickScrollLeft() {
4728        var scrollX = $navigation.scrollLeft();
4729        $navigation.animate({ scrollLeft: scrollX - 200 }, 300);
4730    }
4731
4732
4733
4734    ///<summary>Expose the internal "private" methods for external use (make them public)</summary>
4735    return {
4736        init: init,
4737        initSearchBar: initSearchBar
4738    };
4739}(jQuery);
4740window.eviivo = window.eviivo ? window.eviivo : {};
4741window.eviivo.promos = window.eviivo.promos ? window.eviivo.promos : {};
4742
4743window.eviivo.promos = function ($) {
4744
4745    var defaultOptions = {
4746        resources: {
4747            apply: "Apply",
4748            unlockSpecials: "Click to unlock your special deal",
4749            enterPromoCode: "Enter your code or member ID"
4750        }
4751    };
4752    var options;
4753    var $mainContainer;
4754    var $modOverlay;
4755    var searchTrigger;
4756
4757    function init(settings) {
4758        //extend internal settings with external ones
4759        options = $.extend(defaultOptions, settings);
4760
4761        $mainContainer = $(".main");
4762        $('div.mod-promo button.promo-button-action').on("click", applyPromoButtonHandler);
4763
4764        checkForInvalidPromoCode();
4765
4766        searchTrigger = options.searchTrigger;
4767    }
4768
4769    function checkForInvalidPromoCode() {
4770        var failure = jQuery("#failed_code_message");
4771        if (failure.length > 0) {
4772            showPromoCodeInputPopup(failure.val());
4773        }
4774    }
4775
4776    function applyPromoButtonHandler() {
4777        var bannerType = $(this).data("banner-type");
4778        if (typeof bannerType === "string") {
4779            switch (bannerType.toLowerCase()) {
4780                case options.bannerTypes.webExclusivePromoCode.toLowerCase():
4781                    showPromoCodeInputPopup();
4782                    break;
4783                case options.bannerTypes.webExclusiveInstantDeal.toLowerCase():
4784                    showInstantDealInputPopup();
4785                    break;
4786                case options.bannerTypes.webExclusivePromoCodeUnlocked.toLowerCase():
4787                case options.bannerTypes.webExclusivePromoCodeFail.toLowerCase():
4788                case options.bannerTypes.webExclusiveBannerWarning.toLowerCase():
4789                    removePromotionBanner();
4790                    break;
4791            }
4792        }
4793    }
4794
4795    function removePromotionBanner() {
4796        $("#" + options.queryString.promoCode).remove();
4797        $("#" + options.queryString.askForPromoInstantDeal).remove();
4798        //remove from cookies too
4799        var propertyId = $("#prop-id").val();
4800        if (typeof (propertyId) === "string") {
4801            removePropertyIdFromCookie(propertyId, options.cookie.flags.instantDeal);
4802            removePropertyIdFromCookie(propertyId, options.cookie.flags.promotionCode);
4803        }
4804
4805        searchTrigger();
4806    }
4807
4808    function removePropertyIdFromCookie(propertyId, cookieName) {
4809        var listOfProperties = window.eviivo.webcore.cookieManager.getCookie(cookieName);
4810        if (listOfProperties != null && listOfProperties.length > 0) {
4811            var json = eviivo.utils.queryStringHelper.getAllKeys(listOfProperties, {});
4812            eviivo.utils.queryStringHelper.deleteKey(json, "p_" + propertyId.toLowerCase());
4813            //Sets the cookie to expiry after the session ended (no value b
4813y default does that) this kept the functionallity that was in place before replacing the cookie library
4814            window.eviivo.webcore.cookieManager.setCookie(cookieName, eviivo.utils.queryStringHelper.toString(json, false));
4815        }
4816    }
4817
4818    //The overlay has that faded background over the entire page, so if we show it and not removing it we will not be able to interact with the page.
4819    //Same case when we already have it opened.
4820    //Just as a protection in case it's already opened, we remove it first and then we re-add it from the template.
4821    function displayPopup(popupPromoCodeTitleText, popupPromoCodeForm, popupCloseCallBack) {
4822        $modOverlay = $mainContainer.find("div.dialogue-outer");
4823        eviivo.popup.display({
4824            html: popupPromoCodeForm,
4825            headerTitle: popupPromoCodeTitleText,
4826            bodyClass: "dialogue-medium dialogue-height--auto",
4827            popupCloseCallBack: popupCloseCallBack
4828        });
4829    }
4830
4831    function displayInstantDealPopup(popupPromoCodeTitleText, popupPromoCodeForm) {
4832        displayPopup(popupPromoCodeTitleText, popupPromoCodeForm);
4833        if (grecaptcha) {
4834            grecaptcha.render('grecaptchaId',
4835            {
4836                'sitekey': options.captcha.siteKey,
4837                'callback': recaptchaReceived
4838            });
4839        } else {
4840            console.error("Error: could not load Google Recaptcha API.");
4841        }
4842    }
4843
4844    function recaptchaReceived(response) {
4845        if (response) {
4846            //force removal of promo code (in case the entered one gave error and now we want to enter instant deal only)
4847            $("#" + options.queryString.promoCode).remove();
4848            $("#" + options.queryString.askForPromoInstantDeal).val(true);
4849            window.eviivo.webcore.cookieManager.setCookie('captcha', response, 30);
4850            searchTrigger();
4851        } else {
4852            console.error("Error: recaptcha response not received.");
4853        }
4854    }
4855
4856    function showInstantDealInputPopup() {
4857        var unlockInstantDealTitle = options.resources.unlockSpecials;
4858        var unlockInstantDealForm = $("<div id='grecaptchaId' class='captcha'></div>");
4859
4860        displayInstantDealPopup(unlockInstantDealTitle, unlockInstantDealForm);
4861    }
4862
4863    function removePromoCodeFromQueryString() {
4864        window.location.href = window.location.href.removeKeyFromQueryString(options.queryString.promoCode);
4865    }
4866
4867    function showPromoCodeInputPopup(failureMessage) {
4868        var popupPromoCodeTitleText = options.resources.enterPromoCode;
4869        var popupPromoCodeForm = "<input type='text' class='cp-input' id='apply-promo-code-input' placeholder='' value='' /><button class='cp-button' id='apply-promo-code' type='button'>" + options.resources.apply + "</button>";
4870
4871        if (typeof (failureMessage) === "string" && failureMessage !== "") {
4872            popupPromoCodeForm += '<div class="field-validation-error">' + failureMessage + '</div>';
4873
4874            displayPopup(popupPromoCodeTitleText, popupPromoCodeForm, removePromoCodeFromQueryString);
4875
4876        } else {
4877            displayPopup(popupPromoCodeTitleText, popupPromoCodeForm);
4878        }
4879
4880
4881
4882        //focus on the input straight
4883        $("#apply-promo-code-input").focus();
4884
4885        $("#apply-promo-code-input").bind("paste", function (e) {
4886            e.preventDefault();
4887            $("#apply-promo-code-input").val(e.originalEvent.clipboardData.getData('text').trim())
4888        });
4889    }
4890
4891    function onCodeValid(code) {
4892        $("#" + options.queryString.promoCode).val(code);
4893        // add spinner to indicate that someting is happening
4894        searchTrigger();
4895    }
4896
4897    ///<summary>Expose the internal "private" methods for external use (make them public)</summary>
4898    return {
4899        init: init,
4900        onCodeValid: onCodeValid
4901    };
4902}(jQuery);
4903window.eviivo = window.eviivo ? window.eviivo : {};
4904window.eviivo.promos = window.eviivo.promos ? window.eviivo.promos : {};
4905window.eviivo.promos.codeValidator = window.eviivo.promos.codeValidator ? window.eviivo.promos.codeValidator : {};
4906
4907window.eviivo.promos.codeValidator = function ($) {
4908    var selectors = {
4909        applyPromoCodeButton: "#apply-promo-code",
4910        applyPromoCodeInput: "#apply-promo-code-input",
4911        errorArea: "div.field-validation-error",
4912        popUpApplyPromoCodeButton: "div.mod-dialogueBox #apply-promo-code",
4913        popUpApplyPromoCodeInput: "div.mod-dialogueBox #apply-promo-code-input"
4914    };
4915
4916    var options;
4917
4918    /*
4919     * IMPORTANT:
4920     * Do NOT add the 'g' flag in this regex.
4921     *
4922     * The 'g' flag makes the regex "remember" where it last stopped searching.
4923     * If you test the same string multiple times, the regex may start in the middle, which can lead to it mistakenly saying a valid code is wrong.
4924     * Removing the 'g' flag ensures that every test starts from the beginning of the string.
4925     */
4926    var promoCodeValidationRule = new RegExp("^[a-zA-Z0-9\-\_@\#]+$", "i");
4927
4928    /**
4929     * Validates the promo code entered in the promo code input field.
4930     *
4931     * This function retrieves the promo code value from the input field defined by
4932     * `selectors.applyPromoCodeInput` and removes any existing error styling or messages.
4933     * It then checks if the promo code is empty or fails to meet the pattern defined by
4934     * `promoCodeValidationRule`. If the promo code is invalid, it adds an "invalid" CSS class
4935     * to the input field and inserts an error message after the promo code button. If the promo
4936     * code passes validation, this returns true.
4937     *
4938     * @returns {boolean} Returns true if the promo code is valid; otherwise, false.
4939     */
4940    function validatePromoCode() {
4941        const $applyInput = $(selectors.applyPromoCodeInput);
4942        const $errorArea = $(selectors.errorArea);
4943        const code = $applyInput.val();
4944
4945        $applyInput.removeClass("invalid");
4946        $errorArea.remove();
4947
4948        let isInvalid = false;
4949        let errorMessage = "";
4950
4951        if (code === "") {
4952            errorMessage = options.resources.promoValidationEmptyCode;
4953            isInvalid = true;
4954        } else if (promoCodeValidationRule.test(code) === false) {
4955            errorMessage = options.resources.promoValidationMessage;
4956            isInvalid = true;
4957        }
4958
4959        if (isInvalid === true) {
4960            $applyInput.addClass('invalid');
4961            $("<div class='field-validation-error'>" + errorMessage + "</div>").insertAfter($(selectors.applyPromoCodeButton));
4962        } else {
4963            return true;
4964        }
4965
4966        return false;
4967    }
4968
4969    /**
4970     * Handles the application of a promo code.
4971     *
4972     * This function is invoked when a guest triggers an event, such as pressing the Enter key (key code 13)
4973     * or performing a left mouse click (which code 1). It validates the promo code using the 
4974     * 'validatePromoCode' function. If the promo code is valid and a callback function is provided
4975     * in the 'options' object, the function retrieves the current promo code via 'getCode'
4976     * and passes it to the callback. The function always returns false to prevent any default behavior.
4977     *
4978     * @param {Event} e - The event object triggered by the guest's interaction.
4979     * @returns {boolean} Returns false to stop the default event propagation or true if the promo code is valid.
4980     */
4981    function applyPromoCodeHandler(e) {
4982        // If the guest clicked on enter (13) or left mouse click (1).
4983        if (e != null && (e.keyCode === 13 || e.which === 1)) {
4984            const isPromoCodeValid = validatePromoCode();
4985
4986            if (isPromoCodeValid === true &&
4987                typeof options.callback === "function") {
4988                options.callback(getCode());
4989
4990                return true;
4991            }
4992        }
4993
4994        return false;
4995    }
4996
4997    /**
4998     * Retrieves the current promo code entered by the guest.
4999     *
5000     * This function selects the promo code input element defined by
5001     * 'selectors.applyPromoCodeInput' and returns its current value.
5002     *
5003     * @returns {string} The value of the promo code input field.
5004     */
5005    function getCode() {
5006        const $applyInput = $(selectors.applyPromoCodeInput);
5007        return $applyInput.val();
5008    }
5009
5010    function init(settings) {
5011        //extend internal settings with external ones
5012        options = $.extend({}, settings);
5013
5014        // These two events are specifically for pop up promos in the search page.
5015        $(document).on("click", selectors.popUpApplyPromoCodeButton, applyPromoCodeHandler);
5016        $(document).on("keyup", selectors.popUpApplyPromoCodeInput, applyPromoCodeHandler);
5017    }
5018
5019    ///<summary>Expose the internal "private" methods for external use (make them public)</summary>
5020    return {
5021        init: init
5022    };
5023}(jQuery);
5024window.eviivo = window.eviivo ? window.eviivo : {};
5025window.eviivo.announcement = window.eviivo.announcement ? window.eviivo.announcement : {};
5026
5027window.eviivo.announcement = function ($) {
5028
5029    var options;
5030    var defaultOptions = {
5031        openInNewTab: false
5032    }
5033    var announcementLink;
5034    var searchUrl = "";
5035    var querystringHelper;
5036
5037    function init(settings) {
5038        options = $.extend(defaultOptions, settings);
5039        searchUrl = options.searchUrl;
5040        announcementLink = $('#promo-code-link');
5041
5042        announcementLink.on('click', function () {
5043            window.eviivo.webcore.cookieManager.setCookie("announcement_popup", "announcement_" + options.propertyShortName + "=1", 1);
5044            searchUrl = setUrl();
5045
5046            if (options.linkAddress != "") {
5047                searchUrl = options.linkAddress;
5048            }
5049
5050            if (options.openInNewTab) {
5051                window.open(searchUrl);
5052                return;
5053            }
5054
5055            if (options.promoCode == "") {
5056                window.open(searchUrl, "_self");
5057                return;
5058            }
5059
5060            window.location.href = searchUrl;
5061        });
5062    }
5063
5064    function setUrl() {
5065        querystringHelper = eviivo.utils.queryStringHelper;
5066        if (typeof querystringHelper === "undefined") {
5067            console.warn("eviivo.utils.queryStringHelper was not loaded therefore this page might not work properly");
5068        }
5069
5070        var src = window.location.href;
5071        var originalQueryString = querystringHelper.getAllKeys(src);
5072        var queryStrings = jQuery.extend({}, originalQueryString);
5073
5074        querystringHelper.addKey(queryStrings, "pce", options.promoCode);
5075
5076        return searchUrl += querystringHelper.toString(queryStrings);
5077    }
5078
5079    return {
5080        init: init,
5081    }
5082}(jQuery);
5083window.eviivo = window.eviivo ? window.eviivo : {};
5084window.eviivo.customPage = window.eviivo.customPage ? window.eviivo.customPage : {};
5085
5086window.eviivo.customPage = function ($) {
5087    var selectors = {
5088        collapsibleArticles: ".mod-article.collapsible h4"
5089    };
5090
5091    function init() {
5092        toggleCollapsedClassEvent();
5093        bookingTermsInit();
5094    }
5095
5096
5097    /**
5098     * Toggles the 'collapsed' class on click event for collapsible articles.
5099     */
5100    function toggleCollapsedClassEvent() {
5101        const collapsedClassName = "collapsed";
5102
5103        jQuery(selectors.collapsibleArticles).on('click', function () {
5104            const elementIndex = jQuery(selectors.collapsibleArticles).index(this);
5105
5106            // Remove 'collapsed' class from all collapsible articles except the clicked one
5107            jQuery(selectors.collapsibleArticles)
5108                .filter(index => index != elementIndex)
5109                .removeClass(collapsedClassName);
5110
5111            jQuery(this).toggleClass(collapsedClassName);
5112        });
5113    }
5114
5115    function bookingTermsInit() {
5116
5117        jQuery('#bookingTermsAndConditionsLink').on("click", function () {
5118            jQuery.ajax({
5119                    type: "GET",
5120                url: jQuery(this).data("get-popup-data-url"),
5121                    dataType: "html",
5122                    success: function (response) {
5123                        eviivo.popup.display({
5124                            html: response,
5125                            bodyClass: "dialogue-large is--scrollable space-pre-line"
5126                        });
5127                    },
5128                    error: function (response) {
5129                        eviivo.popup.display({
5130                            html: response,
5131                            bodyClass: "dialogue-medium is--scrollable"
5132                        });
5133                    }
5134                });
5135                return false;
5136            });
5137    }
5138
5139    return {
5140        init: init,
5141    }
5142}(jQuery);
5143(function ($, sr) {
5144
5145    // debouncing function from John Hann
5146    // http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
5147    var debounce = function (func, threshold, execAsap) {
5148        var timeout;
5149
5150        return function debounced() {
5151            var obj = this, args = arguments;
5152            function delayed() {
5153                if (!execAsap)
5154                    func.apply(obj, args);
5155                timeout = null;
5156            };
5157
5158            if (timeout)
5159                clearTimeout(timeout);
5160            else if (execAsap)
5161                func.apply(obj, args);
5162
5163            timeout = setTimeout(delayed, threshold || 100);
5164        };
5165    }
5166    // smartresize 
5167    jQuery.fn[sr] = function (fn) { return fn ? this.bind('resize', debounce(fn)) : this.trigger(sr); };
5168
5169})(jQuery, 'smartresize');
5170
5171window.eviivo = window.eviivo ? window.eviivo : {};
5172window.eviivo.combinedInventory = window.eviivo.combinedInventory ? window.eviivo.combinedInventory : {};
5173
5174window.eviivo.combinedInventory = function ($) {
5175    let options,
5176        defaultOptions = {
5177            resources: {
5178                soldout: "Sold Out",
5179                select: "Select"
5180            }
5181        },
5182        attributes = {
5183            checked: "checked",
5184            id: "id",
5185            name: "name",
5186            disabled: "disabled"
5187        },
5188        classes = {
5189            checked: "ui-checkboxradio-checked",
5190            active: "ui-state-active"
5191        },
5192        selectors = {
5193            roomInventory: {
5194                radio: {
5195                    items: 'input[type="radio"].s-inventory-item',
5196                    groupByName: (name) => `input[type="radio"].s-inventory-item[name='${name}']`,
5197                },
5198                labelById: (id) => `label[for='${id}']`,
5199            },
5200            checked: ":checked",
5201        },
5202        elements = {
5203            roomInventory: {
5204                radio: {
5205                    items: $(),
5206                }
5207            },
5208        },
5209        events = {
5210            change: "change"
5211        },
5212        combinedInventoryManager = {
5213            totalRealInventoryAmount: 0,
5214            inventorySelections: [],
5215            unitTypeMap: {}
5216        };
5217
5218    /**
5219     * Main initialise function.
5220     * @param {any} settings - combined inventory settings.
5221     */
5222    function init(settings) {
5223        options = $.extend(defaultOptions, settings);
5224        initElements();
5225        initCombinedInventoryManager();
5226        initListeners();
5227    }
5228
5229    /**
5230     * Initialises elements needed for combined inventory.
5231     */
5232    function initElements() {
5233        elements.roomInventory.radio.items = $(selectors.roomInventory.radio.items);
5234    }
5235
5236    /**
5237     * Initialises the combined inventory manager which holds information needed to manage combined inventory such as: 
5238     * total real inventory amount, inventory selection and unit type information (e.g. room type amount consumed).
5239     */
5240    function initCombinedInventoryManager() {
5241        // Build the combined inventory manager's unit type map, this holds details such as a unit type's consumed amount and real available amount. 
5242        elements.roomInventory.radio.items.each(function () {
5243            const data = $(this).data();
5244            if (data?.rtid && !combinedInventoryManager.unitTypeMap[data.rtid]) {
5245                combinedInventoryManager.unitTypeMap[data.rtid] = {
5246                    amountAvailable: data.aa == null ? 0 : data.aa,
5247                    amountConsumed: data.ac == null ? 0 : data.ac,
5248                    amountRealAvailable: data.ara == null ? 0 : data.ara
5249                };
5250            }
5251        });
5252
5253        // JIRA EVDEV-35528 - If the search returned no real availability, use the max inventory count for the property which is the count of all real inventory units for the property.
5254        combinedInventoryManager.totalRealInventoryAmount = options.maxInventory;
5255
5256        // Add each pre-selected inventory item to the inventory manager's selections.
5257        elements.roomInventory.radio.items.each(function () {
5258            const element = $(this);
5259            if (element.is(selectors.checked)) {
5260                combinedInventoryManager.inventorySelections.push({ id: element.prop(attributes.id), amountConsumed: element.data().ac });
5261            }
5262        });
5263
5264        // We could already be over the total real inventory amount due to pre-selections, so enforce availability to deal with this. 
5265        enforceAvailabilityOnInventoryItems();
5266    }
5267
5268    /**
5269     * Initialises event listeners needed for managing combined inventory.
5270     */
5271    function initListeners() {
5272        /**
5273         * Change event handler that will trigger on each room selection.
5274         * @param {any} event - The change event.
5275         */
5276        const handleRoomSelection = (event) => {
5277            const element = $(event.target);
5278
5279            // Set text to 'selected' for the selection's associated label.
5280            setLabelTextForInputElement(element, options.resources.select);
5281
5282            // Grab the selections id.
5283            const id = element.prop(attributes.id);
5284
5285            // Add the selection to the manager so we can keep track of what is currently selected when working out the availability to display.
5286            combinedInventoryManager.inventorySelections.push({ id: id, amountConsumed: element.data().ac });
5287
5288            // Enforce the new availability onto the inventory items, passing in the newly selected element's id
5289            // so that we don't enforce availability onto the inventory item that we just selected.
5290            enforceAvailabilityOnInventoryItems(id);
5291        }
5292
5293        // Attach the handler to the change event for each radio button.
5294        elements.roomInventory.radio.items.each(function () {
5295            $(this).off(events.change).on(events.change, handleRoomSelection);
5296        });
5297    }
5298
5299    /**
5300     * Function to set the text content of a specified DOM element to the provided value.
5301     * @param {jQuery} element - The jQuery element for the input.
5302     * @param {string} value - The text content.
5303     */
5304    const setLabelTextForInputElement = (element, value) => {
5305        element.siblings(selectors.roomInventory.labelById(element.attr(attributes.id))).text(value);
5306    }
5307
5308    /**
5309     * Returns the inventory selections sorted by the amount consumed in descending order.
5310     * @returns {Array} Sorted array of inventory selections.
5311     */
5312    const sortSortedSelectionsByConsumed = () => {
5313        return combinedInventoryManager.inventorySelections.sort((a, b) => {
5314            if (a.amountConsumed < b.amountConsumed) {
5315                return 1;
5316            }
5317            if (a.amountConsumed > b.amountConsumed) {
5318                return -1;
5319            }
5320            return 0;
5321        });
5322    }
5323
5324    /**
5325    * Enforces the current availability onto all inventory items, unless a 'selectedId' is provided, in which case the corresponding inventory item will be skipped.
5326    * @param {string} selectedId - (Optional) An id we wish to exclude from having current availability rules applied to it, used when a new selection has just been made
5327    * to avoid applying current availability to the item that was just selected.
5328    */
5329    const enforceAvailabilityOnInventoryItems = (selectedId = null) => {
5330        // Firstly, if we have a 'selectedId' we need to remove any existing selection for this selections radio group.
5331        if (selectedId != null) {
5332            const newlySelectedItem = $(`#${selectedId}`);
5333            const currentlySelectedInventory = $(selectors.roomInventory.radio.groupByName(newlySelectedItem.attr(attributes.name)));
5334
5335            currentlySelectedInventory.each(function () {
5336                // Remove/Deselect this inventory selection:
5337                const element = $(this);
5338                const id = element.prop(attributes.id);
5339
5340                // If the selectedId matches the current inventory item's id skip this inventory item as we don't want to remove the item we just selected.
5341                if (id === selectedId) {
5342                    return;
5343                }
5344
5345                // 1. Remove the current selection from the inventory manager.
5346                combinedInventoryManager.inventorySelections = combinedInventoryManager.inventorySelections.filter(_ => _.id !== id);
5347
5348                // 2. Set text to 'soldout' for the selection's associated label.
5349                setLabelTextForInputElement(element, options.resources.soldout);
5350
5351                // 3. Uncheck the radio button if it's checked and remove any checked/active classes that may be present.
5352                if (element.is(selectors.checked)) {
5353                    element.prop(attributes.checked, false);
5354                    element.siblings(selectors.roomInventory
5355                        .labelById(id))
5356                        .removeClass([classes.checked, classes.active]);
5357                }
5358
5359                // 4. Disable the input
5360                element.attr(attributes.disabled, attributes.disabled);
5361            });       
5362        }
5363
5364        // Next, check whether the amount consumed by the current selections is valid by summing the amount consumed of all current selections and
5365        // comparing it to the combined inventory manager's total real inventory amount.
5366        let selectionsTotalAmountConsumed = combinedInventoryManager.inventorySelections.reduce((acc, item) => acc + item.amountConsumed, 0);
5367
5368        // If the current selections amount consumed is greater than the total real inventory amount we need to resolve this by deselecting some
5369        // selections until the selection amount consumed is valid again.
5370        if (selectionsTotalAmountConsumed > combinedInventoryManager.totalRealInventoryAmount) {
5371            // Sort the current inventory selections by their amount consumed, this way we will be removing the fewest selections possible.
5372            const sortedSelections = sortSortedSelectionsByConsumed();
5373
5374            for (i = 0; i < sortedSelections.length; i++) {
5375                // If the selections amount consumed is now less than or equal to the total real inventory amount break out the loop as
5376                // we do not need to deselect anymore inventory.
5377                if (selectionsTotalAmountConsumed <= combinedInventoryManager.totalRealInventoryAmount) {
5378                    break;
5379                }
5380
5381                const selection = sortedSelections[i];
5382
5383                // If a selectedId was provided and it matches the current selection's id skip this inventory item.
5384                if (selectedId != null && selection.id === selectedId) {
5385                    continue;
5386                }
5387
5388                const element = $(`#${selection.id}`);
5389
5390                // Remove/Deselect this inventory selection:
5391
5392                // 1. Remove the current selection from the inventory manager.
5393                combinedInventoryManager.inventorySelections = combinedInventoryManager.inventorySelections.filter(_ => _.id !== selection.id);
5394
5395                // 2. Reduce the selections amount consumed by this selection's amount consumed.
5396                selectionsTotalAmountConsumed -= selection.amountConsumed;
5397
5398                // 3. Set text to 'soldout' for the selection's associated label.
5399                setLabelTextForInputElement(element, options.resources.soldout);
5400
5401                // 4. Uncheck the radio button if it's checked and remove any checked/active classes that may be present.
5402                if (element.is(selectors.checked)) {
5403                    element.prop(attributes.checked, false);
5404                    element.siblings(selectors.roomInventory
5405                        .labelById(element.attr(attributes.id)))
5406                        .removeClass([classes.checked, classes.active]);
5407                }
5408
5409                // 5. Disable the input
5410                element.attr(attributes.disabled, attributes.disabled);
5411            }
5412        }
5413
5414        // Lastly, enforce availability onto any unselected inventory by checking whether any inventory item that is not selected has an amount consumed
5415        // greater than the remaining available inventory. If it does we need to mark it as sold out, otherwise mark it as selected. 
5416
5417        // Calculate the remaining available inventory.
5418        const remainingAvailableInventory = combinedInventoryManager.totalRealInventoryAmount - selectionsTotalAmountConsumed;
5419
5420        elements.roomInventory.radio.items.each(function () {
5421            const element = $(this);
5422            // We only care about unchecked elements here as the checked elements have been dealt with above.
5423            if (!element.is(selectors.checked)) {
5424                const amountConsumed = element.data().ac;
5425
5426                const currentlySelectedInventoryForGroup = $(selectors.roomInventory.radio.groupByName(element.attr(attributes.name)))
5427                    .not(element)
5428                    .filter(selectors.checked)
5429                    .toArray()
5430                    .reduce((acc, item) => acc + ($(item).data().ac || 0), 0);
5431
5432                const isSoldOut = amountConsumed > (remainingAvailableInventory + currentlySelectedInventoryForGroup);
5433
5434                // Any other units displayed that have a consumed inventory of HIGHER than the remaining available inventory should be marked as Sold Out.
5435                setLabelTextForInputElement(element,
5436                    isSoldOut
5437                        ? options.resources.soldout
5438                        : options.resources.select);
5439
5440                combinedInventoryManager.inventorySelections = combinedInventoryManager.inventorySelections.filter(_ => _.id !== element.attr(attributes.id));
5441
5442                // Disable the input if soldout
5443                isSoldOut
5444                    ? element.attr(attributes.disabled, attributes.disabled)
5445                    : element.removeAttr(attributes.disabled);
5446            }
5447        });
5448    }
5449
5450    return {
5451        init: init
5452    };
5453}(jQuery);
5454window.eviivo = window.eviivo ? window.eviivo : {};
5455window.eviivo.language = window.eviivo.language ? window.eviivo.language : {};
5456
5457window.eviivo.language = function ($) {
5458    var defaultOptions = {
5459    };
5460
5461    var options;
5462    var selectors = {
5463        languageOptions: ".mod-language .language-inner span"
5464    }
5465
5466    function init(settings) {
5467        options = $.extend(defaultOptions, settings);
5468
5469        initEvents();
5470    }
5471
5472    /**
5473     * Attaches click event handlers to all language option elements.
5474     * When a language option is clicked, it invokes onLanguageOptionClick
5475     * with the clicked element to initiate the language change process.
5476     */
5477    function initEvents() {
5478        // Listen for clicks on any element matching the language options selector
5479        $(selectors.languageOptions).on('click', function () {
5480            onLanguageOptionClick($(this));
5481        });
5482    }
5483
5484    /**
5485     * Handles click events on a language option element by sending a request to change
5486     * the application's culture and reloading the page upon success.
5487     *
5488     * @param {jQuery} element - The jQuery-wrapped element that was clicked. It must have
5489     *                            a data attribute "language-code" containing the target culture code.
5490     */
5491    function onLanguageOptionClick(element) {
5492        // Retrieve the desired language code from the clicked element
5493        const languageCode = element.data("language-code");
5494
5495        // Send a POST request to change the culture on the server side
5496        $.post(options.languageChangeUrl, {
5497            cultureCode: languageCode
5498        }, function (data) {
5499            // If the server confirms success, reload the page to apply the new culture
5500            if (data && data.isSuccess === true) {
5501                location.reload();
5502            }
5503        })
5504    }
5505
5506    return {
5507        init: init
5508    };
5509}(jQuery);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.