PageSourceSearch

https://hjertestarter.dk/assets/tg-scripts?v=xcQR2vKDC9YqOMMZAG212NvT10u8aXPqoA06EEtmE601

js hjertestarter.dk collected 2026-09-24 10:24:40 UTC 964,557 bytes, 23,636 lines download raw bytes

1/* Minification failed. Returning unminified contents.
2(10095,122-123): run-time error JS1195: Expected expression: .
3(10095,152-153): run-time error JS1004: Expected ';': )
4(10095,154-155): run-time error JS1004: Expected ';': {
5(10101,25-29): run-time error JS1034: Unmatched 'else'; no 'if' defined: else
6(10106,25-26): run-time error JS1002: Syntax error: }
7(10118,108-109): run-time error JS1195: Expected expression: >
8(10118,134-135): run-time error JS1004: Expected ';': )
9(10132,17-18): run-time error JS1002: Syntax error: }
10(10134,40-41): run-time error JS1004: Expected ';': {
11(10142,14-15): run-time error JS1195: Expected expression: )
12(10922,5-6): run-time error JS1002: Syntax error: }
13(10926,15-16): run-time error JS1004: Expected ';': {
14(11032,2-3): run-time error JS1195: Expected expression: )
15(11034,17-18): run-time error JS1004: Expected ';': {
16(11212,2-3): run-time error JS1195: Expected expression: )
17(11213,4-12): run-time error JS1197: Too many errors. The file might not be a JavaScript file: function
18(10899,9,10920,10): run-time error JS1018: 'return' statement outside of function: return {
19            validateInitiate: validateInitiate,
20            errorScenarios: errorScenarios,
21            serverError: serverError,
22            serverSuccess: serverSuccess,
23            reloadPage: reloadPage,
24            redirectPage: redirectPage,
25            phoneValidate: phoneValidate,
26            altPhoneValidate: altPhoneValidate,
27            setErrorMessage: setErrorMessage,
28            cleanErrors: cleanErrors,
29            liveValidate: liveValidate,
30            repeatValidate: repeatValidate,
31            moreOpportunities: moreOpportunities,
32            getOpportunities: getOpportunities,
33            showExtraSection: showExtraSection,
34            positionTooltip: positionTooltip,
35            initiateExtraHours: initiateExtraHours,
36            initiateInputTimeFormating: initiateInputTimeFormating,
37            toggleSectionOnSelect: toggleSectionOnSelect,
38            removeInterval: removeInterval
39        }
40 */
41/*! jQuery UI - v1.9.2 - 2017-03-01
42* http://jqueryui.com
43* Includes: jquery.ui.core.js, jquery.ui.datepicker.js
44* Copyright jQuery Foundation and other contributors; Licensed MIT */
45
46(function( $, undefined ) {
47
48var uuid = 0,
49	runiqueId = /^ui-id-\d+$/;
50
51// prevent duplicate loading
52// this is only a problem because we proxy existing functions
53// and we don't want to double proxy them
54$.ui = $.ui || {};
55if ( $.ui.version ) {
56	return;
57}
58
59$.extend( $.ui, {
60	version: "1.9.2",
61
62	keyCode: {
63		BACKSPACE: 8,
64		COMMA: 188,
65		DELETE: 46,
66		DOWN: 40,
67		END: 35,
68		ENTER: 13,
69		ESCAPE: 27,
70		HOME: 36,
71		LEFT: 37,
72		NUMPAD_ADD: 107,
73		NUMPAD_DECIMAL: 110,
74		NUMPAD_DIVIDE: 111,
75		NUMPAD_ENTER: 108,
76		NUMPAD_MULTIPLY: 106,
77		NUMPAD_SUBTRACT: 109,
78		PAGE_DOWN: 34,
79		PAGE_UP: 33,
80		PERIOD: 190,
81		RIGHT: 39,
82		SPACE: 32,
83		TAB: 9,
84		UP: 38
85	}
86});
87
88// plugins
89$.fn.extend({
90	_focus: $.fn.focus,
91	focus: function( delay, fn ) {
92		return typeof delay === "number" ?
93			this.each(function() {
94				var elem = this;
95				setTimeout(function() {
96					$( elem ).focus();
97					if ( fn ) {
98						fn.call( elem );
99					}
100				}, delay );
101			}) :
102			this._focus.apply( this, arguments );
103	},
104
105	scrollParent: function() {
106		var scrollParent;
107		if (($.ui.ie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) {
108			scrollParent = this.parents().filter(function() {
109				return (/(relative|absolute|fixed)/).test($.css(this,'position')) && (/(auto|scroll)/).test($.css(this,'overflow')+$.css(this,'overflow-y')+$.css(this,'overflow-x'));
110			}).eq(0);
111		} else {
112			scrollParent = this.parents().filter(function() {
113				return (/(auto|scroll)/).test($.css(this,'overflow')+$.css(this,'overflow-y')+$.css(this,'overflow-x'));
114			}).eq(0);
115		}
116
117		return (/fixed/).test(this.css('position')) || !scrollParent.length ? $(document) : scrollParent;
118	},
119
120	zIndex: function( zIndex ) {
121		if ( zIndex !== undefined ) {
122			return this.css( "zIndex", zIndex );
123		}
124
125		if ( this.length ) {
126			var elem = $( this[ 0 ] ), position, value;
127			while ( elem.length && elem[ 0 ] !== document ) {
128				// Ignore z-index if position is set to a value where z-index is ignored by the browser
129				// This makes behavior of this function consistent across browsers
130				// WebKit always returns auto if the element is positioned
131				position = elem.css( "position" );
132				if ( position === "absolute" || position === "relative" || position === "fixed" ) {
133					// IE returns 0 when zIndex is not specified
134					// other browsers return a string
135					// we ignore the case of nested elements with an explicit value of 0
136					// <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
137					value = parseInt( elem.css( "zIndex" ), 10 );
138					if ( !isNaN( value ) && value !== 0 ) {
139						return value;
140					}
141				}
142				elem = elem.parent();
143			}
144		}
145
146		return 0;
147	},
148
149	uniqueId: function() {
150		return this.each(function() {
151			if ( !this.id ) {
152				this.id = "ui-id-" + (++uuid);
153			}
154		});
155	},
156
157	removeUniqueId: function() {
158		return this.each(function() {
159			if ( runiqueId.test( this.id ) ) {
160				$( this ).removeAttr( "id" );
161			}
162		});
163	}
164});
165
166// selectors
167function focusable( element, isTabIndexNotNaN ) {
168	var map, mapName, img,
169		nodeName = element.nodeName.toLowerCase();
170	if ( "area" === nodeName ) {
171		map = element.parentNode;
172		mapName = map.name;
173		if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
174			return false;
175		}
176		img = $( "img[usemap=#" + mapName + "]" )[0];
177		return !!img && visible( img );
178	}
179	return ( /input|select|textarea|button|object/.test( nodeName ) ?
180		!element.disabled :
181		"a" === nodeName ?
182			element.href || isTabIndexNotNaN :
183			isTabIndexNotNaN) &&
184		// the element and all of its ancestors must be visible
185		visible( element );
186}
187
188function visible( element ) {
189	return $.expr.filters.visible( element ) &&
190		!$( element ).parents().andSelf().filter(function() {
191			return $.css( this, "visibility" ) === "hidden";
192		}).length;
193}
194
195$.extend( $.expr[ ":" ], {
196	data: $.expr.createPseudo ?
197		$.expr.createPseudo(function( dataName ) {
198			return function( elem ) {
199				return !!$.data( elem, dataName );
200			};
201		}) :
202		// support: jQuery <1.8
203		function( elem, i, match ) {
204			return !!$.data( elem, match[ 3 ] );
205		},
206
207	focusable: function( element ) {
208		return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
209	},
210
211	tabbable: function( element ) {
212		var tabIndex = $.attr( element, "tabindex" ),
213			isTabIndexNaN = isNaN( tabIndex );
214		return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
215	}
216});
217
218// support
219$(function() {
220	var body = document.body,
221		div = body.appendChild( div = document.createElement( "div" ) );
222
223	// access offsetHeight before setting the style to prevent a layout bug
224	// in IE 9 which causes the element to continue to take up space even
225	// after it is removed from the DOM (#8026)
226	div.offsetHeight;
227
228	$.extend( div.style, {
229		minHeight: "100px",
230		height: "auto",
231		padding: 0,
232		borderWidth: 0
233	});
234
235	$.support.minHeight = div.offsetHeight === 100;
236	$.support.selectstart = "onselectstart" in div;
237
238	// set display to none to avoid a layout bug in IE
239	// http://dev.jquery.com/ticket/4014
240	body.removeChild( div ).style.display = "none";
241});
242
243// support: jQuery <1.8
244if ( !$( "<a>" ).outerWidth( 1 ).jquery ) {
245	$.each( [ "Width", "Height" ], function( i, name ) {
246		var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
247			type = name.toLowerCase(),
248			orig = {
249				innerWidth: $.fn.innerWidth,
250				innerHeight: $.fn.innerHeight,
251				outerWidth: $.fn.outerWidth,
252				outerHeight: $.fn.outerHeight
253			};
254
255		function reduce( elem, size, border, margin ) {
256			$.each( side, function() {
257				size -= parseFloat( $.css( elem, "padding" + this ) ) || 0;
258				if ( border ) {
259					size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0;
260				}
261				if ( margin ) {
262					size -= parseFloat( $.css( elem, "margin" + this ) ) || 0;
263				}
264			});
265			return size;
266		}
267
268		$.fn[ "inner" + name ] = function( size ) {
269			if ( size === undefined ) {
270				return orig[ "inner" + name ].call( this );
271			}
272
273			return this.each(function() {
274				$( this ).css( type, reduce( this, size ) + "px" );
275			});
276		};
277
278		$.fn[ "outer" + name] = function( size, margin ) {
279			if ( typeof size !== "number" ) {
280				return orig[ "outer" + name ].call( this, size );
281			}
282
283			return this.each(function() {
284				$( this).css( type, reduce( this, size, true, margin ) + "px" );
285			});
286		};
287	});
288}
289
290// support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413)
291if ( $( "<a>" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) {
292	$.fn.removeData = (function( removeData ) {
293		return function( key ) {
294			if ( arguments.length ) {
295				return removeData.call( this, $.camelCase( key ) );
296			} else {
297				return removeData.call( this );
298			}
299		};
300	})( $.fn.removeData );
301}
302
303
304
305
306
307// deprecated
308
309(function() {
310	var uaMatch = /msie ([\w.]+)/.exec( navigator.userAgent.toLowerCase() ) || [];
311	$.ui.ie = uaMatch.length ? true : false;
312	$.ui.ie6 = parseFloat( uaMatch[ 1 ], 10 ) === 6;
313})();
314
315$.fn.extend({
316	disableSelection: function() {
317		return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) +
318			".ui-disableSelection", function( event ) {
319				event.preventDefault();
320			});
321	},
322
323	enableSelection: function() {
324		return this.unbind( ".ui-disableSelection" );
325	}
326});
327
328$.extend( $.ui, {
329	// $.ui.plugin is deprecated.  Use the proxy pattern instead.
330	plugin: {
331		add: function( module, option, set ) {
332			var i,
333				proto = $.ui[ module ].prototype;
334			for ( i in set ) {
335				proto.plugins[ i ] = proto.plugins[ i ] || [];
336				proto.plugins[ i ].push( [ option, set[ i ] ] );
337			}
338		},
339		call: function( instance, name, args ) {
340			var i,
341				set = instance.plugins[ name ];
342			if ( !set || !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) {
343				return;
344			}
345
346			for ( i = 0; i < set.length; i++ ) {
347				if ( instance.options[ set[ i ][ 0 ] ] ) {
348					set[ i ][ 1 ].apply( instance.element, args );
349				}
350			}
351		}
352	},
353
354	contains: $.contains,
355
356	// only used by resizable
357	hasScroll: function( el, a ) {
358
359		//If overflow is hidden, the element might have extra content, but the user wants to hide it
360		if ( $( el ).css( "overflow" ) === "hidden") {
361			return false;
362		}
363
364		var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
365			has = false;
366
367		if ( el[ scroll ] > 0 ) {
368			return true;
369		}
370
371		// TODO: determine which cases actually cause this to happen
372		// if the element doesn't have the scroll set, see if it's possible to
373		// set the scroll
374		el[ scroll ] = 1;
375		has = ( el[ scroll ] > 0 );
376		el[ scroll ] = 0;
377		return has;
378	},
379
380	// these are odd functions, fix the API or move into individual plugins
381	isOverAxis: function( x, reference, size ) {
382		//Determines when x coordinate is over "b" element axis
383		return ( x > reference ) && ( x < ( reference + size ) );
384	},
385	isOver: function( y, x, top, left, height, width ) {
386		//Determines when x, y coordinates is over "b" element
387		return $.ui.isOverAxis( y, top, height ) && $.ui.isOverAxis( x, left, width );
388	}
389});
390
391})( jQuery );
392(function( $, undefined ) {
393
394$.extend($.ui, { datepicker: { version: "1.9.2" } });
395
396var PROP_NAME = 'datepicker';
397var dpuuid = new Date().getTime();
398var instActive;
399
400/* Date picker manager.
401   Use the singleton instance of this class, $.datepicker, to interact with the date picker.
402   Settings for (groups of) date pickers are maintained in an instance object,
403   allowing multiple different settings on the same page. */
404
405function Datepicker() {
406	this.debug = false; // Change this to true to start debugging
407	this._curInst = null; // The current instance in use
408	this._keyEvent = false; // If the last event was a key event
409	this._disabledInputs = []; // List of date picker inputs that have been disabled
410	this._datepickerShowing = false; // True if the popup picker is showing , false if not
411	this._inDialog = false; // True if showing within a "dialog", false if not
412	this._mainDivId = 'ui-datepicker-div'; // The ID of the main datepicker division
413	this._inlineClass = 'ui-datepicker-inline'; // The name of the inline marker class
414	this._appendClass = 'ui-datepicker-append'; // The name of the append marker class
415	this._triggerClass = 'ui-datepicker-trigger'; // The name of the trigger marker class
416	this._dialogClass = 'ui-datepicker-dialog'; // The name of the dialog marker class
417	this._disableClass = 'ui-datepicker-disabled'; // The name of the disabled covering marker class
418	this._unselectableClass = 'ui-datepicker-unselectable'; // The name of the unselectable cell marker class
419	this._currentClass = 'ui-datepicker-current-day'; // The name of the current day marker class
420	this._dayOverClass = 'ui-datepicker-days-cell-over'; // The name of the day hover marker class
421	this.regional = []; // Available regional settings, indexed by language code
422	this.regional[''] = { // Default regional settings
423		closeText: 'Done', // Display text for close link
424		prevText: 'Prev', // Display text for previous month link
425		nextText: 'Next', // Display text for next month link
426		currentText: 'Today', // Display text for current month link
427		monthNames: ['January','February','March','April','May','June',
428			'July','August','September','October','November','December'], // Names of months for drop-down and formatting
429		monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], // For formatting
430		dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // For formatting
431		dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], // For formatting
432		dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], // Column headings for days starting at Sunday
433		weekHeader: 'Wk', // Column header for week of the year
434		dateFormat: 'mm/dd/yy', // See format options on parseDate
435		firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
436		isRTL: false, // True if right-to-left language, false if left-to-right
437		showMonthAfterYear: false, // True if the year select precedes month, false for month then year
438		yearSuffix: '' // Additional text to append to the year in the month headers
439	};
440	this._defaults = { // Global defaults for all the date picker instances
441		showOn: 'focus', // 'focus' for popup on focus,
442			// 'button' for trigger button, or 'both' for either
443		showAnim: 'fadeIn', // Name of jQuery animation for popup
444		showOptions: {}, // Options for enhanced animations
445		defaultDate: null, // Used when field is blank: actual date,
446			// +/-number for offset from today, null for today
447		appendText: '', // Display text following the input box, e.g. showing the format
448		buttonText: '...', // Text for trigger button
449		buttonImage: '', // URL for trigger button image
450		buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
451		hideIfNoPrevNext: false, // True to hide next/previous month links
452			// if not applicable, false to just disable them
453		navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
454		gotoCurrent: false, // True if today link goes back to current selection instead
455		changeMonth: false, // True if month can be selected directly, false if only prev/next
456		changeYear: false, // True if year can be selected directly, false if only prev/next
457		yearRange: 'c-10:c+10', // Range of years to display in drop-down,
458			// either relative to today's year (-nn:+nn), relative to currently displayed year
459			// (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
460		showOtherMonths: false, // True to show dates in other months, false to leave blank
461		selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
462		showWeek: false, // True to show week of the year, false to not show it
463		calculateWeek: this.iso8601Week, // How to calculate the week of the year,
464			// takes a Date and returns the number of the week for it
465		shortYearCutoff: '+10', // Short year values < this are in the current century,
466			// > this are in the previous century,
467			// string value starting with '+' for current year + value
468		minDate: null, // The earliest selectable date, or null for no limit
469		maxDate: null, // The latest selectable date, or null for no limit
470		duration: 'fast', // Duration of display/closure
471		beforeShowDay: null, // Function that takes a date and returns an array with
472			// [0] = true if selectable, false if not, [1] = custom CSS class name(s) or '',
473			// [2] = cell title (optional), e.g. $.datepicker.noWeekends
474		beforeShow: null, // Function that takes an input field and
475			// returns a set of custom settings for the date picker
476		onSelect: null, // Define a callback function when a date is selected
477		onChangeMonthYear: null, // Define a callback function when the month or year is changed
478		onClose: null, // Define a callback function when the datepicker is closed
479		numberOfMonths: 1, // Number of months to show at a time
480		showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
481		stepMonths: 1, // Number of months to step back/forward
482		stepBigMonths: 12, // Number of months to step back/forward for the big links
483		altField: '', // Selector for an alternate field to store selected dates into
484		altFormat: '', // The date format to use for the alternate field
485		constrainInput: true, // The input is constrained by the current date format
486		showButtonPanel: false, // True to show button panel, false to not show it
487		autoSize: false, // True to size the input for the date format, false to leave as is
488		disabled: false // The initial disabled state
489	};
490	$.extend(this._defaults, this.regional['']);
491	this.dpDiv = bindHover($('<div id="' + this._mainDivId + '" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'));
492}
493
494$.extend(Datepicker.prototype, {
495	/* Class name added to elements to indicate already configured with a date picker. */
496	markerClassName: 'hasDatepicker',
497
498	//Keep track of the maximum number of rows displayed (see #7043)
499	maxRows: 4,
500
501	/* Debug logging (if enabled). */
502	log: function () {
503		if (this.debug)
504			console.log.apply('', arguments);
505	},
506
507	// TODO rename to "widget" when switching to widget factory
508	_widgetDatepicker: function() {
509		return this.dpDiv;
510	},
511
512	/* Override the default settings for all instances of the date picker.
513	   @param  settings  object - the new settings to use as defaults (anonymous object)
514	   @return the manager object */
515	setDefaults: function(settings) {
516		extendRemove(this._defaults, settings || {});
517		return this;
518	},
519
520	/* Attach the date picker to a jQuery selection.
521	   @param  target    element - the target input field or division or span
522	   @param  settings  object - the new settings to use for this date picker instance (anonymous) */
523	_attachDatepicker: function(target, settings) {
524		// check for settings on the control itself - in namespace 'date:'
525		var inlineSettings = null;
526		for (var attrName in this._defaults) {
527			var attrValue = target.getAttribute('date:' + attrName);
528			if (attrValue) {
529				inlineSettings = inlineSettings || {};
530				try {
531					inlineSettings[attrName] = eval(attrValue);
532				} catch (err) {
533					inlineSettings[attrName] = attrValue;
534				}
535			}
536		}
537		var nodeName = target.nodeName.toLowerCase();
538		var inline = (nodeName == 'div' || nodeName == 'span');
539		if (!target.id) {
540			this.uuid += 1;
541			target.id = 'dp' + this.uuid;
542		}
543		var inst = this._newInst($(target), inline);
544		inst.settings = $.extend({}, settings || {}, inlineSettings || {});
545		if (nodeName == 'input') {
546			this._connectDatepicker(target, inst);
547		} else if (inline) {
548			this._inlineDatepicker(target, inst);
549		}
550	},
551
552	/* Create a new instance object. */
553	_newInst: function(target, inline) {
554		var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); // escape jQuery meta chars
555		return {id: id, input: target, // associated target
556			selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
557			drawMonth: 0, drawYear: 0, // month being drawn
558			inline: inline, // is datepicker inline or not
559			dpDiv: (!inline ? this.dpDiv : // presentation div
560			bindHover($('<div class="' + this._inlineClass + ' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>')))};
561	},
562
563	/* Attach the date picker to an input field. */
564	_connectDatepicker: function(target, inst) {
565		var input = $(target);
566		inst.append = $([]);
567		inst.trigger = $([]);
568		if (input.hasClass(this.markerClassName))
569			return;
570		this._attachments(input, inst);
571		input.addClass(this.markerClassName).keydown(this._doKeyDown).
572			keypress(this._doKeyPress).keyup(this._doKeyUp).
573			bind("setData.datepicker", function(event, key, value) {
574				inst.settings[key] = value;
575			}).bind("getData.datepicker", function(event, key) {
576				return this._get(inst, key);
577			});
578		this._autoSize(inst);
579		$.data(target, PROP_NAME, inst);
580		//If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
581		if( inst.settings.disabled ) {
582			this._disableDatepicker( target );
583		}
584	},
585
586	/* Make attachments based on settings. */
587	_attachments: function(input, inst) {
588		var appendText = this._get(inst, 'appendText');
589		var isRTL = this._get(inst, 'isRTL');
590		if (inst.append)
591			inst.append.remove();
592		if (appendText) {
593			inst.append = $('<span class="' + this._appendClass + '">' + appendText + '</span>');
594			input[isRTL ? 'before' : 'after'](inst.append);
595		}
596		input.unbind('focus', this._showDatepicker);
597		if (inst.trigger)
598			inst.trigger.remove();
599		var showOn = this._get(inst, 'showOn');
600		if (showOn == 'focus' || showOn == 'both') // pop-up date picker when in the marked field
601			input.focus(this._showDatepicker);
602		if (showOn == 'button' || showOn == 'both') { // pop-up date picker when button clicked
603			var buttonText = this._get(inst, 'buttonText');
604			var buttonImage = this._get(inst, 'buttonImage');
605			inst.trigger = $(this._get(inst, 'buttonImageOnly') ?
606				$('<img/>').addClass(this._triggerClass).
607					attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
608				$('<button type="button"></button>').addClass(this._triggerClass).
609					html(buttonImage == '' ? buttonText : $('<img/>').attr(
610					{ src:buttonImage, alt:buttonText, title:buttonText })));
611			input[isRTL ? 'before' : 'after'](inst.trigger);
612			inst.trigger.click(function() {
613				if ($.datepicker._datepickerShowing && $.datepicker._lastInput == input[0])
614					$.datepicker._hideDatepicker();
615				else if ($.datepicker._datepickerShowing && $.datepicker._lastInput != input[0]) {
616					$.datepicker._hideDatepicker();
617					$.datepicker._showDatepicker(input[0]);
618				} else
619					$.datepicker._showDatepicker(input[0]);
620				return false;
621			});
622		}
623	},
624
625	/* Apply the maximum length for the date format. */
626	_autoSize: function(inst) {
627		if (this._get(inst, 'autoSize') && !inst.inline) {
628			var date = new Date(2009, 12 - 1, 20); // Ensure double digits
629			var dateFormat = this._get(inst, 'dateFormat');
630			if (dateFormat.match(/[DM]/)) {
631				var findMax = function(names) {
632					var max = 0;
633					var maxI = 0;
634					for (var i = 0; i < names.length; i++) {
635						if (names[i].length > max) {
636							max = names[i].length;
637							maxI = i;
638						}
639					}
640					return maxI;
641				};
642				date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ?
643					'monthNames' : 'monthNamesShort'))));
644				date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ?
645					'dayNames' : 'dayNamesShort'))) + 20 - date.getDay());
646			}
647			inst.input.attr('size', this._formatDate(inst, date).length);
648		}
649	},
650
651	/* Attach an inline date picker to a div. */
652	_inlineDatepicker: function(target, inst) {
653		var divSpan = $(target);
654		if (divSpan.hasClass(this.markerClassName))
655			return;
656		divSpan.addClass(this.markerClassName).append(inst.dpDiv).
657			bind("setData.datepicker", function(event, key, value){
658				inst.settings[key] = value;
659			}).bind("getData.datepicker", function(event, key){
660				return this._get(inst, key);
661			});
662		$.data(target, PROP_NAME, inst);
663		this._setDate(inst, this._getDefaultDate(inst), true);
664		this._updateDatepicker(inst);
665		this._updateAlternate(inst);
666		//If disabled option is true, disable the datepicker before showing it (see ticket #5665)
667		if( inst.settings.disabled ) {
668			this._disableDatepicker( target );
669		}
670		// Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
671		// http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
672		inst.dpDiv.css( "display", "block" );
673	},
674
675	/* Pop-up the date picker in a "dialog" box.
676	   @param  input     element - ignored
677	   @param  date      string or Date - the initial date to display
678	   @param  onSelect  function - the function to call when a date is selected
679	   @param  settings  object - update the dialog date picker instance's settings (anonymous object)
680	   @param  pos       int[2] - coordinates for the dialog's position within the screen or
681	                     event - with x/y coordinates or
682	                     leave empty for default (screen centre)
683	   @return the manager object */
684	_dialogDatepicker: function(input, date, onSelect, settings, pos) {
685		var inst = this._dialogInst; // internal instance
686		if (!inst) {
687			this.uuid += 1;
688			var id = 'dp' + this.uuid;
689			this._dialogInput = $('<input type="text" id="' + id +
690				'" style="position: absolute; top: -100px; width: 0px;"/>');
691			this._dialogInput.keydown(this._doKeyDown);
692			$('body').append(this._dialogInput);
693			inst = this._dialogInst = this._newInst(this._dialogInput, false);
694			inst.settings = {};
695			$.data(this._dialogInput[0], PROP_NAME, inst);
696		}
697		extendRemove(inst.settings, settings || {});
698		date = (date && date.constructor == Date ? this._formatDate(inst, date) : date);
699		this._dialogInput.val(date);
700
701		this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
702		if (!this._pos) {
703			var browserWidth = document.documentElement.clientWidth;
704			var browserHeight = document.documentElement.clientHeight;
705			var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
706			var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
707			this._pos = // should use actual width/height below
708				[(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
709		}
710
711		// move input on screen for focus, but hidden behind dialog
712		this._dialogInput.css('left', (this._pos[0] + 20) + 'px').css('top', this._pos[1] + 'px');
713		inst.settings.onSelect = onSelect;
714		this._inDialog = true;
715		this.dpDiv.addClass(this._dialogClass);
716		this._showDatepicker(this._dialogInput[0]);
717		if ($.blockUI)
718			$.blockUI(this.dpDiv);
719		$.data(this._dialogInput[0], PROP_NAME, inst);
720		return this;
721	},
722
723	/* Detach a datepicker from its control.
724	   @param  target    element - the target input field or division or span */
725	_destroyDatepicker: function(target) {
726		var $target = $(target);
727		var inst = $.data(target, PROP_NAME);
728		if (!$target.hasClass(this.markerClassName)) {
729			return;
730		}
731		var nodeName = target.nodeName.toLowerCase();
732		$.removeData(target, PROP_NAME);
733		if (nodeName == 'input') {
734			inst.append.remove();
735			inst.trigger.remove();
736			$target.removeClass(this.markerClassName).
737				unbind('focus', this._showDatepicker).
738				unbind('keydown', this._doKeyDown).
739				unbind('keypress', this._doKeyPress).
740				unbind('keyup', this._doKeyUp);
741		} else if (nodeName == 'div' || nodeName == 'span')
742			$target.removeClass(this.markerClassName).empty();
743	},
744
745	/* Enable the date picker to a jQuery selection.
746	   @param  target    element - the target input field or division or span */
747	_enableDatepicker: function(target) {
748		var $target = $(target);
749		var inst = $.data(target, PROP_NAME);
750		if (!$target.hasClass(this.markerClassName)) {
751			return;
752		}
753		var nodeName = target.nodeName.toLowerCase();
754		if (nodeName == 'input') {
755			target.disabled = false;
756			inst.trigger.filter('button').
757				each(function() { this.disabled = false; }).end().
758				filter('img').css({opacity: '1.0', cursor: ''});
759		}
760		else if (nodeName == 'div' || nodeName == 'span') {
761			var inline = $target.children('.' + this._inlineClass);
762			inline.children().removeClass('ui-state-disabled');
763			inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
764				prop("disabled", false);
765		}
766		this._disabledInputs = $.map(this._disabledInputs,
767			function(value) { return (value == target ? null : value); }); // delete entry
768	},
769
770	/* Disable the date picker to a jQuery selection.
771	   @param  target    element - the target input field or division or span */
772	_disableDatepicker: function(target) {
773		var $target = $(target);
774		var inst = $.data(target, PROP_NAME);
775		if (!$target.hasClass(this.markerClassName)) {
776			return;
777		}
778		var nodeName = target.nodeName.toLowerCase();
779		if (nodeName == 'input') {
780			target.disabled = true;
781			inst.trigger.filter('button').
782				each(function() { this.disabled = true; }).end().
783				filter('img').css({opacity: '0.5', cursor: 'default'});
784		}
785		else if (nodeName == 'div' || nodeName == 'span') {
786			var inline = $target.children('.' + this._inlineClass);
787			inline.children().addClass('ui-state-disabled');
788			inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
789				prop("disabled", true);
790		}
791		this._disabledInputs = $.map(this._disabledInputs,
792			function(value) { return (value == target ? null : value); }); // delete entry
793		this._disabledInputs[this._disabledInputs.length] = target;
794	},
795
796	/* Is the first field in a jQuery collection disabled as a datepicker?
797	   @param  target    element - the target input field or division or span
798	   @return boolean - true if disabled, false if enabled */
799	_isDisabledDatepicker: function(target) {
800		if (!target) {
801			return false;
802		}
803		for (var i = 0; i < this._disabledInputs.length; i++) {
804			if (this._disabledInputs[i] == target)
805				return true;
806		}
807		return false;
808	},
809
810	/* Retrieve the instance data for the target control.
811	   @param  target  element - the target input field or division or span
812	   @return  object - the associated instance data
813	   @throws  error if a jQuery problem getting data */
814	_getInst: function(target) {
815		try {
816			return $.data(target, PROP_NAME);
817		}
818		catch (err) {
819			throw 'Missing instance data for this datepicker';
820		}
821	},
822
823	/* Update or retrieve the settings for a date picker attached to an input field or division.
824	   @param  target  element - the target input field or division or span
825	   @param  name    object - the new settings to update or
826	                   string - the name of the setting to change or retrieve,
827	                   when retrieving also 'all' for all instance settings or
828	                   'defaults' for all global defaults
829	   @param  value   any - the new value for the setting
830	                   (omit if above is an object or to retrieve a value) */
831	_optionDatepicker: function(target, name, value) {
832		var inst = this._getInst(target);
833		if (arguments.length == 2 && typeof name == 'string') {
834			return (name == 'defaults' ? $.extend({}, $.datepicker._defaults) :
835				(inst ? (name == 'all' ? $.extend({}, inst.settings) :
836				this._get(inst, name)) : null));
837		}
838		var settings = name || {};
839		if (typeof name == 'string') {
840			settings = {};
841			settings[name] = value;
842		}
843		if (inst) {
844			if (this._curInst == inst) {
845				this._hideDatepicker();
846			}
847			var date = this._getDateDatepicker(target, true);
848			var minDate = this._getMinMaxDate(inst, 'min');
849			var maxDate = this._getMinMaxDate(inst, 'max');
850			extendRemove(inst.settings, settings);
851			// reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
852			if (minDate !== null && settings['dateFormat'] !== undefined && settings['minDate'] === undefined)
853				inst.settings.minDate = this._formatDate(inst, minDate);
854			if (maxDate !== null && settings['dateFormat'] !== undefined && settings['maxDate'] === undefined)
855				inst.settings.maxDate = this._formatDate(inst, maxDate);
856			this._attachments($(target), inst);
857			this._autoSize(inst);
858			this._setDate(inst, date);
859			this._updateAlternate(inst);
860			this._updateDatepicker(inst);
861		}
862	},
863
864	// change method deprecated
865	_changeDatepicker: function(target, name, value) {
866		this._optionDatepicker(target, name, value);
867	},
868
869	/* Redraw the date picker attached to an input field or division.
870	   @param  target  element - the target input field or division or span */
871	_refreshDatepicker: function(target) {
872		var inst = this._getInst(target);
873		if (inst) {
874			this._updateDatepicker(inst);
875		}
876	},
877
878	/* Set the dates for a jQuery selection.
879	   @param  target   element - the target input field or division or span
880	   @param  date     Date - the new date */
881	_setDateDatepicker: function(target, date) {
882		var inst = this._getInst(target);
883		if (inst) {
884			this._setDate(inst, date);
885			this._updateDatepicker(inst);
886			this._updateAlternate(inst);
887		}
888	},
889
890	/* Get the date(s) for the first entry in a jQuery selection.
891	   @param  target     element - the target input field or division or span
892	   @param  noDefault  boolean - true if no default date is to be used
893	   @return Date - the current date */
894	_getDateDatepicker: function(target, noDefault) {
895		var inst = this._getInst(target);
896		if (inst && !inst.inline)
897			this._setDateFromField(inst, noDefault);
898		return (inst ? this._getDate(inst) : null);
899	},
900
901	/* Handle keystrokes. */
902	_doKeyDown: function(event) {
903		var inst = $.datepicker._getInst(event.target);
904		var handled = true;
905		var isRTL = inst.dpDiv.is('.ui-datepicker-rtl');
906		inst._keyEvent = true;
907		if ($.datepicker._datepickerShowing)
908			switch (event.keyCode) {
909				case 9: $.datepicker._hideDatepicker();
910						handled = false;
911						break; // hide on tab out
912				case 13: var sel = $('td.' + $.datepicker._dayOverClass + ':not(.' +
913									$.datepicker._currentClass + ')', inst.dpDiv);
914						if (sel[0])
915							$.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
916							var onSelect = $.datepicker._get(inst, 'onSelect');
917							if (onSelect) {
918								var dateStr = $.datepicker._formatDate(inst);
919
920								// trigger custom callback
921								onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);
922							}
923						else
924							$.datepicker._hideDatepicker();
925						return false; // don't submit the form
926						break; // select the value on enter
927				case 27: $.datepicker._hideDatepicker();
928						break; // hide on escape
929				case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
930							-$.datepicker._get(inst, 'stepBigMonths') :
931							-$.datepicker._get(inst, 'stepMonths')), 'M');
932						break; // previous month/year on page up/+ ctrl
933				case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
934							+$.datepicker._get(inst, 'stepBigMonths') :
935							+$.datepicker._get(inst, 'stepMonths')), 'M');
936						break; // next month/year on page down/+ ctrl
937				case 35: if (event.ctrlKey || event.metaKey) $.datepicker._clearDate(event.target);
938						handled = event.ctrlKey || event.metaKey;
939						break; // clear on ctrl or command +end
940				case 36: if (event.ctrlKey || event.metaKey) $.datepicker._gotoToday(event.target);
941						handled = event.ctrlKey || event.metaKey;
942						break; // current on ctrl or command +home
943				case 37: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), 'D');
944						handled = event.ctrlKey || event.metaKey;
945						// -1 day on ctrl or command +left
946						if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ?
947									-$.datepicker._get(inst, 'stepBigMonths') :
948									-$.datepicker._get(inst, 'stepMonths')), 'M');
949						// next month/year on alt +left on Mac
950						break;
951				case 38: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, -7, 'D');
952						handled = event.ctrlKey || event.metaKey;
953						break; // -1 week on ctrl or command +up
954				case 39: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), 'D');
955						handled = event.ctrlKey || event.metaKey;
956						// +1 day on ctrl or command +right
957						if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ?
958									+$.datepicker._get(inst, 'stepBigMonths') :
959									+$.datepicker._get(inst, 'stepMonths')), 'M');
960						// next month/year on alt +right
961						break;
962				case 40: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, +7, 'D');
963						handled = event.ctrlKey || event.metaKey;
964						break; // +1 week on ctrl or command +down
965				default: handled = false;
966			}
967		else if (event.keyCode == 36 && event.ctrlKey) // display the date picker on ctrl+home
968			$.datepicker._showDatepicker(this);
969		else {
970			handled = false;
971		}
972		if (handled) {
973			event.preventDefault();
974			event.stopPropagation();
975		}
976	},
977
978	/* Filter entered characters - based on date format. */
979	_doKeyPress: function(event) {
980		var inst = $.datepicker._getInst(event.target);
981		if ($.datepicker._get(inst, 'constrainInput')) {
982			var chars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat'));
983			var chr = String.fromCharCode(event.charCode == undefined ? event.keyCode : event.charCode);
984			return event.ctrlKey || event.metaKey || (chr < ' ' || !chars || chars.indexOf(chr) > -1);
985		}
986	},
987
988	/* Synchronise manual entry and field/alternate field. */
989	_doKeyUp: function(event) {
990		var inst = $.datepicker._getInst(event.target);
991		if (inst.input.val() != inst.lastVal) {
992			try {
993				var date = $.datepicker.parseDate($.datepicker._get(inst, 'dateFormat'),
994					(inst.input ? inst.input.val() : null),
995					$.datepicker._getFormatConfig(inst));
996				if (date) { // only if valid
997					$.datepicker._setDateFromField(inst);
998					$.datepicker._updateAlternate(inst);
999					$.datepicker._updateDatepicker(inst);
1000				}
1001			}
1002			catch (err) {
1003				$.datepicker.log(err);
1004			}
1005		}
1006		return true;
1007	},
1008
1009	/* Pop-up the date picker for a given input field.
1010	   If false returned from beforeShow event handler do not show.
1011	   @param  input  element - the input field attached to the date picker or
1012	                  event - if triggered by focus */
1013	_showDatepicker: function(input) {
1014		input = input.target || input;
1015		if (input.nodeName.toLowerCase() != 'input') // find from button/image trigger
1016			input = $('input', input.parentNode)[0];
1017		if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput == input) // already here
1018			return;
1019		var inst = $.datepicker._getInst(input);
1020		if ($.datepicker._curInst && $.datepicker._curInst != inst) {
1021			$.datepicker._curInst.dpDiv.stop(true, true);
1022			if ( inst && $.datepicker._datepickerShowing ) {
1023				$.datepicker._hideDatepicker( $.datepicker._curInst.input[0] );
1024			}
1025		}
1026		var beforeShow = $.datepicker._get(inst, 'beforeShow');
1027		var beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {};
1028		if(beforeShowSettings === false){
1029			//false
1030			return;
1031		}
1032		extendRemove(inst.settings, beforeShowSettings);
1033		inst.lastVal = null;
1034		$.datepicker._lastInput = input;
1035		$.datepicker._setDateFromField(inst);
1036		if ($.datepicker._inDialog) // hide cursor
1037			input.value = '';
1038		if (!$.datepicker._pos) { // position below input
1039			$.datepicker._pos = $.datepicker._findPos(input);
1040			$.datepicker._pos[1] += input.offsetHeight; // add the height
1041		}
1042		var isFixed = false;
1043		$(input).parents().each(function() {
1044			isFixed |= $(this).css('position') == 'fixed';
1045			return !isFixed;
1046		});
1047		var offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]};
1048		$.datepicker._pos = null;
1049		//to avoid flashes on Firefox
1050		inst.dpDiv.empty();
1051		// determine sizing offscreen
1052		inst.dpDiv.css({position: 'absolute', display: 'block', top: '-1000px'});
1053		$.datepicker._updateDatepicker(inst);
1054		// fix width for dynamic number of date pickers
1055		// and adjust position before showing
1056		offset = $.datepicker._checkOffset(inst, offset, isFixed);
1057		inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ?
1058			'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none',
1059			left: offset.left + 'px', top: offset.top + 'px'});
1060		if (!inst.inline) {
1061			var showAnim = $.datepicker._get(inst, 'showAnim');
1062			var duration = $.datepicker._get(inst, 'duration');
1063			var postProcess = function() {
1064				var cover = inst.dpDiv.find('iframe.ui-datepicker-cover'); // IE6- only
1065				if( !! cover.length ){
1066					var borders = $.datepicker._getBorders(inst.dpDiv);
1067					cover.css({left: -borders[0], top: -borders[1],
1068						width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()});
1069				}
1070			};
1071			inst.dpDiv.zIndex($(input).zIndex()+1);
1072			$.datepicker._datepickerShowing = true;
1073
1074			// DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed
1075			if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) )
1076				inst.dpDiv.show(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess);
1077			else
1078				inst.dpDiv[showAnim || 'show']((showAnim ? duration : null), postProcess);
1079			if (!showAnim || !duration)
1080				postProcess();
1081			if (inst.input.is(':visible') && !inst.input.is(':disabled'))
1082				inst.input.focus();
1083			$.datepicker._curInst = inst;
1084		}
1085	},
1086
1087	/* Generate the date picker content. */
1088	_updateDatepicker: function(inst) {
1089		this.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
1090		var borders = $.datepicker._getBorders(inst.dpDiv);
1091		instActive = inst; // for delegate hover events
1092		inst.dpDiv.empty().append(this._generateHTML(inst));
1093		this._attachHandlers(inst);
1094		var cover = inst.dpDiv.find('iframe.ui-datepicker-cover'); // IE6- only
1095		if( !!cover.length ){ //avoid call to outerXXXX() when not in IE6
1096			cover.css({left: -borders[0], top: -borders[1], width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()})
1097		}
1098		inst.dpDiv.find('.' + this._dayOverClass + ' a').mouseover();
1099		var numMonths = this._getNumberOfMonths(inst);
1100		var cols = numMonths[1];
1101		var width = 17;
1102		inst.dpDiv.removeClass('ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4').width('');
1103		if (cols > 1)
1104			inst.dpDiv.addClass('ui-datepicker-multi-' + cols).css('width', (width * cols) + 'em');
1105		inst.dpDiv[(numMonths[0] != 1 || numMonths[1] != 1 ? 'add' : 'remove') +
1106			'Class']('ui-datepicker-multi');
1107		inst.dpDiv[(this._get(inst, 'isRTL') ? 'add' : 'remove') +
1108			'Class']('ui-datepicker-rtl');
1109		if (inst == $.datepicker._curInst && $.datepicker._datepickerShowing && inst.input &&
1110				// #6694 - don't focus the input if it's already focused
1111				// this breaks the change event in IE
1112				inst.input.is(':visible') && !inst.input.is(':disabled') && inst.input[0] != document.activeElement)
1113			inst.input.focus();
1114		// deffered render of the years select (to avoid flashes on Firefox)
1115		if( inst.yearshtml ){
1116			var origyearshtml = inst.yearshtml;
1117			setTimeout(function(){
1118				//assure that inst.yearshtml didn't change.
1119				if( origyearshtml === inst.yearshtml && inst.yearshtml ){
1120					inst.dpDiv.find('select.ui-datepicker-year:first').replaceWith(inst.yearshtml);
1121				}
1122				origyearshtml = inst.yearshtml = null;
1123			}, 0);
1124		}
1125	},
1126
1127	/* Retrieve the size of left and top borders for an element.
1128	   @param  elem  (jQuery object) the element of interest
1129	   @return  (number[2]) the left and top borders */
1130	_getBorders: function(elem) {
1131		var convert = function(value) {
1132			return {thin: 1, medium: 2, thick: 3}[value] || value;
1133		};
1134		return [parseFloat(convert(elem.css('border-left-width'))),
1135			parseFloat(convert(elem.css('border-top-width')))];
1136	},
1137
1138	/* Check positioning to remain on screen. */
1139	_checkOffset: function(inst, offset, isFixed) {
1140		var dpWidth = inst.dpDiv.outerWidth();
1141		var dpHeight = inst.dpDiv.outerHeight();
1142		var inputWidth = inst.input ? inst.input.outerWidth() : 0;
1143		var inputHeight = inst.input ? inst.input.outerHeight() : 0;
1144		var viewWidth = document.documentElement.clientWidth + (isFixed ? 0 : $(document).scrollLeft());
1145		var viewHeight = document.documentElement.clientHeight + (isFixed ? 0 : $(document).scrollTop());
1146
1147		offset.left -= (this._get(inst, 'isRTL') ? (dpWidth - inputWidth) : 0);
1148		offset.left -= (isFixed && offset.left == inst.input.offset().left) ? $(document).scrollLeft() : 0;
1149		offset.top -= (isFixed && offset.top == (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
1150
1151		// now check if datepicker is showing outside window viewport - move to a better place if so.
1152		offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
1153			Math.abs(offset.left + dpWidth - viewWidth) : 0);
1154		offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
1155			Math.abs(dpHeight + inputHeight) : 0);
1156
1157		return offset;
1158	},
1159
1160	/* Find an object's position on the screen. */
1161	_findPos: function(obj) {
1162		var inst = this._getInst(obj);
1163		var isRTL = this._get(inst, 'isRTL');
1164		while (obj && (obj.type == 'hidden' || obj.nodeType != 1 || $.expr.filters.hidden(obj))) {
1165			obj = obj[isRTL ? 'previousSibling' : 'nextSibling'];
1166		}
1167		var position = $(obj).offset();
1168		return [position.left, position.top];
1169	},
1170
1171	/* Hide the date picker from view.
1172	   @param  input  element - the input field attached to the date picker */
1173	_hideDatepicker: function(input) {
1174		var inst = this._curInst;
1175		if (!inst || (input && inst != $.data(input, PROP_NAME)))
1176			return;
1177		if (this._datepickerShowing) {
1178			var showAnim = this._get(inst, 'showAnim');
1179			var duration = this._get(inst, 'duration');
1180			var postProcess = function() {
1181				$.datepicker._tidyDialog(inst);
1182			};
1183
1184			// DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed
1185			if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) )
1186				inst.dpDiv.hide(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess);
1187			else
1188				inst.dpDiv[(showAnim == 'slideDown' ? 'slideUp' :
1189					(showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((showAnim ? duration : null), postProcess);
1190			if (!showAnim)
1191				postProcess();
1192			this._datepickerShowing = false;
1193			var onClose = this._get(inst, 'onClose');
1194			if (onClose)
1195				onClose.apply((inst.input ? inst.input[0] : null),
1196					[(inst.input ? inst.input.val() : ''), inst]);
1197			this._lastInput = null;
1198			if (this._inDialog) {
1199				this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
1200				if ($.blockUI) {
1201					$.unblockUI();
1202					$('body').append(this.dpDiv);
1203				}
1204			}
1205			this._inDialog = false;
1206		}
1207	},
1208
1209	/* Tidy up after a dialog display. */
1210	_tidyDialog: function(inst) {
1211		inst.dpDiv.removeClass(this._dialogClass).unbind('.ui-datepicker-calendar');
1212	},
1213
1214	/* Close date picker if clicked elsewhere. */
1215	_checkExternalClick: function(event) {
1216		if (!$.datepicker._curInst)
1217			return;
1218
1219		var $target = $(event.target),
1220			inst = $.datepicker._getInst($target[0]);
1221
1222		if ( ( ( $target[0].id != $.datepicker._mainDivId &&
1223				$target.parents('#' + $.datepicker._mainDivId).length == 0 &&
1224				!$target.hasClass($.datepicker.markerClassName) &&
1225				!$target.closest("." + $.datepicker._triggerClass).length &&
1226				$.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) ||
1227			( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst != inst ) )
1228			$.datepicker._hideDatepicker();
1229	},
1230
1231	/* Adjust one of the date sub-fields. */
1232	_adjustDate: function(id, offset, period) {
1233		var target = $(id);
1234		var inst = this._getInst(target[0]);
1235		if (this._isDisabledDatepicker(target[0])) {
1236			return;
1237		}
1238		this._adjustInstDate(inst, offset +
1239			(period == 'M' ? this._get(inst, 'showCurrentAtPos') : 0), // undo positioning
1240			period);
1241		this._updateDatepicker(inst);
1242	},
1243
1244	/* Action for current link. */
1245	_gotoToday: function(id) {
1246		var target = $(id);
1247		var inst = this._getInst(target[0]);
1248		if (this._get(inst, 'gotoCurrent') && inst.currentDay) {
1249			inst.selectedDay = inst.currentDay;
1250			inst.drawMonth = inst.selectedMonth = inst.currentMonth;
1251			inst.drawYear = inst.selectedYear = inst.currentYear;
1252		}
1253		else {
1254			var date = new Date();
1255			inst.selectedDay = date.getDate();
1256			inst.drawMonth = inst.selectedMonth = date.getMonth();
1257			inst.drawYear = inst.selectedYear = date.getFullYear();
1258		}
1259		this._notifyChange(inst);
1260		this._adjustDate(target);
1261	},
1262
1263	/* Action for selecting a new month/year. */
1264	_selectMonthYear: function(id, select, period) {
1265		var target = $(id);
1266		var inst = this._getInst(target[0]);
1267		inst['selected' + (period == 'M' ? 'Month' : 'Year')] =
1268		inst['draw' + (period == 'M' ? 'Month' : 'Year')] =
1269			parseInt(select.options[select.selectedIndex].value,10);
1270		this._notifyChange(inst);
1271		this._adjustDate(target);
1272	},
1273
1274	/* Action for selecting a day. */
1275	_selectDay: function(id, month, year, td) {
1276		var target = $(id);
1277		if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) {
1278			return;
1279		}
1280		var inst = this._getInst(target[0]);
1281		inst.selectedDay = inst.currentDay = $('a', td).html();
1282		inst.selectedMonth = inst.currentMonth = month;
1283		inst.selectedYear = inst.currentYear = year;
1284		this._selectDate(id, this._formatDate(inst,
1285			inst.currentDay, inst.currentMonth, inst.currentYear));
1286	},
1287
1288	/* Erase the input field and hide the date picker. */
1289	_clearDate: function(id) {
1290		var target = $(id);
1291		var inst = this._getInst(target[0]);
1292		this._selectDate(target, '');
1293	},
1294
1295	/* Update the input field with the selected date. */
1296	_selectDate: function(id, dateStr) {
1297		var target = $(id);
1298		var inst = this._getInst(target[0]);
1299		dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
1300		if (inst.input)
1301			inst.input.val(dateStr);
1302		this._updateAlternate(inst);
1303		var onSelect = this._get(inst, 'onSelect');
1304		if (onSelect)
1305			onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);  // trigger custom callback
1306		else if (inst.input)
1307			inst.input.trigger('change'); // fire the change event
1308		if (inst.inline)
1309			this._updateDatepicker(inst);
1310		else {
1311			this._hideDatepicker();
1312			this._lastInput = inst.input[0];
1313			if (typeof(inst.input[0]) != 'object')
1314				inst.input.focus(); // restore focus
1315			this._lastInput = null;
1316		}
1317	},
1318
1319	/* Update any alternate field to synchronise with the main field. */
1320	_updateAlternate: function(inst) {
1321		var altField = this._get(inst, 'altField');
1322		if (altField) { // update alternate field too
1323			var altFormat = this._get(inst, 'altFormat') || this._get(inst, 'dateFormat');
1324			var date = this._getDate(inst);
1325			var dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst));
1326			$(altField).each(function() { $(this).val(dateStr); });
1327		}
1328	},
1329
1330	/* Set as beforeShowDay function to prevent selection of weekends.
1331	   @param  date  Date - the date to customise
1332	   @return [boolean, string] - is this date selectable?, what is its CSS class? */
1333	noWeekends: function(date) {
1334		var day = date.getDay();
1335		return [(day > 0 && day < 6), ''];
1336	},
1337
1338	/* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
1339	   @param  date  Date - the date to get the week for
1340	   @return  number - the number of the week within the year that contains this date */
1341	iso8601Week: function(date) {
1342		var checkDate = new Date(date.getTime());
1343		// Find Thursday of this week starting on Monday
1344		checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
1345		var time = checkDate.getTime();
1346		checkDate.setMonth(0); // Compare with Jan 1
1347		checkDate.setDate(1);
1348		return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
1349	},
1350
1351	/* Parse a string value into a date object.
1352	   See formatDate below for the possible formats.
1353
1354	   @param  format    string - the expected format of the date
1355	   @param  value     string - the date in the above format
1356	   @param  settings  Object - attributes include:
1357	                     shortYearCutoff  number - the cutoff year for determining the century (optional)
1358	                     dayNamesShort    string[7] - abbreviated names of the days from Sunday (optional)
1359	                     dayNames         string[7] - names of the days from Sunday (optional)
1360	                     monthNamesShort  string[12] - abbreviated names of the months (optional)
1361	                     monthNames       string[12] - names of the months (optional)
1362	   @return  Date - the extracted date value or null if value is blank */
1363	parseDate: function (format, value, settings) {
1364		if (format == null || value == null)
1365			throw 'Invalid arguments';
1366		value = (typeof value == 'object' ? value.toString() : value + '');
1367		if (value == '')
1368			return null;
1369		var shortYearCutoff = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff;
1370		shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
1371				new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
1372		var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
1373		var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
1374		var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
1375		var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
1376		var year = -1;
1377		var month = -1;
1378		var day = -1;
1379		var doy = -1;
1380		var literal = false;
1381		// Check whether a format character is doubled
1382		var lookAhead = function(match) {
1383			var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
1384			if (matches)
1385				iFormat++;
1386			return matches;
1387		};
1388		// Extract a number from the string value
1389		var getNumber = function(match) {
1390			var isDoubled = lookAhead(match);
1391			var size = (match == '@' ? 14 : (match == '!' ? 20 :
1392				(match == 'y' && isDoubled ? 4 : (match == 'o' ? 3 : 2))));
1393			var digits = new RegExp('^\\d{1,' + size + '}');
1394			var num = value.substring(iValue).match(digits);
1395			if (!num)
1396				throw 'Missing number at position ' + iValue;
1397			iValue += num[0].length;
1398			return parseInt(num[0], 10);
1399		};
1400		// Extract a name from the string value and convert to an index
1401		var getName = function(match, shortNames, longNames) {
1402			var names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) {
1403				return [ [k, v] ];
1404			}).sort(function (a, b) {
1405				return -(a[1].length - b[1].length);
1406			});
1407			var index = -1;
1408			$.each(names, function (i, pair) {
1409				var name = pair[1];
1410				if (value.substr(iValue, name.length).toLowerCase() == name.toLowerCase()) {
1411					index = pair[0];
1412					iValue += name.length;
1413					return false;
1414				}
1415			});
1416			if (index != -1)
1417				return index + 1;
1418			else
1419				throw 'Unknown name at position ' + iValue;
1420		};
1421		// Confirm that a literal character matches the string value
1422		var checkLiteral = function() {
1423			if (value.charAt(iValue) != format.charAt(iFormat))
1424				throw 'Unexpected literal at position ' + iValue;
1425			iValue++;
1426		};
1427		var iValue = 0;
1428		for (var iFormat = 0; iFormat < format.length; iFormat++) {
1429			if (literal)
1430				if (format.charAt(iFormat) == "'" && !lookAhead("'"))
1431					literal = false;
1432				else
1433					checkLiteral();
1434			else
1435				switch (format.charAt(iFormat)) {
1436					case 'd':
1437						day = getNumber('d');
1438						break;
1439					case 'D':
1440						getName('D', dayNamesShort, dayNames);
1441						break;
1442					case 'o':
1443						doy = getNumber('o');
1444						break;
1445					case 'm':
1446						month = getNumber('m');
1447						break;
1448					case 'M':
1449						month = getName('M', monthNamesShort, monthNames);
1450						break;
1451					case 'y':
1452						year = getNumber('y');
1453						break;
1454					case '@':
1455						var date = new Date(getNumber('@'));
1456						year = date.getFullYear();
1457						month = date.getMonth() + 1;
1458						day = date.getDate();
1459						break;
1460					case '!':
1461						var date = new Date((getNumber('!') - this._ticksTo1970) / 10000);
1462						year = date.getFullYear();
1463						month = date.getMonth() + 1;
1464						day = date.getDate();
1465						break;
1466					case "'":
1467						if (lookAhead("'"))
1468							checkLiteral();
1469						else
1470							literal = true;
1471						break;
1472					default:
1473						checkLiteral();
1474				}
1475		}
1476		if (iValue < value.length){
1477			var extra = value.substr(iValue);
1478			if (!/^\s+/.test(extra)) {
1479				throw "Extra/unparsed characters found in date: " + extra;
1480			}
1481		}
1482		if (year == -1)
1483			year = new Date().getFullYear();
1484		else if (year < 100)
1485			year += new Date().getFullYear() - new Date().getFullYear() % 100 +
1486				(year <= shortYearCutoff ? 0 : -100);
1487		if (doy > -1) {
1488			month = 1;
1489			day = doy;
1490			do {
1491				var dim = this._getDaysInMonth(year, month - 1);
1492				if (day <= dim)
1493					break;
1494				month++;
1495				day -= dim;
1496			} while (true);
1497		}
1498		var date = this._daylightSavingAdjust(new Date(year, month - 1, day));
1499		if (date.getFullYear() != year || date.getMonth() + 1 != month || date.getDate() != day)
1500			throw 'Invalid date'; // E.g. 31/02/00
1501		return date;
1502	},
1503
1504	/* Standard date formats. */
1505	ATOM: 'yy-mm-dd', // RFC 3339 (ISO 8601)
1506	COOKIE: 'D, dd M yy',
1507	ISO_8601: 'yy-mm-dd',
1508	RFC_822: 'D, d M y',
1509	RFC_850: 'DD, dd-M-y',
1510	RFC_1036: 'D, d M y',
1511	RFC_1123: 'D, d M yy',
1512	RFC_2822: 'D, d M yy',
1513	RSS: 'D, d M y', // RFC 822
1514	TICKS: '!',
1515	TIMESTAMP: '@',
1516	W3C: 'yy-mm-dd', // ISO 8601
1517
1518	_ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) +
1519		Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000),
1520
1521	/* Format a date object into a string value.
1522	   The format can be combinations of the following:
1523	   d  - day of month (no leading zero)
1524	   dd - day of month (two digit)
1525	   o  - day of year (no leading zeros)
1526	   oo - day of year (three digit)
1527	   D  - day name short
1528	   DD - day name long
1529	   m  - month of year (no leading zero)
1530	   mm - month of year (two digit)
1531	   M  - month name short
1532	   MM - month name long
1533	   y  - year (two digit)
1534	   yy - year (four digit)
1535	   @ - Unix timestamp (ms since 01/01/1970)
1536	   ! - Windows ticks (100ns since 01/01/0001)
1537	   '...' - literal text
1538	   '' - single quote
1539
1540	   @param  format    string - the desired format of the date
1541	   @param  date      Date - the date value to format
1542	   @param  settings  Object - attributes include:
1543	                     dayNamesShort    string[7] - abbreviated names of the days from Sunday (optional)
1544	                     dayNames         string[7] - names of the days from Sunday (optional)
1545	                     monthNamesShort  string[12] - abbreviated names of the months (optional)
1546	                     monthNames       string[12] - names of the months (optional)
1547	   @return  string - the date in the above format */
1548	formatDate: function (format, date, settings) {
1549		if (!date)
1550			return '';
1551		var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
1552		var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
1553		var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
1554		var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
1555		// Check whether a format character is doubled
1556		var lookAhead = function(match) {
1557			var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
1558			if (matches)
1559				iFormat++;
1560			return matches;
1561		};
1562		// Format a number, with leading zero if necessary
1563		var formatNumber = function(match, value, len) {
1564			var num = '' + value;
1565			if (lookAhead(match))
1566				while (num.length < len)
1567					num = '0' + num;
1568			return num;
1569		};
1570		// Format a name, short or long as requested
1571		var formatName = function(match, value, shortNames, longNames) {
1572			return (lookAhead(match) ? longNames[value] : shortNames[value]);
1573		};
1574		var output = '';
1575		var literal = false;
1576		if (date)
1577			for (var iFormat = 0; iFormat < format.length; iFormat++) {
1578				if (literal)
1579					if (format.charAt(iFormat) == "'" && !lookAhead("'"))
1580						literal = false;
1581					else
1582						output += format.charAt(iFormat);
1583				else
1584					switch (format.charAt(iFormat)) {
1585						case 'd':
1586							output += formatNumber('d', date.getDate(), 2);
1587							break;
1588						case 'D':
1589							output += formatName('D', date.getDay(), dayNamesShort, dayNames);
1590							break;
1591						case 'o':
1592							output += formatNumber('o',
1593								Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3);
1594							break;
1595						case 'm':
1596							output += formatNumber('m', date.getMonth() + 1, 2);
1597							break;
1598						case 'M':
1599							output += formatName('M', date.getMonth(), monthNamesShort, monthNames);
1600							break;
1601						case 'y':
1602							output += (lookAhead('y') ? date.getFullYear() :
1603								(date.getYear() % 100 < 10 ? '0' : '') + date.getYear() % 100);
1604							break;
1605						case '@':
1606							output += date.getTime();
1607							break;
1608						case '!':
1609							output += date.getTime() * 10000 + this._ticksTo1970;
1610							break;
1611						case "'":
1612							if (lookAhead("'"))
1613								output += "'";
1614							else
1615								literal = true;
1616							break;
1617						default:
1618							output += format.charAt(iFormat);
1619					}
1620			}
1621		return output;
1622	},
1623
1624	/* Extract all possible characters from the date format. */
1625	_possibleChars: function (format) {
1626		var chars = '';
1627		var literal = false;
1628		// Check whether a format character is doubled
1629		var lookAhead = function(match) {
1630			var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
1631			if (matches)
1632				iFormat++;
1633			return matches;
1634		};
1635		for (var iFormat = 0; iFormat < format.length; iFormat++)
1636			if (literal)
1637				if (format.charAt(iFormat) == "'" && !lookAhead("'"))
1638					literal = false;
1639				else
1640					chars += format.charAt(iFormat);
1641			else
1642				switch (format.charAt(iFormat)) {
1643					case 'd': case 'm': case 'y': case '@':
1644						chars += '0123456789';
1645						break;
1646					case 'D': case 'M':
1647						return null; // Accept anything
1648					case "'":
1649						if (lookAhead("'"))
1650							chars += "'";
1651						else
1652							literal = true;
1653						break;
1654					default:
1655						chars += format.charAt(iFormat);
1656				}
1657		return chars;
1658	},
1659
1660	/* Get a setting value, defaulting if necessary. */
1661	_get: function(inst, name) {
1662		return inst.settings[name] !== undefined ?
1663			inst.settings[name] : this._defaults[name];
1664	},
1665
1666	/* Parse existing date and initialise date picker. */
1667	_setDateFromField: function(inst, noDefault) {
1668		if (inst.input.val() == inst.lastVal) {
1669			return;
1670		}
1671		var dateFormat = this._get(inst, 'dateFormat');
1672		var dates = inst.lastVal = inst.input ? inst.input.val() : null;
1673		var date, defaultDate;
1674		date = defaultDate = this._getDefaultDate(inst);
1675		var settings = this._getFormatConfig(inst);
1676		try {
1677			date = this.parseDate(dateFormat, dates, settings) || defaultDate;
1678		} catch (event) {
1679			this.log(event);
1680			dates = (noDefault ? '' : dates);
1681		}
1682		inst.selectedDay = date.getDate();
1683		inst.drawMonth = inst.selectedMonth = date.getMonth();
1684		inst.drawYear = inst.selectedYear = date.getFullYear();
1685		inst.currentDay = (dates ? date.getDate() : 0);
1686		inst.currentMonth = (dates ? date.getMonth() : 0);
1687		inst.currentYear = (dates ? date.getFullYear() : 0);
1688		this._adjustInstDate(inst);
1689	},
1690
1691	/* Retrieve the default date shown on opening. */
1692	_getDefaultDate: function(inst) {
1693		return this._restrictMinMax(inst,
1694			this._determineDate(inst, this._get(inst, 'defaultDate'), new Date()));
1695	},
1696
1697	/* A date may be specified as an exact value or a relative one. */
1698	_determineDate: function(inst, date, defaultDate) {
1699		var offsetNumeric = function(offset) {
1700			var date = new Date();
1701			date.setDate(date.getDate() + offset);
1702			return date;
1703		};
1704		var offsetString = function(offset) {
1705			try {
1706				return $.datepicker.parseDate($.datepicker._get(inst, 'dateFormat'),
1707					offset, $.datepicker._getFormatConfig(inst));
1708			}
1709			catch (e) {
1710				// Ignore
1711			}
1712			var date = (offset.toLowerCase().match(/^c/) ?
1713				$.datepicker._getDate(inst) : null) || new Date();
1714			var year = date.getFullYear();
1715			var month = date.getMonth();
1716			var day = date.getDate();
1717			var pattern = /([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g;
1718			var matches = pattern.exec(offset);
1719			while (matches) {
1720				switch (matches[2] || 'd') {
1721					case 'd' : case 'D' :
1722						day += parseInt(matches[1],10); break;
1723					case 'w' : case 'W' :
1724						day += parseInt(matches[1],10) * 7; break;
1725					case 'm' : case 'M' :
1726						month += parseInt(matches[1],10);
1727						day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
1728						break;
1729					case 'y': case 'Y' :
1730						year += parseInt(matches[1],10);
1731						day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
1732						break;
1733				}
1734				matches = pattern.exec(offset);
1735			}
1736			return new Date(year, month, day);
1737		};
1738		var newDate = (date == null || date === '' ? defaultDate : (typeof date == 'string' ? offsetString(date) :
1739			(typeof date == 'number' ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime()))));
1740		newDate = (newDate && newDate.toString() == 'Invalid Date' ? defaultDate : newDate);
1741		if (newDate) {
1742			newDate.setHours(0);
1743			newDate.setMinutes(0);
1744			newDate.setSeconds(0);
1745			newDate.setMilliseconds(0);
1746		}
1747		return this._daylightSavingAdjust(newDate);
1748	},
1749
1750	/* Handle switch to/from daylight saving.
1751	   Hours may be non-zero on daylight saving cut-over:
1752	   > 12 when midnight changeover, but then cannot generate
1753	   midnight datetime, so jump to 1AM, otherwise reset.
1754	   @param  date  (Date) the date to check
1755	   @return  (Date) the corrected date */
1756	_daylightSavingAdjust: function(date) {
1757		if (!date) return null;
1758		date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
1759		return date;
1760	},
1761
1762	/* Set the date(s) directly. */
1763	_setDate: function(inst, date, noChange) {
1764		var clear = !date;
1765		var origMonth = inst.selectedMonth;
1766		var origYear = inst.selectedYear;
1767		var newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date()));
1768		inst.selectedDay = inst.currentDay = newDate.getDate();
1769		inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
1770		inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
1771		if ((origMonth != inst.selectedMonth || origYear != inst.selectedYear) && !noChange)
1772			this._notifyChange(inst);
1773		this._adjustInstDate(inst);
1774		if (inst.input) {
1775			inst.input.val(clear ? '' : this._formatDate(inst));
1776		}
1777	},
1778
1779	/* Retrieve the date(s) directly. */
1780	_getDate: function(inst) {
1781		var startDate = (!inst.currentYear || (inst.input && inst.input.val() == '') ? null :
1782			this._daylightSavingAdjust(new Date(
1783			inst.currentYear, inst.currentMonth, inst.currentDay)));
1784			return startDate;
1785	},
1786
1787	/* Attach the onxxx handlers.  These are declared statically so
1788	 * they work with static code transformers like Caja.
1789	 */
1790	_attachHandlers: function(inst) {
1791		var stepMonths = this._get(inst, 'stepMonths');
1792		var id = '#' + inst.id.replace( /\\\\/g, "\\" );
1793		inst.dpDiv.find('[data-handler]').map(function () {
1794			var handler = {
1795				prev: function () {
1796					window['DP_jQuery_' + dpuuid].datepicker._adjustDate(id, -stepMonths, 'M');
1797				},
1798				next: function () {
1799					window['DP_jQuery_' + dpuuid].datepicker._adjustDate(id, +stepMonths, 'M');
1800				},
1801				hide: function () {
1802					window['DP_jQuery_' + dpuuid].datepicker._hideDatepicker();
1803				},
1804				today: function () {
1805					window['DP_jQuery_' + dpuuid].datepicker._gotoToday(id);
1806				},
1807				selectDay: function () {
1808					window['DP_jQuery_' + dpuuid].datepicker._selectDay(id, +this.getAttribute('data-month'), +this.getAttribute('data-year'), this);
1809					return false;
1810				},
1811				selectMonth: function () {
1812					window['DP_jQuery_' + dpuuid].datepicker._selectMonthYear(id, this, 'M');
1813					return false;
1814				},
1815				selectYear: function () {
1816					window['DP_jQuery_' + dpuuid].datepicker._selectMonthYear(id, this, 'Y');
1817					return false;
1818				}
1819			};
1820			$(this).bind(this.getAttribute('data-event'), handler[this.getAttribute('data-handler')]);
1821		});
1822	},
1823
1824	/* Generate the HTML for the current state of the date picker. */
1825	_generateHTML: function(inst) {
1826		var today = new Date();
1827		today = this._daylightSavingAdjust(
1828			new Date(today.getFullYear(), today.getMonth(), today.getDate())); // clear time
1829		var isRTL = this._get(inst, 'isRTL');
1830		var showButtonPanel = this._get(inst, 'showButtonPanel');
1831		var hideIfNoPrevNext = this._get(inst, 'hideIfNoPrevNext');
1832		var navigationAsDateFormat = this._get(inst, 'navigationAsDateFormat');
1833		var numMonths = this._getNumberOfMonths(inst);
1834		var showCurrentAtPos = this._get(inst, 'showCurrentAtPos');
1835		var stepMonths = this._get(inst, 'stepMonths');
1836		var isMultiMonth = (numMonths[0] != 1 || numMonths[1] != 1);
1837		var currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) :
1838			new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
1839		var minDate = this._getMinMaxDate(inst, 'min');
1840		var maxDate = this._getMinMaxDate(inst, 'max');
1841		var drawMonth = inst.drawMonth - showCurrentAtPos;
1842		var drawYear = inst.drawYear;
1843		if (drawMonth < 0) {
1844			drawMonth += 12;
1845			drawYear--;
1846		}
1847		if (maxDate) {
1848			var maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),
1849				maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate()));
1850			maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
1851			while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {
1852				drawMonth--;
1853				if (drawMonth < 0) {
1854					drawMonth = 11;
1855					drawYear--;
1856				}
1857			}
1858		}
1859		inst.drawMonth = drawMonth;
1860		inst.drawYear = drawYear;
1861		var prevText = this._get(inst, 'prevText');
1862		prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText,
1863			this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),
1864			this._getFormatConfig(inst)));
1865		var prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?
1866			'<a class="ui-datepicker-prev ui-corner-all" data-handler="prev" data-event="click"' +
1867			' title="' + prevText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'e' : 'w') + '">' + prevText + '</span></a>' :
1868			(hideIfNoPrevNext ? '' : '<a class="ui-datepicker-prev ui-corner-all ui-state-disabled" title="'+ prevText +'"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'e' : 'w') + '">' + prevText + '</span></a>'));
1869		var nextText = this._get(inst, 'nextText');
1870		nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText,
1871			this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),
1872			this._getFormatConfig(inst)));
1873		var next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?
1874			'<a class="ui-datepicker-next ui-corner-all" data-handler="next" data-event="click"' +
1875			' title="' + nextText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'w' : 'e') + '">' + nextText + '</span></a>' :
1876			(hideIfNoPrevNext ? '' : '<a class="ui-datepicker-next ui-corner-all ui-state-disabled" title="'+ nextText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'w' : 'e') + '">' + nextText + '</span></a>'));
1877		var currentText = this._get(inst, 'currentText');
1878		var gotoDate = (this._get(inst, 'gotoCurrent') && inst.currentDay ? currentDate : today);
1879		currentText = (!navigationAsDateFormat ? currentText :
1880			this.formatDate(currentText, gotoDate, this._getFormatConfig(inst)));
1881		var controls = (!inst.inline ? '<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" data-handler="hide" data-event="click">' +
1882			this._get(inst, 'closeText') + '</button>' : '');
1883		var buttonPanel = (showButtonPanel) ? '<div class="ui-datepicker-buttonpane ui-widget-content">' + (isRTL ? controls : '') +
1884			(this._isInRange(inst, gotoDate) ? '<button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all" data-handler="today" data-event="click"' +
1885			'>' + currentText + '</button>' : '') + (isRTL ? '' : controls) + '</div>' : '';
1886		var firstDay = parseInt(this._get(inst, 'firstDay'),10);
1887		firstDay = (isNaN(firstDay) ? 0 : firstDay);
1888		var showWeek = this._get(inst, 'showWeek');
1889		var dayNames = this._get(inst, 'dayNames');
1890		var dayNamesShort = this._get(inst, 'dayNamesShort');
1891		var dayNamesMin = this._get(inst, 'dayNamesMin');
1892		var monthNames = this._get(inst, 'monthNames');
1893		var monthNamesShort = this._get(inst, 'monthNamesShort');
1894		var beforeShowDay = this._get(inst, 'beforeShowDay');
1895		var showOtherMonths = this._get(inst, 'showOtherMonths');
1896		var selectOtherMonths = this._get(inst, 'selectOtherMonths');
1897		var calculateWeek = this._get(inst, 'calculateWeek') || this.iso8601Week;
1898		var defaultDate = this._getDefaultDate(inst);
1899		var html = '';
1900		for (var row = 0; row < numMonths[0]; row++) {
1901			var group = '';
1902			this.maxRows = 4;
1903			for (var col = 0; col < numMonths[1]; col++) {
1904				var selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));
1905				var cornerClass = ' ui-corner-all';
1906				var calender = '';
1907				if (isMultiMonth) {
1908					calender += '<div class="ui-datepicker-group';
1909					if (numMonths[1] > 1)
1910						switch (col) {
1911							case 0: calender += ' ui-datepicker-group-first';
1912								cornerClass = ' ui-corner-' + (isRTL ? 'right' : 'left'); break;
1913							case numMonths[1]-1: calender += ' ui-datepicker-group-last';
1914								cornerClass = ' ui-corner-' + (isRTL ? 'left' : 'right'); break;
1915							default: calender += ' ui-datepicker-group-middle'; cornerClass = ''; break;
1916						}
1917					calender += '">';
1918				}
1919				calender += '<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix' + cornerClass + '">' +
1920					(/all|left/.test(cornerClass) && row == 0 ? (isRTL ? next : prev) : '') +
1921					(/all|right/.test(cornerClass) && row == 0 ? (isRTL ? prev : next) : '') +
1922					this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
1923					row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers
1924					'</div><table class="ui-datepicker-calendar"><thead>' +
1925					'<tr>';
1926				var thead = (showWeek ? '<th class="ui-datepicker-week-col">' + this._get(inst, 'weekHeader') + '</th>' : '');
1927				for (var dow = 0; dow < 7; dow++) { // days of the week
1928					var day = (dow + firstDay) % 7;
1929					thead += '<th' + ((dow + firstDay + 6) % 7 >= 5 ? ' class="ui-datepicker-week-end"' : '') + '>
1929' +
1930						'<span title="' + dayNames[day] + '">' + dayNamesMin[day] + '</span></th>';
1931				}
1932				calender += thead + '</tr></thead><tbody>';
1933				var daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
1934				if (drawYear == inst.selectedYear && drawMonth == inst.selectedMonth)
1935					inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);
1936				var leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
1937				var curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate
1938				var numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043)
1939				this.maxRows = numRows;
1940				var printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
1941				for (var dRow = 0; dRow < numRows; dRow++) { // create date picker rows
1942					calender += '<tr>';
1943					var tbody = (!showWeek ? '' : '<td class="ui-datepicker-week-col">' +
1944						this._get(inst, 'calculateWeek')(printDate) + '</td>');
1945					for (var dow = 0; dow < 7; dow++) { // create date picker days
1946						var daySettings = (beforeShowDay ?
1947							beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, '']);
1948						var otherMonth = (printDate.getMonth() != drawMonth);
1949						var unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] ||
1950							(minDate && printDate < minDate) || (maxDate && printDate > maxDate);
1951						tbody += '<td class="' +
1952							((dow + firstDay + 6) % 7 >= 5 ? ' ui-datepicker-week-end' : '') + // highlight weekends
1953							(otherMonth ? ' ui-datepicker-other-month' : '') + // highlight days from other months
1954							((printDate.getTime() == selectedDate.getTime() && drawMonth == inst.selectedMonth && inst._keyEvent) || // user pressed key
1955							(defaultDate.getTime() == printDate.getTime() && defaultDate.getTime() == selectedDate.getTime()) ?
1956							// or defaultDate is current printedDate and defaultDate is selectedDate
1957							' ' + this._dayOverClass : '') + // highlight selected day
1958							(unselectable ? ' ' + this._unselectableClass + ' ui-state-disabled': '') +  // highlight unselectable days
1959							(otherMonth && !showOtherMonths ? '' : ' ' + daySettings[1] + // highlight custom dates
1960							(printDate.getTime() == currentDate.getTime() ? ' ' + this._currentClass : '') + // highlight selected day
1961							(printDate.getTime() == today.getTime() ? ' ui-datepicker-today' : '')) + '"' + // highlight today (if different)
1962							((!otherMonth || showOtherMonths) && daySettings[2] ? ' title="' + daySettings[2] + '"' : '') + // cell title
1963							(unselectable ? '' : ' data-handler="selectDay" data-event="click" data-month="' + printDate.getMonth() + '" data-year="' + printDate.getFullYear() + '"') + '>' + // actions
1964							(otherMonth && !showOtherMonths ? '&#xa0;' : // display for other months
1965							(unselectable ? '<span class="ui-state-default">' + printDate.getDate() + '</span>' : '<a class="ui-state-default' +
1966							(printDate.getTime() == today.getTime() ? ' ui-state-highlight' : '') +
1967							(printDate.getTime() == currentDate.getTime() ? ' ui-state-active' : '') + // highlight selected day
1968							(otherMonth ? ' ui-priority-secondary' : '') + // distinguish dates from other months
1969							'" href="#">' + printDate.getDate() + '</a>')) + '</td>'; // display selectable date
1970						printDate.setDate(printDate.getDate() + 1);
1971						printDate = this._daylightSavingAdjust(printDate);
1972					}
1973					calender += tbody + '</tr>';
1974				}
1975				drawMonth++;
1976				if (drawMonth > 11) {
1977					drawMonth = 0;
1978					drawYear++;
1979				}
1980				calender += '</tbody></table>' + (isMultiMonth ? '</div>' +
1981							((numMonths[0] > 0 && col == numMonths[1]-1) ? '<div class="ui-datepicker-row-break"></div>' : '') : '');
1982				group += calender;
1983			}
1984			html += group;
1985		}
1986		html += buttonPanel + ($.ui.ie6 && !inst.inline ?
1987			'<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>' : '');
1988		inst._keyEvent = false;
1989		return html;
1990	},
1991
1992	/* Generate the month and year header. */
1993	_generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate,
1994			secondary, monthNames, monthNamesShort) {
1995		var changeMonth = this._get(inst, 'changeMonth');
1996		var changeYear = this._get(inst, 'changeYear');
1997		var showMonthAfterYear = this._get(inst, 'showMonthAfterYear');
1998		var html = '<div class="ui-datepicker-title">';
1999		var monthHtml = '';
2000		// month selection
2001		if (secondary || !changeMonth)
2002			monthHtml += '<span class="ui-datepicker-month">' + monthNames[drawMonth] + '</span>';
2003		else {
2004			var inMinYear = (minDate && minDate.getFullYear() == drawYear);
2005			var inMaxYear = (maxDate && maxDate.getFullYear() == drawYear);
2006			monthHtml += '<select class="ui-datepicker-month" data-handler="selectMonth" data-event="change">';
2007			for (var month = 0; month < 12; month++) {
2008				if ((!inMinYear || month >= minDate.getMonth()) &&
2009						(!inMaxYear || month <= maxDate.getMonth()))
2010					monthHtml += '<option value="' + month + '"' +
2011						(month == drawMonth ? ' selected="selected"' : '') +
2012						'>' + monthNamesShort[month] + '</option>';
2013			}
2014			monthHtml += '</select>';
2015		}
2016		if (!showMonthAfterYear)
2017			html += monthHtml + (secondary || !(changeMonth && changeYear) ? '&#xa0;' : '');
2018		// year selection
2019		if ( !inst.yearshtml ) {
2020			inst.yearshtml = '';
2021			if (secondary || !changeYear)
2022				html += '<span class="ui-datepicker-year">' + drawYear + '</span>';
2023			else {
2024				// determine range of years to display
2025				var years = this._get(inst, 'yearRange').split(':');
2026				var thisYear = new Date().getFullYear();
2027				var determineYear = function(value) {
2028					var year = (value.match(/c[+-].*/) ? drawYear + parseInt(value.substring(1), 10) :
2029						(value.match(/[+-].*/) ? thisYear + parseInt(value, 10) :
2030						parseInt(value, 10)));
2031					return (isNaN(year) ? thisYear : year);
2032				};
2033				var year = determineYear(years[0]);
2034				var endYear = Math.max(year, determineYear(years[1] || ''));
2035				year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
2036				endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
2037				inst.yearshtml += '<select class="ui-datepicker-year" data-handler="selectYear" data-event="change">';
2038				for (; year <= endYear; year++) {
2039					inst.yearshtml += '<option value="' + year + '"' +
2040						(year == drawYear ? ' selected="selected"' : '') +
2041						'>' + year + '</option>';
2042				}
2043				inst.yearshtml += '</select>';
2044
2045				html += inst.yearshtml;
2046				inst.yearshtml = null;
2047			}
2048		}
2049		html += this._get(inst, 'yearSuffix');
2050		if (showMonthAfterYear)
2051			html += (secondary || !(changeMonth && changeYear) ? '&#xa0;' : '') + monthHtml;
2052		html += '</div>'; // Close datepicker_header
2053		return html;
2054	},
2055
2056	/* Adjust one of the date sub-fields. */
2057	_adjustInstDate: function(inst, offset, period) {
2058		var year = inst.drawYear + (period == 'Y' ? offset : 0);
2059		var month = inst.drawMonth + (period == 'M' ? offset : 0);
2060		var day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) +
2061			(period == 'D' ? offset : 0);
2062		var date = this._restrictMinMax(inst,
2063			this._daylightSavingAdjust(new Date(year, month, day)));
2064		inst.selectedDay = date.getDate();
2065		inst.drawMonth = inst.selectedMonth = date.getMonth();
2066		inst.drawYear = inst.selectedYear = date.getFullYear();
2067		if (period == 'M' || period == 'Y')
2068			this._notifyChange(inst);
2069	},
2070
2071	/* Ensure a date is within any min/max bounds. */
2072	_restrictMinMax: function(inst, date) {
2073		var minDate = this._getMinMaxDate(inst, 'min');
2074		var maxDate = this._getMinMaxDate(inst, 'max');
2075		var newDate = (minDate && date < minDate ? minDate : date);
2076		newDate = (maxDate && newDate > maxDate ? maxDate : newDate);
2077		return newDate;
2078	},
2079
2080	/* Notify change of month/year. */
2081	_notifyChange: function(inst) {
2082		var onChange = this._get(inst, 'onChangeMonthYear');
2083		if (onChange)
2084			onChange.apply((inst.input ? inst.input[0] : null),
2085				[inst.selectedYear, inst.selectedMonth + 1, inst]);
2086	},
2087
2088	/* Determine the number of months to show. */
2089	_getNumberOfMonths: function(inst) {
2090		var numMonths = this._get(inst, 'numberOfMonths');
2091		return (numMonths == null ? [1, 1] : (typeof numMonths == 'number' ? [1, numMonths] : numMonths));
2092	},
2093
2094	/* Determine the current maximum date - ensure no time components are set. */
2095	_getMinMaxDate: function(inst, minMax) {
2096		return this._determineDate(inst, this._get(inst, minMax + 'Date'), null);
2097	},
2098
2099	/* Find the number of days in a given month. */
2100	_getDaysInMonth: function(year, month) {
2101		return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate();
2102	},
2103
2104	/* Find the day of the week of the first of a month. */
2105	_getFirstDayOfMonth: function(year, month) {
2106		return new Date(year, month, 1).getDay();
2107	},
2108
2109	/* Determines if we should allow a "next/prev" month display change. */
2110	_canAdjustMonth: function(inst, offset, curYear, curMonth) {
2111		var numMonths = this._getNumberOfMonths(inst);
2112		var date = this._daylightSavingAdjust(new Date(curYear,
2113			curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1));
2114		if (offset < 0)
2115			date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
2116		return this._isInRange(inst, date);
2117	},
2118
2119	/* Is the given date in the accepted range? */
2120	_isInRange: function(inst, date) {
2121		var minDate = this._getMinMaxDate(inst, 'min');
2122		var maxDate = this._getMinMaxDate(inst, 'max');
2123		return ((!minDate || date.getTime() >= minDate.getTime()) &&
2124			(!maxDate || date.getTime() <= maxDate.getTime()));
2125	},
2126
2127	/* Provide the configuration settings for formatting/parsing. */
2128	_getFormatConfig: function(inst) {
2129		var shortYearCutoff = this._get(inst, 'shortYearCutoff');
2130		shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
2131			new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
2132		return {shortYearCutoff: shortYearCutoff,
2133			dayNamesShort: this._get(inst, 'dayNamesShort'), dayNames: this._get(inst, 'dayNames'),
2134			monthNamesShort: this._get(inst, 'monthNamesShort'), monthNames: this._get(inst, 'monthNames')};
2135	},
2136
2137	/* Format the given date for display. */
2138	_formatDate: function(inst, day, month, year) {
2139		if (!day) {
2140			inst.currentDay = inst.selectedDay;
2141			inst.currentMonth = inst.selectedMonth;
2142			inst.currentYear = inst.selectedYear;
2143		}
2144		var date = (day ? (typeof day == 'object' ? day :
2145			this._daylightSavingAdjust(new Date(year, month, day))) :
2146			this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
2147		return this.formatDate(this._get(inst, 'dateFormat'), date, this._getFormatConfig(inst));
2148	}
2149});
2150
2151/*
2152 * Bind hover events for datepicker elements.
2153 * Done via delegate so the binding only occurs once in the lifetime of the parent div.
2154 * Global instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
2155 */
2156function bindHover(dpDiv) {
2157	var selector = 'button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a';
2158	return dpDiv.delegate(selector, 'mouseout', function() {
2159			$(this).removeClass('ui-state-hover');
2160			if (this.className.indexOf('ui-datepicker-prev') != -1) $(this).removeClass('ui-datepicker-prev-hover');
2161			if (this.className.indexOf('ui-datepicker-next') != -1) $(this).removeClass('ui-datepicker-next-hover');
2162		})
2163		.delegate(selector, 'mouseover', function(){
2164			if (!$.datepicker._isDisabledDatepicker( instActive.inline ? dpDiv.parent()[0] : instActive.input[0])) {
2165				$(this).parents('.ui-datepicker-calendar').find('a').removeClass('ui-state-hover');
2166				$(this).addClass('ui-state-hover');
2167				if (this.className.indexOf('ui-datepicker-prev') != -1) $(this).addClass('ui-datepicker-prev-hover');
2168				if (this.className.indexOf('ui-datepicker-next') != -1) $(this).addClass('ui-datepicker-next-hover');
2169			}
2170		});
2171}
2172
2173/* jQuery extend now ignores nulls! */
2174function extendRemove(target, props) {
2175	$.extend(target, props);
2176	for (var name in props)
2177		if (props[name] == null || props[name] == undefined)
2178			target[name] = props[name];
2179	return target;
2180};
2181
2182/* Invoke the datepicker functionality.
2183   @param  options  string - a command, optionally followed by additional parameters or
2184	                Object - settings for attaching new datepicker functionality
2185   @return  jQuery object */
2186$.fn.datepicker = function(options){
2187
2188	/* Verify an empty collection wasn't passed - Fixes #6976 */
2189	if ( !this.length ) {
2190		return this;
2191	}
2192
2193	/* Initialise the date picker. */
2194	if (!$.datepicker.initialized) {
2195		$(document).mousedown($.datepicker._checkExternalClick).
2196			find(document.body).append($.datepicker.dpDiv);
2197		$.datepicker.initialized = true;
2198	}
2199
2200	var otherArgs = Array.prototype.slice.call(arguments, 1);
2201	if (typeof options == 'string' && (options == 'isDisabled' || options == 'getDate' || options == 'widget'))
2202		return $.datepicker['_' + options + 'Datepicker'].
2203			apply($.datepicker, [this[0]].concat(otherArgs));
2204	if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string')
2205		return $.datepicker['_' + options + 'Datepicker'].
2206			apply($.datepicker, [this[0]].concat(otherArgs));
2207	return this.each(function() {
2208		typeof options == 'string' ?
2209			$.datepicker['_' + options + 'Datepicker'].
2210				apply($.datepicker, [this].concat(otherArgs)) :
2211			$.datepicker._attachDatepicker(this, options);
2212	});
2213};
2214
2215$.datepicker = new Datepicker(); // singleton instance
2216$.datepicker.initialized = false;
2217$.datepicker.uuid = new Date().getTime();
2218$.datepicker.version = "1.9.2";
2219
2220// Workaround for #4055
2221// Add another global to avoid noConflict issues with inline event handlers
2222window['DP_jQuery_' + dpuuid] = $;
2223
2224})(jQuery);
2225;
2226/*!
2227 * @copyright Copyright (c) 2017 IcoMoon.io
2228 * @license   Licensed under MIT license
2229 *            See https://github.com/Keyamoon/svgxuse
2230 * @version   1.2.6
2231 */
2232/*jslint browser: true */
2233/*global XDomainRequest, MutationObserver, window */
2234(function () {
2235    "use strict";
2236    if (typeof window !== "undefined" && window.addEventListener) {
2237        var cache = Object.create(null); // holds xhr objects to prevent multiple requests
2238        var checkUseElems;
2239        var tid; // timeout id
2240        var debouncedCheck = function () {
2241            clearTimeout(tid);
2242            tid = setTimeout(checkUseElems, 100);
2243        };
2244        var unobserveChanges = function () {
2245            return;
2246        };
2247        var observeChanges = function () {
2248            var observer;
2249            window.addEventListener("resize", debouncedCheck, false);
2250            window.addEventListener("orientationchange", debouncedCheck, false);
2251            if (window.MutationObserver) {
2252                observer = new MutationObserver(debouncedCheck);
2253                observer.observe(document.documentElement, {
2254                    childList: true,
2255                    subtree: true,
2256                    attributes: true
2257                });
2258                unobserveChanges = function () {
2259                    try {
2260                        observer.disconnect();
2261                        window.removeEventListener("resize", debouncedCheck, false);
2262                        window.removeEventListener("orientationchange", debouncedCheck, false);
2263                    } catch (ignore) {}
2264                };
2265            } else {
2266                document.documentElement.addEventListener("DOMSubtreeModified", debouncedCheck, false);
2267                unobserveChanges = function () {
2268                    document.documentElement.removeEventListener("DOMSubtreeModified", debouncedCheck, false);
2269                    window.removeEventListener("resize", debouncedCheck, false);
2270                    window.removeEventListener("orientationchange", debouncedCheck, false);
2271                };
2272            }
2273        };
2274        var createRequest = function (url) {
2275            // In IE 9, cross origin requests can only be sent using XDomainRequest.
2276            // XDomainRequest would fail if CORS headers are not set.
2277            // Therefore, XDomainRequest should only be used with cross origin requests.
2278            function getOrigin(loc) {
2279                var a;
2280                if (loc.protocol !== undefined) {
2281                    a = loc;
2282                } else {
2283                    a = document.createElement("a");
2284                    a.href = loc;
2285                }
2286                return a.protocol.replace(/:/g, "") + a.host;
2287            }
2288            var Request;
2289            var origin;
2290            var origin2;
2291            if (window.XMLHttpRequest) {
2292                Request = new XMLHttpRequest();
2293                origin = getOrigin(location);
2294                origin2 = getOrigin(url);
2295                if (Request.withCredentials === undefined && origin2 !== "" && origin2 !== origin) {
2296                    Request = XDomainRequest || undefined;
2297                } else {
2298                    Request = XMLHttpRequest;
2299                }
2300            }
2301            return Request;
2302        };
2303        var xlinkNS = "http://www.w3.org/1999/xlink";
2304        checkUseElems = function () {
2305            var base;
2306            var bcr;
2307            var fallback = ""; // optional fallback URL in case no base path to SVG file was given and no symbol definition was found.
2308            var hash;
2309            var href;
2310            var i;
2311            var inProgressCount = 0;
2312            var isHidden;
2313            var Request;
2314            var url;
2315            var uses;
2316            var xhr;
2317            function observeIfDone() {
2318                // If done with making changes, start watching for chagnes in DOM again
2319                inProgressCount -= 1;
2320                if (inProgressCount === 0) { // if all xhrs were resolved
2321                    unobserveChanges(); // make sure to remove old handlers
2322                    observeChanges(); // watch for changes to DOM
2323                }
2324            }
2325            function attrUpdateFunc(spec) {
2326                return function () {
2327                    if (cache[spec.base] !== true) {
2328                        spec.useEl.setAttributeNS(xlinkNS, "xlink:href", "#" + spec.hash);
2329                        if (spec.useEl.hasAttribute("href")) {
2330                            spec.useEl.setAttribute("href", "#" + spec.hash);
2331                        }
2332                    }
2333                };
2334            }
2335            function onloadFunc(xhr) {
2336                return function () {
2337                    var body = document.body;
2338                    var x = document.createElement("x");
2339                    var svg;
2340                    xhr.onload = null;
2341                    x.innerHTML = xhr.responseText;
2342                    svg = x.getElementsByTagName("svg")[0];
2343                    if (svg) {
2344                        svg.setAttribute("aria-hidden", "true");
2345                        svg.style.position = "absolute";
2346                        svg.style.width = 0;
2347                        svg.style.height = 0;
2348                        svg.style.overflow = "hidden";
2349                        body.insertBefore(svg, body.firstChild);
2350                    }
2351                    observeIfDone();
2352                };
2353            }
2354            function onErrorTimeout(xhr) {
2355                return function () {
2356                    xhr.onerror = null;
2357                    xhr.ontimeout = null;
2358                    observeIfDone();
2359                };
2360            }
2361            unobserveChanges(); // stop watching for changes to DOM
2362            // find all use elements
2363            uses = document.getElementsByTagName("use");
2364            for (i = 0; i < uses.length; i += 1) {
2365                try {
2366                    bcr = uses[i].getBoundingClientRect();
2367                } catch (ignore) {
2368                    // failed to get bounding rectangle of the use element
2369                    bcr = false;
2370                }
2371                href = uses[i].getAttribute("href")
2372                        || uses[i].getAttributeNS(xlinkNS, "href")
2373                        || uses[i].getAttribute("xlink:href");
2374                if (href && href.split) {
2375                    url = href.split("#");
2376                } else {
2377                    url = ["", ""];
2378                }
2379                base = url[0];
2380                hash = url[1];
2381                isHidden = bcr && bcr.left === 0 && bcr.right === 0 && bcr.top === 0 && bcr.bottom === 0;
2382                if (bcr && bcr.width === 0 && bcr.height === 0 && !isHidden) {
2383                    // the use element is empty
2384                    // if there is a reference to an external SVG, try to fetch it
2385                    // use the optional fallback URL if there is no reference to an external SVG
2386                    if (fallback && !base.length && hash && !document.getElementById(hash)) {
2387                        base = fallback;
2388                    }
2389                    if (uses[i].hasAttribute("href")) {
2390                        uses[i].setAttributeNS(xlinkNS, "xlink:href", href);
2391                    }
2392                    if (base.length) {
2393                        // schedule updating xlink:href
2394                        xhr = cache[base];
2395                        if (xhr !== true) {
2396                            // true signifies that prepending the SVG was not required
2397                            setTimeout(attrUpdateFunc({
2398                                useEl: uses[i],
2399                                base: base,
2400                                hash: hash
2401                            }), 0);
2402                        }
2403                        if (xhr === undefined) {
2404                            Request = createRequest(base);
2405                            if (Request !== undefined) {
2406                                xhr = new Request();
2407                                cache[base] = xhr;
2408                                xhr.onload = onloadFunc(xhr);
2409                                xhr.onerror = onErrorTimeout(xhr);
2410                                xhr.ontimeout = onErrorTimeout(xhr);
2411                                xhr.open("GET", base);
2412                                xhr.send();
2413                                inProgressCount += 1;
2414                            }
2415                        }
2416                    }
2417                } else {
2418                    if (!isHidden) {
2419                        if (cache[base] === undefined) {
2420                            // remember this URL if the use element was not empty and no request was sent
2421                            cache[base] = true;
2422                        } else if (cache[base].onload) {
2423                            // if it turns out that prepending the SVG is not necessary,
2424                            // abort the in-progress xhr.
2425                            cache[base].abort();
2426                            delete cache[base].onload;
2427                            cache[base] = true;
2428                        }
2429                    } else if (base.length && cache[base]) {
2430                        setTimeout(attrUpdateFunc({
2431                            useEl: uses[i],
2432                            base: base,
2433                            hash: hash
2434                        }), 0);
2435                    }
2436                }
2437            }
2438            uses = "";
2439            inProgressCount += 1;
2440            observeIfDone();
2441        };
2442        var winLoad;
2443        winLoad = function () {
2444            window.removeEventListener("load", winLoad, false); // to prevent memory leaks
2445            tid = setTimeout(checkUseElems, 0);
2446        };
2447        if (document.readyState !== "complete") {
2448            // The load event fires when all resources have finished loading, which allows detecting whether SVG use elements are empty.
2449            window.addEventListener("load", winLoad, false);
2450        } else {
2451            // No need to add a listener if the document is already loaded, initialize immediately.
2452            winLoad();
2453        }
2454    }
2455}());
2456;
2457/*!
2458 * JavaScript Cookie v2.0.3
2459 * https://github.com/js-cookie/js-cookie
2460 *
2461 * Copyright 2006, 2015 Klaus Hartl & Fagner Brack
2462 * Released under the MIT license
2463 */
2464(function (factory) {
2465    if (typeof define === 'function' && define.amd) {
2466        define(factory);
2467    } else if (typeof exports === 'object') {
2468        module.exports = factory();
2469    } else {
2470        var _OldCookies = window.Cookies;
2471        var api = window.Cookies = factory();
2472        api.noConflict = function () {
2473            window.Cookies = _OldCookies;
2474            return api;
2475        };
2476    }
2477}(function () {
2478    function extend() {
2479        var i = 0;
2480        var result = {};
2481        for (; i < arguments.length; i++) {
2482            var attributes = arguments[i];
2483            for (var key in attributes) {
2484                result[key] = attributes[key];
2485            }
2486        }
2487        return result;
2488    }
2489
2490    function init(converter) {
2491        function api(key, value, attributes) {
2492            var result;
2493
2494            // Write
2495
2496            if (arguments.length > 1) {
2497                attributes = extend({
2498                    path: '/'
2499                }, api.defaults, attributes);
2500
2501                if (typeof attributes.expires === 'number') {
2502                    var expires = new Date();
2503                    expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5);
2504                    attributes.expires = expires;
2505                }
2506
2507                try {
2508                    result = JSON.stringify(value);
2509                    if (/^[\{\[]/.test(result)) {
2510                        value = result;
2511                    }
2512                } catch (e) { }
2513
2514                value = encodeURIComponent(String(value));
2515                value = value.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
2516
2517                key = encodeURIComponent(String(key));
2518                key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent);
2519                key = key.replace(/[\(\)]/g, escape);
2520
2521                return (document.cookie = [
2522					key, '=', value,
2523					attributes.expires && '; expires=' + attributes.expires.toUTCString(), // use expires attribute, max-age is not supported by IE
2524					attributes.path && '; path=' + attributes.path,
2525					attributes.domain && '; domain=' + attributes.domain,
2526					attributes.secure ? '; secure' : ''
2527                ].join(''));
vendor: 1,584 bytes, lines 2528-2577
2528            }
2529
2530            // Read
2531
2532            if (!key) {
2533                result = {};
2534            }
2535
2536            // To prevent the for loop in the first place assign an empty array
2537            // in case there are no cookies at all. Also prevents odd result when
2538            // calling "get()"
2539            var cookies = document.cookie ? document.cookie.split('; ') : [];
2540            var rdecode = /(%[0-9A-Z]{2})+/g;
2541            var i = 0;
2542
2543            for (; i < cookies.length; i++) {
2544                var parts = cookies[i].split('=');
2545                var name = parts[0].replace(rdecode, decodeURIComponent);
2546                var cookie = parts.slice(1).join('=');
2547
2548                if (cookie.charAt(0) === '"') {
2549                    cookie = cookie.slice(1, -1);
2550                }
2551
2552                try {
2553                    cookie = converter && converter(cookie, name) || cookie.replace(rdecode, decodeURIComponent);
2554
2555                    if (this.json) {
2556                        try {
2557                            cookie = JSON.parse(cookie);
2558                        } catch (e) { }
2559                    }
2560
2561                    if (key === name) {
2562                        result = cookie;
2563                        break;
2564                    }
2565
2566                    if (!key) {
2567                        result[name] = cookie;
2568                    }
2569                } catch (e) { }
2570            }
2571
2572            return result;
2573        }
2574
2575        api.get = api.set = api;
2576        api.getJSON = function () {
2577            return api.apply({
2578                json: true
2579            }, [].slice.call(arguments));
2580        };
2581        api.defaults = {};
2582
2583        api.remove = function (key, attributes) {
2584            api(key, '', extend(attributes, {
2585                expires: -1
2586            }));
2587        };
2588
2589        api.withConverter = init;
2590
2591        return api;
2592    }
2593
2594    return init();
2595}));;
2596/*
2597 Copyright 2010 Redfin Corporation
2598 Licensed under the Apache License, Version 2.0:
2599 http://www.apache.org/licenses/LICENSE-2.0
2600 */
2601
2602// Wait for Google Maps to be available before initializing
2603(function initFastMarkerOverlay() {
2604  'use strict';
2605  
2606  if (typeof google === 'undefined' || typeof google.maps === 'undefined') {
2607    // Google Maps not loaded yet, wait and try again
2608    setTimeout(initFastMarkerOverlay, 50);
2609    return;
2610  }
2611
2612  window.com = window.com || {};
2613  window.com.redfin = window.com.redfin || {};
2614
2615  /* Construct a new FastMarkerOverlay layer for a V2 map
2616   * @constructor
2617   * @param {google.maps.Map} map the map to which we'll add markers
2618   * @param {Array.<com.redfin.FastMarker>} markers the array of markers to display on the map
2619   */
2620  com.redfin.FastMarkerOverlay = function(map, markers, callback) {
2621    this.setMap(map);
2622    this._markers = markers;
2623    this._callback = callback;
2624    this._cachedZoom = null;
2625    this._cachedHTML = null;
2626    this._markerCache = null;
2627  }
2628
2629  com.redfin.FastMarkerOverlay.prototype = new google.maps.OverlayView();
2630
2631com.redfin.FastMarkerOverlay.prototype.onAdd = function() {
2632  this._div = document.createElement("div");
2633  var panes = this.getPanes();
2634
2635  // HACK: google maps z-index overlay hack
2636  $(panes.overlayLayer).parent().addClass('marker-overlay');
2637
2638  panes.overlayLayer.appendChild(this._div);
2639}
2640
2641/* Copy our data to a new FastMarkerOverlay
2642 * @param {google.maps.Map} map the map to which the copy will add markers
2643 * @return {FastMarkerOverlay} Copy of FastMarkerOverlay
2644 */
2645com.redfin.FastMarkerOverlay.prototype.copy = function(map) {
2646  var markers = this._markers;
2647  var i = markers.length;
2648  var markersCopy = new Array(i);
2649  while (i--) {
2650    markersCopy[i] = markers[i].copy();
2651  }
2652  return new com.redfin.FastMarkerOverlay(map, markers);
2653};
2654
2655/* Draw the FastMarkerOverlay based on the current projection and zoom level; called by Gmaps */
2656com.redfin.FastMarkerOverlay.prototype.draw = function() {
2657  // if already removed, never draw
2658  if (!this._div) return;
2659
2660  var overlayProjection = this.getProjection();
2661  var map = this.getMap();
2662  var currentZoom = map ? map.getZoom() : null;
2663  var markers = this._markers;
2664  var length = markers.length;
2665  
2666  // Build marker metadata cache if not exists or markers changed
2667  if (!this._markerCache || this._markerCache.length !== length) {
2668    this._markerCache = new Array(length);
2669    for (var i = 0; i < length; i++) {
2670      var marker = markers[i];
2671      // Cache static HTML parts that don't change with position
2672      var classAttr = marker._divClassName ? ' class="' + marker._divClassName + '"' : '';
2673      var zIndexStyle = marker._zIndex ? 'z-index:' + marker._zIndex + ';' : '';
2674      var content = marker._htmlTextArray.join('');
2675      
2676      this._markerCache[i] = {
2677        id: marker._id,
2678        latLng: marker._latLng,
2679        leftOffset: marker._leftOffset,
2680        topOffset: marker._topOffset,
2681        classAttr: classAttr,
2682        zIndexStyle: zIndexStyle,
2683        content: content
2684      };
2685    }
2686    this._cachedZoom = null; // Force full redraw when cache rebuilt
2687  }
2688  
2689  // On zoom change or first draw, rebuild everything
2690  // On pan only, we could optimize further but position changes require full rebuild
2691  var textArray = new Array(length * 8);
2692  var arrayIndex = 0;
2693  
2694  for (var i = 0; i < length; i++) {
2695    var cached = this._markerCache[i];
2696    var divPixel = overlayProjection.fromLatLngToDivPixel(cached.latLng);
2697    var left = divPixel.x + cached.leftOffset;
2698    var top = divPixel.y + cached.topOffset;
2699    
2700    // Build complete marker HTML with cached parts
2701    textArray[arrayIndex++] = '<div style="position:absolute;
2701left:';
2702    textArray[arrayIndex++] = left;
2703    textArray[arrayIndex++] = 'px;top:';
2704    textArray[arrayIndex++] = top;
2705    textArray[arrayIndex++] = 'px;';
2706    textArray[arrayIndex++] = cached.zIndexStyle;
2707    textArray[arrayIndex++] = '"';
2708    textArray[arrayIndex++] = cached.classAttr;
2709    textArray[arrayIndex++] = ' id="';
2710    textArray[arrayIndex++] = cached.id;
2711    textArray[arrayIndex++] = '">';
2712    textArray[arrayIndex++] = cached.content;
2713    textArray[arrayIndex++] = '</div>';
2714  }
2715
2716  textArray.length = arrayIndex;
2717  this._div.innerHTML = textArray.join('');
2718  this._div.className = 'marker-container';
2719  this._cachedZoom = currentZoom;
2720
2721  if (typeof this._callback === 'function') {
2722    this._callback();
2723  }
2724}
2725
2726/** Hide all of the markers */
2727com.redfin.FastMarkerOverlay.prototype.hide = function() {
2728  if (!this._div) return;
2729  this._div.style.display = "none";
2730}
2731
2732/** Show all of the markers after hiding them */
2733com.redfin.FastMarkerOverlay.prototype.unhide = function() {
2734  if (!this._div) return;
2735  this._div.style.display = "block";
2736}
2737
2738/** Remove the overlay from the map; never use the overlay again after calling this function */
2739com.redfin.FastMarkerOverlay.prototype.onRemove = function() {
2740  this._div.parentNode.removeChild(this._div);
2741  this._div = null;
2742  // Clear caches to prevent memory leaks
2743  this._markerCache = null;
2744  this._cachedHTML = null;
2745  this._cachedZoom = null;
2746}
2747
2748
2749/** Create a single marker for use in FastMarkerOverlay
2750 * @constructor
2751 * @param {string} id DOM node ID of the div that will contain the marker
2752 * @param {google.maps.LatLng} latLng geographical location of the marker
2753 * @param {Array.<string>} htmlTextArray an array of strings which we'll join together to form the HTML of your marker
2754 * @param {string=} divClassName the CSS class of the div that will contain the marker. (optional)
2755 * @param {string=} zIndex zIndex of the div that will contain the marker. (optional, 'auto' by default)
2756 * @param {number=} leftOffset the offset in pixels by which we'll horizontally adjust the marker position (optional)
2757 * @param {number=} topOffset the offset in pixels by which we'll vertically adjust the marker position (optional)
2758*/
2759com.redfin.FastMarker = function(id, latLng, htmlTextArray, divClassName, zIndex, leftOffset, topOffset) {
2760  this._id = id;
2761  this._latLng = latLng;
2762  this._htmlTextArray = htmlTextArray;
2763  this._divClassName = divClassName;
2764  this._zIndex = zIndex;
2765  this._leftOffset = leftOffset || 0;
2766  this._topOffset = topOffset || 0;
2767}
2768
2769/** Copy the FastMarker
2770 * @return {com.redfin.FastMarker} duplicate of this marker
2771 */
2772com.redfin.FastMarker.prototype.copy = function() {
2773  // Use slice for faster array copy
2774  var htmlArrayCopy = this._htmlTextArray.slice();
2775  return new com.redfin.FastMarker(this._id, this._latLng, htmlArrayCopy, this._divClassName, this._zIndex, this._leftOffset, this._topOffset);
2776}
2777
2778})(); // End of initFastMarkerOverlay wrapper
2779;
2780/*!
2781 * Isotope PACKAGED v2.0.0
2782 * Filter & sort magical layouts
2783 * http://isotope.metafizzy.co
2784 */
2785
2786(function(t){function e(){}function i(t){function i(e){e.prototype.option||(e.prototype.option=function(e){t.isPlainObject(e)&&(this.options=t.extend(!0,this.options,e))})}function n(e,i){t.fn[e]=function(n){if("string"==typeof n){for(var s=o.call(arguments,1),a=0,u=this.length;u>a;a++){var p=this[a],h=t.data(p,e);if(h)if(t.isFunction(h[n])&&"_"!==n.charAt(0)){var f=h[n].apply(h,s);if(void 0!==f)return f}else r("no such method '"+n+"' for "+e+" instance");else r("cannot call methods on "+e+" prior to initialization; "+"attempted to call '"+n+"'")}return this}return this.each(function(){var o=t.data(this,e);o?(o.option(n),o._init()):(o=new i(this,n),t.data(this,e,o))})}}if(t){var r="undefined"==typeof console?e:function(t){console.error(t)};return t.bridget=function(t,e){i(e),n(t,e)},t.bridget}}var o=Array.prototype.slice;"function"==typeof define&&define.amd?define("jquery-bridget/jquery.bridget",["jquery"],i):i(t.jQuery)})(window),function(t){function e(e){var i=t.event;return i.target=i.target||i.srcElement||e,i}var i=document.documentElement,o=function(){};
2786i.addEventListener?o=function(t,e,i){t.addEventListener(e,i,!1)}:i.attachEvent&&(o=function(t,i,o){t[i+o]=o.handleEvent?function(){var i=e(t);o.handleEvent.call(o,i)}:function(){var i=e(t);o.call(t,i)},t.attachEvent("on"+i,t[i+o])});var n=function(){};i.removeEventListener?n=function(t,e,i){t.removeEventListener(e,i,!1)}:i.detachEvent&&(n=function(t,e,i){t.detachEvent("on"+e,t[e+i]);try{delete t[e+i]}catch(o){t[e+i]=void 0}});var r={bind:o,unbind:n};"function"==typeof define&&define.amd?define("eventie/eventie",r):"object"==typeof exports?module.exports=r:t.eventie=r}(this),function(t){function e(t){"function"==typeof t&&(e.isReady?t():r.push(t))}function i(t){var i="readystatechange"===t.type&&"complete"!==n.readyState;if(!e.isReady&&!i){e.isReady=!0;for(var o=0,s=r.length;s>o;o++){var a=r[o];a()}}}function o(o){return o.bind(n,"DOMContentLoaded",i),o.bind(n,"readystatechange",i),o.bind(t,"load",i),e}var n=t.document,r=[];e.isReady=!1,"function"==typeof define&&define.amd?(e.isReady="function"==typeof requirejs,define("doc-ready/doc-ready",["eventie/eventie"],o)):t.docReady=o(t.eventie)}(this),function(){function t(){}function e(t,e){for(var i=t.length;i--;)if(t[i].listener===e)return i;return-1}function i(t){return function(){return this[t].apply(this,arguments)}}var o=t.prototype,n=this,r=n.EventEmitter;o.getListeners=function(t){var e,i,o=this._getEvents();if(t instanceof RegExp){e={};for(i in o)o.hasOwnProperty(i)&&t.test(i)&&(e[i]=o[i])}else e=o[t]||(o[t]=[]);return e},o.flattenListeners=function(t){var e,i=[];for(e=0;t.length>e;e+=1)i.push(t[e].listener);return i},o.getListenersAsObject=function(t){var e,i=this.getListeners(t);return i instanceof Array&&(e={},e[t]=i),e||i},o.addListener=function(t,i){var o,n=this.getListenersAsObject(t),r="object"==typeof i;for(o in n)n.hasOwnProperty(o)&&-1===e(n[o],i)&&n[o].push(r?i:{listener:i,once:!1});return this},o.on=i("addListener"),o.addOnceListener=function(t,e){return this.addListener(t,{listener:e,once:!0})},o.once=i("addOnceListener"),o.defineEvent=function(t){return this.getListeners(t),this},o.defineEvents=function(t){for(var e=0;t.length>e;e+=1)this.defineEvent(t[e]);return this},o.removeListener=function(t,i){var o,n,r=this.getListenersAsObject(t);for(n in r)r.hasOwnProperty(n)&&(o=e(r[n],i),-1!==o&&r[n].splice(o,1));return this},o.off=i("removeListener"),o.addListeners=function(t,e){return this.manipulateListeners(!1,t,e)},o.removeListeners=function(t,e){return this.manipulateListeners(!0,t,e)},o.manipulateListeners=function(t,e,i){var o,n,r=t?this.removeListener:this.addListener,s=t?this.removeListeners:this.addListeners;if("object"!=typeof e||e instanceof RegExp)for(o=i.length;o--;)r.call(this,e,i[o]);else for(o in e)e.hasOwnProperty(o)&&(n=e[o])&&("function"==typeof n?r.call(this,o,n):s.call(this,o,n));return this},o.removeEvent=function(t){var e,i=typeof t,o=this._getEvents();if("string"===i)delete o[t];else if(t instanceof RegExp)for(e in o)o.hasOwnProperty(e)&&t.test(e)&&delete o[e];else delete this._events;return this},o.removeAllListeners=i("removeEvent"),o.emitEvent=function(t,e){var i,o,n,r,s=this.getListenersAsObject(t);for(n in s)if(s.hasOwnProperty(n))for(o=s[n].length;o--;)i=s[n][o],i.once===!0&&this.removeListener(t,i.listener),r=i.listener.apply(this,e||[]),r===this._getOnceReturnValue()&&this.removeListener(t,i.listener);return this},o.trigger=i("emitEvent"),o.emit=function(t){var e=Array.prototype.slice.call(arguments,1);return this.emitEvent(t,e)},o.setOnceReturnValue=function(t){return this._onceReturnValue=t,this},o._getOnceReturnValue=function(){return this.hasOwnProperty("_onceReturnValue")?this._onceReturnValue:!0},o._getEvents=function(){return this._events||(this._events={})},t.noConflict=function(){return n.EventEmitter=r,t},"function"==typeof define&&define.amd?define("eventEmitter/EventEmitter",[],function(){return t}):"object"==typeof module&&module.exports?module.exports=t:this.EventEmitter=t}.call(this),function(t){function e(t){if(t){if("string"==typeof o[t])return t;t=t.charAt(0).toUpperCase()+t.slice(1);for(var e,n=0,r=i.length;r>n;
2786n++)if(e=i[n]+t,"string"==typeof o[e])return e}}var i="Webkit Moz ms Ms O".split(" "),o=document.documentElement.style;"function"==typeof define&&define.amd?define("get-style-property/get-style-property",[],function(){return e}):"object"==typeof exports?module.exports=e:t.getStyleProperty=e}(window),function(t){function e(t){var e=parseFloat(t),i=-1===t.indexOf("%")&&!isNaN(e);return i&&e}function i(){for(var t={width:0,height:0,innerWidth:0,innerHeight:0,outerWidth:0,outerHeight:0},e=0,i=s.length;i>e;e++){var o=s[e];t[o]=0}return t}function o(t){function o(t){if("string"==typeof t&&(t=document.querySelector(t)),t&&"object"==typeof t&&t.nodeType){var o=r(t);if("none"===o.display)return i();var n={};n.width=t.offsetWidth,n.height=t.offsetHeight;for(var h=n.isBorderBox=!(!p||!o[p]||"border-box"!==o[p]),f=0,c=s.length;c>f;f++){var d=s[f],l=o[d];l=a(t,l);var y=parseFloat(l);n[d]=isNaN(y)?0:y}var m=n.paddingLeft+n.paddingRight,g=n.paddingTop+n.paddingBottom,v=n.marginLeft+n.marginRight,_=n.marginTop+n.marginBottom,I=n.borderLeftWidth+n.borderRightWidth,L=n.borderTopWidth+n.borderBottomWidth,z=h&&u,S=e(o.width);S!==!1&&(n.width=S+(z?0:m+I));var b=e(o.height);return b!==!1&&(n.height=b+(z?0:g+L)),n.innerWidth=n.width-(m+I),n.innerHeight=n.height-(g+L),n.outerWidth=n.width+v,n.outerHeight=n.height+_,n}}function a(t,e){if(n||-1===e.indexOf("%"))return e;var i=t.style,o=i.left,r=t.runtimeStyle,s=r&&r.left;return s&&(r.left=t.currentStyle.left),i.left=e,e=i.pixelLeft,i.left=o,s&&(r.left=s),e}var u,p=t("boxSizing");return function(){if(p){var t=document.createElement("div");t.style.width="200px",t.style.padding="1px 2px 3px 4px",t.style.borderStyle="solid",t.style.borderWidth="1px 2px 3px 4px",t.style[p]="border-box";var i=document.body||document.documentElement;i.appendChild(t);var o=r(t);u=200===e(o.width),i.removeChild(t)}}(),o}var n=t.getComputedStyle,r=n?function(t){return n(t,null)}:function(t){return t.currentStyle},s=["paddingLeft","paddingRight","paddingTop","paddingBottom","marginLeft","marginRight","marginTop","marginBottom","borderLeftWidth","borderRightWidth","borderTopWidth","borderBottomWidth"];"function"==typeof define&&define.amd?define("get-size/get-size",["get-style-property/get-style-property"],o):"object"==typeof exports?module.exports=o(require("get-style-property")):t.getSize=o(t.getStyleProperty)}(window),function(t,e){function i(t,e){return t[a](e)}function o(t){if(!t.parentNode){var e=document.createDocumentFragment();e.appendChild(t)}}function n(t,e){o(t);for(var i=t.parentNode.querySelectorAll(e),n=0,r=i.length;r>n;n++)if(i[n]===t)return!0;return!1}function r(t,e){return o(t),i(t,e)}var s,a=function(){if(e.matchesSelector)return"matchesSelector";for(var t=["webkit","moz","ms","o"],i=0,o=t.length;o>i;i++){var n=t[i],r=n+"MatchesSelector";if(e[r])return r}}();if(a){var u=document.createElement("div"),p=i(u,"div");s=p?i:r}else s=n;"function"==typeof define&&define.amd?define("matches-selector/matches-selector",[],function(){return s}):window.matchesSelector=s}(this,Element.prototype),function(t){function e(t,e){for(var i in e)t[i]=e[i];return t}function i(t){for(var e in t)return!1;return e=null,!0}function o(t){return t.replace(/([A-Z])/g,function(t){return"-"+t.toLowerCase()})}function n(t,n,r){function a(t,e){t&&(this.element=t,this.layout=e,this.position={x:0,y:0},this._create())}var u=r("transition"),p=r("transform"),h=u&&p,f=!!r("perspective"),c={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"otransitionend",transition:"transitionend"}[u],d=["transform","transition","transitionDuration","transitionProperty"],l=function(){for(var t={},e=0,i=d.length;i>e;e++){var o=d[e],n=r(o);n&&n!==o&&(t[o]=n)}return t}();e(a.prototype,t.prototype),a.prototype._create=function(){this._transn={ingProperties:{},clean:{},onEnd:{}},this.css({position:"absolute"})},a.prototype.handleEvent=function(t){var e="on"+t.type;this[e]&&this[e](t)},a.prototype.getSize=function(){this.size=n(this.element)},a.prototype.css=function(t){var e=this.element.style;for(var i in t){var o=l[i]||i;e[o]=t[i]}},a.prototype.getPosition=function(){var t=s(this.element),e=this.layout.options,i=e.isOriginLeft,o=e.isOriginTop,n=parseInt(t[i?"left":"right"],10),r=parseInt(t[o?"top":"bottom"],10);n=isNaN(n)?0:n,r=isNaN(r)?0:r;var a=this.layout.size;n-=i?a.paddingLeft:a.paddingRight,r-=o?a.paddingTop:a.paddingBottom,this.position.x=n,this.position.y=r},a.prototype.layoutPosition=function(){var t=this.layout.size,e=this.layout.options,i={};
2786e.isOriginLeft?(i.left=this.position.x+t.paddingLeft+"px",i.right=""):(i.right=this.position.x+t.paddingRight+"px",i.left=""),e.isOriginTop?(i.top=this.position.y+t.paddingTop+"px",i.bottom=""):(i.bottom=this.position.y+t.paddingBottom+"px",i.top=""),this.css(i),this.emitEvent("layout",[this])};var y=f?function(t,e){return"translate3d("+t+"px, "+e+"px, 0)"}:function(t,e){return"translate("+t+"px, "+e+"px)"};a.prototype._transitionTo=function(t,e){this.getPosition();var i=this.position.x,o=this.position.y,n=parseInt(t,10),r=parseInt(e,10),s=n===this.position.x&&r===this.position.y;if(this.setPosition(t,e),s&&!this.isTransitioning)return this.layoutPosition(),void 0;var a=t-i,u=e-o,p={},h=this.layout.options;a=h.isOriginLeft?a:-a,u=h.isOriginTop?u:-u,p.transform=y(a,u),this.transition({to:p,onTransitionEnd:{transform:this.layoutPosition},isCleaning:!0})},a.prototype.goTo=function(t,e){this.setPosition(t,e),this.layoutPosition()},a.prototype.moveTo=h?a.prototype._transitionTo:a.prototype.goTo,a.prototype.setPosition=function(t,e){this.position.x=parseInt(t,10),this.position.y=parseInt(e,10)},a.prototype._nonTransition=function(t){this.css(t.to),t.isCleaning&&this._removeStyles(t.to);for(var e in t.onTransitionEnd)t.onTransitionEnd[e].call(this)},a.prototype._transition=function(t){if(!parseFloat(this.layout.options.transitionDuration))return this._nonTransition(t),void 0;var e=this._transn;for(var i in t.onTransitionEnd)e.onEnd[i]=t.onTransitionEnd[i];for(i in t.to)e.ingProperties[i]=!0,t.isCleaning&&(e.clean[i]=!0);if(t.from){this.css(t.from);var o=this.element.offsetHeight;o=null}this.enableTransition(t.to),this.css(t.to),this.isTransitioning=!0};var m=p&&o(p)+",opacity";a.prototype.enableTransition=function(){this.isTransitioning||(this.css({transitionProperty:m,transitionDuration:this.layout.options.transitionDuration}),this.element.addEventListener(c,this,!1))},a.prototype.transition=a.prototype[u?"_transition":"_nonTransition"],a.prototype.onwebkitTransitionEnd=function(t){this.ontransitionend(t)},a.prototype.onotransitionend=function(t){this.ontransitionend(t)};var g={"-webkit-transform":"transform","-moz-transform":"transform","-o-transform":"transform"};a.prototype.ontransitionend=function(t){if(t.target===this.element){var e=this._transn,o=g[t.propertyName]||t.propertyName;if(delete e.ingProperties[o],i(e.ingProperties)&&this.disableTransition(),o in e.clean&&(this.element.style[t.propertyName]="",delete e.clean[o]),o in e.onEnd){var n=e.onEnd[o];n.call(this),delete e.onEnd[o]}this.emitEvent("transitionEnd",[this])}},a.prototype.disableTransition=function(){this.removeTransitionStyles(),this.element.removeEventListener(c,this,!1),this.isTransitioning=!1},a.prototype._removeStyles=function(t){var e={};for(var i in t)e[i]="";this.css(e)};var v={transitionProperty:"",transitionDuration:""};return a.prototype.removeTransitionStyles=function(){this.css(v)},a.prototype.removeElem=function(){this.element.parentNode.removeChild(this.element),this.emitEvent("remove",[this])},a.prototype.remove=function(){if(!u||!parseFloat(this.layout.options.transitionDuration))return this.removeElem(),void 0;var t=this;this.on("transitionEnd",function(){return t.removeElem(),!0}),this.hide()},a.prototype.reveal=function(){delete this.isHidden,this.css({display:""});var t=this.layout.options;this.transition({from:t.hiddenStyle,to:t.visibleStyle,isCleaning:!0})},a.prototype.hide=function(){this.isHidden=!0,this.css({display:""});var t=this.layout.options;this.transition({from:t.visibleStyle,to:t.hiddenStyle,isCleaning:!0,onTransitionEnd:{opacity:function(){this.isHidden&&this.css({display:"none"})}}})},a.prototype.destroy=function(){this.css({position:"",left:"",right:"",top:"",bottom:"",transition:"",transform:""})},a}var r=t.getComputedStyle,s=r?function(t){return r(t,null)}:function(t){return t.currentStyle};"function"==typeof define&&define.amd?define("outlayer/item",["eventEmitter/EventEmitter","get-size/get-size","get-style-property/get-style-property"],n):(t.Outlayer={},t.Outlayer.Item=n(t.EventEmitter,t.getSize,t.getStyleProperty))}(window),function(t){function e(t,e){for(var i in e)t[i]=e[i];return t}function i(t){return"[object Array]"===f.call(t)}function o(t){var e=[];
2786if(i(t))e=t;else if(t&&"number"==typeof t.length)for(var o=0,n=t.length;n>o;o++)e.push(t[o]);else e.push(t);return e}function n(t,e){var i=d(e,t);-1!==i&&e.splice(i,1)}function r(t){return t.replace(/(.)([A-Z])/g,function(t,e,i){return e+"-"+i}).toLowerCase()}function s(i,s,f,d,l,y){function m(t,i){if("string"==typeof t&&(t=a.querySelector(t)),!t||!c(t))return u&&u.error("Bad "+this.constructor.namespace+" element: "+t),void 0;this.element=t,this.options=e({},this.constructor.defaults),this.option(i);var o=++g;this.element.outlayerGUID=o,v[o]=this,this._create(),this.options.isInitLayout&&this.layout()}var g=0,v={};return m.namespace="outlayer",m.Item=y,m.defaults={containerStyle:{position:"relative"},isInitLayout:!0,isOriginLeft:!0,isOriginTop:!0,isResizeBound:!0,isResizingContainer:!0,transitionDuration:"0.4s",hiddenStyle:{opacity:0,transform:"scale(0.001)"},visibleStyle:{opacity:1,transform:"scale(1)"}},e(m.prototype,f.prototype),m.prototype.option=function(t){e(this.options,t)},m.prototype._create=function(){this.reloadItems(),this.stamps=[],this.stamp(this.options.stamp),e(this.element.style,this.options.containerStyle),this.options.isResizeBound&&this.bindResize()},m.prototype.reloadItems=function(){this.items=this._itemize(this.element.children)},m.prototype._itemize=function(t){for(var e=this._filterFindItemElements(t),i=this.constructor.Item,o=[],n=0,r=e.length;r>n;n++){var s=e[n],a=new i(s,this);o.push(a)}return o},m.prototype._filterFindItemElements=function(t){t=o(t);for(var e=this.options.itemSelector,i=[],n=0,r=t.length;r>n;n++){var s=t[n];if(c(s))if(e){l(s,e)&&i.push(s);for(var a=s.querySelectorAll(e),u=0,p=a.length;p>u;u++)i.push(a[u])}else i.push(s)}return i},m.prototype.getItemElements=function(){for(var t=[],e=0,i=this.items.length;i>e;e++)t.push(this.items[e].element);return t},m.prototype.layout=function(){this._resetLayout(),this._manageStamps();var t=void 0!==this.options.isLayoutInstant?this.options.isLayoutInstant:!this._isLayoutInited;this.layoutItems(this.items,t),this._isLayoutInited=!0},m.prototype._init=m.prototype.layout,m.prototype._resetLayout=function(){this.getSize()},m.prototype.getSize=function(){this.size=d(this.element)},m.prototype._getMeasurement=function(t,e){var i,o=this.options[t];o?("string"==typeof o?i=this.element.querySelector(o):c(o)&&(i=o),this[t]=i?d(i)[e]:o):this[t]=0},m.prototype.layoutItems=function(t,e){t=this._getItemsForLayout(t),this._layoutItems(t,e),this._postLayout()},m.prototype._getItemsForLayout=function(t){for(var e=[],i=0,o=t.length;o>i;i++){var n=t[i];n.isIgnored||e.push(n)}return e},m.prototype._layoutItems=function(t,e){function i(){o.emitEvent("layoutComplete",[o,t])}var o=this;if(!t||!t.length)return i(),void 0;this._itemsOn(t,"layout",i);for(var n=[],r=0,s=t.length;s>r;r++){var a=t[r],u=this._getItemLayoutPosition(a);u.item=a,u.isInstant=e||a.isLayoutInstant,n.push(u)}this._processLayoutQueue(n)},m.prototype._getItemLayoutPosition=function(){return{x:0,y:0}},m.prototype._processLayoutQueue=function(t){for(var e=0,i=t.length;i>e;e++){var o=t[e];this._positionItem(o.item,o.x,o.y,o.isInstant)}},m.prototype._positionItem=function(t,e,i,o){o?t.goTo(e,i):t.moveTo(e,i)},m.prototype._postLayout=function(){this.resizeContainer()},m.prototype.resizeContainer=function(){if(this.options.isResizingContainer){var t=this._getContainerSize();t&&(this._setContainerMeasure(t.width,!0),this._setContainerMeasure(t.height,!1))}},m.prototype._getContainerSize=h,m.prototype._setContainerMeasure=function(t,e){if(void 0!==t){var i=this.size;i.isBorderBox&&(t+=e?i.paddingLeft+i.paddingRight+i.borderLeftWidth+i.borderRightWidth:i.paddingBottom+i.paddingTop+i.borderTopWidth+i.borderBottomWidth),t=Math.max(t,0),this.element.style[e?"width":"height"]=t+"px"}},m.prototype._itemsOn=function(t,e,i){function o(){return n++,n===r&&i.call(s),!0}for(var n=0,r=t.length,s=this,a=0,u=t.length;u>a;a++){var p=t[a];p.on(e,o)}},m.prototype.ignore=function(t){var e=this.getItem(t);e&&(e.isIgnored=!0)},m.prototype.unignore=function(t){var e=this.getItem(t);e&&delete e.isIgnored},m.prototype.stamp=function(t){if(t=this._find(t)){this.stamps=this.stamps.
2786concat(t);for(var e=0,i=t.length;i>e;e++){var o=t[e];this.ignore(o)}}},m.prototype.unstamp=function(t){if(t=this._find(t))for(var e=0,i=t.length;i>e;e++){var o=t[e];n(o,this.stamps),this.unignore(o)}},m.prototype._find=function(t){return t?("string"==typeof t&&(t=this.element.querySelectorAll(t)),t=o(t)):void 0},m.prototype._manageStamps=function(){if(this.stamps&&this.stamps.length){this._getBoundingRect();for(var t=0,e=this.stamps.length;e>t;t++){var i=this.stamps[t];this._manageStamp(i)}}},m.prototype._getBoundingRect=function(){var t=this.element.getBoundingClientRect(),e=this.size;this._boundingRect={left:t.left+e.paddingLeft+e.borderLeftWidth,top:t.top+e.paddingTop+e.borderTopWidth,right:t.right-(e.paddingRight+e.borderRightWidth),bottom:t.bottom-(e.paddingBottom+e.borderBottomWidth)}},m.prototype._manageStamp=h,m.prototype._getElementOffset=function(t){var e=t.getBoundingClientRect(),i=this._boundingRect,o=d(t),n={left:e.left-i.left-o.marginLeft,top:e.top-i.top-o.marginTop,right:i.right-e.right-o.marginRight,bottom:i.bottom-e.bottom-o.marginBottom};return n},m.prototype.handleEvent=function(t){var e="on"+t.type;this[e]&&this[e](t)},m.prototype.bindResize=function(){this.isResizeBound||(i.bind(t,"resize",this),this.isResizeBound=!0)},m.prototype.unbindResize=function(){this.isResizeBound&&i.unbind(t,"resize",this),this.isResizeBound=!1},m.prototype.onresize=function(){function t(){e.resize(),delete e.resizeTimeout}this.resizeTimeout&&clearTimeout(this.resizeTimeout);var e=this;this.resizeTimeout=setTimeout(t,100)},m.prototype.resize=function(){this.isResizeBound&&this.needsResizeLayout()&&this.layout()},m.prototype.needsResizeLayout=function(){var t=d(this.element),e=this.size&&t;return e&&t.innerWidth!==this.size.innerWidth},m.prototype.addItems=function(t){var e=this._itemize(t);return e.length&&(this.items=this.items.concat(e)),e},m.prototype.appended=function(t){var e=this.addItems(t);e.length&&(this.layoutItems(e,!0),this.reveal(e))},m.prototype.prepended=function(t){var e=this._itemize(t);if(e.length){var i=this.items.slice(0);this.items=e.concat(i),this._resetLayout(),this._manageStamps(),this.layoutItems(e,!0),this.reveal(e),this.layoutItems(i)}},m.prototype.reveal=function(t){var e=t&&t.length;if(e)for(var i=0;e>i;i++){var o=t[i];o.reveal()}},m.prototype.hide=function(t){var e=t&&t.length;if(e)for(var i=0;e>i;i++){var o=t[i];o.hide()}},m.prototype.getItem=function(t){for(var e=0,i=this.items.length;i>e;e++){var o=this.items[e];if(o.element===t)return o}},m.prototype.getItems=function(t){if(t&&t.length){for(var e=[],i=0,o=t.length;o>i;i++){var n=t[i],r=this.getItem(n);r&&e.push(r)}return e}},m.prototype.remove=function(t){t=o(t);var e=this.getItems(t);if(e&&e.length){this._itemsOn(e,"remove",function(){this.emitEvent("removeComplete",[this,e])});for(var i=0,r=e.length;r>i;i++){var s=e[i];s.remove(),n(s,this.items)}}},m.prototype.destroy=function(){var t=this.element.style;t.height="",t.position="",t.width="";for(var e=0,i=this.items.length;i>e;e++){var o=this.items[e];o.destroy()}this.unbindResize(),delete this.element.outlayerGUID,p&&p.removeData(this.element,this.constructor.namespace)},m.data=function(t){var e=t&&t.outlayerGUID;return e&&v[e]},m.create=function(t,i){function o(){m.apply(this,arguments)}return Object.create?o.prototype=Object.create(m.prototype):e(o.prototype,m.prototype),o.prototype.constructor=o,o.defaults=e({},m.defaults),e(o.defaults,i),o.prototype.settings={},o.namespace=t,o.data=m.data,o.Item=function(){y.apply(this,arguments)},o.Item.prototype=new y,s(function(){for(var e=r(t),i=a.querySelectorAll(".js-"+e),n="data-"+e+"-options",s=0,h=i.length;h>s;s++){var f,c=i[s],d=c.getAttribute(n);try{f=d&&JSON.parse(d)}catch(l){u&&u.error("Error parsing "+n+" on "+c.nodeName.toLowerCase()+(c.id?"#"+c.id:"")+": "+l);continue}var y=new o(c,f);p&&p.data(c,t,y)}}),p&&p.bridget&&p.bridget(t,o),o},m.Item=y,m}var a=t.document,u=t.console,p=t.jQuery,h=function(){},f=Object.prototype.toString,c="object"==typeof HTMLElement?function(t){return t instanceof HTMLElement}:function(t){return t&&"object"==typeof t&&1===t.nodeType&&"string"==typeof t.nodeName},d=Array.prototype.indexOf?function(t,e){return t.indexOf(e)}:function(t,e){for(var i=0,o=t.length;o>i;i++)if(t[i]===e)return i;return-1};"function"==typeof define&&define.amd?define("outlayer/outlayer",["eventie/eventie","doc-ready/doc-ready","eventEmitter/EventEmitter","get-size/get-size","matches-selector/matches-selector","./item"],s):t.Outlayer=s(t.eventie,t.docReady,t.EventEmitter,t.getSize,t.matchesSelector,t.Outlayer.Item)}(window),function(t){function e(t){function e(){t.Item.apply(this,arguments)}return e.prototype=new t.Item,e.prototype._create=function(){this.id=this.layout.itemGUID++,t.Item.prototype._create.call(this),this.sortData={}},e.prototype.updateSortData=function(){if(!this.isIgnored){this.sortData.id=this.id,this.sortData["original-order"]=this.id,this.sortData.random=Math.random();var t=this.layout.options.getSortData,e=this.layout._sorters;for(var i in t){var o=e[i];this.sortData[i]=o(this.element,this)}}},e}"function"==typeof define&&define.amd?define("isotope/js/item",["outlayer/outlayer"],e):(t.Isotope=t.Isotope||{},t.Isotope.Item=e(t.Outlayer))}(window),function(t){function e(t,e){function i(t){this.isotope=t,t&&(this.options=t.options[this.namespace],this.element=t.element,this.items=t.filteredItems,this.size=t.size)}return function(){function t(t){return function(){return e.prototype[t].apply(this.isotope,arguments)}}for(var o=["_resetLayout","_getItemLayoutPosition","_manageStamp","_getContainerSize","_getElementOffset","needsResizeLayout"],n=0,r=o.length;r>n;n++){var s=o[n];i.prototype[s]=t(s)}}(),i.prototype.needsVerticalResizeLayout=function(){var e=t(this.isotope.element),i=this.isotope.size&&e;
2786return i&&e.innerHeight!==this.isotope.size.innerHeight},i.prototype._getMeasurement=function(){this.isotope._getMeasurement.apply(this,arguments)},i.prototype.getColumnWidth=function(){this.getSegmentSize("column","Width")},i.prototype.getRowHeight=function(){this.getSegmentSize("row","Height")},i.prototype.getSegmentSize=function(t,e){var i=t+e,o="outer"+e;if(this._getMeasurement(i,o),!this[i]){var n=this.getFirstItemSize();this[i]=n&&n[o]||this.isotope.size["inner"+e]}},i.prototype.getFirstItemSize=function(){var e=this.isotope.filteredItems[0];return e&&e.element&&t(e.element)},i.prototype.layout=function(){this.isotope.layout.apply(this.isotope,arguments)},i.prototype.getSize=function(){this.isotope.getSize(),this.size=this.isotope.size},i.modes={},i.create=function(t,e){function o(){i.apply(this,arguments)}return o.prototype=new i,e&&(o.options=e),o.prototype.namespace=t,i.modes[t]=o,o},i}"function"==typeof define&&define.amd?define("isotope/js/layout-mode",["get-size/get-size","outlayer/outlayer"],e):(t.Isotope=t.Isotope||{},t.Isotope.LayoutMode=e(t.getSize,t.Outlayer))}(window),function(t){function e(t,e){var o=t.create("masonry");return o.prototype._resetLayout=function(){this.getSize(),this._getMeasurement("columnWidth","outerWidth"),this._getMeasurement("gutter","outerWidth"),this.measureColumns();var t=this.cols;for(this.colYs=[];t--;)this.colYs.push(0);this.maxY=0},o.prototype.measureColumns=function(){if(this.getContainerWidth(),!this.columnWidth){var t=this.items[0],i=t&&t.element;this.columnWidth=i&&e(i).outerWidth||this.containerWidth}this.columnWidth+=this.gutter,this.cols=Math.floor((this.containerWidth+this.gutter)/this.columnWidth),this.cols=Math.max(this.cols,1)},o.prototype.getContainerWidth=function(){var t=this.options.isFitWidth?this.element.parentNode:this.element,i=e(t);this.containerWidth=i&&i.innerWidth},o.prototype._getItemLayoutPosition=function(t){t.getSize();var e=t.size.outerWidth%this.columnWidth,o=e&&1>e?"round":"ceil",n=Math[o](t.size.outerWidth/this.columnWidth);n=Math.min(n,this.cols);for(var r=this._getColGroup(n),s=Math.min.apply(Math,r),a=i(r,s),u={x:this.columnWidth*a,y:s},p=s+t.size.outerHeight,h=this.cols+1-r.length,f=0;h>f;f++)this.colYs[a+f]=p;return u},o.prototype._getColGroup=function(t){if(2>t)return this.colYs;for(var e=[],i=this.cols+1-t,o=0;i>o;o++){var n=this.colYs.slice(o,o+t);e[o]=Math.max.apply(Math,n)}return e},o.prototype._manageStamp=function(t){var i=e(t),o=this._getElementOffset(t),n=this.options.isOriginLeft?o.left:o.right,r=n+i.outerWidth,s=Math.floor(n/this.columnWidth);s=Math.max(0,s);var a=Math.floor(r/this.columnWidth);a-=r%this.columnWidth?0:1,a=Math.min(this.cols-1,a);for(var u=(this.options.isOriginTop?o.top:o.bottom)+i.outerHeight,p=s;a>=p;p++)this.colYs[p]=Math.max(u,this.colYs[p])},o.prototype._getContainerSize=function(){this.maxY=Math.max.apply(Math,this.colYs);var t={height:this.maxY};return this.options.isFitWidth&&(t.width=this._getContainerFitWidth()),t},o.prototype._getContainerFitWidth=function(){for(var t=0,e=this.cols;--e&&0===this.colYs[e];)t++;return(this.cols-t)*this.columnWidth-this.gutter},o.prototype.needsResizeLayout=function(){var t=this.containerWidth;return this.getContainerWidth(),t!==this.containerWidth},o}var i=Array.prototype.indexOf?function(t,e){return t.indexOf(e)}:function(t,e){for(var i=0,o=t.length;o>i;i++){var n=t[i];if(n===e)return i}return-1};"function"==typeof define&&define.amd?define("masonry/masonry",["outlayer/outlayer","get-size/get-size"],e):t.Masonry=e(t.Outlayer,t.getSize)}(window),function(t){function e(t,e){for(var i in e)t[i]=e[i];return t}function i(t,i){var o=t.create("masonry"),n=o.prototype._getElementOffset,r=o.prototype.layout,s=o.prototype._getMeasurement;e(o.prototype,i.prototype),o.prototype._getElementOffset=n,o.prototype.layout=r,o.prototype._getMeasurement=s;var a=o.prototype.measureColumns;o.prototype.measureColumns=function(){this.items=this.isotope.filteredItems,a.call(this)};var u=o.prototype._manageStamp;return o.prototype._manageStamp=function(){this.options.isOriginLeft=this.isotope.options.isOriginLeft,this.options.isOriginTop=this.isotope.options.isOriginTop,u.apply(this,arguments)},o}"function"==typeof define&&define.amd?define("isotope/js/layout-modes/masonry",["../layout-mode","masonry/masonry"],i):i(t.Isotope.LayoutMode,t.Masonry)}(window),function(t){function e(t){var e=t.create("fitRows");return e.prototype._resetLayout=function(){this.x=0,this.y=0,this.maxY=0},e.prototype._getItemLayoutPosition=function(t){t.getSize(),0!==this.x&&t.size.outerWidth+this.x>this.isotope.size.innerWidth&&(this.x=0,this.y=this.maxY);var e={x:this.x,y:this.y};return this.maxY=Math.max(this.maxY,this.y+t.size.outerHeight),this.x+=t.size.outerWidth,e},e.prototype._getContainerSize=function(){return{height:this.maxY}},e}"function"==typeof define&&define.amd?define("isotope/js/layout-modes/fit-rows",["../layout-mode"],e):e(t.Isotope.LayoutMode)}(window),function(t){function e(t){var e=t.create("vertical",{horizontalAlignment:0});return e.prototype._resetLayout=function(){this.y=0},e.prototype._getItemLayoutPosition=function(t){t.getSize();var e=(this.isotope.size.innerWidth-t.size.outerWidth)*this.options.horizontalAlignment,i=this.y;return this.y+=t.size.outerHeight,{x:e,y:i}},e.prototype._getContainerSize=function(){return{height:this.y}},e}"function"==typeof define&&define.amd?define("isotope/js/layout-modes/vertical",["../layout-mode"],e):e(t.Isotope.LayoutMode)}(window),function(t){function e(t,e){for(var i in e)t[i]=e[i];return t}function i(t){return"[object Array]"===h.call(t)}function o(t){var e=[];
2786if(i(t))e=t;else if(t&&"number"==typeof t.length)for(var o=0,n=t.length;n>o;o++)e.push(t[o]);else e.push(t);return e}function n(t,e){var i=f(e,t);-1!==i&&e.splice(i,1)}function r(t,i,r,u,h){function f(t,e){return function(i,o){for(var n=0,r=t.length;r>n;n++){var s=t[n],a=i.sortData[s],u=o.sortData[s];if(a>u||u>a){var p=void 0!==e[s]?e[s]:e,h=p?1:-1;return(a>u?1:-1)*h}}return 0}}var c=t.create("isotope",{layoutMode:"masonry",isJQueryFiltering:!0,sortAscending:!0});c.Item=u,c.LayoutMode=h,c.prototype._create=function(){this.itemGUID=0,this._sorters={},this._getSorters(),t.prototype._create.call(this),this.modes={},this.filteredItems=this.items,this.sortHistory=["original-order"];for(var e in h.modes)this._initLayoutMode(e)},c.prototype.reloadItems=function(){this.itemGUID=0,t.prototype.reloadItems.call(this)},c.prototype._itemize=function(){for(var e=t.prototype._itemize.apply(this,arguments),i=0,o=e.length;o>i;i++){var n=e[i];n.id=this.itemGUID++}return this._updateItemsSortData(e),e},c.prototype._initLayoutMode=function(t){var i=h.modes[t],o=this.options[t]||{};this.options[t]=i.options?e(i.options,o):o,this.modes[t]=new i(this)},c.prototype.layout=function(){return!this._isLayoutInited&&this.options.isInitLayout?(this.arrange(),void 0):(this._layout(),void 0)},c.prototype._layout=function(){var t=this._getIsInstant();this._resetLayout(),this._manageStamps(),this.layoutItems(this.filteredItems,t),this._isLayoutInited=!0},c.prototype.arrange=function(t){this.option(t),this._getIsInstant(),this.filteredItems=this._filter(this.items),this._sort(),this._layout()},c.prototype._init=c.prototype.arrange,c.prototype._getIsInstant=function(){var t=void 0!==this.options.isLayoutInstant?this.options.isLayoutInstant:!this._isLayoutInited;return this._isInstant=t,t},c.prototype._filter=function(t){function e(){f.reveal(n),f.hide(r)}var i=this.options.filter;i=i||"*";for(var o=[],n=[],r=[],s=this._getFilterTest(i),a=0,u=t.length;u>a;a++){var p=t[a];if(!p.isIgnored){var h=s(p);h&&o.push(p),h&&p.isHidden?n.push(p):h||p.isHidden||r.push(p)}}var f=this;return this._isInstant?this._noTransition(e):e(),o},c.prototype._getFilterTest=function(t){return s&&this.options.isJQueryFiltering?function(e){return s(e.element).is(t)}:"function"==typeof t?function(e){return t(e.element)}:function(e){return r(e.element,t)}},c.prototype.updateSortData=function(t){this._getSorters(),t=o(t);var e=this.getItems(t);e=e.length?e:this.items,this._updateItemsSortData(e)
2787},c.prototype._getSorters=function(){var t=this.options.getSortData;for(var e in t){var i=t[e];this._sorters[e]=d(i)}},c.prototype._updateItemsSortData=function(t){for(var e=0,i=t.length;i>e;e++){var o=t[e];o.updateSortData()}};var d=function(){function t(t){if("string"!=typeof t)return t;var i=a(t).split(" "),o=i[0],n=o.match(/^\[(.+)\]$/),r=n&&n[1],s=e(r,o),u=c.sortDataParsers[i[1]];return t=u?function(t){return t&&u(s(t))}:function(t){return t&&s(t)}}function e(t,e){var i;return i=t?function(e){return e.getAttribute(t)}:function(t){var i=t.querySelector(e);return i&&p(i)}}return t}();c.sortDataParsers={parseInt:function(t){return parseInt(t,10)},parseFloat:function(t){return parseFloat(t)}},c.prototype._sort=function(){var t=this.options.sortBy;if(t){var e=[].concat.apply(t,this.sortHistory),i=f(e,this.options.sortAscending);this.filteredItems.sort(i),t!==this.sortHistory[0]&&this.sortHistory.unshift(t)}},c.prototype._mode=function(){var t=this.options.layoutMode,e=this.modes[t];if(!e)throw Error("No layout mode: "+t);return e.options=this.options[t],e},c.prototype._resetLayout=function(){t.prototype._resetLayout.call(this),this._mode()._resetLayout()},c.prototype._getItemLayoutPosition=function(t){return this._mode()._getItemLayoutPosition(t)},c.prototype._manageStamp=function(t){this._mode()._manageStamp(t)},c.prototype._getContainerSize=function(){return this._mode()._getContainerSize()},c.prototype.needsResizeLayout=function(){return this._mode().needsResizeLayout()},c.prototype.appended=function(t){var e=this.addItems(t);if(e.length){var i=this._filterRevealAdded(e);this.filteredItems=this.filteredItems.concat(i)}},c.prototype.prepended=function(t){var e=this._itemize(t);if(e.length){var i=this.items.slice(0);this.items=e.concat(i),this._resetLayout(),this._manageStamps();var o=this._filterRevealAdded(e);this.layoutItems(i),this.filteredItems=o.concat(this.filteredItems)}},c.prototype._filterRevealAdded=function(t){var e=this._noTransition(function(){return this._filter(t)});return this.layoutItems(e,!0),this.reveal(e),t},c.prototype.insert=function(t){var e=this.addItems(t);if(e.length){var i,o,n=e.length;for(i=0;n>i;i++)o=e[i],this.element.appendChild(o.element);var r=this._filter(e);for(this._noTransition(function(){this.hide(r)}),i=0;n>i;i++)e[i].isLayoutInstant=!0;for(this.arrange(),i=0;n>i;i++)delete e[i].isLayoutInstant;this.reveal(r)}};var l=c.prototype.remove;return c.prototype.remove=function(t){t=o(t);var e=this.getItems(t);if(l.call(this,t),e&&e.length)for(var i=0,r=e.length;r>i;i++){var s=e[i];n(s,this.filteredItems)}},c.prototype._noTransition=function(t){var e=this.options.transitionDuration;this.options.transitionDuration=0;var i=t.call(this);return this.options.transitionDuration=e,i},c}var s=t.jQuery,a=String.prototype.trim?function(t){return t.trim()}:function(t){return t.replace(/^\s+|\s+$/g,"")},u=document.documentElement,p=u.textContent?function(t){return t.textContent}:function(t){return t.innerText},h=Object.prototype.toString,f=Array.prototype.indexOf?function(t,e){return t.indexOf(e)}:function(t,e){for(var i=0,o=t.length;o>i;i++)if(t[i]===e)return i;return-1};"function"==typeof define&&define.amd?define(["outlayer/outlayer","get-size/get-size","matches-selector/matches-selector","isotope/js/item","isotope/js/layout-mode","isotope/js/layout-modes/masonry","isotope/js/layout-modes/fit-rows","isotope/js/layout-modes/vertical"],r):t.Isotope=r(t.Outlayer,t.getSize,t.matchesSelector,t.Isotope.Item,t.Isotope.LayoutMode)}(window);;
2788(function ($) {
2789    'use strict';
2790
2791    $.fn.prettySocial = function () {
2792        /**
2793         * Supported social sites
2794         * @type {Object}
2795         */
2796        var _sites = {
2797                pinterest: {
2798                    url: 'http://pinterest.com/pin/create/button/?url={{url}}&media={{media}}&description={{description}}',
2799                    popup: {
2800                        width: 685,
2801                        height: 500
2802                    }
2803                },
2804                facebook: {
2805                    url: 'https://www.facebook.com/sharer/sharer.php?u={{url}}',
2806                    popup: {
2807                        width: 626,
2808                        height: 436
2809                    }
2810                },
2811                twitter: {
2812                    url: 'https://twitter.com/share?url={{url}}{{via}}&text={{description}}',
2813                    popup: {
2814                        width: 685,
2815                        height: 500
2816                    }
2817                },
2818                googleplus: {
2819                    url: 'https://plus.google.com/share?url={{url}}',
2820                    popup: {
2821                        width: 600,
2822                        height: 600
2823                    }
2824                },
2825                linkedin: {
2826                    url: 'https://www.linkedin.com/shareArticle?mini=true&url={{url}}&title={{title}}&summary={{description}}+&source={{via}}',
2827                    popup: {
2828                        width: 600,
2829                        height: 600
2830                    }
2831                }
2832            },
2833
2834            /**
2835             * Pop-up window
2836             * This method is only used on desktop browsers
2837             * @param  {Object} site Selected social site
2838             * @param  {String} url  Fixed URL to open
2839             */
2840            _popup = function (site, url) {
2841                // center window
2842                var left = (window.innerWidth/2) - (site.popup.width/2),
2843                    top = (window.innerHeight/2) - (site.popup.height/2);
2844
2845                return window.open(url, '', 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width=' + site.popup.width + ', height=' + site.popup.height + ', top=' 
2845+ top + ', left=' + left);
2846            },
2847
2848            /**
2849             * Prepare link based on social sites
2850             * @param  {Object} site Selected social site
2851             * @param  {Object} link Link with variables
2852             * @return {String}      Polished link based on the social site template
2853             */
2854            _linkFix = function (site, link) {
2855                // replace template url
2856                var url = site.url.replace(/{{url}}/g, encodeURIComponent(link.url))
2857                                  .replace(/{{title}}/g, encodeURIComponent(link.title))
2858                                  .replace(/{{description}}/g, encodeURIComponent(link.description))
2859                                  .replace(/{{media}}/g, encodeURIComponent(link.media))
2860                                  .replace(/{{via}}/g, link.via ? '&via=' + encodeURIComponent(link.via) : '');
2861
2862                return url;
2863            };
2864
2865        return this.each(function() {
2866
2867            // declare $(this) as variable
2868            var $this = $(this);
2869
2870            // link type
2871            var type = $this.data('type'),
2872
2873                // set site
2874                site = _sites[type] || null;
2875
2876            // check if social site is selected
2877            if (!site) {
2878                $.error('Social site is not set.');
2879            }
2880
2881            // gather link info
2882            var link = {
2883                url: $this.data('url') || '',
2884                title: $this.data('title') || '',
2885                description: $this.data('description') || '',
2886                media: $this.data('media') || '',
2887                via: $this.data('via') || ''
2888            };
2889
2890            // prepare link
2891            var url = _linkFix(site, link);
2892
2893            // if not, set click trigger
2894            if (navigator.userAgent.match(/Android|IEMobile|BlackBerry|iPhone|iPad|iPod|Opera Mini/i)) {
2895                $this
2896                .bind('touchstart', function (e) {
2897                    if(e.originalEvent.touches.length > 1) {
2898                        return;
2899                    }
2900
2901                    $this.data('touchWithoutScroll', true);
2902                })
2903                .bind('touchmove', function () {
2904                    $this.data('touchWithoutScroll', false);
2905
2906                    return;
2907                }).bind('touchend', function (e) {
2908                    e.preventDefault();
2909
2910                    var touchWithoutScroll = $this.data('touchWithoutScroll');
2911
2912                    if (e.originalEvent.touches.length > 1 || !touchWithoutScroll) {
2913                        return;
2914                    }
2915
2916                    // call popup window
2917                    _popup(site, url);
2918                });
2919            } else {
2920                $this.bind('click', function (e) {
2921                    e.preventDefault();
2922
2923                    // call popup window
2924                    _popup(site, url);
2925                });
2926            }
2927        });
2928    };
2929
2930})(jQuery);;
2931/*!
2932 * imagesLoaded PACKAGED v3.1.8
2933 * JavaScript is all like "You images are done yet or what?"
2934 * MIT License
2935 */
2936
2937(function(){function e(){}function t(e,t){for(var n=e.length;n--;)if(e[n].listener===t)return n;return-1}function n(e){return function(){return this[e].apply(this,arguments)}}var i=e.prototype,r=this,o=r.EventEmitter;i.getListeners=function(e){var t,n,i=this._getEvents();if("object"==typeof e){t={};for(n in i)i.hasOwnProperty(n)&&e.test(n)&&(t[n]=i[n])}else t=i[e]||(i[e]=[]);return t},i.flattenListeners=function(e){var t,n=[];for(t=0;e.length>t;t+=1)n.push(e[t].listener);return n},i.getListenersAsObject=function(e){var t,n=this.getListeners(e);return n instanceof Array&&(t={},t[e]=n),t||n},i.addListener=function(e,n){var i,r=this.getListenersAsObject(e),o="object"==typeof n;for(i in r)r.hasOwnProperty(i)&&-1===t(r[i],n)&&r[i].push(o?n:{listener:n,once:!1});return this},i.on=n("addListener"),i.addOnceListener=function(e,t){return this.addListener(e,{listener:t,once:!0})},i.once=n("addOnceListener"),i.defineEvent=function(e){return this.getListeners(e),this},i.defineEvents=function(e){for(var t=0;e.length>t;t+=1)this.defineEvent(e[t]);return this},i.removeListener=function(e,n){var i,r,o=this.getListenersAsObject(e);for(r in o)o.hasOwnProperty(r)&&(i=t(o[r],n),-1!==i&&o[r].splice(i,1));return this},i.off=n("removeListener"),i.addListeners=function(e,t){return this.manipulateListeners(!1,e,t)},i.removeListeners=function(e,t){return this.manipulateListeners(!0,e,t)},i.manipulateListeners=function(e,t,n){var i,r,o=e?this.removeListener:this.addListener,s=e?this.removeListeners:this.addListeners;if("object"!=typeof t||t instanceof RegExp)for(i=n.length;i--;)o.call(this,t,n[i]);else for(i in t)t.hasOwnProperty(i)&&(r=t[i])&&("function"==typeof r?o.call(this,i,r):s.call(this,i,r));return this},i.removeEvent=function(e){var t,n=typeof e,i=this._getEvents();if("string"===n)delete i[e];else if("object"===n)for(t in i)i.hasOwnProperty(t)&&e.test(t)&&delete i[t];else delete this._events;return this},i.removeAllListeners=n("removeEvent"),i.emitEvent=function(e,t){var n,i,r,o,s=this.getListenersAsObject(e);for(r in s)if(s.hasOwnProperty(r))for(i=s[r].length;i--;)n=s[r][i],n.once===!0&&this.removeListener(e,n.listener),o=n.listener.apply(this,t||[]),o===this._getOnceReturnValue()&&this.removeListener(e,n.listener);return this},i.trigger=n("emitEvent"),i.emit=function(e){var t=Array.prototype.slice.call(arguments,1
2937);return this.emitEvent(e,t)},i.setOnceReturnValue=function(e){return this._onceReturnValue=e,this},i._getOnceReturnValue=function(){return this.hasOwnProperty("_onceReturnValue")?this._onceReturnValue:!0},i._getEvents=function(){return this._events||(this._events={})},e.noConflict=function(){return r.EventEmitter=o,e},"function"==typeof define&&define.amd?define("eventEmitter/EventEmitter",[],function(){return e}):"object"==typeof module&&module.exports?module.exports=e:this.EventEmitter=e}).call(this),function(e){function t(t){var n=e.event;return n.target=n.target||n.srcElement||t,n}var n=document.documentElement,i=function(){};n.addEventListener?i=function(e,t,n){e.addEventListener(t,n,!1)}:n.attachEvent&&(i=function(e,n,i){e[n+i]=i.handleEvent?function(){var n=t(e);i.handleEvent.call(i,n)}:function(){var n=t(e);i.call(e,n)},e.attachEvent("on"+n,e[n+i])});var r=function(){};n.removeEventListener?r=function(e,t,n){e.removeEventListener(t,n,!1)}:n.detachEvent&&(r=function(e,t,n){e.detachEvent("on"+t,e[t+n]);try{delete e[t+n]}catch(i){e[t+n]=void 0}});var o={bind:i,unbind:r};"function"==typeof define&&define.amd?define("eventie/eventie",o):e.eventie=o}(this),function(e,t){"function"==typeof define&&define.amd?define(["eventEmitter/EventEmitter","eventie/eventie"],function(n,i){return t(e,n,i)}):"object"==typeof exports?module.exports=t(e,require("wolfy87-eventemitter"),require("eventie")):e.imagesLoaded=t(e,e.EventEmitter,e.eventie)}(window,function(e,t,n){function i(e,t){for(var n in t)e[n]=t[n];return e}function r(e){return"[object Array]"===d.call(e)}function o(e){var t=[];if(r(e))t=e;else if("number"==typeof e.length)for(var n=0,i=e.length;i>n;n++)t.push(e[n]);else t.push(e);return t}function s(e,t,n){if(!(this instanceof s))return new s(e,t);"string"==typeof e&&(e=document.querySelectorAll(e)),this.elements=o(e),this.options=i({},this.options),"function"==typeof t?n=t:i(this.options,t),n&&this.on("always",n),this.getImages(),a&&(this.jqDeferred=new a.Deferred);var r=this;setTimeout(function(){r.check()})}function f(e){this.img=e}function c(e){this.src=e,v[e]=this}var a=e.jQuery,u=e.console,h=u!==void 0,d=Object.prototype.toString;s.prototype=new t,s.prototype.options={},s.prototype.getImages=function(){this.images=[];for(var e=0,t=this.elements.length;t>e;e++){var n=this.elements[e];"IMG"===n.nodeName&&this.addImage(n);var i=n.nodeType;if(i&&(1===i||9===i||11===i))for(var r=n.querySelectorAll("img"),o=0,s=r.length;s>o;o++){var f=r[o];this.addImage(f)}}},s.prototype.addImage=function(e){var t=new f(e);this.images.push(t)},s.prototype.check=function(){function e(e,r){return t.options.debug&&h&&u.log("confirm",e,r),t.progress(e),n++,n===i&&t.complete(),!0}var t=this,n=0,i=this.images.length;if(this.hasAnyBroken=!1,!i)return this.complete(),void 0;for(var r=0;i>r;r++){var o=this.images[r];o.on("confirm",e),o.check()}},s.prototype.progress=function(e){this.hasAnyBroken=this.hasAnyBroken||!e.isLoaded;var t=this;setTimeout(function(){t.emit("progress",t,e),t.jqDeferred&&t.jqDeferred.notify&&t.jqDeferred.notify(t,e)})},s.prototype.complete=function(){var e=this.hasAnyBroken?"fail":"done";this.isComplete=!0;var t=this;setTimeout(function(){if(t.emit(e,t),t.emit("always",t),t.jqDeferred){var n=t.hasAnyBroken?"reject":"resolve";t.jqDeferred[n](t)}})},a&&(a.fn.imagesLoaded=function(e,t){var n=new s(this,e,t);return n.jqDeferred.promise(a(this))}),f.prototype=new t,f.prototype.check=function(){var e=v[this.img.src]||new c(this.img.src);if(e.isConfirmed)return this.confirm(e.isLoaded,"cached was confirmed"),void 0;if(this.img.complete&&void 0!==this.img.naturalWidth)return this.confirm(0!==this.img.naturalWidth,"naturalWidth"),void 0;var t=this;e.on("confirm",function(e,n){return t.confirm(e.isLoaded,n),!0}),e.check()},f.prototype.confirm=function(e,t){this.isLoaded=e,this.emit("confirm",this,t)};var v={};return c.prototype=new t,c.prototype.check=function(){if(!this.isChecked){var e=new Image;n.bind(e,"load",this),n.bind(e,"error",this),e.src=this.src,this.isChecked=!0}},c.prototype.handleEvent=function(e){var t="on"+e.type;this[t]&&this[t](e)},c.prototype.onload=function(e){this.confirm(!0,"onload"),this.unbindProxyEvents(e)},c.prototype.onerror=function(e){this.confirm(!1,"onerror"),this.unbindProxyEvents(e)},c.prototype.confirm=function(e,t){this.isConfirmed=!0,this.isLoaded=e,this.emit
2937("confirm",this,t)},c.prototype.unbindProxyEvents=function(e){n.unbind(e.target,"load",this),n.unbind(e.target,"error",this)},s});;
2938// The progress meter functionality is available as a series of plugins.
2939// You can put this code in a separate file if you want to keep things tidy.
2940
2941(function($){
2942
2943	// Creating a number of jQuery plugins that you can use to
2944	// initialize and control the progress meters.
2945
2946	$.fn.progressInitialize = function(){
2947
2948		// This function creates the necessary markup for the progress meter
2949		// and sets up a few event listeners.
2950
2951
2952		// Loop through all the buttons:
2953
2954		return this.each(function(){
2955
2956			var button = $(this),
2957				progress = 0;
2958
2959			// Extract the data attributes into the options object.
2960			// If they are missing, they will receive default values.
2961
2962			var options = $.extend({
2963				type:'background-horizontal',
2964				loading: 'Loading..',
2965				finished: 'Done!'
2966			}, button.data());
2967
2968			// Add the data attributes if they are missing from the element.
2969			// They are used by our CSS code to show the messages
2970			button.attr({'data-loading': options.loading, 'data-finished': options.finished});
2971
2972			// Add the needed markup for the progress bar to the button
2973			var bar = $('<span class="tz-bar ' + options.type + '">').appendTo(button);
2974
2975
2976			// The progress event tells the button to update the progress bar
2977			button.on('progress', function(e, val, absolute, finish){
2978
2979				if(!button.hasClass('in-progress')){
2980
2981					// This is the first progress event for the button (or the
2982					// first after it has finished in a previous run). Re-initialize
2983					// the progress and remove some classes that may be left.
2984
2985					bar.show();
2986					progress = 0;
2987					button.removeClass('finished').addClass('in-progress')
2988				}
2989
2990				// val, absolute and finish are event data passed by the progressIncrement
2991				// and progressSet methods that you can see near the end of this file.
2992
2993				if(absolute){
2994					progress = val;
2995				}
2996				else{
2997					progress += val;
2998				}
2999
3000				if(progress >= 100){
3001					progress = 100;
3002				}
3003
3004				if(finish){
3005
3006					button.removeClass('in-progress').addClass('finished');
3007
3008					bar.delay(500).fadeOut(function(){
3009
3010						// Trigger the custom progress-finish event
3011						button.trigger('progress-finish');
3012						setProgress(0);
3013					});
3014
3015				}
3016
3017				setProgress(progress);
3018			});
3019
3020			function setProgress(percentage){
3021				bar.filter('.background-horizontal,.background-bar').width(percentage+'%');
3022				bar.filter('.background-vertical').height(percentage+'%');
3023			}
3024
3025		});
3026
3027	};
3028
3029	// progressStart simulates activity on the progress meter. Call it first,
3030	// if the progress is going to take a long time to finish.
3031
3032	$.fn.progressStart = function(){
3033
3034		var button = this.first(),
3035			last_progress = new Date().getTime();
3036
3037		if(button.hasClass('in-progress')){
3038			// Don't start it a second time!
3039			return this;
3040		}
3041
3042		button.on('progress', function(){
3043			last_progress = new Date().getTime();
3044		});
3045
3046		// Every half a second check whether the progress
3047		// has been incremented in the last two seconds
3048
3049		var interval = window.setInterval(function(){
3050
3051			if( new Date().getTime() > 2000+last_progress){
3052
3053				// There has been no activity for two seconds. Increment the progress
3054				// bar a little bit to show that something is happening
3055
3056				button.progressIncrement(5);
3057			}
3058
3059		}, 500);
3060
3061		button.on('progress-finish',function(){
3062			window.clearInterval(interval);
3063		});
3064
3065		return button.progressIncrement(10);
3066	};
3067
3068	$.fn.progressFinish = function(){
3069		return this.first().progressSet(100);
3070	};
3071
3072	$.fn.progressIncrement = function(val){
3073
3074		val = val || 10;
3075
3076		var button = this.first();
3077
3078		button.trigger('progress',[val])
3079
3080		return this;
3081	};
3082
3083	$.fn.progressSet = function(val){
3084		val = val || 10;
3085
3086		var finish = false;
3087		if(val >= 100){
3088			finish = true;
3089		}
3090
3091		return this.first().trigger('progress',[val, true, finish]);
3092	};
3093
3094	// This function creates a progress meter that
3095	// finishes in a specified amount of time.
3096
3097	$.fn.progressTimed = function(seconds, cb){
3098
3099		var button = this.first(),
3100			bar = button.find('.tz-bar');
3101
3102		if(button.is('.in-progress')){
3103			return this;
3104		}
3105
3106		// Set a transition declaration for the duration of the meter.
3107		// CSS will do the job of animating the progress bar for us.
3108
3109		bar.css('transition', seconds+'s linear');
3110		button.progressSet(99);
3111
3112		window.setTimeout(function(){
3113			bar.css('transition','');
3114			button.progressFinish();
3115
3116			if($.isFunction(cb)){
3117				cb();
3118			}
3119
3120		}, seconds*1000);
3121	};
3122
3123})(jQuery);
3124;
3125!function(t){function e(){var e,i,n={height:a.innerHeight,width:a.innerWidth};
3125return n.height||(e=r.compatMode,(e||!t.support.boxModel)&&(i="CSS1Compat"===e?f:r.body,n={height:i.clientHeight,width:i.clientWidth})),n}function i(){return{top:a.pageYOffset||f.scrollTop||r.body.scrollTop,left:a.pageXOffset||f.scrollLeft||r.body.scrollLeft}}function n(){var n,l=t(),r=0;if(t.each(d,function(t,e){var i=e.data.selector,n=e.$element;l=l.add(i?n.find(i):n)}),n=l.length)for(o=o||e(),h=h||i();n>r;r++)if(t.contains(f,l[r])){var a,c,p,s=t(l[r]),u={height:s.height(),width:s.width()},g=s.offset(),v=s.data("inview");if(!h||!o)return;g.top+u.height>h.top&&g.top<h.top+o.height&&g.left+u.width>h.left&&g.left<h.left+o.width?(a=h.left>g.left?"right":h.left+o.width<g.left+u.width?"left":"both",c=h.top>g.top?"bottom":h.top+o.height<g.top+u.height?"top":"both",p=a+"-"+c,v&&v===p||s.data("inview",p).trigger("inview",[!0,a,c])):v&&s.data("inview",!1).trigger("inview",[!1])}}var o,h,l,d={},r=document,a=window,f=r.documentElement,c=t.expando;t.event.special.inview={add:function(e){d[e.guid+"-"+this[c]]={data:e,$element:t(this)},l||t.isEmptyObject(d)||(l=setInterval(n,250))},remove:function(e){try{delete d[e.guid+"-"+this[c]]}catch(i){}t.isEmptyObject(d)&&(clearInterval(l),l=null)}},t(a).bind("scroll resize scrollstop",function(){o=h=null}),!f.addEventListener&&f.attachEvent&&f.attachEvent("onfocusin",function(){h=null})}(jQuery);;
3126/*
3127 *	jQuery dotdotdot 1.7.4
3128 *
3129 *	Copyright (c) Fred Heusschen
3130 *	www.frebsite.nl
3131 *
3132 *	Plugin website:
3133 *	dotdotdot.frebsite.nl
3134 *
3135 *	Licensed under the MIT license.
3136 *	http://en.wikipedia.org/wiki/MIT_License
3137 */
3138!function(t,e){function n(t,e,n){var r=t.children(),o=!1;t.empty();for(var i=0,d=r.length;d>i;i++){var l=r.eq(i);if(t.append(l),n&&t.append(n),a(t,e)){l.remove(),o=!0;break}n&&n.detach()}return o}function r(e,n,i,d,l){var s=!1,c="a, table, thead, tbody, tfoot, tr, col, colgroup, object, embed, param, ol, ul, dl, blockquote, select, optgroup, option, textarea, script, style",u="script, .dotdotdot-keep";return e.contents().detach().each(function(){var h=this,f=t(h);if("undefined"==typeof h)return!0;if(f.is(u))e.append(f);else{if(s)return!0;e.append(f),!l||f.is(d.after)||f.find(d.after).length||e[e.is(c)?"after":"append"](l),a(i,d)&&(s=3==h.nodeType?o(f,n,i,d,l):r(f,n,i,d,l),s||(f.detach(),s=!0)),s||l&&l.detach()}}),n.addClass("is-truncated"),s}function o(e,n,r,o,d){var c=e[0];if(!c)return!1;var h=s(c),f=-1!==h.indexOf(" ")?" ":" ",p="letter"==o.wrap?"":f,g=h.split(p),v=-1,w=-1,b=0,y=g.length-1;for(o.fallbackToLetter&&0==b&&0==y&&(p="",g=h.split(p),y=g.length-1);y>=b&&(0!=b||0!=y);){var m=Math.floor((b+y)/2);if(m==w)break;w=m,l(c,g.slice(0,w+1).join(p)+o.ellipsis),r.children().each(function(){t(this).toggle().toggle()}),a(r,o)?(y=w,o.fallbackToLetter&&0==b&&0==y&&(p="",g=g[0].split(p),v=-1,w=-1,b=0,y=g.length-1)):(v=w,b=w)}if(-1==v||1==g.length&&0==g[0].length){var x=e.parent();e.detach();var T=d&&d.closest(x).length?d.length:0;x.contents().length>T?c=u(x.contents().eq(-1-T),n):(c=u(x,n,!0),T||x.detach()),c&&(h=i(s(c),o),l(c,h),T&&d&&t(c).parent().append(d))}else h=i(g.slice(0,v+1).join(p),o),l(c,h);return!0}function a(t,e){return t.innerHeight()>e.maxHeight}function i(e,n){for(;t.inArray(e.slice(-1),n.lastCharacter.remove)>-1;)e=e.slice(0,-1);return t.inArray(e.slice(-1),n.lastCharacter.noEllipsis)<0&&(e+=n.ellipsis),e}function d(t){return{width:t.innerWidth(),height:t.innerHeight()}}function l(t,e){t.innerText?t.innerText=e:t.nodeValue?t.nodeValue=e:t.textContent&&(t.textContent=e)}function s(t){return t.innerText?t.innerText:t.nodeValue?t.nodeValue:t.textContent?t.textContent:""}function c(t){do t=t.previousSibling;while(t&&1!==t.nodeType&&3!==t.nodeType);return t}function u(e,n,r){var o,a=e&&e[0];if(a){if(!r){if(3===a.nodeType)return a;
3138if(t.trim(e.text()))return u(e.contents().last(),n)}for(o=c(a);!o;){if(e=e.parent(),e.is(n)||!e.length)return!1;o=c(e[0])}if(o)return u(t(o),n)}return!1}function h(e,n){return e?"string"==typeof e?(e=t(e,n),e.length?e:!1):e.jquery?e:!1:!1}function f(t){for(var e=t.innerHeight(),n=["paddingTop","paddingBottom"],r=0,o=n.length;o>r;r++){var a=parseInt(t.css(n[r]),10);isNaN(a)&&(a=0),e-=a}return e}if(!t.fn.dotdotdot){t.fn.dotdotdot=function(e){if(0==this.length)return t.fn.dotdotdot.debug('No element found for "'+this.selector+'".'),this;if(this.length>1)return this.each(function(){t(this).dotdotdot(e)});var o=this;o.data("dotdotdot")&&o.trigger("destroy.dot"),o.data("dotdotdot-style",o.attr("style")||""),o.css("word-wrap","break-word"),"nowrap"===o.css("white-space")&&o.css("white-space","normal"),o.bind_events=function(){return o.bind("update.dot",function(e,d){switch(o.removeClass("is-truncated"),e.preventDefault(),e.stopPropagation(),typeof l.height){case"number":l.maxHeight=l.height;break;case"function":l.maxHeight=l.height.call(o[0]);break;default:l.maxHeight=f(o)}l.maxHeight+=l.tolerance,"undefined"!=typeof d&&(("string"==typeof d||"nodeType"in d&&1===d.nodeType)&&(d=t("<div />").append(d).contents()),d instanceof t&&(i=d)),g=o.wrapInner('<div class="dotdotdot" />').children(),g.contents().detach().end().append(i.clone(!0)).find("br").replaceWith("  <br />  ").end().css({height:"auto",width:"auto",border:"none",padding:0,margin:0});var c=!1,u=!1;return s.afterElement&&(c=s.afterElement.clone(!0),c.show(),s.afterElement.detach()),a(g,l)&&(u="children"==l.wrap?n(g,l,c):r(g,o,g,l,c)),g.replaceWith(g.contents()),g=null,t.isFunction(l.callback)&&l.callback.call(o[0],u,i),s.isTruncated=u,u}).bind("isTruncated.dot",function(t,e){return t.preventDefault(),t.stopPropagation(),"function"==typeof e&&e.call(o[0],s.isTruncated),s.isTruncated}).bind("originalContent.dot",function(t,e){return t.preventDefault(),t.stopPropagation(),"function"==typeof e&&e.call(o[0],i),i}).bind("destroy.dot",function(t){t.preventDefault(),t.stopPropagation(),o.unwatch().unbind_events().contents().detach().end().append(i).attr("style",o.data("dotdotdot-style")||"").data("dotdotdot",!1)}),o},o.unbind_events=function(){return o.unbind(".dot"),o},o.watch=function(){if(o.unwatch(),"window"==l.watch){var e=t(window),n=e.width(),r=e.height();e.bind("resize.dot"+s.dotId,function(){n==e.width()&&r==e.height()&&l.windowResizeFix||(n=e.width(),r=e.height(),u&&clearInterval(u),u=setTimeout(function(){o.trigger("update.dot")},100))})}else c=d(o),u=setInterval(function(){if(o.is(":visible")){var t=d(o);(c.width!=t.width||c.height!=t.height)&&(o.trigger("update.dot"),c=t)}},500);return o},o.unwatch=function(){return t(window).unbind("resize.dot"+s.dotId),u&&clearInterval(u),o};var i=o.contents(),l=t.extend(!0,{},t.fn.dotdotdot.defaults,e),s={},c={},u=null,g=null;return l.lastCharacter.remove instanceof Array||(l.lastCharacter.remove=t.fn.dotdotdot.defaultArrays.lastCharacter.remove),l.lastCharacter.noEllipsis instanceof Array||(l.lastCharacter.noEllipsis=t.fn.dotdotdot.defaultArrays.lastCharacter.noEllipsis),s.afterElement=h(l.after,o),s.isTruncated=!1,s.dotId=p++,o.data("dotdotdot",!0).bind_events().trigger("update.dot"),l.watch&&o.watch(),o},t.fn.dotdotdot.defaults={ellipsis:"... ",wrap:"word",fallbackToLetter:!0,lastCharacter:{},tolerance:0,callback:null,after:null,height:null,watch:!1,windowResizeFix:!0},t.fn.dotdotdot.defaultArrays={lastCharacter:{remove:[" "," ",",",";",".","!","?"],noEllipsis:[]}},t.fn.dotdotdot.debug=function(){};var p=1,g=t.fn.html;t.fn.html=function(n){return n!=e&&!t.isFunction(n)&&this.data("dotdotdot")?this.trigger("update",[n]):g.apply(this,arguments)};var v=t.fn.text;t.fn.text=function(n){return n!=e&&!t.isFunction(n)&&this.data("dotdotdot")?(n=t("<div />").text(n).html(),this.trigger("update",[n])):v.apply(this,arguments)}}}(jQuery);;
3139/*
3140     _ _      _       _
3141 ___| (_) ___| | __  (_)___
3142/ __| | |/ __| |/ /  | / __|
3143\__ \ | | (__|   < _ | \__ \
3144|___/_|_|\___|_|\_(_)/ |___/
3145                   |__/
3146
3147 Version: 1.5.8
3148  Author: Ken Wheeler
3149 Website: http://kenwheeler.github.io
3150    Docs: http://kenwheeler.github.io/slick
3151    Repo: http://github.com/kenwheeler/slick
3152  Issues: http://github.com/kenwheeler/slick/issues
3153
3154 */
3155!function(a){"use strict";"function"==typeof define&&define.amd?define(["jquery"],a):"undefined"!=typeof exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){"use strict";var b=window.Slick||{};b=function(){function c(c,d){var f,e=this;e.defaults={accessibility:!0,adaptiveHeight:!1,appendArrows:a(c),appendDots:a(c),arrows:!0,asNavFor:null,prevArrow:'<button type="button" data-role="none" class="slick-prev" aria-label="Previous" tabindex="0" role="button">Previous</button>',nextArrow:'<button type="button" data-role="none" class="slick-next" aria-label="Next" tabindex="0" role="button">Next</button>',autoplay:!1,autoplaySpeed:3e3,centerMode:!1,centerPadding:"50px",cssEase:"ease",customPaging:function(a,b){return'<button type="button" data-role="none" role="button" aria-required="false" tabindex="0">'+(b+1)+"</button>"},dots:!1,dotsClass:"slick-dots",draggable:!0,easing:"linear",edgeFriction:.35,fade:!1,focusOnSelect:!1,infinite:!0,initialSlide:0,lazyLoad:"ondemand",mobileFirst:!1,pauseOnHover:!0,pauseOnDotsHover:!1,respondTo:"window",responsive:null,rows:1,rtl:!1,slide:"",slidesPerRow:1,slidesToShow:1,slidesToScroll:1,speed:500,swipe:!0,swipeToSlide:!1,touchMove:!0,touchThreshold:5,useCSS:!0,variableWidth:!1,vertical:!1,verticalSwiping:!1,waitForAnimate:!0,zIndex:1e3},e.initials={animating:!1,dragging:!1,autoPlayTimer:null,currentDirection:0,currentLeft:null,currentSlide:0,direction:1,$dots:null,listWidth:null,listHeight:null,loadIndex:0,$nextArrow:null,$prevArrow:null,slideCount:null,slideWidth:null,$slideTrack:null,$slides:null,
vendor: 9,275 bytes, line 3155
3155sliding:!1,slideOffset:0,swipeLeft:null,$list:null,touchObject:{},transformsEnabled:!1,unslicked:!1},a.extend(e,e.initials),e.activeBreakpoint=null,e.animType=null,e.animProp=null,e.breakpoints=[],e.breakpointSettings=[],e.cssTransitions=!1,e.hidden="hidden",e.paused=!1,e.positionProp=null,e.respondTo=null,e.rowCount=1,e.shouldClick=!0,e.$slider=a(c),e.$slidesCache=null,e.transformType=null,e.transitionType=null,e.visibilityChange="visibilitychange",e.windowWidth=0,e.windowTimer=null,f=a(c).data("slick")||{},e.options=a.extend({},e.defaults,f,d),e.currentSlide=e.options.initialSlide,e.originalSettings=e.options,"undefined"!=typeof document.mozHidden?(e.hidden="mozHidden",e.visibilityChange="mozvisibilitychange"):"undefined"!=typeof document.webkitHidden&&(e.hidden="webkitHidden",e.visibilityChange="webkitvisibilitychange"),e.autoPlay=a.proxy(e.autoPlay,e),e.autoPlayClear=a.proxy(e.autoPlayClear,e),e.changeSlide=a.proxy(e.changeSlide,e),e.clickHandler=a.proxy(e.clickHandler,e),e.selectHandler=a.proxy(e.selectHandler,e),e.setPosition=a.proxy(e.setPosition,e),e.swipeHandler=a.proxy(e.swipeHandler,e),e.dragHandler=a.proxy(e.dragHandler,e),e.keyHandler=a.proxy(e.keyHandler,e),e.autoPlayIterator=a.proxy(e.autoPlayIterator,e),e.instanceUid=b++,e.htmlExpr=/^(?:\s*(<[\w\W]+>)[^>]*)$/,e.registerBreakpoints(),e.init(!0),e.checkResponsive(!0)}var b=0;return c}(),b.prototype.addSlide=b.prototype.slickAdd=function(b,c,d){var e=this;if("boolean"==typeof c)d=c,c=null;else if(0>c||c>=e.slideCount)return!1;e.unload(),"number"==typeof c?0===c&&0===e.$slides.length?a(b).appendTo(e.$slideTrack):d?a(b).insertBefore(e.$slides.eq(c)):a(b).insertAfter(e.$slides.eq(c)):d===!0?a(b).prependTo(e.$slideTrack):a(b).appendTo(e.$slideTrack),e.$slides=e.$slideTrack.children(this.options.slide),e.$slideTrack.children(this.options.slide).detach(),e.$slideTrack.append(e.$slides),e.$slides.each(function(b,c){a(c).attr("data-slick-index",b)}),e.$slidesCache=e.$slides,e.reinit()},b.prototype.animateHeight=function(){var a=this;if(1===a.options.slidesToShow&&a.options.adaptiveHeight===!0&&a.options.vertical===!1){var b=a.$slides.eq(a.currentSlide).outerHeight(!0);a.$list.animate({height:b},a.options.speed)}},b.prototype.animateSlide=function(b,c){var d={},e=this;e.animateHeight(),e.options.rtl===!0&&e.options.vertical===!1&&(b=-b),e.transformsEnabled===!1?e.options.vertical===!1?e.$slideTrack.animate({left:b},e.options.speed,e.options.easing,c):e.$slideTrack.animate({top:b},e.options.speed,e.options.easing,c):e.cssTransitions===!1?(e.options.rtl===!0&&(e.currentLeft=-e.currentLeft),a({animStart:e.currentLeft}).animate({animStart:b},{duration:e.options.speed,easing:e.options.easing,step:function(a){a=Math.ceil(a),e.options.vertical===!1?(d[e.animType]="translate("+a+"px, 0px)",e.$slideTrack.css(d)):(d[e.animType]="translate(0px,"+a+"px)",e.$slideTrack.css(d))},complete:function(){c&&c.call()}})):(e.applyTransition(),b=Math.ceil(b),d[e.animType]=e.options.vertical===!1?"translate3d("+b+"px, 0px, 0px)":"translate3d(0px,"+b+"px, 0px)",e.$slideTrack.css(d),c&&setTimeout(function(){e.disableTransition(),c.call()},e.options.speed))},b.prototype.asNavFor=function(b){var c=this,d=c.options.asNavFor;d&&null!==d&&(d=a(d).not(c.$slider)),null!==d&&"object"==typeof d&&d.each(function(){var c=a(this).slick("getSlick");c.unslicked||c.slideHandler(b,!0)})},b.prototype.applyTransition=function(a){var b=this,c={};c[b.transitionType]=b.options.fade===!1?b.transformType+" "+b.options.speed+"ms "+b.options.cssEase:"opacity "+b.options.speed+"ms "+b.options.cssEase,b.options.fade===!1?b.$slideTrack.css(c):b.$slides.eq(a).css(c)},b.prototype.autoPlay=function(){var a=this;a.autoPlayTimer&&clearInterval(a.autoPlayTimer),a.slideCount>a.options.slidesToShow&&a.paused!==!0&&(a.autoPlayTimer=setInterval(a.autoPlayIterator,a.options.autoplaySpeed))},b.prototype.autoPlayClear=function(){var a=this;a.autoPlayTimer&&clearInterval(a.autoPlayTimer)},b.prototype.autoPlayIterator=function(){var a=this;a.options.infinite===!1?1===a.direction?(a.currentSlide+1===a.slideCount-1&&(a.direction=0),a.slideHandler(a.currentSlide+a.options.slidesToScroll)):(0===a.currentSlide-1&&(a.direction=1),a.slideHandler(a.currentSlide-a.options.slidesToScroll)):a.slideHandler(a.currentSlide+a.options.slidesToScroll)},b.prototype.buildArrows=function(){var b=this;b.options.arrows===!0&&(b.$prevArrow=a(b.options.prevArrow).addClass("slick-arrow"),b.$nextArrow=a(b.options.nextArrow).addClass("slick-arrow"),b.slideCount>b.options.slidesToShow?(b.$prevArrow.removeClass("slick-hidden").removeAttr("aria-hidden tabindex"),b.$nextArrow.removeClass("slick-hidden").removeAttr("aria-hidden tabindex"),b.htmlExpr.test(b.options.prevArrow)&&b.$prevArrow.prependTo(b.options.appendArrows),b.htmlExpr.test(b.options.nextArrow)&&b.$nextArrow.appendTo(b.options.appendArrows),b.options.infinite!==!0&&b.$prevArrow.addClass("slick-disabled").attr("aria-disabled","true")):b.$prevArrow.add(b.$nextArrow).addClass("slick-hidden").attr({"aria-disabled":"true",tabindex:"-1"}))},b.prototype.buildDots=function(){var c,d,b=this;if(b.options.dots===!0&&b.slideCount>b.options.slidesToShow){for(d='<ul class="'+b.options.dotsClass+'">',c=0;c<=b.getDotCount();c+=1)d+="<li>"+b.options.customPaging.call(this,b,c)+"</li>";d+="</ul>",b.$dots=a(d).appendTo(b.options.appendDots),b.$dots.find("li").first().addClass("slick-active").attr("aria-hidden","false")}},b.prototype.buildOut=function(){var b=this;b.$slides=b.$slider.children(b.options.slide+":not(.slick-cloned)").addClass("slick-slide"),b.slideCount=b.$slides.length,b.$slides.each(function(b,c){a(c).attr("data-slick-index",b).data("originalStyling",a(c).attr("style")||"")}),b.$slidesCache=b.$slides,b.$slider.addClass("slick-slider"),b.$slideTrack=0===b.slideCount?a('<div class="slick-track"/>').appendTo(b.$slider):b.$slides.wrapAll('<div class="slick-track"/>').parent(),b.$list=b.$slideTrack.wrap('<div aria-live="polite" class="slick-list"/>').parent(),b.$slideTrack.css("opacity",0),(b.options.centerMode===!0||b.options.swipeToSlide===!0)&&(b.options.slidesToScroll=1),a("img[data-lazy]",b.$slider).not("[src]").addClass("slick-loading"),b.setupInfinite(),b.buildArrows(),b.buildDots(),b.updateDots(),b.setSlideClasses("number"==typeof b.currentSlide?b.currentSlide:0),b.options.draggable===!0&&b.$list.addClass("draggable")},b.prototype.buildRows=function(){var b,c,d,e,f,g,h,a=this;if(e=document.createDocumentFragment(),g=a.$slider.children(),a.options.rows>1){for(h=a.options.slidesPerRow*a.options.rows,f=Math.ceil(g.length/h),b=0;f>b;b++){var i=document.createElement("div");for(c=0;c<a.options.rows;c++){var j=document.createElement("div");for(d=0;d<a.options.slidesPerRow;d++){var k=b*h+(c*a.options.slidesPerRow+d);g.get(k)&&j.appendChild(g.get(k))}i.appendChild(j)}e.appendChild(i)}a.$slider.html(e),a.$slider.children().children().children().css({width:100/a.options.slidesPerRow+"%",display:"inline-block"})}},b.prototype.checkResponsive=function(b,c){var e,f,g,d=this,h=!1,i=d.$slider.width(),j=window.innerWidth||a(window).width();if("window"===d.respondTo?g=j:"slider"===d.respondTo?g=i:"min"===d.respondTo&&(g=Math.min(j,i)),d.options.responsive&&d.options.responsive.length&&null!==d.options.responsive){f=null;for(e in d.breakpoints)d.breakpoints.hasOwnProperty(e)&&(d.originalSettings.mobileFirst===!1?g<d.breakpoints[e]&&(f=d.breakpoints[e]):g>d.breakpoints[e]&&(f=d.breakpoints[e]));null!==f?null!==d.activeBreakpoint?(f!==d.activeBreakpoint||c)&&(d.activeBreakpoint=f,"unslick"===d.breakpointSettings[f]?d.unslick(f):(d.options=a.extend({},d.originalSettings,d.breakpointSettings[f]),b===!0&&(d.currentSlide=d.options.initialSlide),d.refresh(b)),h=f):(d.activeBreakpoint=f,"unslick"===d.breakpointSettings[f]?d.unslick(f):(d.options=a.extend({},d.originalSettings,d.breakpointSettings[f]),b===!0&&(d.currentSlide=d.options.initialSlide),d.refresh(b)),h=f):null!==d.activeBreakpoint&&(d.activeBreakpoint=null,d.options=d.originalSettings,b===!0&&(d.currentSlide=d.options.initialSlide),d.refresh(b),h=f),b||h===!1||d.$slider.trigger("breakpoint",[d,h])}},b.prototype.changeSlide=function(b,c){var f,g,h,d=this,e=a(b.target);switch(e.is("a")&&b.preventDefault(),e.is("li")||(e=e.closest("li")),h=0!==d.slideCount%d.options.slidesToScroll,f=h?0:(d.slideCount-d.currentSlide)%d.options.slidesToScroll,b.data.message){case"previous":g=0===f?d.options.slidesToScroll:d.options.slidesToShow-f,d.slideCount>d.options.slidesToShow&&d.slideHandler(d.currentSlide-g,!1,c);break;case"next":g=0===f?d.options.slidesToScroll:f,d.slideCount>d.options.slidesToShow&&d.slideHandler(d.currentSlide+g,!1,c);break;case"index":var i=0===b.data.index?0:b.data.index||e.index()*d.options.slidesToScroll;d.slideHandler(d.checkNavigable(i),!1,c),e.children().trigger("focus");break;default:return}},b.prototype.checkNavigable=function(a){var c,d,b=this;if(c=b.getNavigableIndexes(),d=0,a>c[c.length-1])a=c[c.length-1];else for(var e in c){if(a<c[e]){a=d;break}d=c[e]}return a},b.prototype.cleanUpEvents=function(){var b=this;b.options.dots&&null!==b.$dots&&(a("li",b.$dots).off("click.slick",b.changeSlide),b.options.pauseOnDotsHover===!0&&b.options.autoplay===!0&&a("li",b.$dots).off("mouseenter.slick",a.proxy(b.setPaused,b,!0)).off("mouseleave.
3155slick",a.proxy(b.setPaused,b,!1))),b.options.arrows===!0&&b.slideCount>b.options.slidesToShow&&(b.$prevArrow&&b.$prevArrow.off("click.slick",b.changeSlide),b.$nextArrow&&b.$nextArrow.off("click.slick",b.changeSlide)),b.$list.off("touchstart.slick mousedown.slick",b.swipeHandler),b.$list.off("touchmove.slick mousemove.slick",b.swipeHandler),b.$list.off("touchend.slick mouseup.slick",b.swipeHandler),b.$list.off("touchcancel.slick mouseleave.slick",b.swipeHandler),b.$list.off("click.slick",b.clickHandler),a(document).off(b.visibilityChange,b.visibility),b.$list.off("mouseenter.slick",a.proxy(b.setPaused,b,!0)),b.$list.off("mouseleave.slick",a.proxy(b.setPaused,b,!1)),b.options.accessibility===!0&&b.$list.off("keydown.slick",b.keyHandler),b.options.focusOnSelect===!0&&a(b.$slideTrack).children().off("click.slick",b.selectHandler),a(window).off("orientationchange.slick.slick-"+b.instanceUid,b.orientationChange),a(window).off("resize.slick.slick-"+b.instanceUid,b.resize),a("[draggable!=true]",b.$slideTrack).off("dragstart",b.preventDefault),a(window).off("load.slick.slick-"+b.instanceUid,b.setPosition),a(document).off("ready.slick.slick-"+b.instanceUid,b.setPosition)},b.prototype.cleanUpRows=function(){var b,a=this;a.options.rows>1&&(b=a.$slides.children().children(),b.removeAttr("style"),a.$slider.html(b))},b.prototype.clickHandler=function(a){var b=this;b.shouldClick===!1&&(a.stopImmediatePropagation(),a.stopPropagation(),a.preventDefault())},b.prototype.destroy=function(b){var c=this;c.autoPlayClear(),c.touchObject={},c.cleanUpEvents(),a(".slick-cloned",c.$slider).detach(),c.$dots&&c.$dots.remove(),c.$prevArrow&&c.$prevArrow.length&&(c.$prevArrow.removeClass("slick-disabled slick-arrow slick-hidden").removeAttr("aria-hidden aria-disabled tabindex").css("display",""),c.htmlExpr.test(c.options.prevArrow)&&c.$prevArrow.remove()),c.$nextArrow&&c.$nextArrow.length&&(c.$nextArrow.removeClass("slick-disabled slick-arrow slick-hidden").removeAttr("aria-hidden aria-disabled tabindex").css("display",""),c.htmlExpr.test(c.options.nextArrow)&&c.$nextArrow.remove()),c.$slides&&(c.$slides.removeClass("slick-slide slick-active slick-center slick-visible slick-current").removeAttr("aria-hidden").removeAttr("data-slick-index").each(function(){a(this).attr("style",a(this).data("originalStyling"))}),c.$slideTrack.children(this.options.slide).detach(),c.$slideTrack.detach(),c.$list.detach(),c.$slider.append(c.$slides)),c.cleanUpRows(),c.$slider.removeClass("slick-slider"),c.$slider.removeClass("slick-initialized"),c.unslicked=!0,b||c.$slider.trigger("destroy",[c])},b.prototype.disableTransition=function(a){var b=this,c={};c[b.transitionType]="",b.options.fade===!1?b.$slideTrack.css(c):b.$slides.eq(a).css(c)},b.prototype.fadeSlide=function(a,b){var c=this;c.cssTransitions===!1?(c.$slides.eq(a).css({zIndex:c.options.zIndex}),c.$slides.eq(a).animate({opacity:1},c.options.speed,c.options.easing,b)):(c.applyTransition(a),c.$slides.eq(a).css({opacity:1,zIndex:c.options.zIndex}),b&&setTimeout(function(){c.disableTransition(a),b.call()},c.options.speed))},b.prototype.fadeSlideOut=function(a){var b=this;b.cssTransitions===!1?b.$slides.eq(a).animate({opacity:0,zIndex:b.options.zIndex-2},b.options.speed,b.options.easing):(b.applyTransition(a),b.$slides.eq(a).css({opacity:0,zIndex:b.options.zIndex-2}))},b.prototype.filterSlides=b.prototype.slickFilter=function(a){var b=this;null!==a&&(b.unload(),b.$slideTrack.children(this.options.slide).detach(),b.$slidesCache.filter(a).appendTo(b.$slideTrack),b.reinit())},b.prototype.getCurrent=b.prototype.slickCurrentSlide=function(){var a=this;return a.currentSlide},b.prototype.getDotCount=function(){var a=this,b=0,c=0,d=0;if(a.options.infinite===!0)for(;b<a.slideCount;)++d,b=c+a.options.slidesToShow,c+=a.options.slidesToScroll<=a.options.slidesToShow?a.options.slidesToScroll:a.options.slidesToShow;else if(a.options.centerMode===!0)d=a.slideCount;else for(;b<a.slideCount;)++d,b=c+a.options.slidesToShow,c+=a.options.slidesToScroll<=a.options.slidesToShow?a.options.slidesToScroll:a.options.slidesToShow;return d-1},b.prototype.getLeft=function(a){var c,d,f,b=this,e=0;return b.slideOffset=0,d=b.$slides.first().outerHeight(!0),b.options.infinite===!0?(b.slideCount>b.options.slidesToShow&&(b.slideOffset=-1*b.slideWidth*b.options.slidesToShow,e=-1*d*b.options.slidesToShow),0!==b.slideCount%b.options.slidesToScroll&&a+b.options.slidesToScroll>b.slideCount&&b.slideCount>b.options.slidesToShow&&(a>b.slideCount?(b.slideOffset=-1*(b.options.slidesToShow-(a-b.slideCount))*b.slideWidth,e=-1*(b.options.slidesToShow-(a-b.slideCount))*d):(b.slideOffset=-1*b.slideCount%b.options.slidesToScroll*b.slideWidth,e=-1*b.slideCount%b.options.slidesToScroll*d))):a+b.o
3155ptions.slidesToShow>b.slideCount&&(b.slideOffset=(a+b.options.slidesToShow-b.slideCount)*b.slideWidth,e=(a+b.options.slidesToShow-b.slideCount)*d),b.slideCount<=b.options.slidesToShow&&(b.slideOffset=0,e=0),b.options.centerMode===!0&&b.options.infinite===!0?b.slideOffset+=b.slideWidth*Math.floor(b.options.slidesToShow/2)-b.slideWidth:b.options.centerMode===!0&&(b.slideOffset=0,b.slideOffset+=b.slideWidth*Math.floor(b.options.slidesToShow/2)),c=b.options.vertical===!1?-1*a*b.slideWidth+b.slideOffset:-1*a*d+e,b.options.variableWidth===!0&&(f=b.slideCount<=b.options.slidesToShow||b.options.infinite===!1?b.$slideTrack.children(".slick-slide").eq(a):b.$slideTrack.children(".slick-slide").eq(a+b.options.slidesToShow),c=f[0]?-1*f[0].offsetLeft:0,b.options.centerMode===!0&&(f=b.options.infinite===!1?b.$slideTrack.children(".slick-slide").eq(a):b.$slideTrack.children(".slick-slide").eq(a+b.options.slidesToShow+1),c=f[0]?-1*f[0].offsetLeft:0,c+=(b.$list.width()-f.outerWidth())/2)),c},b.prototype.getOption=b.prototype.slickGetOption=function(a){var b=this;return b.options[a]},b.prototype.getNavigableIndexes=function(){var e,a=this,b=0,c=0,d=[];for(a.options.infinite===!1?e=a.slideCount:(b=-1*a.options.slidesToScroll,c=-1*a.options.slidesToScroll,e=2*a.slideCount);e>b;)d.push(b),b=c+a.options.slidesToScroll,c+=a.options.slidesToScroll<=a.options.slidesToShow?a.options.slidesToScroll:a.options.slidesToShow;return d},b.prototype.getSlick=function(){return this},b.prototype.getSlideCount=function(){var c,d,e,b=this;return e=b.options.centerMode===!0?b.slideWidth*Math.floor(b.options.slidesToShow/2):0,b.options.swipeToSlide===!0?(b.$slideTrack.find(".slick-slide").each(function(c,f){return f.offsetLeft-e+a(f).outerWidth()/2>-1*b.swipeLeft?(d=f,!1):void 0}),c=Math.abs(a(d).attr("data-slick-index")-b.currentSlide)||1):b.options.slidesToScroll},b.prototype.goTo=b.prototype.slickGoTo=function(a,b){var c=this;c.changeSlide({data:{message:"index",index:parseInt(a)}},b)},b.prototype.init=function(b){var c=this;a(c.$slider).hasClass("slick-initialized")||(a(c.$slider).addClass("slick-initialized"),c.buildRows(),c.buildOut(),c.setProps(),c.startLoad(),c.loadSlider(),c.initializeEvents(),c.updateArrows(),c.updateDots()),b&&c.$slider.trigger("init",[c]),c.options.accessibility===!0&&c.initADA()},b.prototype.initArrowEvents=function(){var a=this;a.options.arrows===!0&&a.slideCount>a.options.slidesToShow&&(a.$prevArrow.on("click.slick",{message:"previous"},a.changeSlide),a.$nextArrow.on("click.slick",{message:"next"},a.changeSlide))},b.prototype.initDotEvents=function(){var b=this;b.options.dots===!0&&b.slideCount>b.options.slidesToShow&&a("li",b.$dots).on("click.slick",{message:"index"},b.changeSlide),b.options.dots===!0&&b.options.pauseOnDotsHover===!0&&b.options.autoplay===!0&&a("li",b.$dots).on("mouseenter.slick",a.proxy(b.setPaused,b,!0)).on("mouseleave.slick",a.proxy(b.setPaused,b,!1))},b.prototype.initializeEvents=function(){var b=this;b.initArrowEvents(),b.initDotEvents(),b.$list.on("touchstart.slick mousedown.slick",{action:"start"},b.swipeHandler),b.$list.on("touchmove.slick mousemove.slick",{action:"move"},b.swipeHandler),b.$list.on("touchend.slick mouseup.slick",{action:"end"}
vendor: 1,558 bytes, line 3155
3155,b.swipeHandler),b.$list.on("touchcancel.slick mouseleave.slick",{action:"end"},b.swipeHandler),b.$list.on("click.slick",b.clickHandler),a(document).on(b.visibilityChange,a.proxy(b.visibility,b)),b.$list.on("mouseenter.slick",a.proxy(b.setPaused,b,!0)),b.$list.on("mouseleave.slick",a.proxy(b.setPaused,b,!1)),b.options.accessibility===!0&&b.$list.on("keydown.slick",b.keyHandler),b.options.focusOnSelect===!0&&a(b.$slideTrack).children().on("click.slick",b.selectHandler),a(window).on("orientationchange.slick.slick-"+b.instanceUid,a.proxy(b.orientationChange,b)),a(window).on("resize.slick.slick-"+b.instanceUid,a.proxy(b.resize,b)),a("[draggable!=true]",b.$slideTrack).on("dragstart",b.preventDefault),a(window).on("load.slick.slick-"+b.instanceUid,b.setPosition),a(document).on("ready.slick.slick-"+b.instanceUid,b.setPosition)},b.prototype.initUI=function(){var a=this;a.options.arrows===!0&&a.slideCount>a.options.slidesToShow&&(a.$prevArrow.show(),a.$nextArrow.show()),a.options.dots===!0&&a.slideCount>a.options.slidesToShow&&a.$dots.show(),a.options.autoplay===!0&&a.autoPlay()},b.prototype.keyHandler=function(a){var b=this;a.target.tagName.match("TEXTAREA|INPUT|SELECT")||(37===a.keyCode&&b.options.accessibility===!0?b.changeSlide({data:{message:"previous"}}):39===a.keyCode&&b.options.accessibility===!0&&b.changeSlide({data:{message:"next"}}))},b.prototype.lazyLoad=function(){function g(b){a("img[data-lazy]",b).each(function(){var b=a(this),c=a(this).attr("data-lazy"),d=document.createElement("img");d.onload=function(){b.animate({opacity:0}
3155,100,function(){b.attr("src",c).animate({opacity:1},200,function(){b.removeAttr("data-lazy").removeClass("slick-loading")})})},d.src=c})}var c,d,e,f,b=this;b.options.centerMode===!0?b.options.infinite===!0?(e=b.currentSlide+(b.options.slidesToShow/2+1),f=e+b.options.slidesToShow+2):(e=Math.max(0,b.currentSlide-(b.options.slidesToShow/2+1)),f=2+(b.options.slidesToShow/2+1)+b.currentSlide):(e=b.options.infinite?b.options.slidesToShow+b.currentSlide:b.currentSlide,f=e+b.options.slidesToShow,b.options.fade===!0&&(e>0&&e--,f<=b.slideCount&&f++)),c=b.$slider.find(".slick-slide").slice(e,f),g(c),b.slideCount<=b.options.slidesToShow?(d=b.$slider.find(".slick-slide"),g(d)):b.currentSlide>=b.slideCount-b.options.slidesToShow?(d=b.$slider.find(".slick-cloned").slice(0,b.options.slidesToShow),g(d)):0===b.currentSlide&&(d=b.$slider.find(".slick-cloned").slice(-1*b.options.slidesToShow),g(d))},b.prototype.loadSlider=function(){var a=this;a.setPosition(),a.$slideTrack.css({opacity:1}),a.$slider.removeClass("slick-loading"),a.initUI(),"progressive"===a.options.lazyLoad&&a.progressiveLazyLoad()},b.prototype.next=b.prototype.slickNext=function(){var a=this;a.changeSlide({data:{message:"next"}})},b.prototype.orientationChange=function(){var a=this;a.checkResponsive(),a.setPosition()},b.prototype.pause=b.prototype.slickPause=function(){var a=this;a.autoPlayClear(),a.paused=!0},b.prototype.play=b.prototype.slickPlay=function(){var a=this;a.paused=!1,a.autoPlay()},b.prototype.postSlide=function(a){var b=this;b.$slider.trigger("afterChange",[b,a]),b.animating=!1,b.setPosition(),b.swipeLeft=null,b.options.autoplay===!0&&b.paused===!1&&b.autoPlay(),b.options.accessibility===!0&&b.initADA()},b.prototype.prev=b.prototype.slickPrev=function(){var a=this;a.changeSlide({data:{message:"previous"}})},b.prototype.preventDefault=function(a){a.preventDefault()},b.prototype.progressiveLazyLoad=function(){var c,d,b=this;c=a("img[data-lazy]",b.$slider).length,c>0&&(d=a("img[data-lazy]",b.$slider).first(),d.attr("src",d.attr("data-lazy")).removeClass("slick-loading").load(function(){d.removeAttr("data-lazy"),b.progressiveLazyLoad(),b.options.adaptiveHeight===!0&&b.setPosition()}).error(function(){d.removeAttr("data-lazy"),b.progressiveLazyLoad()}))},b.prototype.refresh=function(b){var c=this,d=c.currentSlide;c.destroy(!0),a.extend(c,c.initials,{currentSlide:d}),c.init(),b||c.changeSlide({data:{message:"index",index:d}},!1)},b.prototype.registerBreakpoints=function(){var c,d,e,b=this,f=b.options.responsive||null;if("array"===a.type(f)&&f.length){b.respondTo=b.options.respondTo||"window";for(c in f)if(e=b.breakpoints.length-1,d=f[c].breakpoint,f.hasOwnProperty(c)){for(;e>=0;)b.breakpoints[e]&&b.breakpoints[e]===d&&b.breakpoints.splice(e,1),e--;b.breakpoints.push(d),b.breakpointSettings[d]=f[c].settings}b.breakpoints.sort(function(a,c){return b.options.mobileFirst?a-c:c-a})}},b.prototype.reinit=function(){var b=this;b.$slides=b.$slideTrack.children(b.options.slide).addClass("slick-slide"),b.slideCount=b.$slides.length,b.currentSlide>=b.slideCount&&0!==b.currentSlide&&(b.currentSlide=b.currentSlide-b.options.slidesToScroll),b.slideCount<=b.options.slidesToShow&&(b.currentSlide=0),b.registerBreakpoints(),b.setProps(),b.setupInfinite(),b.buildArrows(),b.updateArrows(),b.initArrowEvents(),b.buildDots(),b.updateDots(),b.initDotEvents(),b.checkResponsive(!1,!0),b.options.focusOnSelect===!0&&a(b.$slideTrack).children().on("click.slick",b.selectHandler),b.setSlideClasses(0),b.setPosition(),b.$slider.trigger("reInit",[b]),b.options.autoplay===!0&&b.focusHandler()},b.prototype.resize=function(){var b=this;a(window).width()!==b.windowWidth&&(clearTimeout(b.windowDelay),b.windowDelay=window.setTimeout(function(){b.windowWidth=a(window).width(),b.checkResponsive(),b.unslicked||b.setPosition()},50))},b.prototype.removeSlide=b.prototype.slickRemove=function(a,b,c){var d=this;return"boolean"==typeof a?(b=a,a=b===!0?0:d.slideCount-1):a=b===!0?--a:a,d.slideCount<1||0>a||a>d.slideCount-1?!1:(d.unload(),c===!0?d.$slideTrack.children().remove():d.$slideTrack.children(this.options.slide).eq(a).remove(),d.$slides=d.$slideTrack.children(this.options.slide),d.$slideTrack.children(this.options.slide).detach(),d.$slideTrack.append(d.$slides),d.$slidesCache=d.$slides,d.reinit(),void 0)},b.prototype.setCSS=function(a){var d,e,b=this,c={};b.options.rtl===!0&&(a=-a),d="left"==b.positionProp?Math.ceil(a)+"px":"0px",e="top"==b.positionProp?Math.ceil(a)+"px":"0px",c[b.positionProp]=a,b.transformsEnabled===!1?b.$slideTrack.css(c):(c={},b.cssTransitions===!1?(c[b.animType]="translate("+d+", "+e+")",b.$slideTrack.css(c)):(c[b.animType]="translate3d("+d+", "+e+", 0px)",b.$
vendor: 1,161 bytes, line 3155
3155slideTrack.css(c)))},b.prototype.setDimensions=function(){var a=this;a.options.vertical===!1?a.options.centerMode===!0&&a.$list.css({padding:"0px "+a.options.centerPadding}):(a.$list.height(a.$slides.first().outerHeight(!0)*a.options.slidesToShow),a.options.centerMode===!0&&a.$list.css({padding:a.options.centerPadding+" 0px"})),a.listWidth=a.$list.width(),a.listHeight=a.$list.height(),a.options.vertical===!1&&a.options.variableWidth===!1?(a.slideWidth=Math.ceil(a.listWidth/a.options.slidesToShow),a.$slideTrack.width(Math.ceil(a.slideWidth*a.$slideTrack.children(".slick-slide").length))):a.options.variableWidth===!0?a.$slideTrack.width(5e3*a.slideCount):(a.slideWidth=Math.ceil(a.listWidth),a.$slideTrack.height(Math.ceil(a.$slides.first().outerHeight(!0)*a.$slideTrack.children(".slick-slide").length)));var b=a.$slides.first().outerWidth(!0)-a.$slides.first().width();a.options.variableWidth===!1&&a.$slideTrack.children(".slick-slide").width(a.slideWidth-b)},b.prototype.setFade=function(){var c,b=this;b.$slides.each(function(d,e){c=-1*b.slideWidth*d,b.options.rtl===!0?a(e).css({position:"relative",right:c,top:0,zIndex:b.options.zIndex-2,opacity:0}
3155):a(e).css({position:"relative",left:c,top:0,zIndex:b.options.zIndex-2,opacity:0})}),b.$slides.eq(b.currentSlide).css({zIndex:b.options.zIndex-1,opacity:1})},b.prototype.setHeight=function(){var a=this;if(1===a.options.slidesToShow&&a.options.adaptiveHeight===!0&&a.options.vertical===!1){var b=a.$slides.eq(a.currentSlide).outerHeight(!0);a.$list.css("height",b)}},b.prototype.setOption=b.prototype.slickSetOption=function(b,c,d){var f,g,e=this;if("responsive"===b&&"array"===a.type(c))for(g in c)if("array"!==a.type(e.options.responsive))e.options.responsive=[c[g]];else{for(f=e.options.responsive.length-1;f>=0;)e.options.responsive[f].breakpoint===c[g].breakpoint&&e.options.responsive.splice(f,1),f--;e.options.responsive.push(c[g])}else e.options[b]=c;d===!0&&(e.unload(),e.reinit())},b.prototype.setPosition=function(){var a=this;a.setDimensions(),a.setHeight(),a.options.fade===!1?a.setCSS(a.getLeft(a.currentSlide)):a.setFade(),a.$slider.trigger("setPosition",[a])},b.prototype.setProps=function(){var a=this,b=document.body.style;a.positionProp=a.options.vertical===!0?"top":"left","top"===a.positionProp?a.$slider.addClass("slick-vertical"):a.$slider.removeClass("slick-vertical"),(void 0!==b.WebkitTransition||void 0!==b.MozTransition||void 0!==b.msTransition)&&a.options.useCSS===!0&&(a.cssTransitions=!0),a.options.fade&&("number"==typeof a.options.zIndex?a.options.zIndex<3&&(a.options.zIndex=3):a.options.zIndex=a.defaults.zIndex),void 0!==b.OTransform&&(a.animType="OTransform",a.transformType="-o-transform",a.transitionType="OTransition",void 0===b.perspectiveProperty&&void 0===b.webkitPerspective&&(a.animType=!1)),void 0!==b.MozTransform&&(a.animType="MozTransform",a.transformType="-moz-transform",a.transitionType="MozTransition",void 0===b.perspectiveProperty&&void 0===b.MozPerspective&&(a.animType=!1)),void 0!==b.webkitTransform&&(a.animType="webkitTransform",a.transformType="-webkit-transform",a.transitionType="webkitTransition",void 0===b.perspectiveProperty&&void 0===b.webkitPerspective&&(a.animType=!1)),void 0!==b.msTransform&&(a.animType="msTransform",a.transformType="-ms-transform",a.transitionType="msTransition",void 0===b.msTransform&&(a.animType=!1)),void 0!==b.transform&&a.animType!==!1&&(a.animType="transform",a.transformType="transform",a.transitionType="transition"),a.transformsEnabled=null!==a.animType&&a.animType!==!1},b.prototype.setSlideClasses=function(a){var c,d,e,f,b=this;d=b.$slider.find(".slick-slide").removeClass("slick-active slick-center slick-current").attr("aria-hidden","true"),b.$slides.eq(a).addClass("slick-current"),b.options.centerMode===!0?(c=Math.floor(b.options.slidesToShow/2),b.options.infinite===!0&&(a>=c&&a<=b.slideCount-1-c?b.$slides.slice(a-c,a+c+1).addClass("slick-active").attr("aria-hidden","false"):(e=b.options.slidesToShow+a,d.slice(e-c+1,e+c+2).addClass("slick-active").attr("aria-hidden","false")),0===a?d.eq(d.length-1-b.o
vendor: 1,404 bytes, line 3155
3155ptions.slidesToShow).addClass("slick-center"):a===b.slideCount-1&&d.eq(b.options.slidesToShow).addClass("slick-center")),b.$slides.eq(a).addClass("slick-center")):a>=0&&a<=b.slideCount-b.options.slidesToShow?b.$slides.slice(a,a+b.options.slidesToShow).addClass("slick-active").attr("aria-hidden","false"):d.length<=b.options.slidesToShow?d.addClass("slick-active").attr("aria-hidden","false"):(f=b.slideCount%b.options.slidesToShow,e=b.options.infinite===!0?b.options.slidesToShow+a:a,b.options.slidesToShow==b.options.slidesToScroll&&b.slideCount-a<b.options.slidesToShow?d.slice(e-(b.options.slidesToShow-f),e+f).addClass("slick-active").attr("aria-hidden","false"):d.slice(e,e+b.options.slidesToShow).addClass("slick-active").attr("aria-hidden","false")),"ondemand"===b.options.lazyLoad&&b.lazyLoad()},b.prototype.setupInfinite=function(){var c,d,e,b=this;if(b.options.fade===!0&&(b.options.centerMode=!1),b.options.infinite===!0&&b.options.fade===!1&&(d=null,b.slideCount>b.options.slidesToShow)){for(e=b.options.centerMode===!0?b.options.slidesToShow+1:b.options.slidesToShow,c=b.slideCount;c>b.slideCount-e;c-=1)d=c-1,a(b.$slides[d]).clone(!0).attr("id","").attr("data-slick-index",d-b.slideCount).prependTo(b.$slideTrack).addClass("slick-cloned");for(c=0;e>c;c+=1)d=c,a(b.$slides[d]).clone(!0).attr("id","").attr("data-slick-index",d+b.slideCount).appendTo(b.$slideTrack).addClass("slick-cloned");
3155b.$slideTrack.find(".slick-cloned").find("[id]").each(function(){a(this).attr("id","")})}},b.prototype.setPaused=function(a){var b=this;b.options.autoplay===!0&&b.options.pauseOnHover===!0&&(b.paused=a,a?b.autoPlayClear():b.autoPlay())},b.prototype.selectHandler=function(b){var c=this,d=a(b.target).is(".slick-slide")?a(b.target):a(b.target).parents(".slick-slide"),e=parseInt(d.attr("data-slick-index"));return e||(e=0),c.slideCount<=c.options.slidesToShow?(c.setSlideClasses(e),c.asNavFor(e),void 0):(c.slideHandler(e),void 0)},b.prototype.slideHandler=function(a,b,c){var d,e,f,g,h=null,i=this;return b=b||!1,i.animating===!0&&i.options.waitForAnimate===!0||i.options.fade===!0&&i.currentSlide===a||i.slideCount<=i.options.slidesToShow?void 0:(b===!1&&i.asNavFor(a),d=a,h=i.getLeft(d),g=i.getLeft(i.currentSlide),i.currentLeft=null===i.swipeLeft?g:i.swipeLeft,i.options.infinite===!1&&i.options.centerMode===!1&&(0>a||a>i.getDotCount()*i.options.slidesToScroll)?(i.options.fade===!1&&(d=i.currentSlide,c!==!0?i.animateSlide(g,function(){i.postSlide(d)}):i.postSlide(d)),void 0):i.options.infinite===!1&&i.options.centerMode===!0&&(0>a||a>i.slideCount-i.options.slidesToScroll)?(i.options.fade===!1&&(d=i.currentSlide,c!==!0?i.animateSlide(g,function(){i.postSlide(d)}):i.postSlide(d)),void 0):(i.options.autoplay===!0&&clearInterval(i.autoPlayTimer),e=0>d?0!==i.slideCount%i.options.slidesToScroll?i.slideCount-i.slideCount%i.options.slidesToScroll:i.slideCount+d:d>=i.slideCount?0!==i.slideCount%i.options.slidesToScroll?0:d-i.slideCount:d,i.animating=!0,i.$slider.trigger("beforeChange",[i,i.currentSlide,e]),f=i.currentSlide,i.currentSlide=e,i.setSlideClasses(i.currentSlide),i.updateDots(),i.updateArrows(),i.options.fade===!0?(c!==!0?(i.fadeSlideOut(f),i.fadeSlide(e,function(){i.postSlide(e)
3156})):i.postSlide(e),i.animateHeight(),void 0):(c!==!0?i.animateSlide(h,function(){i.postSlide(e)}):i.postSlide(e),void 0)))},b.prototype.startLoad=function(){var a=this;a.options.arrows===!0&&a.slideCount>a.options.slidesToShow&&(a.$prevArrow.hide(),a.$nextArrow.hide()),a.options.dots===!0&&a.slideCount>a.options.slidesToShow&&a.$dots.hide(),a.$slider.addClass("slick-loading")},b.prototype.swipeDirection=function(){var a,b,c,d,e=this;return a=e.touchObject.startX-e.touchObject.curX,b=e.touchObject.startY-e.touchObject.curY,c=Math.atan2(b,a),d=Math.round(180*c/Math.PI),0>d&&(d=360-Math.abs(d)),45>=d&&d>=0?e.options.rtl===!1?"left":"right":360>=d&&d>=315?e.options.rtl===!1?"left":"right":d>=135&&225>=d?e.options.rtl===!1?"right":"left":e.options.verticalSwiping===!0?d>=35&&135>=d?"left":"right":"vertical"},b.prototype.swipeEnd=function(){var c,b=this;if(b.dragging=!1,b.shouldClick=b.touchObject.swipeLength>10?!1:!0,void 0===b.touchObject.curX)return!1;if(b.touchObject.edgeHit===!0&&b.$slider.trigger("edge",[b,b.swipeDirection()]),b.touchObject.swipeLength>=b.touchObject.minSwipe)switch(b.swipeDirection()){case"left":c=b.options.swipeToSlide?b.checkNavigable(b.currentSlide+b.getSlideCount()):b.currentSlide+b.getSlideCount(),b.slideHandler(c),b.currentDirection=0,b.touchObject={},b.$slider.trigger("swipe",[b,"left"]);break;case"right":c=b.options.swipeToSlide?b.checkNavigable(b.currentSlide-b.getSlideCount()):b.currentSlide-b.getSlideCount(),b.slideHandler(c),b.currentDirection=1,b.touchObject={},b.$slider.trigger("swipe",[b,"right"])}else b.touchObject.startX!==b.touchObject.curX&&(b.slideHandler(b.currentSlide),b.touchObject={})},b.prototype.swipeHandler=function(a){var b=this;if(!(b.options.swipe===!1||"ontouchend"in document&&b.options.swipe===!1||b.options.draggable===!1&&-1!==a.type.indexOf("mouse")))switch(b.touchObject.fingerCount=a.originalEvent&&void 0!==a.originalEvent.touches?a.originalEvent.touches.length:1,b.touchObject.minSwipe=b.listWidth/b.options.touchThreshold,b.options.verticalSwiping===!0&&(b.touchObject.minSwipe=b.listHeight/b.options.touchThreshold),a.data.action){case"start":b.swipeStart(a);break;case"move":b.swipeMove(a);break;case"end":b.swipeEnd(a)}},b.prototype.swipeMove=function(a){var d,e,f,g,h,b=this;return h=void 0!==a.originalEvent?a.originalEvent.touches:null,!b.dragging||h&&1!==h.length?!1:(d=b.getLeft(b.currentSlide),b.touchObject.curX=void 0!==h?h[0].pageX:a.clientX,b.touchObject.curY=void 0!==h?h[0].pageY:a.clientY,b.touchObject.swipeLength=Math.round(Math.sqrt(Math.pow(b.touchObject.curX-b.touchObject.startX,2))),b.options.verticalSwiping===!0&&(b.touchObject.swipeLength=Math.round(Math.sqrt(Math.pow(b.touchObject.curY-b.touchObject.startY,2)))),e=b.swipeDirection(),"vertical"!==e?(void 0!==a.originalEvent&&b.touchObject.swipeLength>
31564&&a.preventDefault(),g=(b.options.rtl===!1?1:-1)*(b.touchObject.curX>b.touchObject.startX?1:-1),b.options.verticalSwiping===!0&&(g=b.touchObject.curY>b.touchObject.startY?1:-1),f=b.touchObject.swipeLength,b.touchObject.edgeHit=!1,b.options.infinite===!1&&(0===b.currentSlide&&"right"===e||b.currentSlide>=b.getDotCount()&&"left"===e)&&(f=b.touchObject.swipeLength*b.options.edgeFriction,b.touchObject.edgeHit=!0),b.swipeLeft=b.options.vertical===!1?d+f*g:d+f*(b.$list.height()/b.listWidth)*g,b.options.verticalSwiping===!0&&(b.swipeLeft=d+f*g),b.options.fade===!0||b.options.touchMove===!1?!1:b.animating===!0?(b.swipeLeft=null,!1):(b.setCSS(b.swipeLeft),void 0)):void 0)},b.prototype.swipeStart=function(a){var c,b=this;return 1!==b.touchObject.fingerCount||b.slideCount<=b.options.slidesToShow?(b.touchObject={},!1):(void 0!==a.originalEvent&&void 0!==a.originalEvent.touches&&(c=a.originalEvent.touches[0]),b.touchObject.startX=b.touchObject.curX=void 0!==c?c.pageX:a.clientX,b.touchObject.startY=b.touchObject.curY=void 0!==c?c.pageY:a.clientY,b.dragging=!0,void 0)},b.prototype.unfilterSlides=b.prototype.slickUnfilter=function(){var a=this;null!==a.$slidesCache&&(a.unload(),a.$slideTrack.children(this.options.slide).detach(),a.$slidesCache.appendTo(a.$slideTrack),a.reinit())},b.prototype.unload=function(){var b=this;a(".slick-cloned",b.$slider).remove(),b.$dots&&b.$dots.remove(),b.$prevArrow&&b.htmlExpr.test(b.options.prevArrow)&&b.$prevArrow.remove(),b.$nextArrow&&b.htmlExpr.test(b.options.nextArrow)&&b.$nextArrow.remove(),b.$slides.removeClass("slick-slide slick-active slick-visible slick-current").attr("aria-hidden","true").css("width","")},b.prototype.unslick=function(a){var b=this;b.$slider.trigger("unslick",[b,a]),b.destroy()},b.prototype.updateArrows=function(){var b,a=this;b=Math.floor(a.options.slidesToShow/2),a.options.arrows===!0&&a.slideCount>a.options.slidesToShow&&!a.options.infinite&&(a.$prevArrow.removeClass("slick-disabled").attr("aria-disabled","false"),a.$nextArrow.removeClass("slick-disabled").attr("aria-disabled","false"),0===a.currentSlide?(a.$prevArrow.addClass("slick-disabled").attr("aria-disabled","true"),a.$nextArrow.removeClass("slick-disabled").attr("aria-disabled","false")):a.currentSlide>=a.slideCount-a.options.slidesToShow&&a.options.centerMode===!1?(a.$nextArrow.addClass("slick-disabled").attr("aria-disabled","true"),a.$prevArrow.removeClass("slick-disabled").attr("aria-disabled","false")):a.currentSlide>=a.slideCount-1&&a.options.centerMode===!0&&(a.$nextArrow.addClass("slick-disabled").attr("aria-disabled","true"),a.$prevArrow.removeClass("slick-disabled").attr("aria-disabled","false")))},b.prototype.updateDots=function(){var a=this;null!==a.$dots&&(a.$dots.find("li").removeClass("slick-active").attr("aria-hidden","true"),a.$dots.find("li").eq(Math.floor(a.currentSlide/a.options.slidesToScroll)).addClass("slick-active").attr("aria-hidden","false"))},b.prototype.visibility=function(){var a=this;document[a.hidden]?(a.paused=!0,a.autoPlayClear()):a.options.autoplay===!0&&(a.paused=!1,a.autoPlay())},b.prototype.initADA=function(){var b=this;b.$slides.add(b.$slideTrack.find(".slick-cloned")).attr({"aria-hidden":"true",tabindex:"-1"}).find("a, input, button, select").attr({tabindex:"-1"}),b.$slideTrack.attr("role","listbox"),b.$slides.not(b.$slideTrack.find(".slick-cloned")).each(function(c){a(this).attr({role:"option","aria-describedby":"slick-slide"+b.instanceUid+c})}),null!==b.$dots&&b.$dots.attr("role","tablist").find("li").each(function(c){a(this).attr({role:"presentation","aria-selected":"false","aria-controls":"navigation"+b.instanceUid+c,id:"slick-slide"+b.instanceUid+c})}).first().attr("aria-selected","true").end().find("button").attr("role","button").end().closest("div").attr("role","toolbar"),b.activateADA()},b.prototype.activateADA=function(){var a=this,b=a.$slider.find("*").is(":focus");a.$slideTrack.find(".slick-active").attr({"aria-hidden":"false",tabindex:"0"}).find("a, input, button, select").attr({tabindex:"0"}),b&&a.$slideTrack.find(".slick-active").focus()},b.prototype.focusHandler=function(){var b=this;b.$slider.on("focus.slick blur.slick","*",function(c){c.stopImmediatePropagation();var d=a(this);setTimeout(function(){b.isPlay&&(d.is(":focus")?(b.autoPlayClear(),b.paused=!0):(b.paused=!1,b.autoPlay()))},0)})},a.fn.slick=function(){var g,a=this,c=arguments[0],d=Array.prototype.slice.call(arguments,1
3156),e=a.length,f=0;for(f;e>f;f++)if("object"==typeof c||"undefined"==typeof c?a[f].slick=new b(a[f],c):g=a[f].slick[c].apply(a[f].slick,d),"undefined"!=typeof g)return g;return a}});;
3157/*!
3158 * parallax.js v1.4.2 (http://pixelcog.github.io/parallax.js/)
3159 * @copyright 2016 PixelCog, Inc.
3160 * @license MIT (https://github.com/pixelcog/parallax.js/blob/master/LICENSE)
3161 */
3162!function(t,i,e,s){function o(i,e){var h=this;"object"==typeof e&&(delete e.refresh,delete e.render,t.extend(this,e)),this.$element=t(i),!this.imageSrc&&this.$element.is("img")&&(this.imageSrc=this.$element.attr("src"));var r=(this.position+"").toLowerCase().match(/\S+/g)||[];if(r.length<1&&r.push("center"),1==r.length&&r.push(r[0]),("top"==r[0]||"bottom"==r[0]||"left"==r[1]||"right"==r[1])&&(r=[r[1],r[0]]),this.positionX!=s&&(r[0]=this.positionX.toLowerCase()),this.positionY!=s&&(r[1]=this.positionY.toLowerCase()),h.positionX=r[0],h.positionY=r[1],"left"!=this.positionX&&"right"!=this.positionX&&(this.positionX=isNaN(parseInt(this.positionX))?"center":parseInt(this.positionX)),"top"!=this.positionY&&"bottom"!=this.positionY&&(this.positionY=isNaN(parseInt(this.positionY))?"center":parseInt(this.positionY)),this.position=this.positionX+(isNaN(this.positionX)?"":"px")+" "+this.positionY+(isNaN(this.positionY)?"":"px"),navigator.userAgent.match(/(iPod|iPhone|iPad)/))return this.imageSrc&&this.iosFix&&!this.$element.is("img")&&this.$element.css({backgroundImage:"url("+this.imageSrc+")",backgroundSize:"cover",backgroundPosition:this.position}),this;if(navigator.userAgent.match(/(Android)/))return this.imageSrc&&this.androidFix&&!this.$element.is("img")&&this.$element.css({backgroundImage:"url("+this.imageSrc+")",backgroundSize:"cover",backgroundPosition:this.position}),this;this.$mirror=t("<div />").prependTo("body");var a=this.$element.find(">.parallax-slider"),n=!1;0==a.length?this.$slider=t("<img />").prependTo(this.$mirror):(this.$slider=a.prependTo(this.$mirror),n=!0),this.$mirror.addClass("parallax-mirror").css({visibility:"hidden",zIndex:this.zIndex,position:"fixed",top:0,left:0,overflow:"hidden"}),this.$slider.addClass("parallax-slider").one("load",function(){h.naturalHeight&&h.naturalWidth||(h.naturalHeight=this.naturalHeight||this.height||1,h.naturalWidth=this.naturalWidth||this.width||1),h.aspectRatio=h.naturalWidth/h.naturalHeight,o.isSetup||o.setup(),o.sliders.push(h),o.isFresh=!1,o.requestRender()}),n||(this.$slider[0].src=this.imageSrc),(this.naturalHeight&&this.naturalWidth||this.$slider[0].complete||a.length>0)&&this.$slider.trigger("load")}function h(s){return this.each(function(){var h=t(this),r="object"==typeof s&&s;this==i||this==e||h.is("body")?o.configure(r):h.data("px.parallax")?"object"==typeof s&&t.extend(h.data("px.parallax"),r):(r=t.extend({},h.data(),r),h.data("px.parallax",new o(this,r))),"string"==typeof s&&("destroy"==s?o.destroy(this):o[s]())})}!function(){for(var t=0,e=["ms","moz","webkit","o"],s=0;s<e.length&&!i.requestAnimationFrame;++s)i.requestAnimationFrame=i[e[s]+"RequestAnimationFrame"],i.cancelAnimationFrame=i[e[s]+"CancelAnimationFrame"]||i[e[s]+"CancelRequestAnimationFrame"];i.requestAnimationFrame||(i.requestAnimationFrame=function(e){var s=(new Date).getTime(),o=Math.max(0,16-(s-t)),h=i.setTimeout(function(){e(s+o)},o);return t=s+o,h}),i.cancelAnimationFrame||(i.cancelAnimationFrame=function(t){clearTimeout(t)})}(),t.extend(o.prototype,{speed:.2,bleed:0,zIndex:-100,iosFix:!0,androidFix:!0,position:"center",overScrollFix:!1,refresh:function(){this.boxWidth=this.$element.outerWidth(),this.boxHeight=this.$element.outerHeight()+2*this.bleed,this.boxOffsetTop=this.$element.offset().top-this.bleed,this.boxOffsetLeft=this.$element.offset().left,this.boxOffsetBottom=this.boxOffsetTop+this.boxHeight;var t=o.winHeight,i=o.docHeight,e=Math.min(this.boxOffsetTop,i-t),s=Math.max(this.boxOffsetTop+this.boxHeight-t,0),h=this.boxHeight+(e-s)*(1-this.speed)|0,r=(this.boxOffsetTop-e)*(1-this.speed)|0;if(h*this.aspectRatio>=this.boxWidth){this.imageWidth=h*this.aspectRatio|0,this.imageHeight=h,this.offsetBaseTop=r;var a=this.imageWidth-this.boxWidth;this.offsetLeft="left"==this.positionX?0:"right"==this.positionX?-
3162a:isNaN(this.positionX)?-a/2|0:Math.max(this.positionX,-a)}else{this.imageWidth=this.boxWidth,this.imageHeight=this.boxWidth/this.aspectRatio|0,this.offsetLeft=0;var a=this.imageHeight-h;this.offsetBaseTop="top"==this.positionY?r:"bottom"==this.positionY?r-a:isNaN(this.positionY)?r-a/2|0:r+Math.max(this.positionY,-a)}},render:function(){var t=o.scrollTop,i=o.scrollLeft,e=this.overScrollFix?o.overScroll:0,s=t+o.winHeight;this.boxOffsetBottom>t&&this.boxOffsetTop<=s?(this.visibility="visible",this.mirrorTop=this.boxOffsetTop-t,this.mirrorLeft=this.boxOffsetLeft-i,this.offsetTop=this.offsetBaseTop-this.mirrorTop*(1-this.speed)):this.visibility="hidden",this.$mirror.css({transform:"translate3d(0px, 0px, 0px)",visibility:this.visibility,top:this.mirrorTop-e,left:this.mirrorLeft,height:this.boxHeight,width:this.boxWidth}),this.$slider.css({transform:"translate3d(0px, 0px, 0px)",position:"absolute",top:this.offsetTop,left:this.offsetLeft,height:this.imageHeight,width:this.imageWidth,maxWidth:"none"})}}),t.extend(o,{scrollTop:0,scrollLeft:0,winHeight:0,winWidth:0,docHeight:1<<30,docWidth:1<<30,sliders:[],isReady:!1,isFresh:!1,isBusy:!1,setup:function(){if(!this.isReady){var s=t(e),h=t(i),r=function(){o.winHeight=h.height(),o.winWidth=h.width(),o.docHeight=s.height(),o.docWidth=s.width()},a=function(){var t=h.scrollTop(),i=o.docHeight-o.winHeight,e=o.docWidth-o.winWidth;o.scrollTop=Math.max(0,Math.min(i,t)),o.scrollLeft=Math.max(0,Math.min(e,h.scrollLeft())),o.overScroll=Math.max(t-i,Math.min(t,0))};h.on("resize.px.parallax load.px.parallax",function(){r(),o.isFresh=!1,o.requestRender()}).on("scroll.px.parallax load.px.parallax",function(){a(),o.requestRender()}),r(),a(),this.isReady=!0}},configure:function(i){"object"==typeof i&&(delete i.refresh,delete i.render,t.extend(this.prototype,i))},refresh:function(){t.each(this.sliders,function(){this.refresh()}),this.isFresh=!0},render:function(){this.isFresh||this.refresh(),t.each(this.sliders,function(){this.render()})},requestRender:function(){var t=this;this.isBusy||(this.isBusy=!0,i.requestAnimationFrame(function(){t.render(),t.isBusy=!1}))},destroy:function(e){var s,h=t(e).data("px.parallax");for(h.$mirror.remove(),s=0;s<this.sliders.length;s+=1)this.sliders[s]==h&&this.sliders.splice(s,1);t(e).data("px.parallax",!1),0===this.sliders.length&&(t(i).off("scroll.px.parallax resize.px.parallax load.px.parallax"),this.isReady=!1,o.isSetup=!1)}});var r=t.fn.parallax;t.fn.parallax=h,t.fn.parallax.Constructor=o,t.fn.parallax.noConflict=function(){return t.fn.parallax=r,this},t(e).on("ready.px.parallax.data-api",function(){t('[data-parallax="scroll"]').parallax()})}(jQuery,window,document);
3163;
3164/*!
3165 * Cropper v4.0.0
3166 * https://github.com/fengyuanchen/cropper
3167 *
3168 * Copyright (c) 2014-2018 Chen Fengyuan
3169 * Released under the MIT license
3170 *
3171 * Date: 2018-04-01T06:27:27.267Z
3172 */
3173
3174(function (global, factory) {
3175  typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('jquery')) :
3176  typeof define === 'function' && define.amd ? define(['jquery'], factory) :
3177  (factory(global.jQuery));
3178}(this, (function ($) { 'use strict';
3179
3180  $ = $ && $.hasOwnProperty('default') ? $['default'] : $;
3181
3182  var IN_BROWSER = typeof window !== 'undefined';
3183  var WINDOW = IN_BROWSER ? window : {};
3184  var NAMESPACE = 'cropper';
3185
3186  // Actions
3187  var ACTION_ALL = 'all';
3188  var ACTION_CROP = 'crop';
3189  var ACTION_MOVE = 'move';
3190  var ACTION_ZOOM = 'zoom';
3191  var ACTION_EAST = 'e';
3192  var ACTION_WEST = 'w';
3193  var ACTION_SOUTH = 's';
3194  var ACTION_NORTH = 'n';
3195  var ACTION_NORTH_EAST = 'ne';
3196  var ACTION_NORTH_WEST = 'nw';
3197  var ACTION_SOUTH_EAST = 'se';
3198  var ACTION_SOUTH_WEST = 'sw';
3199
3200  // Classes
3201  var CLASS_CROP = NAMESPACE + '-crop';
3202  var CLASS_DISABLED = NAMESPACE + '-disabled';
3203  var CLASS_HIDDEN = NAMESPACE + '-hidden';
3204  var CLASS_HIDE = NAMESPACE + '-hide';
3205  var CLASS_INVISIBLE = NAMESPACE + '-invisible';
3206  var CLASS_MODAL = NAMESPACE + '-modal';
3207  var CLASS_MOVE = NAMESPACE + '-move';
3208
3209  // Data keys
3210  var DATA_ACTION = 'action';
3211  var DATA_PREVIEW = 'preview';
3212
3213  // Drag modes
3214  var DRAG_MODE_CROP = 'crop';
3215  var DRAG_MODE_MOVE = 'move';
3216  var DRAG_MODE_NONE = 'none';
3217
3218  // Events
3219  var EVENT_CROP = 'crop';
3220  var EVENT_CROP_END = 'cropend';
3221  var EVENT_CROP_MOVE = 'cropmove';
3222  var EVENT_CROP_START = 'cropstart';
3223  var EVENT_DBLCLICK = 'dblclick';
3224  var EVENT_LOAD = 'load';
3225  var EVENT_POINTER_DOWN = WINDOW.PointerEvent ? 'pointerdown' : 'touchstart mousedown';
3226  var EVENT_POINTER_MOVE = WINDOW.PointerEvent ? 'pointermove' : 'touchmove mousemove';
3227  var EVENT_POINTER_UP = WINDOW.PointerEvent ? 'pointerup pointercancel' : 'touchend touchcancel mouseup';
3228  var EVENT_READY = 'ready';
3229  var EVENT_RESIZE = 'resize';
3230  var EVENT_WHEEL = 'wheel mousewheel DOMMouseScroll';
3231  var EVENT_ZOOM = 'zoom';
3232
3233  // RegExps
3234  var REGEXP_ACTIONS = /^(?:e|w|s|n|se|sw|ne|nw|all|crop|move|zoom)$/;
3235  var REGEXP_DATA_URL = /^data:/;
3236  var REGEXP_DATA_URL_JPEG = /^data:image\/jpeg;base64,/;
3237  var REGEXP_TAG_NAME = /^(?:img|canvas)$/i;
3238
3239  var DEFAULTS = {
3240    // Define the view mode of the cropper
3241    viewMode: 0, // 0, 1, 2, 3
3242
3243    // Define the dragging mode of the cropper
3244    dragMode: DRAG_MODE_CROP, // 'crop', 'move' or 'none'
3245
3246    // Define the aspect ratio of the crop box
3247    aspectRatio: NaN,
3248
3249    // An object with the previous cropping result data
3250    data: null,
3251
3252    // A selector for adding extra containers to preview
3253    preview: '',
3254
3255    // Re-render the cropper when resize the window
3256    responsive: true,
3257
3258    // Restore the cropped area after resize the window
3259    restore: true,
3260
3261    // Check if the current image is a cross-origin image
3262    checkCrossOrigin: true,
3263
3264    // Check the current image's Exif Orientation information
3265    checkOrientation: true,
3266
3267    // Show the black modal
3268    modal: true,
3269
3270    // Show the dashed lines for guiding
3271    guides: true,
3272
3273    // Show the center indicator for guiding
3274    center: true,
3275
3276    // Show the white modal to highlight the crop box
3277    highlight: true,
3278
3279    // Show the grid background
3280    background: true,
3281
3282    // Enable to crop the image automatically when initialize
3283    autoCrop: true,
3284
3285    // Define the percentage of automatic cropping area when initializes
3286    autoCropArea: 0.8,
3287
3288    // Enable to move the image
3289    movable: true,
3290
3291    // Enable to rotate the image
3292    rotatable: true,
3293
3294    // Enable to scale the image
3295    scalable: true,
3296
3297    // Enable to zoom the image
3298    zoomable: true,
3299
3300    // Enable to zoom the image by dragging touch
3301    zoomOnTouch: true,
3302
3303    // Enable to zoom the image by wheeling mouse
3304    zoomOnWheel: true,
3305
3306    // Define zoom ratio when zoom the image by wheeling mouse
3307    wheelZoomRatio: 0.1,
3308
3309    // Enable to move the crop box
3310    cropBoxMovable: true,
3311
3312    // Enable to resize the crop box
3313    cropBoxResizable: true,
3314
3315    // Toggle drag mode between "crop" and "move" when click twice on the cropper
3316    toggleDragModeOnDblclick: true,
3317
3318    // Size limitation
3319    minCanvasWidth: 0,
3320    minCanvasHeight: 0,
3321    minCropBoxWidth: 0,
3322    minCropBoxHeight: 0,
3323    minContainerWidth: 200,
3324    minContainerHeight: 100,
3325
3326    // Shortcuts of events
3327    ready: null,
3328    cropstart: null,
3329    cropmove: null,
3330    cropend: null,
3331    crop: null,
3332    zoom: null
3333  };
3334
3335  var TEMPLATE = '<div class="cropper-container" touch-action="none">' + '<div class="cropper-wrap-box">' + '<div class="cropper-canvas"></div>' + '</div>' + '<div class="cropper-drag-box"></div>' + '<div class="cropper-crop-box">' + '<span class="cropper-view-box"></span>' + '<span class="cropper-dashed dashed-h"></span>' + '<span class="cropper-dashed dashed-v"></span>' + '<span class="cropper-center"></span>' + '<span class="cropper-face"></span>' + '<span class="cropper-line line-e" data-action="e"></span>' + '<span class="cropper-line line-n" data-action="n"></span>' + '<span class="cropper-line line-w" data-action="w"></span>' + '<span class="cropper-line line-s" data-action="s"></span>' + '<span class="cropper-point point-e" data-action="e"></span>' + '<span class="cropper-point point-n" data-action="n"></span>' + '<span class="cropper-point point-w" data-action="w"></span>' + '<span class="cropper-point point-s" data-action="s"></span>' + '<span class="cropper-point point-ne" data-action="ne"></span>' + '<span class="cropper-point point-nw" data-action="nw"></span>' + '<span class="cropper-point point-sw" data-action="sw"></span>' + '<span class="cropper-point point-se" data-action="se"></span>' + '</div>' + '</div>';
3336
3337  var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
3338    return typeof obj;
3339  } : function (obj) {
3340    return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
3341  };
3342
3343  var classCallCheck = function (instance, Constructor) {
3344    if (!(instance instanceof Constructor)) {
3345      throw new TypeError("Cannot call a class as a function");
3346    }
3347  };
3348
3349  var createClass = function () {
3350    function defineProperties(target, props) {
3351      for (var i = 0; i < props.length; i++) {
3352        var descriptor = props[i];
3353        descriptor.enumerable = descriptor.enumerable || false;
3354        descriptor.configurable = true;
3355        if ("value" in descriptor) descriptor.writable = true;
3356        Object.defineProperty(target, descriptor.key, descriptor);
3357      }
3358    }
3359
3360    return function (Constructor, protoProps, staticProps) {
3361      if (protoProps) defineProperties(Constructor.prototype, protoProps);
3362      if (staticProps) defineProperties(Constructor, staticProps);
3363      return Constructor;
3364    };
3365  }();
3366
3367  var toConsumableArray = function (arr) {
3368    if (Array.isArray(arr)) {
3369      for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
3370
3371      return arr2;
3372    } else {
3373      return Array.from(arr);
3374    }
3375  };
3376
3377  /**
3378   * Check if the given value is not a number.
3379   */
3380  var isNaN = Number.isNaN || WINDOW.isNaN;
3381
3382  /**
3383   * Check if the given value is a number.
3384   * @param {*} value - The value to check.
3385   * @returns {boolean} Returns `true` if the given value is a number, else `false`.
3386   */
3387  function isNumber(value) {
3388    return typeof value === 'number' && !isNaN(value);
3389  }
3390
3391  /**
3392   * Check if the given value is undefined.
3393   * @param {*} value - The value to check.
3394   * @returns {boolean} Returns `true` if the given value is undefined, else `false`.
3395   */
3396  function isUndefined(value) {
3397    return typeof value === 'undefined';
3398  }
3399
3400  /**
3401   * Check if the given value is an object.
3402   * @param {*} value - The value to check.
3403   * @returns {boolean} Returns `true` if the given value is an object, else `false`.
3404   */
3405  function isObject(value) {
3406    return (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' && value !== null;
3407  }
3408
3409  var hasOwnProperty = Object.prototype.hasOwnProperty;
3410
3411  /**
3412   * Check if the given value is a plain object.
3413   * @param {*} value - The value to check.
3414   * @returns {boolean} Returns `true` if the given value is a plain object, else `false`.
3415   */
3416
3417  function isPlainObject(value) {
3418    if (!isObject(value)) {
3419      return false;
3420    }
3421
3422    try {
3423      var _constructor = value.constructor;
3424      var prototype = _constructor.prototype;
3425
3426
3427      return _constructor && prototype && hasOwnProperty.call(prototype, 'isPrototypeOf');
3428    } catch (e) {
3429      return false;
3430    }
3431  }
3432
3433  /**
3434   * Check if the given value is a function.
3435   * @param {*} value - The value to check.
3436   * @returns {boolean} Returns `true` if the given value is a function, else `false`.
3437   */
3438  function isFunction(value) {
3439    return typeof value === 'function';
3440  }
3441
3442  /**
3443   * Iterate the given data.
3444   * @param {*} data - The data to iterate.
3445   * @param {Function} callback - The process function for each element.
3446   * @returns {*} The original data.
3447   */
3448  function forEach(data, callback) {
3449    if (data && isFunction(callback)) {
3450      if (Array.isArray(data) || isNumber(data.length) /* array-like */) {
3451          var length = data.length;
3452
3453          var i = void 0;
3454
3455          for (i = 0; i < length; i += 1) {
vendor: 8,315 bytes, lines 3456-3753
3456            if (callback.call(data, data[i], i, data) === false) {
3457              break;
3458            }
3459          }
3460        } else if (isObject(data)) {
3461        Object.keys(data).forEach(function (key) {
3462          callback.call(data, data[key], key, data);
3463        });
3464      }
3465    }
3466
3467    return data;
3468  }
3469
3470  /**
3471   * Extend the given object.
3472   * @param {*} obj - The object to be extended.
3473   * @param {*} args - The rest objects which will be merged to the first object.
3474   * @returns {Object} The extended object.
3475   */
3476  var assign = Object.assign || function assign(obj) {
3477    for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
3478      args[_key - 1] = arguments[_key];
3479    }
3480
3481    if (isObject(obj) && args.length > 0) {
3482      args.forEach(function (arg) {
3483        if (isObject(arg)) {
3484          Object.keys(arg).forEach(function (key) {
3485            obj[key] = arg[key];
3486          });
3487        }
3488      });
3489    }
3490
3491    return obj;
3492  };
3493
3494  var REGEXP_DECIMALS = /\.\d*(?:0|9){12}\d*$/i;
3495
3496  /**
3497   * Normalize decimal number.
3498   * Check out {@link http://0.30000000000000004.com/}
3499   * @param {number} value - The value to normalize.
3500   * @param {number} [times=100000000000] - The times for normalizing.
3501   * @returns {number} Returns the normalized number.
3502   */
3503  function normalizeDecimalNumber(value) {
3504    var times = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 100000000000;
3505
3506    return REGEXP_DECIMALS.test(value) ? Math.round(value * times) / times : value;
3507  }
3508
3509  var REGEXP_SUFFIX = /^(?:width|height|left|top|marginLeft|marginTop)$/;
3510
3511  /**
3512   * Apply styles to the given element.
3513   * @param {Element} element - The target element.
3514   * @param {Object} styles - The styles for applying.
3515   */
3516  function setStyle(element, styles) {
3517    var style = element.style;
3518
3519
3520    forEach(styles, function (value, property) {
3521      if (REGEXP_SUFFIX.test(property) && isNumber(value)) {
3522        value += 'px';
3523      }
3524
3525      style[property] = value;
3526    });
3527  }
3528
3529  /**
3530   * Check if the given element has a special class.
3531   * @param {Element} element - The element to check.
3532   * @param {string} value - The class to search.
3533   * @returns {boolean} Returns `true` if the special class was found.
3534   */
3535  function hasClass(element, value) {
3536    return element.classList ? element.classList.contains(value) : element.className.indexOf(value) > -1;
3537  }
3538
3539  /**
3540   * Add classes to the given element.
3541   * @param {Element} element - The target element.
3542   * @param {string} value - The classes to be added.
3543   */
3544  function addClass(element, value) {
3545    if (!value) {
3546      return;
3547    }
3548
3549    if (isNumber(element.length)) {
3550      forEach(element, function (elem) {
3551        addClass(elem, value);
3552      });
3553      return;
3554    }
3555
3556    if (element.classList) {
3557      element.classList.add(value);
3558      return;
3559    }
3560
3561    var className = element.className.trim();
3562
3563    if (!className) {
3564      element.className = value;
3565    } else if (className.indexOf(value) < 0) {
3566      element.className = className + ' ' + value;
3567    }
3568  }
3569
3570  /**
3571   * Remove classes from the given element.
3572   * @param {Element} element - The target element.
3573   * @param {string} value - The classes to be removed.
3574   */
3575  function removeClass(element, value) {
3576    if (!value) {
3577      return;
3578    }
3579
3580    if (isNumber(element.length)) {
3581      forEach(element, function (elem) {
3582        removeClass(elem, value);
3583      });
3584      return;
3585    }
3586
3587    if (element.classList) {
3588      element.classList.remove(value);
3589      return;
3590    }
3591
3592    if (element.className.indexOf(value) >= 0) {
3593      element.className = element.className.replace(value, '');
3594    }
3595  }
3596
3597  /**
3598   * Add or remove classes from the given element.
3599   * @param {Element} element - The target element.
3600   * @param {string} value - The classes to be toggled.
3601   * @param {boolean} added - Add only.
3602   */
3603  function toggleClass(element, value, added) {
3604    if (!value) {
3605      return;
3606    }
3607
3608    if (isNumber(element.length)) {
3609      forEach(element, function (elem) {
3610        toggleClass(elem, value, added);
3611      });
3612      return;
3613    }
3614
3615    // IE10-11 doesn't support the second parameter of `classList.toggle`
3616    if (added) {
3617      addClass(element, value);
3618    } else {
3619      removeClass(element, value);
3620    }
3621  }
3622
3623  var REGEXP_HYPHENATE = /([a-z\d])([A-Z])/g;
3624
3625  /**
3626   * Transform the given string from camelCase to kebab-case
3627   * @param {string} value - The value to transform.
3628   * @returns {string} The transformed value.
3629   */
3630  function hyphenate(value) {
3631    return value.replace(REGEXP_HYPHENATE, '$1-$2').toLowerCase();
3632  }
3633
3634  /**
3635   * Get data from the given element.
3636   * @param {Element} element - The target element.
3637   * @param {string} name - The data key to get.
3638   * @returns {string} The data value.
3639   */
3640  function getData(element, name) {
3641    if (isObject(element[name])) {
3642      return element[name];
3643    } else if (element.dataset) {
3644      return element.dataset[name];
3645    }
3646
3647    return element.getAttribute('data-' + hyphenate(name));
3648  }
3649
3650  /**
3651   * Set data to the given element.
3652   * @param {Element} element - The target element.
3653   * @param {string} name - The data key to set.
3654   * @param {string} data - The data value.
3655   */
3656  function setData(element, name, data) {
3657    if (isObject(data)) {
3658      element[name] = data;
3659    } else if (element.dataset) {
3660      element.dataset[name] = data;
3661    } else {
3662      element.setAttribute('data-' + hyphenate(name), data);
3663    }
3664  }
3665
3666  /**
3667   * Remove data from the given element.
3668   * @param {Element} element - The target element.
3669   * @param {string} name - The data key to remove.
3670   */
3671  function removeData(element, name) {
3672    if (isObject(element[name])) {
3673      try {
3674        delete element[name];
3675      } catch (e) {
3676        element[name] = undefined;
3677      }
3678    } else if (element.dataset) {
3679      // #128 Safari not allows to delete dataset property
3680      try {
3681        delete element.dataset[name];
3682      } catch (e) {
3683        element.dataset[name] = undefined;
3684      }
3685    } else {
3686      element.removeAttribute('data-' + hyphenate(name));
3687    }
3688  }
3689
3690  var REGEXP_SPACES = /\s\s*/;
3691  var onceSupported = function () {
3692    var supported = false;
3693
3694    if (IN_BROWSER) {
3695      var once = false;
3696      var listener = function listener() {};
3697      var options = Object.defineProperty({}, 'once', {
3698        get: function get$$1() {
3699          supported = true;
3700          return once;
3701        },
3702
3703
3704        /**
3705         * This setter can fix a `TypeError` in strict mode
3706         * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Getter_only}
3707         * @param {boolean} value - The value to set
3708         */
3709        set: function set$$1(value) {
3710          once = value;
3711        }
3712      });
3713
3714      WINDOW.addEventListener('test', listener, options);
3715      WINDOW.removeEventListener('test', listener, options);
3716    }
3717
3718    return supported;
3719  }();
3720
3721  /**
3722   * Remove event listener from the target element.
3723   * @param {Element} element - The event target.
3724   * @param {string} type - The event type(s).
3725   * @param {Function} listener - The event listener.
3726   * @param {Object} options - The event options.
3727   */
3728  function removeListener(element, type, listener) {
3729    var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
3730
3731    var handler = listener;
3732
3733    type.trim().split(REGEXP_SPACES).forEach(function (event) {
3734      if (!onceSupported) {
3735        var listeners = element.listeners;
3736
3737
3738        if (listeners && listeners[event] && listeners[event][listener]) {
3739          handler = listeners[event][listener];
3740          delete listeners[event][listener];
3741
3742          if (Object.keys(listeners[event]).length === 0) {
3743            delete listeners[event];
3744          }
3745
3746          if (Object.keys(listeners).length === 0) {
3747            delete element.listeners;
3748          }
3749        }
3750      }
3751
3752      element.removeEventListener(event, handler, options);
3753    });
3754  }
3755
3756  /**
3757   * Add event listener to the target element.
3758   * @param {Element} element - The event target.
3759   * @param {string} type - The event type(s).
3760   * @param {Function} listener - The event listener.
3761   * @param {Object} options - The event options.
3762   */
3763  function addListener(element, type, listener) {
3764    var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
3765
3766    var _handler = listener;
3767
3768    type.trim().split(REGEXP_SPACES).forEach(function (event) {
3769      if (options.once && !onceSupported) {
3770        var _element$listeners = element.listeners,
3771            listeners = _element$listeners === undefined ? {} : _element$listeners;
3772
3773
3774        _handler = function handler() {
3775          for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
3776            args[_key2] = arguments[_key2];
3777          }
3778
3779          delete listeners[event][listener];
3780          element.removeEventListener(event, _handler, options);
3781          listener.apply(element, args);
3782        };
3783
3784        if (!listeners[event]) {
3785          listeners[event] = {};
3786        }
3787
3788        if (listeners[event][listener]) {
3789          element.removeEventListener(event, listeners[event][listener], options);
3790        }
3791
3792        listeners[event][listener] = _handler;
3793        element.listeners = listeners;
3794      }
3795
3796      element.addEventListener(event, _handler, options);
3797    });
3798  }
3799
3800  /**
3801   * Dispatch event on the target element.
3802   * @param {Element} element - The event target.
3803   * @param {string} type - The event type(s).
3804   * @param {Object} data - The additional event data.
3805   * @returns {boolean} Indicate if the event is default prevented or not.
3806   */
3807  function dispatchEvent(element, type, data) {
3808    var event = void 0;
3809
3810    // Event and CustomEvent on IE9-11 are global objects, not constructors
3811    if (isFunction(Event) && isFunction(CustomEvent)) {
3812      event = new CustomEvent(type, {
3813        detail: data,
3814        bubbles: true,
3815        cancelable: true
3816      });
3817    } else {
3818      event = document.createEvent('CustomEvent');
3819      event.initCustomEvent(type, true, true, data);
3820    }
3821
3822    return element.dispatchEvent(event);
3823  }
3824
3825  /**
3826   * Get the offset base on the document.
3827   * @param {Element} element - The target element.
3828   * @returns {Object} The offset data.
3829   */
3830  function getOffset(element) {
3831    var box = element.getBoundingClientRect();
3832
3833    return {
3834      left: box.left + (window.pageXOffset - document.documentElement.clientLeft),
3835      top: box.top + (window.pageYOffset - document.documentElement.clientTop)
3836    };
3837  }
3838
3839  var location = WINDOW.location;
3840
3841  var REGEXP_ORIGINS = /^(https?:)\/\/([^:/?#]+):?(\d*)/i;
3842
3843  /**
3844   * Check if the given URL is a cross origin URL.
3845   * @param {string} url - The target URL.
3846   * @returns {boolean} Returns `true` if the given URL is a cross origin URL, else `false`.
3847   */
3848  function isCrossOriginURL(url) {
3849    var parts = url.match(REGEXP_ORIGINS);
3850
3851    return parts && (parts[1] !== location.protocol || parts[2] !== location.hostname || parts[3] !== location.port);
3852  }
3853
3854  /**
3855   * Add timestamp to the given URL.
3856   * @param {string} url - The target URL.
3857   * @returns {string} The result URL.
3858   */
3859  function addTimestamp(url) {
3860    var timestamp = 'timestamp=' + new Date().getTime();
3861
3862    return url + (url.indexOf('?') === -1 ? '?' : '&') + timestamp;
3863  }
3864
3865  /**
3866   * Get transforms base on the given object.
3867   * @param {Object} obj - The target object.
3868   * @returns {string} A string contains transform values.
3869   */
3870  function getTransforms(_ref) {
3871    var rotate = _ref.rotate,
3872        scaleX = _ref.scaleX,
3873        scaleY = _ref.scaleY,
3874        translateX = _ref.translateX,
3875        translateY = _ref.translateY;
3876
3877    var values = [];
3878
3879    if (isNumber(translateX) && translateX !== 0) {
3880      values.push('translateX(' + translateX + 'px)');
3881    }
3882
3883    if (isNumber(translateY) && translateY !== 0) {
3884      values.push('translateY(' + translateY + 'px)');
3885    }
3886
3887    // Rotate should come first before scale to match orientation transform
3888    if (isNumber(rotate) && rotate !== 0) {
3889      values.push('rotate(' + rotate + 'deg)');
3890    }
3891
3892    if (isNumber(scaleX) && scaleX !== 1) {
3893      values.push('scaleX(' + scaleX + ')');
3894    }
3895
3896    if (isNumber(scaleY) && scaleY !== 1) {
3897      values.push('scaleY(' + scaleY + ')');
3898    }
3899
3900    var transform = values.length ? values.join(' ') : 'none';
3901
3902    return {
3903      WebkitTransform: transform,
3904      msTransform: transform,
3905      transform: transform
3906    };
3907  }
3908
3909  /**
3910   * Get the max ratio of a group of pointers.
3911   * @param {string} pointers - The target pointers.
3912   * @returns {number} The result ratio.
3913   */
3914  function getMaxZoomRatio(pointers) {
3915    var pointers2 = assign({}, pointers);
3916    var ratios = [];
3917
3918    forEach(pointers, function (pointer, pointerId) {
3919      delete pointers2[pointerId];
3920
3921      forEach(pointers2, function (pointer2) {
3922        var x1 = Math.abs(pointer.startX - pointer2.startX);
3923        var y1 = Math.abs(pointer.startY - pointer2.startY);
3924        var x2 = Math.abs(pointer.endX - pointer2.endX);
3925        var y2 = Math.abs(pointer.endY - pointer2.endY);
3926        var z1 = Math.sqrt(x1 * x1 + y1 * y1);
3927        var z2 = Math.sqrt(x2 * x2 + y2 * y2);
3928        var ratio = (z2 - z1) / z1;
3929
3930        ratios.push(ratio);
3931      });
3932    });
3933
3934    ratios.sort(function (a, b) {
3935      return Math.abs(a) < Math.abs(b);
3936    });
3937
3938    return ratios[0];
3939  }
3940
3941  /**
3942   * Get a pointer from an event object.
3943   * @param {Object} event - The target event object.
3944   * @param {boolean} endOnly - Indicates if only returns the end point coordinate or not.
3945   * @returns {Object} The result pointer contains start and/or end point coordinates.
3946   */
3947  function getPointer(_ref2, endOnly) {
3948    var pageX = _ref2.pageX,
3949        pageY = _ref2.pageY;
3950
3951    var end = {
3952      endX: pageX,
3953      endY: pageY
3954    };
3955
3956    return endOnly ? end : assign({
3957      startX: pageX,
3958      startY: pageY
3959    }, end);
3960  }
3961
3962  /**
3963   * Get the center point coordinate of a group of pointers.
3964   * @param {Object} pointers - The target pointers.
3965   * @returns {Object} The center point coordinate.
3966   */
3967  function getPointersCenter(pointers) {
3968    var pageX = 0;
3969    var pageY = 0;
3970    var count = 0;
3971
3972    forEach(pointers, function (_ref3) {
3973      var startX = _ref3.startX,
3974          startY = _ref3.startY;
3975
3976      pageX += startX;
3977      pageY += startY;
3978      count += 1;
3979    });
3980
3981    pageX /= count;
3982    pageY /= count;
3983
3984    return {
3985      pageX: pageX,
3986      pageY: pageY
3987    };
3988  }
3989
3990  /**
3991   * Check if the given value is a finite number.
3992   */
3993  var isFinite = Number.isFinite || WINDOW.isFinite;
3994
3995  /**
3996   * Get the max sizes in a rectangle under the given aspect ratio.
3997   * @param {Object} data - The original sizes.
3998   * @param {string} [type='contain'] - The adjust type.
3999   * @returns {Object} The result sizes.
4000   */
4001  function getAdjustedSizes(_ref4) // or 'cover'
4002  {
4003    var aspectRatio = _ref4.aspectRatio,
4004        height = _ref4.height,
4005        width = _ref4.width;
4006    var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'contain';
4007
4008    var isValidNumber = function isValidNumber(value) {
4009      return isFinite(value) && value > 0;
4010    };
4011
4012    if (isValidNumber(width) && isValidNumber(height)) {
4013      var adjustedWidth = height * aspectRatio;
4014
4015      if (type === 'contain' && adjustedWidth > width || type === 'cover' && adjustedWidth < width) {
4016        height = width / aspectRatio;
4017      } else {
4018        width = height * aspectRatio;
4019      }
4020    } else if (isValidNumber(width)) {
4021      height = width / aspectRatio;
4022    } else if (isValidNumber(height)) {
4023      width = height * aspectRatio;
4024    }
4025
4026    return {
4027      width: width,
4028      height: height
4029    };
4030  }
4031
4032  /**
4033   * Get the new sizes of a rectangle after rotated.
4034   * @param {Object} data - The original sizes.
4035   * @returns {Object} The result sizes.
4036   */
4037  function getRotatedSizes(_ref5) {
4038    var width = _ref5.width,
4039        height = _ref5.height,
4040        degree = _ref5.degree;
4041
4042    degree = Math.abs(degree) % 180;
4043
4044    if (degree === 90) {
4045      return {
4046        width: height,
4047        height: width
4048      };
4049    }
4050
4051    var arc = degree % 90 * Math.PI / 180;
4052    var sinArc = Math.sin(arc);
4053    var cosArc = Math.cos(arc);
4054    var newWidth = width * cosArc + height * sinArc;
4055    var newHeight = width * sinArc + height * cosArc;
4056
4057    return degree > 90 ? {
4058      width: newHeight,
4059      height: newWidth
4060    } : {
4061      width: newWidth,
4062      height: newHeight
4063    };
4064  }
4065
4066  /**
4067   * Get a canvas which drew the given image.
4068   * @param {HTMLImageElement} image - The image for drawing.
4069   * @param {Object} imageData - The image data.
4070   * @param {Object} canvasData - The canvas data.
4071   * @param {Object} options - The options.
4072   * @returns {HTMLCanvasElement} The result canvas.
4073   */
4074  function getSourceCanvas(image, _ref6, _ref7, _ref8) {
4075    var imageAspectRatio = _ref6.aspectRatio,
vendor: 4,419 bytes, lines 4076-4185
4076        imageNaturalWidth = _ref6.naturalWidth,
4077        imageNaturalHeight = _ref6.naturalHeight,
4078        _ref6$rotate = _ref6.rotate,
4079        rotate = _ref6$rotate === undefined ? 0 : _ref6$rotate,
4080        _ref6$scaleX = _ref6.scaleX,
4081        scaleX = _ref6$scaleX === undefined ? 1 : _ref6$scaleX,
4082        _ref6$scaleY = _ref6.scaleY,
4083        scaleY = _ref6$scaleY === undefined ? 1 : _ref6$scaleY;
4084    var aspectRatio = _ref7.aspectRatio,
4085        naturalWidth = _ref7.naturalWidth,
4086        naturalHeight = _ref7.naturalHeight;
4087    var _ref8$fillColor = _ref8.fillColor,
4088        fillColor = _ref8$fillColor === undefined ? 'transparent' : _ref8$fillColor,
4089        _ref8$imageSmoothingE = _ref8.imageSmoothingEnabled,
4090        imageSmoothingEnabled = _ref8$imageSmoothingE === undefined ? true : _ref8$imageSmoothingE,
4091        _ref8$imageSmoothingQ = _ref8.imageSmoothingQuality,
4092        imageSmoothingQuality = _ref8$imageSmoothingQ === undefined ? 'low' : _ref8$imageSmoothingQ,
4093        _ref8$maxWidth = _ref8.maxWidth,
4094        maxWidth = _ref8$maxWidth === undefined ? Infinity : _ref8$maxWidth,
4095        _ref8$maxHeight = _ref8.maxHeight,
4096        maxHeight = _ref8$maxHeight === undefined ? Infinity : _ref8$maxHeight,
4097        _ref8$minWidth = _ref8.minWidth,
4098        minWidth = _ref8$minWidth === undefined ? 0 : _ref8$minWidth,
4099        _ref8$minHeight = _ref8.minHeight,
4100        minHeight = _ref8$minHeight === undefined ? 0 : _ref8$minHeight;
4101
4102    var canvas = document.createElement('canvas');
4103    var context = canvas.getContext('2d');
4104    var maxSizes = getAdjustedSizes({
4105      aspectRatio: aspectRatio,
4106      width: maxWidth,
4107      height: maxHeight
4108    });
4109    var minSizes = getAdjustedSizes({
4110      aspectRatio: aspectRatio,
4111      width: minWidth,
4112      height: minHeight
4113    }, 'cover');
4114    var width = Math.min(maxSizes.width, Math.max(minSizes.width, naturalWidth));
4115    var height = Math.min(maxSizes.height, Math.max(minSizes.height, naturalHeight));
4116
4117    // Note: should always use image's natural sizes for drawing as
4118    // imageData.naturalWidth === canvasData.naturalHeight when rotate % 180 === 90
4119    var destMaxSizes = getAdjustedSizes({
4120      aspectRatio: imageAspectRatio,
4121      width: maxWidth,
4122      height: maxHeight
4123    });
4124    var destMinSizes = getAdjustedSizes({
4125      aspectRatio: imageAspectRatio,
4126      width: minWidth,
4127      height: minHeight
4128    }, 'cover');
4129    var destWidth = Math.min(destMaxSizes.width, Math.max(destMinSizes.width, imageNaturalWidth));
4130    var destHeight = Math.min(destMaxSizes.height, Math.max(destMinSizes.height, imageNaturalHeight));
4131    var params = [-destWidth / 2, -destHeight / 2, destWidth, destHeight];
4132
4133    canvas.width = normalizeDecimalNumber(width);
4134    canvas.height = normalizeDecimalNumber(height);
4135    context.fillStyle = fillColor;
4136    context.fillRect(0, 0, width, height);
4137    context.save();
4138    context.translate(width / 2, height / 2);
4139    context.rotate(rotate * Math.PI / 180);
4140    context.scale(scaleX, scaleY);
4141    context.imageSmoothingEnabled = imageSmoothingEnabled;
4142    context.imageSmoothingQuality = imageSmoothingQuality;
4143    context.drawImage.apply(context, [image].concat(toConsumableArray(params.map(function (param) {
4144      return Math.floor(normalizeDecimalNumber(param));
4145    }))));
4146    context.restore();
4147    return canvas;
4148  }
4149
4150  var fromCharCode = String.fromCharCode;
4151
4152  /**
4153   * Get string from char code in data view.
4154   * @param {DataView} dataView - The data view for read.
4155   * @param {number} start - The start index.
4156   * @param {number} length - The read length.
4157   * @returns {string} The read result.
4158   */
4159
4160  function getStringFromCharCode(dataView, start, length) {
4161    var str = '';
4162    var i = void 0;
4163
4164    length += start;
4165
4166    for (i = start; i < length; i += 1) {
4167      str += fromCharCode(dataView.getUint8(i));
4168    }
4169
4170    return str;
4171  }
4172
4173  var REGEXP_DATA_URL_HEAD = /^data:.*,/;
4174
4175  /**
4176   * Transform Data URL to array buffer.
4177   * @param {string} dataURL - The Data URL to transform.
4178   * @returns {ArrayBuffer} The result array buffer.
4179   */
4180  function dataURLToArrayBuffer(dataURL) {
4181    var base64 = dataURL.replace(REGEXP_DATA_URL_HEAD, '');
4182    var binary = atob(base64);
4183    var arrayBuffer = new ArrayBuffer(binary.length);
4184    var uint8 = new Uint8Array(arrayBuffer);
4185
4186    forEach(uint8, function (value, i) {
4187      uint8[i] = binary.charCodeAt(i);
4188    });
4189
4190    return arrayBuffer;
4191  }
4192
4193  /**
4194   * Transform array buffer to Data URL.
4195   * @param {ArrayBuffer} arrayBuffer - The array buffer to transform.
4196   * @param {string} mimeType - The mime type of the Data URL.
4197   * @returns {string} The result Data URL.
4198   */
4199  function arrayBufferToDataURL(arrayBuffer, mimeType) {
4200    var uint8 = new Uint8Array(arrayBuffer);
4201    var data = '';
4202
4203    // TypedArray.prototype.forEach is not supported in some browsers.
4204    forEach(uint8, function (value) {
4205      data += fromCharCode(value);
4206    });
4207
4208    return 'data:' + mimeType + ';base64,' + btoa(data);
4209  }
4210
4211  /**
4212   * Get orientation value from given array buffer.
4213   * @param {ArrayBuffer} arrayBuffer - The array buffer to read.
4214   * @returns {number} The read orientation value.
4215   */
4216  function getOrientation(arrayBuffer) {
4217    var dataView = new DataView(arrayBuffer);
4218    var orientation = void 0;
4219    var littleEndian = void 0;
4220    var app1Start = void 0;
4221    var ifdStart = void 0;
4222
4223    // Only handle JPEG image (start by 0xFFD8)
4224    if (dataView.getUint8(0) === 0xFF && dataView.getUint8(1) === 0xD8) {
4225      var length = dataView.byteLength;
4226      var offset = 2;
4227
4228      while (offset < length) {
4229        if (dataView.getUint8(offset) === 0xFF && dataView.getUint8(offset + 1) === 0xE1) {
4230          app1Start = offset;
4231          break;
4232        }
4233
4234        offset += 1;
4235      }
4236    }
4237
4238    if (app1Start) {
4239      var exifIDCode = app1Start + 4;
4240      var tiffOffset = app1Start + 10;
4241
4242      if (getStringFromCharCode(dataView, exifIDCode, 4) === 'Exif') {
4243        var endianness = dataView.getUint16(tiffOffset);
4244
4245        littleEndian = endianness === 0x4949;
4246
4247        if (littleEndian || endianness === 0x4D4D /* bigEndian */) {
4248            if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002A) {
4249              var firstIFDOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
4250
4251              if (firstIFDOffset >= 0x00000008) {
4252                ifdStart = tiffOffset + firstIFDOffset;
4253              }
4254            }
4255          }
4256      }
4257    }
4258
4259    if (ifdStart) {
4260      var _length = dataView.getUint16(ifdStart, littleEndian);
4261      var _offset = void 0;
4262      var i = void 0;
4263
4264      for (i = 0; i < _length; i += 1) {
4265        _offset = ifdStart + i * 12 + 2;
4266
4267        if (dataView.getUint16(_offset, littleEndian) === 0x0112 /* Orientation */) {
4268            // 8 is the offset of the current tag's value
4269            _offset += 8;
4270
4271            // Get the original orientation value
4272            orientation = dataView.getUint16(_offset, littleEndian);
4273
4274            // Override the orientation with its default value
4275            dataView.setUint16(_offset, 1, littleEndian);
4276            break;
4277          }
4278      }
4279    }
4280
4281    return orientation;
4282  }
4283
4284  /**
4285   * Parse Exif Orientation value.
4286   * @param {number} orientation - The orientation to parse.
4287   * @returns {Object} The parsed result.
4288   */
4289  function parseOrientation(orientation) {
4290    var rotate = 0;
4291    var scaleX = 1;
4292    var scaleY = 1;
4293
4294    switch (orientation) {
4295      // Flip horizontal
4296      case 2:
4297        scaleX = -1;
4298        break;
4299
4300      // Rotate left 180°
4301      case 3:
4302        rotate = -180;
4303        break;
4304
4305      // Flip vertical
4306      case 4:
4307        scaleY = -1;
4308        break;
4309
4310      // Flip vertical and rotate right 90°
4311      case 5:
4312        rotate = 90;
4313        scaleY = -1;
4314        break;
4315
4316      // Rotate right 90°
4317      case 6:
4318        rotate = 90;
4319        break;
4320
4321      // Flip horizontal and rotate right 90°
4322      case 7:
4323        rotate = 90;
4324        scaleX = -1;
4325        break;
4326
4327      // Rotate left 90°
4328      case 8:
4329        rotate = -90;
4330        break;
4331
4332      default:
4333    }
4334
4335    return {
4336      rotate: rotate,
4337      scaleX: scaleX,
4338      scaleY: scaleY
4339    };
4340  }
4341
4342  var render = {
4343    render: function render() {
4344      this.initContainer();
4345      this.initCanvas();
4346      this.initCropBox();
4347      this.renderCanvas();
4348
4349      if (this.cropped) {
4350        this.renderCropBox();
4351      }
4352    },
4353    initContainer: function initContainer() {
4354      var element = this.element,
4355          options = this.options,
4356          container = this.container,
4357          cropper = this.cropper;
4358
4359
4360      addClass(cropper, CLASS_HIDDEN);
4361      removeClass(element, CLASS_HIDDEN);
4362
4363      var containerData = {
4364        width: Math.max(container.offsetWidth, Number(options.minContainerWidth) || 200),
4365        height: Math.max(container.offsetHeight, Number(options.minContainerHeight) || 100)
4366      };
4367
4368      this.containerData = containerData;
4369
4370      setStyle(cropper, {
4371        width: containerData.width,
4372        height: containerData.height
4373      });
4374
4375      addClass(element, CLASS_HIDDEN);
4376      removeClass(cropper, CLASS_HIDDEN);
4377    },
4378
4379
4380    // Canvas (image wrapper)
4381    initCanvas: function initCanvas() {
4382      var containerData = this.containerData,
4383          imageData = this.imageData;
4384      var viewMode = this.options.viewMode;
4385
4386      var rotated = Math.abs(imageData.rotate) % 180 === 90;
4387      var naturalWidth = rotated ? imageData.naturalHeight : imageData.naturalWidth;
4388      var naturalHeight = rotated ? imageData.naturalWidth : imageData.naturalHeight;
vendor: 61,811 bytes, lines 4389-6453
4389      var aspectRatio = naturalWidth / naturalHeight;
4390      var canvasWidth = containerData.width;
4391      var canvasHeight = containerData.height;
4392
4393      if (containerData.height * aspectRatio > containerData.width) {
4394        if (viewMode === 3) {
4395          canvasWidth = containerData.height * aspectRatio;
4396        } else {
4397          canvasHeight = containerData.width / aspectRatio;
4398        }
4399      } else if (viewMode === 3) {
4400        canvasHeight = containerData.width / aspectRatio;
4401      } else {
4402        canvasWidth = containerData.height * aspectRatio;
4403      }
4404
4405      var canvasData = {
4406        aspectRatio: aspectRatio,
4407        naturalWidth: naturalWidth,
4408        naturalHeight: naturalHeight,
4409        width: canvasWidth,
4410        height: canvasHeight
4411      };
4412
4413      canvasData.left = (containerData.width - canvasWidth) / 2;
4414      canvasData.top = (containerData.height - canvasHeight) / 2;
4415      canvasData.oldLeft = canvasData.left;
4416      canvasData.oldTop = canvasData.top;
4417
4418      this.canvasData = canvasData;
4419      this.limited = viewMode === 1 || viewMode === 2;
4420      this.limitCanvas(true, true);
4421      this.initialImageData = assign({}, imageData);
4422      this.initialCanvasData = assign({}, canvasData);
4423    },
4424    limitCanvas: function limitCanvas(sizeLimited, positionLimited) {
4425      var options = this.options,
4426          containerData = this.containerData,
4427          canvasData = this.canvasData,
4428          cropBoxData = this.cropBoxData;
4429      var viewMode = options.viewMode;
4430      var aspectRatio = canvasData.aspectRatio;
4431
4432      var cropped = this.cropped && cropBoxData;
4433
4434      if (sizeLimited) {
4435        var minCanvasWidth = Number(options.minCanvasWidth) || 0;
4436        var minCanvasHeight = Number(options.minCanvasHeight) || 0;
4437
4438        if (viewMode > 1) {
4439          minCanvasWidth = Math.max(minCanvasWidth, containerData.width);
4440          minCanvasHeight = Math.max(minCanvasHeight, containerData.height);
4441
4442          if (viewMode === 3) {
4443            if (minCanvasHeight * aspectRatio > minCanvasWidth) {
4444              minCanvasWidth = minCanvasHeight * aspectRatio;
4445            } else {
4446              minCanvasHeight = minCanvasWidth / aspectRatio;
4447            }
4448          }
4449        } else if (viewMode > 0) {
4450          if (minCanvasWidth) {
4451            minCanvasWidth = Math.max(minCanvasWidth, cropped ? cropBoxData.width : 0);
4452          } else if (minCanvasHeight) {
4453            minCanvasHeight = Math.max(minCanvasHeight, cropped ? cropBoxData.height : 0);
4454          } else if (cropped) {
4455            minCanvasWidth = cropBoxData.width;
4456            minCanvasHeight = cropBoxData.height;
4457
4458            if (minCanvasHeight * aspectRatio > minCanvasWidth) {
4459              minCanvasWidth = minCanvasHeight * aspectRatio;
4460            } else {
4461              minCanvasHeight = minCanvasWidth / aspectRatio;
4462            }
4463          }
4464        }
4465
4466        var _getAdjustedSizes = getAdjustedSizes({
4467          aspectRatio: aspectRatio,
4468          width: minCanvasWidth,
4469          height: minCanvasHeight
4470        });
4471
4472        minCanvasWidth = _getAdjustedSizes.width;
4473        minCanvasHeight = _getAdjustedSizes.height;
4474
4475
4476        canvasData.minWidth = minCanvasWidth;
4477        canvasData.minHeight = minCanvasHeight;
4478        canvasData.maxWidth = Infinity;
4479        canvasData.maxHeight = Infinity;
4480      }
4481
4482      if (positionLimited) {
4483        if (viewMode) {
4484          var newCanvasLeft = containerData.width - canvasData.width;
4485          var newCanvasTop = containerData.height - canvasData.height;
4486
4487          canvasData.minLeft = Math.min(0, newCanvasLeft);
4488          canvasData.minTop = Math.min(0, newCanvasTop);
4489          canvasData.maxLeft = Math.max(0, newCanvasLeft);
4490          canvasData.maxTop = Math.max(0, newCanvasTop);
4491
4492          if (cropped && this.limited) {
4493            canvasData.minLeft = Math.min(cropBoxData.left, cropBoxData.left + (cropBoxData.width - canvasData.width));
4494            canvasData.minTop = Math.min(cropBoxData.top, cropBoxData.top + (cropBoxData.height - canvasData.height));
4495            canvasData.maxLeft = cropBoxData.left;
4496            canvasData.maxTop = cropBoxData.top;
4497
4498            if (viewMode === 2) {
4499              if (canvasData.width >= containerData.width) {
4500                canvasData.minLeft = Math.min(0, newCanvasLeft);
4501                canvasData.maxLeft = Math.max(0, newCanvasLeft);
4502              }
4503
4504              if (canvasData.height >= containerData.height) {
4505                canvasData.minTop = Math.min(0, newCanvasTop);
4506                canvasData.maxTop = Math.max(0, newCanvasTop);
4507              }
4508            }
4509          }
4510        } else {
4511          canvasData.minLeft = -canvasData.width;
4512          canvasData.minTop = -canvasData.height;
4513          canvasData.maxLeft = containerData.width;
4514          canvasData.maxTop = containerData.height;
4515        }
4516      }
4517    },
4518    renderCanvas: function renderCanvas(changed, transformed) {
4519      var canvasData = this.canvasData,
4520          imageData = this.imageData;
4521
4522
4523      if (transformed) {
4524        var _getRotatedSizes = getRotatedSizes({
4525          width: imageData.naturalWidth * Math.abs(imageData.scaleX || 1),
4526          height: imageData.naturalHeight * Math.abs(imageData.scaleY || 1),
4527          degree: imageData.rotate || 0
4528        }),
4529            naturalWidth = _getRotatedSizes.width,
4530            naturalHeight = _getRotatedSizes.height;
4531
4532        var width = canvasData.width * (naturalWidth / canvasData.naturalWidth);
4533        var height = canvasData.height * (naturalHeight / canvasData.naturalHeight);
4534
4535        canvasData.left -= (width - canvasData.width) / 2;
4536        canvasData.top -= (height - canvasData.height) / 2;
4537        canvasData.width = width;
4538        canvasData.height = height;
4539        canvasData.aspectRatio = naturalWidth / naturalHeight;
4540        canvasData.naturalWidth = naturalWidth;
4541        canvasData.naturalHeight = naturalHeight;
4542        this.limitCanvas(true, false);
4543      }
4544
4545      if (canvasData.width > canvasData.maxWidth || canvasData.width < canvasData.minWidth) {
4546        canvasData.left = canvasData.oldLeft;
4547      }
4548
4549      if (canvasData.height > canvasData.maxHeight || canvasData.height < canvasData.minHeight) {
4550        canvasData.top = canvasData.oldTop;
4551      }
4552
4553      canvasData.width = Math.min(Math.max(canvasData.width, canvasData.minWidth), canvasData.maxWidth);
4554      canvasData.height = Math.min(Math.max(canvasData.height, canvasData.minHeight), canvasData.maxHeight);
4555
4556      this.limitCanvas(false, true);
4557
4558      canvasData.left = Math.min(Math.max(canvasData.left, canvasData.minLeft), canvasData.maxLeft);
4559      canvasData.top = Math.min(Math.max(canvasData.top, canvasData.minTop), canvasData.maxTop);
4560      canvasData.oldLeft = canvasData.left;
4561      canvasData.oldTop = canvasData.top;
4562
4563      setStyle(this.canvas, assign({
4564        width: canvasData.width,
4565        height: canvasData.height
4566      }, getTransforms({
4567        translateX: canvasData.left,
4568        translateY: canvasData.top
4569      })));
4570
4571      this.renderImage(changed);
4572
4573      if (this.cropped && this.limited) {
4574        this.limitCropBox(true, true);
4575      }
4576    },
4577    renderImage: function renderImage(changed) {
4578      var canvasData = this.canvasData,
4579          imageData = this.imageData;
4580
4581      var width = imageData.naturalWidth * (canvasData.width / canvasData.naturalWidth);
4582      var height = imageData.naturalHeight * (canvasData.height / canvasData.naturalHeight);
4583
4584      assign(imageData, {
4585        width: width,
4586        height: height,
4587        left: (canvasData.width - width) / 2,
4588        top: (canvasData.height - height) / 2
4589      });
4590      setStyle(this.image, assign({
4591        width: imageData.width,
4592        height: imageData.height
4593      }, getTransforms(assign({
4594        translateX: imageData.left,
4595        translateY: imageData.top
4596      }, imageData))));
4597
4598      if (changed) {
4599        this.output();
4600      }
4601    },
4602    initCropBox: function initCropBox() {
4603      var options = this.options,
4604          canvasData = this.canvasData;
4605      var aspectRatio = options.aspectRatio;
4606
4607      var autoCropArea = Number(options.autoCropArea) || 0.8;
4608      var cropBoxData = {
4609        width: canvasData.width,
4610        height: canvasData.height
4611      };
4612
4613      if (aspectRatio) {
4614        if (canvasData.height * aspectRatio > canvasData.width) {
4615          cropBoxData.height = cropBoxData.width / aspectRatio;
4616        } else {
4617          cropBoxData.width = cropBoxData.height * aspectRatio;
4618        }
4619      }
4620
4621      this.cropBoxData = cropBoxData;
4622      this.limitCropBox(true, true);
4623
4624      // Initialize auto crop area
4625      cropBoxData.width = Math.min(Math.max(cropBoxData.width, cropBoxData.minWidth), cropBoxData.maxWidth);
4626      cropBoxData.height = Math.min(Math.max(cropBoxData.height, cropBoxData.minHeight), cropBoxData.maxHeight);
4627
4628      // The width/height of auto crop area must large than "minWidth/Height"
4629      cropBoxData.width = Math.max(cropBoxData.minWidth, cropBoxData.width * autoCropArea);
4630      cropBoxData.height = Math.max(cropBoxData.minHeight, cropBoxData.height * autoCropArea);
4631      cropBoxData.left = canvasData.left + (canvasData.width - cropBoxData.width) / 2;
4632      cropBoxData.top = canvasData.top + (canvasData.height - cropBoxData.height) / 2;
4633      cropBoxData.oldLeft = cropBoxData.left;
4634      cropBoxData.oldTop = cropBoxData.top;
4635
4636      this.initialCropBoxData = assign({}, cropBoxData);
4637    },
4638    limitCropBox: function limitCropBox(sizeLimited, positionLimited) {
4639      var options = this.options,
4640          containerData = this.containerData,
4641          canvasData = this.canvasData,
4642          cropBoxData = this.cropBoxData,
4643          limited = this.limited;
4644      var aspectRatio = options.aspectRatio;
4645
4646
4647      if (sizeLimited) {
4648        var minCropBoxWidth = Number(options.minCropBoxWidth) || 0;
4649        var minCropBoxHeight = Number(options.minCropBoxHeight) || 0;
4650        var maxCropBoxWidth = Math.min(containerData.width, limited ? canvasData.width : containerData.width);
4651        var maxCropBoxHeight = Math.min(containerData.height, limited ? canvasData.height : containerData.height);
4652
4653        // The min/maxCropBoxWidth/Height must be less than container's width/height
4654        minCropBoxWidth = Math.min(minCropBoxWidth, containerData.width);
4655        minCropBoxHeight = Math.min(minCropBoxHeight, containerData.height);
4656
4657        if (aspectRatio) {
4658          if (minCropBoxWidth && minCropBoxHeight) {
4659            if (minCropBoxHeight * aspectRatio > minCropBoxWidth) {
4660              minCropBoxHeight = minCropBoxWidth / aspectRatio;
4661            } else {
4662              minCropBoxWidth = minCropBoxHeight * aspectRatio;
4663            }
4664          } else if (minCropBoxWidth) {
4665            minCropBoxHeight = minCropBoxWidth / aspectRatio;
4666          } else if (minCropBoxHeight) {
4667            minCropBoxWidth = minCropBoxHeight * aspectRatio;
4668          }
4669
4670          if (maxCropBoxHeight * aspectRatio > maxCropBoxWidth) {
4671            maxCropBoxHeight = maxCropBoxWidth / aspectRatio;
4672          } else {
4673            maxCropBoxWidth = maxCropBoxHeight * aspectRatio;
4674          }
4675        }
4676
4677        // The minWidth/Height must be less than maxWidth/Height
4678        cropBoxData.minWidth = Math.min(minCropBoxWidth, maxCropBoxWidth);
4679        cropBoxData.minHeight = Math.min(minCropBoxHeight, maxCropBoxHeight);
4680        cropBoxData.maxWidth = maxCropBoxWidth;
4681        cropBoxData.maxHeight = maxCropBoxHeight;
4682      }
4683
4684      if (positionLimited) {
4685        if (limited) {
4686          cropBoxData.minLeft = Math.max(0, canvasData.left);
4687          cropBoxData.minTop = Math.max(0, canvasData.top);
4688          cropBoxData.maxLeft = Math.min(containerData.width, canvasData.left + canvasData.width) - cropBoxData.width;
4689          cropBoxData.maxTop = Math.min(containerData.height, canvasData.top + canvasData.height) - cropBoxData.height;
4690        } else {
4691          cropBoxData.minLeft = 0;
4692          cropBoxData.minTop = 0;
4693          cropBoxData.maxLeft = containerData.width - cropBoxData.width;
4694          cropBoxData.maxTop = containerData.height - cropBoxData.height;
4695        }
4696      }
4697    },
4698    renderCropBox: function renderCropBox() {
4699      var options = this.options,
4700          containerData = this.containerData,
4701          cropBoxData = this.cropBoxData;
4702
4703
4704      if (cropBoxData.width > cropBoxData.maxWidth || cropBoxData.width < cropBoxData.minWidth) {
4705        cropBoxData.left = cropBoxData.oldLeft;
4706      }
4707
4708      if (cropBoxData.height > cropBoxData.maxHeight || cropBoxData.height < cropBoxData.minHeight) {
4709        cropBoxData.top = cropBoxData.oldTop;
4710      }
4711
4712      cropBoxData.width = Math.min(Math.max(cropBoxData.width, cropBoxData.minWidth), cropBoxData.maxWidth);
4713      cropBoxData.height = Math.min(Math.max(cropBoxData.height, cropBoxData.minHeight), cropBoxData.maxHeight);
4714
4715      this.limitCropBox(false, true);
4716
4717      cropBoxData.left = Math.min(Math.max(cropBoxData.left, cropBoxData.minLeft), cropBoxData.maxLeft);
4718      cropBoxData.top = Math.min(Math.max(cropBoxData.top, cropBoxData.minTop), cropBoxData.maxTop);
4719      cropBoxData.oldLeft = cropBoxData.left;
4720      cropBoxData.oldTop = cropBoxData.top;
4721
4722      if (options.movable && options.cropBoxMovable) {
4723        // Turn to move the canvas when the crop box is equal to the container
4724        setData(this.face, DATA_ACTION, cropBoxData.width >= containerData.width && cropBoxData.height >= containerData.height ? ACTION_MOVE : ACTION_ALL);
4725      }
4726
4727      setStyle(this.cropBox, assign({
4728        width: cropBoxData.width,
4729        height: cropBoxData.height
4730      }, getTransforms({
4731        translateX: cropBoxData.left,
4732        translateY: cropBoxData.top
4733      })));
4734
4735      if (this.cropped && this.limited) {
4736        this.limitCanvas(true, true);
4737      }
4738
4739      if (!this.disabled) {
4740        this.output();
4741      }
4742    },
4743    output: function output() {
4744      this.preview();
4745      dispatchEvent(this.element, EVENT_CROP, this.getData());
4746    }
4747  };
4748
4749  var preview = {
4750    initPreview: function initPreview() {
4751      var crossOrigin = this.crossOrigin;
4752      var preview = this.options.preview;
4753
4754      var url = crossOrigin ? this.crossOriginUrl : this.url;
4755      var image = document.createElement('img');
4756
4757      if (crossOrigin) {
4758        image.crossOrigin = crossOrigin;
4759      }
4760
4761      image.src = url;
4762      this.viewBox.appendChild(image);
4763      this.viewBoxImage = image;
4764
4765      if (!preview) {
4766        return;
4767      }
4768
4769      var previews = preview;
4770
4771      if (typeof preview === 'string') {
4772        previews = this.element.ownerDocument.querySelectorAll(preview);
4773      } else if (preview.querySelector) {
4774        previews = [preview];
4775      }
4776
4777      this.previews = previews;
4778
4779      forEach(previews, function (el) {
4780        var img = document.createElement('img');
4781
4782        // Save the original size for recover
4783        setData(el, DATA_PREVIEW, {
4784          width: el.offsetWidth,
4785          height: el.offsetHeight,
4786          html: el.innerHTML
4787        });
4788
4789        if (crossOrigin) {
4790          img.crossOrigin = crossOrigin;
4791        }
4792
4793        img.src = url;
4794
4795        /**
4796         * Override img element styles
4797         * Add `display:block` to avoid margin top issue
4798         * Add `height:auto` to override `height` attribute on IE8
4799         * (Occur only when margin-top <= -height)
4800         */
4801        img.style.cssText = 'display:block;' + 'width:100%;' + 'height:auto;' + 'min-width:0!important;' + 'min-height:0!important;' + 'max-width:none!important;' + 'max-height:none!important;' + 'image-orientation:0deg!important;"';
4802
4803        el.innerHTML = '';
4804        el.appendChild(img);
4805      });
4806    },
4807    resetPreview: function resetPreview() {
4808      forEach(this.previews, function (element) {
4809        var data = getData(element, DATA_PREVIEW);
4810
4811        setStyle(element, {
4812          width: data.width,
4813          height: data.height
4814        });
4815
4816        element.innerHTML = data.html;
4817        removeData(element, DATA_PREVIEW);
4818      });
4819    },
4820    preview: function preview() {
4821      var imageData = this.imageData,
4822          canvasData = this.canvasData,
4823          cropBoxData = this.cropBoxData;
4824      var cropBoxWidth = cropBoxData.width,
4825          cropBoxHeight = cropBoxData.height;
4826      var width = imageData.width,
4827          height = imageData.height;
4828
4829      var left = cropBoxData.left - canvasData.left - imageData.left;
4830      var top = cropBoxData.top - canvasData.top - imageData.top;
4831
4832      if (!this.cropped || this.disabled) {
4833        return;
4834      }
4835
4836      setStyle(this.viewBoxImage, assign({
4837        width: width,
4838        height: height
4839      }, getTransforms(assign({
4840        translateX: -left,
4841        translateY: -top
4842      }, imageData))));
4843
4844      forEach(this.previews, function (element) {
4845        var data = getData(element, DATA_PREVIEW);
4846        var originalWidth = data.width;
4847        var originalHeight = data.height;
4848        var newWidth = originalWidth;
4849        var newHeight = originalHeight;
4850        var ratio = 1;
4851
4852        if (cropBoxWidth) {
4853          ratio = originalWidth / cropBoxWidth;
4854          newHeight = cropBoxHeight * ratio;
4855        }
4856
4857        if (cropBoxHeight && newHeight > originalHeight) {
4858          ratio = originalHeight / cropBoxHeight;
4859          newWidth = cropBoxWidth * ratio;
4860          newHeight = originalHeight;
4861        }
4862
4863        setStyle(element, {
4864          width: newWidth,
4865          height: newHeight
4866        });
4867
4868        setStyle(element.getElementsByTagName('img')[0], assign({
4869          width: width * ratio,
4870          height: height * ratio
4871        }, getTransforms(assign({
4872          translateX: -left * ratio,
4873          translateY: -top * ratio
4874        }, imageData))));
4875      });
4876    }
4877  };
4878
4879  var events = {
4880    bind: function bind() {
4881      var element = this.element,
4882          options = this.options,
4883          cropper = this.cropper;
4884
4885
4886      if (isFunction(options.cropstart)) {
4887        addListener(element, EVENT_CROP_START, options.cropstart);
4888      }
4889
4890      if (isFunction(options.cropmove)) {
4891        addListener(element, EVENT_CROP_MOVE, options.cropmove);
4892      }
4893
4894      if (isFunction(options.cropend)) {
4895        addListener(element, EVENT_CROP_END, options.cropend);
4896      }
4897
4898      if (isFunction(options.crop)) {
4899        addListener(element, EVENT_CROP, options.crop);
4900      }
4901
4902      if (isFunction(options.zoom)) {
4903        addListener(element, EVENT_ZOOM, options.zoom);
4904      }
4905
4906      addListener(cropper, EVENT_POINTER_DOWN, this.onCropStart = this.cropStart.bind(this));
4907
4908      if (options.zoomable && options.zoomOnWheel) {
4909        addListener(cropper, EVENT_WHEEL, this.onWheel = this.wheel.bind(this));
4910      }
4911
4912      if (options.toggleDragModeOnDblclick) {
4913        addListener(cropper, EVENT_DBLCLICK, this.onDblclick = this.dblclick.bind(this));
4914      }
4915
4916      addListener(element.ownerDocument, EVENT_POINTER_MOVE, this.onCropMove = this.cropMove.bind(this));
4917      addListener(element.ownerDocument, EVENT_POINTER_UP, this.onCropEnd = this.cropEnd.bind(this));
4918
4919      if (options.responsive) {
4920        addListener(window, EVENT_RESIZE, this.onResize = this.resize.bind(this));
4921      }
4922    },
4923    unbind: function unbind() {
4924      var element = this.element,
4925          options = this.options,
4926          cropper = this.cropper;
4927
4928
4929      if (isFunction(options.cropstart)) {
4930        removeListener(element, EVENT_CROP_START, options.cropstart);
4931      }
4932
4933      if (isFunction(options.cropmove)) {
4934        removeListener(element, EVENT_CROP_MOVE, options.cropmove);
4935      }
4936
4937      if (isFunction(options.cropend)) {
4938        removeListener(element, EVENT_CROP_END, options.cropend);
4939      }
4940
4941      if (isFunction(options.crop)) {
4942        removeListener(element, EVENT_CROP, options.crop);
4943      }
4944
4945      if (isFunction(options.zoom)) {
4946        removeListener(element, EVENT_ZOOM, options.zoom);
4947      }
4948
4949      removeListener(cropper, EVENT_POINTER_DOWN, this.onCropStart);
4950
4951      if (options.zoomable && options.zoomOnWheel) {
4952        removeListener(cropper, EVENT_WHEEL, this.onWheel);
4953      }
4954
4955      if (options.toggleDragModeOnDblclick) {
4956        removeListener(cropper, EVENT_DBLCLICK, this.onDblclick);
4957      }
4958
4959      removeListener(element.ownerDocument, EVENT_POINTER_MOVE, this.onCropMove);
4960      removeListener(element.ownerDocument, EVENT_POINTER_UP, this.onCropEnd);
4961
4962      if (options.responsive) {
4963        removeListener(window, EVENT_RESIZE, this.onResize);
4964      }
4965    }
4966  };
4967
4968  var handlers = {
4969    resize: function resize() {
4970      var options = this.options,
4971          container = this.container,
4972          containerData = this.containerData;
4973
4974      var minContainerWidth = Number(options.minContainerWidth) || 200;
4975      var minContainerHeight = Number(options.minContainerHeight) || 100;
4976
4977      if (this.disabled || containerData.width <= minContainerWidth || containerData.height <= minContainerHeight) {
4978        return;
4979      }
4980
4981      var ratio = container.offsetWidth / containerData.width;
4982
4983      // Resize when width changed or height changed
4984      if (ratio !== 1 || container.offsetHeight !== containerData.height) {
4985        var canvasData = void 0;
4986        var cropBoxData = void 0;
4987
4988        if (options.restore) {
4989          canvasData = this.getCanvasData();
4990          cropBoxData = this.getCropBoxData();
4991        }
4992
4993        this.render();
4994
4995        if (options.restore) {
4996          this.setCanvasData(forEach(canvasData, function (n, i) {
4997            canvasData[i] = n * ratio;
4998          }));
4999          this.setCropBoxData(forEach(cropBoxData, function (n, i) {
5000            cropBoxData[i] = n * ratio;
5001          }));
5002        }
5003      }
5004    },
5005    dblclick: function dblclick() {
5006      if (this.disabled || this.options.dragMode === DRAG_MODE_NONE) {
5007        return;
5008      }
5009
5010      this.setDragMode(hasClass(this.dragBox, CLASS_CROP) ? DRAG_MODE_MOVE : DRAG_MODE_CROP);
5011    },
5012    wheel: function wheel(e) {
5013      var _this = this;
5014
5015      var ratio = Number(this.options.wheelZoomRatio) || 0.1;
5016      var delta = 1;
5017
5018      if (this.disabled) {
5019        return;
5020      }
5021
5022      e.preventDefault();
5023
5024      // Limit wheel speed to prevent zoom too fast (#21)
5025      if (this.wheeling) {
5026        return;
5027      }
5028
5029      this.wheeling = true;
5030
5031      setTimeout(function () {
5032        _this.wheeling = false;
5033      }, 50);
5034
5035      if (e.deltaY) {
5036        delta = e.deltaY > 0 ? 1 : -1;
5037      } else if (e.wheelDelta) {
5038        delta = -e.wheelDelta / 120;
5039      } else if (e.detail) {
5040        delta = e.detail > 0 ? 1 : -1;
5041      }
5042
5043      this.zoom(-delta * ratio, e);
5044    },
5045    cropStart: function cropStart(e) {
5046      if (this.disabled) {
5047        return;
5048      }
5049
5050      var options = this.options,
5051          pointers = this.pointers;
5052
5053      var action = void 0;
5054
5055      if (e.changedTouches) {
5056        // Handle touch event
5057        forEach(e.changedTouches, function (touch) {
5058          pointers[touch.identifier] = getPointer(touch);
5059        });
5060      } else {
5061        // Handle mouse event and pointer event
5062        pointers[e.pointerId || 0] = getPointer(e);
5063      }
5064
5065      if (Object.keys(pointers).length > 1 && options.zoomable && options.zoomOnTouch) {
5066        action = ACTION_ZOOM;
5067      } else {
5068        action = getData(e.target, DATA_ACTION);
5069      }
5070
5071      if (!REGEXP_ACTIONS.test(action)) {
5072        return;
5073      }
5074
5075      if (dispatchEvent(this.element, EVENT_CROP_START, {
5076        originalEvent: e,
5077        action: action
5078      }) === false) {
5079        return;
5080      }
5081
5082      e.preventDefault();
5083
5084      this.action = action;
5085      this.cropping = false;
5086
5087      if (action === ACTION_CROP) {
5088        this.cropping = true;
5089        addClass(this.dragBox, CLASS_MODAL);
5090      }
5091    },
5092    cropMove: function cropMove(e) {
5093      var action = this.action;
5094
5095
5096      if (this.disabled || !action) {
5097        return;
5098      }
5099
5100      var pointers = this.pointers;
5101
5102
5103      e.preventDefault();
5104
5105      if (dispatchEvent(this.element, EVENT_CROP_MOVE, {
5106        originalEvent: e,
5107        action: action
5108      }) === false) {
5109        return;
5110      }
5111
5112      if (e.changedTouches) {
5113        forEach(e.changedTouches, function (touch) {
5114          assign(pointers[touch.identifier], getPointer(touch, true));
5115        });
5116      } else {
5117        assign(pointers[e.pointerId || 0], getPointer(e, true));
5118      }
5119
5120      this.change(e);
5121    },
5122    cropEnd: function cropEnd(e) {
5123      if (this.disabled) {
5124        return;
5125      }
5126
5127      var action = this.action,
5128          pointers = this.pointers;
5129
5130
5131      if (e.changedTouches) {
5132        forEach(e.changedTouches, function (touch) {
5133          delete pointers[touch.identifier];
5134        });
5135      } else {
5136        delete pointers[e.pointerId || 0];
5137      }
5138
5139      if (!action) {
5140        return;
5141      }
5142
5143      e.preventDefault();
5144
5145      if (!Object.keys(pointers).length) {
5146        this.action = '';
5147      }
5148
5149      if (this.cropping) {
5150        this.cropping = false;
5151        toggleClass(this.dragBox, CLASS_MODAL, this.cropped && this.options.modal);
5152      }
5153
5154      dispatchEvent(this.element, EVENT_CROP_END, {
5155        originalEvent: e,
5156        action: action
5157      });
5158    }
5159  };
5160
5161  var change = {
5162    change: function change(e) {
5163      var options = this.options,
5164          canvasData = this.canvasData,
5165          containerData = this.containerData,
5166          cropBoxData = this.cropBoxData,
5167          pointers = this.pointers;
5168      var action = this.action;
5169      var aspectRatio = options.aspectRatio;
5170      var left = cropBoxData.left,
5171          top = cropBoxData.top,
5172          width = cropBoxData.width,
5173          height = cropBoxData.height;
5174
5175      var right = left + width;
5176      var bottom = top + height;
5177      var minLeft = 0;
5178      var minTop = 0;
5179      var maxWidth = containerData.width;
5180      var maxHeight = containerData.height;
5181      var renderable = true;
5182      var offset = void 0;
5183
5184      // Locking aspect ratio in "free mode" by holding shift key
5185      if (!aspectRatio && e.shiftKey) {
5186        aspectRatio = width && height ? width / height : 1;
5187      }
5188
5189      if (this.limited) {
5190        minLeft = cropBoxData.minLeft;
5191        minTop = cropBoxData.minTop;
5192
5193        maxWidth = minLeft + Math.min(containerData.width, canvasData.width, canvasData.left + canvasData.width);
5194        maxHeight = minTop + Math.min(containerData.height, canvasData.height, canvasData.top + canvasData.height);
5195      }
5196
5197      var pointer = pointers[Object.keys(pointers)[0]];
5198      var range = {
5199        x: pointer.endX - pointer.startX,
5200        y: pointer.endY - pointer.startY
5201      };
5202      var check = function check(side) {
5203        switch (side) {
5204          case ACTION_EAST:
5205            if (right + range.x > maxWidth) {
5206              range.x = maxWidth - right;
5207            }
5208
5209            break;
5210
5211          case ACTION_WEST:
5212            if (left + range.x < minLeft) {
5213              range.x = minLeft - left;
5214            }
5215
5216            break;
5217
5218          case ACTION_NORTH:
5219            if (top + range.y < minTop) {
5220              range.y = minTop - top;
5221            }
5222
5223            break;
5224
5225          case ACTION_SOUTH:
5226            if (bottom + range.y > maxHeight) {
5227              range.y = maxHeight - bottom;
5228            }
5229
5230            break;
5231
5232          default:
5233        }
5234      };
5235
5236      switch (action) {
5237        // Move crop box
5238        case ACTION_ALL:
5239          left += range.x;
5240          top += range.y;
5241          break;
5242
5243        // Resize crop box
5244        case ACTION_EAST:
5245          if (range.x >= 0 && (right >= maxWidth || aspectRatio && (top <= minTop || bottom >= maxHeight))) {
5246            renderable = false;
5247            break;
5248          }
5249
5250          check(ACTION_EAST);
5251          width += range.x;
5252
5253          if (aspectRatio) {
5254            height = width / aspectRatio;
5255            top -= range.x / aspectRatio / 2;
5256          }
5257
5258          if (width < 0) {
5259            action = ACTION_WEST;
5260            width = 0;
5261          }
5262
5263          break;
5264
5265        case ACTION_NORTH:
5266          if (range.y <= 0 && (top <= minTop || aspectRatio && (left <= minLeft || right >= maxWidth))) {
5267            renderable = false;
5268            break;
5269          }
5270
5271          check(ACTION_NORTH);
5272          height -= range.y;
5273          top += range.y;
5274
5275          if (aspectRatio) {
5276            width = height * aspectRatio;
5277            left += range.y * aspectRatio / 2;
5278          }
5279
5280          if (height < 0) {
5281            action = ACTION_SOUTH;
5282            height = 0;
5283          }
5284
5285          break;
5286
5287        case ACTION_WEST:
5288          if (range.x <= 0 && (left <= minLeft || aspectRatio && (top <= minTop || bottom >= maxHeight))) {
5289            renderable = false;
5290            break;
5291          }
5292
5293          check(ACTION_WEST);
5294          width -= range.x;
5295          left += range.x;
5296
5297          if (aspectRatio) {
5298            height = width / aspectRatio;
5299            top += range.x / aspectRatio / 2;
5300          }
5301
5302          if (width < 0) {
5303            action = ACTION_EAST;
5304            width = 0;
5305          }
5306
5307          break;
5308
5309        case ACTION_SOUTH:
5310          if (range.y >= 0 && (bottom >= maxHeight || aspectRatio && (left <= minLeft || right >= maxWidth))) {
5311            renderable = false;
5312            break;
5313          }
5314
5315          check(ACTION_SOUTH);
5316          height += range.y;
5317
5318          if (aspectRatio) {
5319            width = height * aspectRatio;
5320            left -= range.y * aspectRatio / 2;
5321          }
5322
5323          if (height < 0) {
5324            action = ACTION_NORTH;
5325            height = 0;
5326          }
5327
5328          break;
5329
5330        case ACTION_NORTH_EAST:
5331          if (aspectRatio) {
5332            if (range.y <= 0 && (top <= minTop || right >= maxWidth)) {
5333              renderable = false;
5334              break;
5335            }
5336
5337            check(ACTION_NORTH);
5338            height -= range.y;
5339            top += range.y;
5340            width = height * aspectRatio;
5341          } else {
5342            check(ACTION_NORTH);
5343            check(ACTION_EAST);
5344
5345            if (range.x >= 0) {
5346              if (right < maxWidth) {
5347                width += range.x;
5348              } else if (range.y <= 0 && top <= minTop) {
5349                renderable = false;
5350              }
5351            } else {
5352              width += range.x;
5353            }
5354
5355            if (range.y <= 0) {
5356              if (top > minTop) {
5357                height -= range.y;
5358                top += range.y;
5359              }
5360            } else {
5361              height -= range.y;
5362              top += range.y;
5363            }
5364          }
5365
5366          if (width < 0 && height < 0) {
5367            action = ACTION_SOUTH_WEST;
5368            height = 0;
5369            width = 0;
5370          } else if (width < 0) {
5371            action = ACTION_NORTH_WEST;
5372            width = 0;
5373          } else if (height < 0) {
5374            action = ACTION_SOUTH_EAST;
5375            height = 0;
5376          }
5377
5378          break;
5379
5380        case ACTION_NORTH_WEST:
5381          if (aspectRatio) {
5382            if (range.y <= 0 && (top <= minTop || left <= minLeft)) {
5383              renderable = false;
5384              break;
5385            }
5386
5387            check(ACTION_NORTH);
5388            height -= range.y;
5389            top += range.y;
5390            width = height * aspectRatio;
5391            left += range.y * aspectRatio;
5392          } else {
5393            check(ACTION_NORTH);
5394            check(ACTION_WEST);
5395
5396            if (range.x <= 0) {
5397              if (left > minLeft) {
5398                width -= range.x;
5399                left += range.x;
5400              } else if (range.y <= 0 && top <= minTop) {
5401                renderable = false;
5402              }
5403            } else {
5404              width -= range.x;
5405              left += range.x;
5406            }
5407
5408            if (range.y <= 0) {
5409              if (top > minTop) {
5410                height -= range.y;
5411                top += range.y;
5412              }
5413            } else {
5414              height -= range.y;
5415              top += range.y;
5416            }
5417          }
5418
5419          if (width < 0 && height < 0) {
5420            action = ACTION_SOUTH_EAST;
5421            height = 0;
5422            width = 0;
5423          } else if (width < 0) {
5424            action = ACTION_NORTH_EAST;
5425            width = 0;
5426          } else if (height < 0) {
5427            action = ACTION_SOUTH_WEST;
5428            height = 0;
5429          }
5430
5431          break;
5432
5433        case ACTION_SOUTH_WEST:
5434          if (aspectRatio) {
5435            if (range.x <= 0 && (left <= minLeft || bottom >= maxHeight)) {
5436              renderable = false;
5437              break;
5438            }
5439
5440            check(ACTION_WEST);
5441            width -= range.x;
5442            left += range.x;
5443            height = width / aspectRatio;
5444          } else {
5445            check(ACTION_SOUTH);
5446            check(ACTION_WEST);
5447
5448            if (range.x <= 0) {
5449              if (left > minLeft) {
5450                width -= range.x;
5451                left += range.x;
5452              } else if (range.y >= 0 && bottom >= maxHeight) {
5453                renderable = false;
5454              }
5455            } else {
5456              width -= range.x;
5457              left += range.x;
5458            }
5459
5460            if (range.y >= 0) {
5461              if (bottom < maxHeight) {
5462                height += range.y;
5463              }
5464            } else {
5465              height += range.y;
5466            }
5467          }
5468
5469          if (width < 0 && height < 0) {
5470            action = ACTION_NORTH_EAST;
5471            height = 0;
5472            width = 0;
5473          } else if (width < 0) {
5474            action = ACTION_SOUTH_EAST;
5475            width = 0;
5476          } else if (height < 0) {
5477            action = ACTION_NORTH_WEST;
5478            height = 0;
5479          }
5480
5481          break;
5482
5483        case ACTION_SOUTH_EAST:
5484          if (aspectRatio) {
5485            if (range.x >= 0 && (right >= maxWidth || bottom >= maxHeight)) {
5486              renderable = false;
5487              break;
5488            }
5489
5490            check(ACTION_EAST);
5491            width += range.x;
5492            height = width / aspectRatio;
5493          } else {
5494            check(ACTION_SOUTH);
5495            check(ACTION_EAST);
5496
5497            if (range.x >= 0) {
5498              if (right < maxWidth) {
5499                width += range.x;
5500              } else if (range.y >= 0 && bottom >= maxHeight) {
5501                renderable = false;
5502              }
5503            } else {
5504              width += range.x;
5505            }
5506
5507            if (range.y >= 0) {
5508              if (bottom < maxHeight) {
5509                height += range.y;
5510              }
5511            } else {
5512              height += range.y;
5513            }
5514          }
5515
5516          if (width < 0 && height < 0) {
5517            action = ACTION_NORTH_WEST;
5518            height = 0;
5519            width = 0;
5520          } else if (width < 0) {
5521            action = ACTION_SOUTH_WEST;
5522            width = 0;
5523          } else if (height < 0) {
5524            action = ACTION_NORTH_EAST;
5525            height = 0;
5526          }
5527
5528          break;
5529
5530        // Move canvas
5531        case ACTION_MOVE:
5532          this.move(range.x, range.y);
5533          renderable = false;
5534          break;
5535
5536        // Zoom canvas
5537        case ACTION_ZOOM:
5538          this.zoom(getMaxZoomRatio(pointers), e);
5539          renderable = false;
5540          break;
5541
5542        // Create crop box
5543        case ACTION_CROP:
5544          if (!range.x || !range.y) {
5545            renderable = false;
5546            break;
5547          }
5548
5549          offset = getOffset(this.cropper);
5550          left = pointer.startX - offset.left;
5551          top = pointer.startY - offset.top;
5552          width = cropBoxData.minWidth;
5553          height = cropBoxData.minHeight;
5554
5555          if (range.x > 0) {
5556            action = range.y > 0 ? ACTION_SOUTH_EAST : ACTION_NORTH_EAST;
5557          } else if (range.x < 0) {
5558            left -= width;
5559            action = range.y > 0 ? ACTION_SOUTH_WEST : ACTION_NORTH_WEST;
5560          }
5561
5562          if (range.y < 0) {
5563            top -= height;
5564          }
5565
5566          // Show the crop box if is hidden
5567          if (!this.cropped) {
5568            removeClass(this.cropBox, CLASS_HIDDEN);
5569            this.cropped = true;
5570
5571            if (this.limited) {
5572              this.limitCropBox(true, true);
5573            }
5574          }
5575
5576          break;
5577
5578        default:
5579      }
5580
5581      if (renderable) {
5582        cropBoxData.width = width;
5583        cropBoxData.height = height;
5584        cropBoxData.left = left;
5585        cropBoxData.top = top;
5586        this.action = action;
5587        this.renderCropBox();
5588      }
5589
5590      // Override
5591      forEach(pointers, function (p) {
5592        p.startX = p.endX;
5593        p.startY = p.endY;
5594      });
5595    }
5596  };
5597
5598  var methods = {
5599    // Show the crop box manually
5600    crop: function crop() {
5601      if (this.ready && !this.cropped && !this.disabled) {
5602        this.cropped = true;
5603        this.limitCropBox(true, true);
5604
5605        if (this.options.modal) {
5606          addClass(this.dragBox, CLASS_MODAL);
5607        }
5608
5609        removeClass(this.cropBox, CLASS_HIDDEN);
5610        this.setCropBoxData(this.initialCropBoxData);
5611      }
5612
5613      return this;
5614    },
5615
5616
5617    // Reset the image and crop box to their initial states
5618    reset: function reset() {
5619      if (this.ready && !this.disabled) {
5620        this.imageData = assign({}, this.initialImageData);
5621        this.canvasData = assign({}, this.initialCanvasData);
5622        this.cropBoxData = assign({}, this.initialCropBoxData);
5623        this.renderCanvas();
5624
5625        if (this.cropped) {
5626          this.renderCropBox();
5627        }
5628      }
5629
5630      return this;
5631    },
5632
5633
5634    // Clear the crop box
5635    clear: function clear() {
5636      if (this.cropped && !this.disabled) {
5637        assign(this.cropBoxData, {
5638          left: 0,
5639          top: 0,
5640          width: 0,
5641          height: 0
5642        });
5643
5644        this.cropped = false;
5645        this.renderCropBox();
5646        this.limitCanvas(true, true);
5647
5648        // Render canvas after crop box rendered
5649        this.renderCanvas();
5650        removeClass(this.dragBox, CLASS_MODAL);
5651        addClass(this.cropBox, CLASS_HIDDEN);
5652      }
5653
5654      return this;
5655    },
5656
5657
5658    /**
5659     * Replace the image's src and rebuild the cropper
5660     * @param {string} url - The new URL.
5661     * @param {boolean} [hasSameSize] - Indicate if the new image has the same size as the old one.
5662     * @returns {Cropper} this
5663     */
5664    replace: function replace(url) {
5665      var hasSameSize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
5666
5667      if (!this.disabled && url) {
5668        if (this.isImg) {
5669          this.element.src = url;
5670        }
5671
5672        if (hasSameSize) {
5673          this.url = url;
5674          this.image.src = url;
5675
5676          if (this.ready) {
5677            this.viewBoxImage.src = url;
5678
5679            forEach(this.previews, function (element) {
5680              element.getElementsByTagName('img')[0].src = url;
5681            });
5682          }
5683        } else {
5684          if (this.isImg) {
5685            this.replaced = true;
5686          }
5687
5688          this.options.data = null;
5689          this.uncreate();
5690          this.load(url);
5691        }
5692      }
5693
5694      return this;
5695    },
5696
5697
5698    // Enable (unfreeze) the cropper
5699    enable: function enable() {
5700      if (this.ready && this.disabled) {
5701        this.disabled = false;
5702        removeClass(this.cropper, CLASS_DISABLED);
5703      }
5704
5705      return this;
5706    },
5707
5708
5709    // Disable (freeze) the cropper
5710    disable: function disable() {
5711      if (this.ready && !this.disabled) {
5712        this.disabled = true;
5713        addClass(this.cropper, CLASS_DISABLED);
5714      }
5715
5716      return this;
5717    },
5718
5719
5720    /**
5721     * Destroy the cropper and remove the instance from the image
5722     * @returns {Cropper} this
5723     */
5724    destroy: function destroy() {
5725      var element = this.element;
5726
5727
5728      if (!getData(element, NAMESPACE)) {
5729        return this;
5730      }
5731
5732      if (this.isImg && this.replaced) {
5733        element.src = this.originalUrl;
5734      }
5735
5736      this.uncreate();
5737      removeData(element, NAMESPACE);
5738
5739      return this;
5740    },
5741
5742
5743    /**
5744     * Move the canvas with relative offsets
5745     * @param {number} offsetX - The relative offset distance on the x-axis.
5746     * @param {number} [offsetY=offsetX] - The relative offset distance on the y-axis.
5747     * @returns {Cropper} this
5748     */
5749    move: function move(offsetX) {
5750      var offsetY = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : offsetX;
5751      var _canvasData = this.canvasData,
5752          left = _canvasData.left,
5753          top = _canvasData.top;
5754
5755
5756      return this.moveTo(isUndefined(offsetX) ? offsetX : left + Number(offsetX), isUndefined(offsetY) ? offsetY : top + Number(offsetY));
5757    },
5758
5759
5760    /**
5761     * Move the canvas to an absolute point
5762     * @param {number} x - The x-axis coordinate.
5763     * @param {number} [y=x] - The y-axis coordinate.
5764     * @returns {Cropper} this
5765     */
5766    moveTo: function moveTo(x) {
5767      var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : x;
5768      var canvasData = this.canvasData;
5769
5770      var changed = false;
5771
5772      x = Number(x);
5773      y = Number(y);
5774
5775      if (this.ready && !this.disabled && this.options.movable) {
5776        if (isNumber(x)) {
5777          canvasData.left = x;
5778          changed = true;
5779        }
5780
5781        if (isNumber(y)) {
5782          canvasData.top = y;
5783          changed = true;
5784        }
5785
5786        if (changed) {
5787          this.renderCanvas(true);
5788        }
5789      }
5790
5791      return this;
5792    },
5793
5794
5795    /**
5796     * Zoom the canvas with a relative ratio
5797     * @param {number} ratio - The target ratio.
5798     * @param {Event} _originalEvent - The original event if any.
5799     * @returns {Cropper} this
5800     */
5801    zoom: function zoom(ratio, _originalEvent) {
5802      var canvasData = this.canvasData;
5803
5804
5805      ratio = Number(ratio);
5806
5807      if (ratio < 0) {
5808        ratio = 1 / (1 - ratio);
5809      } else {
5810        ratio = 1 + ratio;
5811      }
5812
5813      return this.zoomTo(canvasData.width * ratio / canvasData.naturalWidth, null, _originalEvent);
5814    },
5815
5816
5817    /**
5818     * Zoom the canvas to an absolute ratio
5819     * @param {number} ratio - The target ratio.
5820     * @param {Object} pivot - The zoom pivot point coordinate.
5821     * @param {Event} _originalEvent - The original event if any.
5822     * @returns {Cropper} this
5823     */
5824    zoomTo: function zoomTo(ratio, pivot, _originalEvent) {
5825      var options = this.options,
5826          canvasData = this.canvasData;
5827      var width = canvasData.width,
5828          height = canvasData.height,
5829          naturalWidth = canvasData.naturalWidth,
5830          naturalHeight = canvasData.naturalHeight;
5831
5832
5833      ratio = Number(ratio);
5834
5835      if (ratio >= 0 && this.ready && !this.disabled && options.zoomable) {
5836        var newWidth = naturalWidth * ratio;
5837        var newHeight = naturalHeight * ratio;
5838
5839        if (dispatchEvent(this.element, EVENT_ZOOM, {
5840          originalEvent: _originalEvent,
5841          oldRatio: width / naturalWidth,
5842          ratio: newWidth / naturalWidth
5843        }) === false) {
5844          return this;
5845        }
5846
5847        if (_originalEvent) {
5848          var pointers = this.pointers;
5849
5850          var offset = getOffset(this.cropper);
5851          var center = pointers && Object.keys(pointers).length ? getPointersCenter(pointers) : {
5852            pageX: _originalEvent.pageX,
5853            pageY: _originalEvent.pageY
5854          };
5855
5856          // Zoom from the triggering point of the event
5857          canvasData.left -= (newWidth - width) * ((center.pageX - offset.left - canvasData.left) / width);
5858          canvasData.top -= (newHeight - height) * ((center.pageY - offset.top - canvasData.top) / height);
5859        } else if (isPlainObject(pivot) && isNumber(pivot.x) && isNumber(pivot.y)) {
5860          canvasData.left -= (newWidth - width) * ((pivot.x - canvasData.left) / width);
5861          canvasData.top -= (newHeight - height) * ((pivot.y - canvasData.top) / height);
5862        } else {
5863          // Zoom from the center of the canvas
5864          canvasData.left -= (newWidth - width) / 2;
5865          canvasData.top -= (newHeight - height) / 2;
5866        }
5867
5868        canvasData.width = newWidth;
5869        canvasData.height = newHeight;
5870        this.renderCanvas(true);
5871      }
5872
5873      return this;
5874    },
5875
5876
5877    /**
5878     * Rotate the canvas with a relative degree
5879     * @param {number} degree - The rotate degree.
5880     * @returns {Cropper} this
5881     */
5882    rotate: function rotate(degree) {
5883      return this.rotateTo((this.imageData.rotate || 0) + Number(degree));
5884    },
5885
5886
5887    /**
5888     * Rotate the canvas to an absolute degree
5889     * @param {number} degree - The rotate degree.
5890     * @returns {Cropper} this
5891     */
5892    rotateTo: function rotateTo(degree) {
5893      degree = Number(degree);
5894
5895      if (isNumber(degree) && this.ready && !this.disabled && this.options.rotatable) {
5896        this.imageData.rotate = degree % 360;
5897        this.renderCanvas(true, true);
5898      }
5899
5900      return this;
5901    },
5902
5903
5904    /**
5905     * Scale the image on the x-axis.
5906     * @param {number} scaleX - The scale ratio on the x-axis.
5907     * @returns {Cropper} this
5908     */
5909    scaleX: function scaleX(_scaleX) {
5910      var scaleY = this.imageData.scaleY;
5911
5912
5913      return this.scale(_scaleX, isNumber(scaleY) ? scaleY : 1);
5914    },
5915
5916
5917    /**
5918     * Scale the image on the y-axis.
5919     * @param {number} scaleY - The scale ratio on the y-axis.
5920     * @returns {Cropper} this
5921     */
5922    scaleY: function scaleY(_scaleY) {
5923      var scaleX = this.imageData.scaleX;
5924
5925
5926      return this.scale(isNumber(scaleX) ? scaleX : 1, _scaleY);
5927    },
5928
5929
5930    /**
5931     * Scale the image
5932     * @param {number} scaleX - The scale ratio on the x-axis.
5933     * @param {number} [scaleY=scaleX] - The scale ratio on the y-axis.
5934     * @returns {Cropper} this
5935     */
5936    scale: function scale(scaleX) {
5937      var scaleY = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : scaleX;
5938      var imageData = this.imageData;
5939
5940      var transformed = false;
5941
5942      scaleX = Number(scaleX);
5943      scaleY = Number(scaleY);
5944
5945      if (this.ready && !this.disabled && this.options.scalable) {
5946        if (isNumber(scaleX)) {
5947          imageData.scaleX = scaleX;
5948          transformed = true;
5949        }
5950
5951        if (isNumber(scaleY)) {
5952          imageData.scaleY = scaleY;
5953          transformed = true;
5954        }
5955
5956        if (transformed) {
5957          this.renderCanvas(true, true);
5958        }
5959      }
5960
5961      return this;
5962    },
5963
5964
5965    /**
5966     * Get the cropped area position and size data (base on the original image)
5967     * @param {boolean} [rounded=false] - Indicate if round the data values or not.
5968     * @returns {Object} The result cropped data.
5969     */
5970    getData: function getData$$1() {
5971      var rounded = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
5972      var options = this.options,
5973          imageData = this.imageData,
5974          canvasData = this.canvasData,
5975          cropBoxData = this.cropBoxData;
5976
5977      var data = void 0;
5978
5979      if (this.ready && this.cropped) {
5980        data = {
5981          x: cropBoxData.left - canvasData.left,
5982          y: cropBoxData.top - canvasData.top,
5983          width: cropBoxData.width,
5984          height: cropBoxData.height
5985        };
5986
5987        var ratio = imageData.width / imageData.naturalWidth;
5988
5989        forEach(data, function (n, i) {
5990          n /= ratio;
5991          data[i] = rounded ? Math.round(n) : n;
5992        });
5993      } else {
5994        data = {
5995          x: 0,
5996          y: 0,
5997          width: 0,
5998          height: 0
5999        };
6000      }
6001
6002      if (options.rotatable) {
6003        data.rotate = imageData.rotate || 0;
6004      }
6005
6006      if (options.scalable) {
6007        data.scaleX = imageData.scaleX || 1;
6008        data.scaleY = imageData.scaleY || 1;
6009      }
6010
6011      return data;
6012    },
6013
6014
6015    /**
6016     * Set the cropped area position and size with new data
6017     * @param {Object} data - The new data.
6018     * @returns {Cropper} this
6019     */
6020    setData: function setData$$1(data) {
6021      var options = this.options,
6022          imageData = this.imageData,
6023          canvasData = this.canvasData;
6024
6025      var cropBoxData = {};
6026
6027      if (this.ready && !this.disabled && isPlainObject(data)) {
6028        var transformed = false;
6029
6030        if (options.rotatable) {
6031          if (isNumber(data.rotate) && data.rotate !== imageData.rotate) {
6032            imageData.rotate = data.rotate;
6033            transformed = true;
6034          }
6035        }
6036
6037        if (options.scalable) {
6038          if (isNumber(data.scaleX) && data.scaleX !== imageData.scaleX) {
6039            imageData.scaleX = data.scaleX;
6040            transformed = true;
6041          }
6042
6043          if (isNumber(data.scaleY) && data.scaleY !== imageData.scaleY) {
6044            imageData.scaleY = data.scaleY;
6045            transformed = true;
6046          }
6047        }
6048
6049        if (transformed) {
6050          this.renderCanvas(true, true);
6051        }
6052
6053        var ratio = imageData.width / imageData.naturalWidth;
6054
6055        if (isNumber(data.x)) {
6056          cropBoxData.left = data.x * ratio + canvasData.left;
6057        }
6058
6059        if (isNumber(data.y)) {
6060          cropBoxData.top = data.y * ratio + canvasData.top;
6061        }
6062
6063        if (isNumber(data.width)) {
6064          cropBoxData.width = data.width * ratio;
6065        }
6066
6067        if (isNumber(data.height)) {
6068          cropBoxData.height = data.height * ratio;
6069        }
6070
6071        this.setCropBoxData(cropBoxData);
6072      }
6073
6074      return this;
6075    },
6076
6077
6078    /**
6079     * Get the container size data.
6080     * @returns {Object} The result container data.
6081     */
6082    getContainerData: function getContainerData() {
6083      return this.ready ? assign({}, this.containerData) : {};
6084    },
6085
6086
6087    /**
6088     * Get the image position and size data.
6089     * @returns {Object} The result image data.
6090     */
6091    getImageData: function getImageData() {
6092      return this.sized ? assign({}, this.imageData) : {};
6093    },
6094
6095
6096    /**
6097     * Get the canvas position and size data.
6098     * @returns {Object} The result canvas data.
6099     */
6100    getCanvasData: function getCanvasData() {
6101      var canvasData = this.canvasData;
6102
6103      var data = {};
6104
6105      if (this.ready) {
6106        forEach(['left', 'top', 'width', 'height', 'naturalWidth', 'naturalHeight'], function (n) {
6107          data[n] = canvasData[n];
6108        });
6109      }
6110
6111      return data;
6112    },
6113
6114
6115    /**
6116     * Set the canvas position and size with new data.
6117     * @param {Object} data - The new canvas data.
6118     * @returns {Cropper} this
6119     */
6120    setCanvasData: function setCanvasData(data) {
6121      var canvasData = this.canvasData;
6122      var aspectRatio = canvasData.aspectRatio;
6123
6124
6125      if (this.ready && !this.disabled && isPlainObject(data)) {
6126        if (isNumber(data.left)) {
6127          canvasData.left = data.left;
6128        }
6129
6130        if (isNumber(data.top)) {
6131          canvasData.top = data.top;
6132        }
6133
6134        if (isNumber(data.width)) {
6135          canvasData.width = data.width;
6136          canvasData.height = data.width / aspectRatio;
6137        } else if (isNumber(data.height)) {
6138          canvasData.height = data.height;
6139          canvasData.width = data.height * aspectRatio;
6140        }
6141
6142        this.renderCanvas(true);
6143      }
6144
6145      return this;
6146    },
6147
6148
6149    /**
6150     * Get the crop box position and size data.
6151     * @returns {Object} The result crop box data.
6152     */
6153    getCropBoxData: function getCropBoxData() {
6154      var cropBoxData = this.cropBoxData;
6155
6156      var data = void 0;
6157
6158      if (this.ready && this.cropped) {
6159        data = {
6160          left: cropBoxData.left,
6161          top: cropBoxData.top,
6162          width: cropBoxData.width,
6163          height: cropBoxData.height
6164        };
6165      }
6166
6167      return data || {};
6168    },
6169
6170
6171    /**
6172     * Set the crop box position and size with new data.
6173     * @param {Object} data - The new crop box data.
6174     * @returns {Cropper} this
6175     */
6176    setCropBoxData: function setCropBoxData(data) {
6177      var cropBoxData = this.cropBoxData;
6178      var aspectRatio = this.options.aspectRatio;
6179
6180      var widthChanged = void 0;
6181      var heightChanged = void 0;
6182
6183      if (this.ready && this.cropped && !this.disabled && isPlainObject(data)) {
6184        if (isNumber(data.left)) {
6185          cropBoxData.left = data.left;
6186        }
6187
6188        if (isNumber(data.top)) {
6189          cropBoxData.top = data.top;
6190        }
6191
6192        if (isNumber(data.width) && data.width !== cropBoxData.width) {
6193          widthChanged = true;
6194          cropBoxData.width = data.width;
6195        }
6196
6197        if (isNumber(data.height) && data.height !== cropBoxData.height) {
6198          heightChanged = true;
6199          cropBoxData.height = data.height;
6200        }
6201
6202        if (aspectRatio) {
6203          if (widthChanged) {
6204            cropBoxData.height = cropBoxData.width / aspectRatio;
6205          } else if (heightChanged) {
6206            cropBoxData.width = cropBoxData.height * aspectRatio;
6207          }
6208        }
6209
6210        this.renderCropBox();
6211      }
6212
6213      return this;
6214    },
6215
6216
6217    /**
6218     * Get a canvas drawn the cropped image.
6219     * @param {Object} [options={}] - The config options.
6220     * @returns {HTMLCanvasElement} - The result canvas.
6221     */
6222    getCroppedCanvas: function getCroppedCanvas() {
6223      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
6224
6225      if (!this.ready || !window.HTMLCanvasElement) {
6226        return null;
6227      }
6228
6229      var canvasData = this.canvasData;
6230
6231      var source = getSourceCanvas(this.image, this.imageData, canvasData, options);
6232
6233      // Returns the source canvas if it is not cropped.
6234      if (!this.cropped) {
6235        return source;
6236      }
6237
6238      var _getData = this.getData(),
6239          initialX = _getData.x,
6240          initialY = _getData.y,
6241          initialWidth = _getData.width,
6242          initialHeight = _getData.height;
6243
6244      var ratio = source.width / Math.floor(canvasData.naturalWidth);
6245
6246      if (ratio !== 1) {
6247        initialX *= ratio;
6248        initialY *= ratio;
6249        initialWidth *= ratio;
6250        initialHeight *= ratio;
6251      }
6252
6253      var aspectRatio = initialWidth / initialHeight;
6254      var maxSizes = getAdjustedSizes({
6255        aspectRatio: aspectRatio,
6256        width: options.maxWidth || Infinity,
6257        height: options.maxHeight || Infinity
6258      });
6259      var minSizes = getAdjustedSizes({
6260        aspectRatio: aspectRatio,
6261        width: options.minWidth || 0,
6262        height: options.minHeight || 0
6263      }, 'cover');
6264
6265      var _getAdjustedSizes = getAdjustedSizes({
6266        aspectRatio: aspectRatio,
6267        width: options.width || (ratio !== 1 ? source.width : initialWidth),
6268        height: options.height || (ratio !== 1 ? source.height : initialHeight)
6269      }),
6270          width = _getAdjustedSizes.width,
6271          height = _getAdjustedSizes.height;
6272
6273      width = Math.min(maxSizes.width, Math.max(minSizes.width, width));
6274      height = Math.min(maxSizes.height, Math.max(minSizes.height, height));
6275
6276      var canvas = document.createElement('canvas');
6277      var context = canvas.getContext('2d');
6278
6279      canvas.width = normalizeDecimalNumber(width);
6280      canvas.height = normalizeDecimalNumber(height);
6281
6282      context.fillStyle = options.fillColor || 'transparent';
6283      context.fillRect(0, 0, width, height);
6284
6285      var _options$imageSmoothi = options.imageSmoothingEnabled,
6286          imageSmoothingEnabled = _options$imageSmoothi === undefined ? true : _options$imageSmoothi,
6287          imageSmoothingQuality = options.imageSmoothingQuality;
6288
6289
6290      context.imageSmoothingEnabled = imageSmoothingEnabled;
6291
6292      if (imageSmoothingQuality) {
6293        context.imageSmoothingQuality = imageSmoothingQuality;
6294      }
6295
6296      // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.drawImage
6297      var sourceWidth = source.width;
6298      var sourceHeight = source.height;
6299
6300      // Source canvas parameters
6301      var srcX = initialX;
6302      var srcY = initialY;
6303      var srcWidth = void 0;
6304      var srcHeight = void 0;
6305
6306      // Destination canvas parameters
6307      var dstX = void 0;
6308      var dstY = void 0;
6309      var dstWidth = void 0;
6310      var dstHeight = void 0;
6311
6312      if (srcX <= -initialWidth || srcX > sourceWidth) {
6313        srcX = 0;
6314        srcWidth = 0;
6315        dstX = 0;
6316        dstWidth = 0;
6317      } else if (srcX <= 0) {
6318        dstX = -srcX;
6319        srcX = 0;
6320        srcWidth = Math.min(sourceWidth, initialWidth + srcX);
6321        dstWidth = srcWidth;
6322      } else if (srcX <= sourceWidth) {
6323        dstX = 0;
6324        srcWidth = Math.min(initialWidth, sourceWidth - srcX);
6325        dstWidth = srcWidth;
6326      }
6327
6328      if (srcWidth <= 0 || srcY <= -initialHeight || srcY > sourceHeight) {
6329        srcY = 0;
6330        srcHeight = 0;
6331        dstY = 0;
6332        dstHeight = 0;
6333      } else if (srcY <= 0) {
6334        dstY = -srcY;
6335        srcY = 0;
6336        srcHeight = Math.min(sourceHeight, initialHeight + srcY);
6337        dstHeight = srcHeight;
6338      } else if (srcY <= sourceHeight) {
6339        dstY = 0;
6340        srcHeight = Math.min(initialHeight, sourceHeight - srcY);
6341        dstHeight = srcHeight;
6342      }
6343
6344      var params = [srcX, srcY, srcWidth, srcHeight];
6345
6346      // Avoid "IndexSizeError"
6347      if (dstWidth > 0 && dstHeight > 0) {
6348        var scale = width / initialWidth;
6349
6350        params.push(dstX * scale, dstY * scale, dstWidth * scale, dstHeight * scale);
6351      }
6352
6353      // All the numerical parameters should be integer for `drawImage`
6354      // https://github.com/fengyuanchen/cropper/issues/476
6355      context.drawImage.apply(context, [source].concat(toConsumableArray(params.map(function (param) {
6356        return Math.floor(normalizeDecimalNumber(param));
6357      }))));
6358
6359      return canvas;
6360    },
6361
6362
6363    /**
6364     * Change the aspect ratio of the crop box.
6365     * @param {number} aspectRatio - The new aspect ratio.
6366     * @returns {Cropper} this
6367     */
6368    setAspectRatio: function setAspectRatio(aspectRatio) {
6369      var options = this.options;
6370
6371
6372      if (!this.disabled && !isUndefined(aspectRatio)) {
6373        // 0 -> NaN
6374        options.aspectRatio = Math.max(0, aspectRatio) || NaN;
6375
6376        if (this.ready) {
6377          this.initCropBox();
6378
6379          if (this.cropped) {
6380            this.renderCropBox();
6381          }
6382        }
6383      }
6384
6385      return this;
6386    },
6387
6388
6389    /**
6390     * Change the drag mode.
6391     * @param {string} mode - The new drag mode.
6392     * @returns {Cropper} this
6393     */
6394    setDragMode: function setDragMode(mode) {
6395      var options = this.options,
6396          dragBox = this.dragBox,
6397          face = this.face;
6398
6399
6400      if (this.ready && !this.disabled) {
6401        var croppable = mode === DRAG_MODE_CROP;
6402        var movable = options.movable && mode === DRAG_MODE_MOVE;
6403
6404        mode = croppable || movable ? mode : DRAG_MODE_NONE;
6405
6406        options.dragMode = mode;
6407        setData(dragBox, DATA_ACTION, mode);
6408        toggleClass(dragBox, CLASS_CROP, croppable);
6409        toggleClass(dragBox, CLASS_MOVE, movable);
6410
6411        if (!options.cropBoxMovable) {
6412          // Sync drag mode to crop box when it is not movable
6413          setData(face, DATA_ACTION, mode);
6414          toggleClass(face, CLASS_CROP, croppable);
6415          toggleClass(face, CLASS_MOVE, movable);
6416        }
6417      }
6418
6419      return this;
6420    }
6421  };
6422
6423  var AnotherCropper = WINDOW.Cropper;
6424
6425  var Cropper = function () {
6426    /**
6427     * Create a new Cropper.
6428     * @param {Element} element - The target element for cropping.
6429     * @param {Object} [options={}] - The configuration options.
6430     */
6431    function Cropper(element) {
6432      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
6433      classCallCheck(this, Cropper);
6434
6435      if (!element || !REGEXP_TAG_NAME.test(element.tagName)) {
6436        throw new Error('The first argument is required and must be an <img> or <canvas> element.');
6437      }
6438
6439      this.element = element;
6440      this.options = assign({}, DEFAULTS, isPlainObject(options) && options);
6441      this.cropped = false;
6442      this.disabled = false;
6443      this.pointers = {};
6444      this.ready = false;
6445      this.reloading = false;
6446      this.replaced = false;
6447      this.sized = false;
6448      this.sizing = false;
6449      this.init();
6450    }
6451
6452    createClass(Cropper, [{
6453      key: 'init',
6454      value: function init() {
6455        var element = this.element;
6456
6457        var tagName = element.tagName.toLowerCase();
6458        var url = void 0;
6459
6460        if (getData(element, NAMESPACE)) {
6461          return;
6462        }
6463
6464        setData(element, NAMESPACE, this);
6465
6466        if (tagName === 'img') {
6467          this.isImg = true;
6468
6469          // e.g.: "img/picture.jpg"
6470          url = element.getAttribute('src') || '';
6471          this.originalUrl = url;
6472
6473          // Stop when it's a blank image
6474          if (!url) {
6475            return;
6476          }
6477
6478          // e.g.: "http://example.com/img/picture.jpg"
6479          url = element.src;
6480        } else if (tagName === 'canvas' && window.HTMLCanvasElement) {
6481          url = element.toDataURL();
6482        }
6483
6484        this.load(url);
6485      }
6486    }, {
6487      key: 'load',
6488      value: function load(url) {
6489        var _this = this;
6490
6491        if (!url) {
6492          return;
6493        }
6494
6495        this.url = url;
6496        this.imageData = {};
6497
6498        var element = this.element,
6499            options = this.options;
6500
6501
6502        if (!options.checkOrientation || !window.ArrayBuffer) {
6503          this.clone();
6504          return;
6505        }
6506
6507        // XMLHttpRequest disallows to open a Data URL in some browsers like IE11 and Safari
6508        if (REGEXP_DATA_URL.test(url)) {
6509          if (REGEXP_DATA_URL_JPEG.test(url)) {
6510            this.read(dataURLToArrayBuffer(url));
6511          } else {
6512            this.clone();
6513          }
6514
6515          return;
6516        }
6517
6518        var xhr = new XMLHttpRequest();
6519
6520        this.reloading = true;
6521        this.xhr = xhr;
6522
6523        var done = function done() {
6524          _this.reloading = false;
6525          _this.xhr = null;
6526        };
6527
6528        xhr.ontimeout = done;
6529        xhr.onabort = done;
6530        xhr.onerror = function () {
6531          done();
6532          _this.clone();
6533        };
6534
6535        xhr.onload = function () {
6536          done();
6537          _this.read(xhr.response);
6538        };
6539
6540        // Bust cache when there is a "crossOrigin" property
6541        if (options.checkCrossOrigin && isCrossOriginURL(url) && element.crossOrigin) {
6542          url = addTimestamp(url);
6543        }
6544
6545        xhr.open('get', url);
6546        xhr.responseType = 'arraybuffer';
6547        xhr.withCredentials = element.crossOrigin === 'use-credentials';
6548        xhr.send();
6549      }
6550    }, {
6551      key: 'read',
6552      value: function read(arrayBuffer) {
6553        var options = this.options,
6554            imageData = this.imageData;
6555
6556        var orientation = getOrientation(arrayBuffer);
6557        var rotate = 0;
6558        var scaleX = 1;
6559        var scaleY = 1;
6560
6561        if (orientation > 1) {
6562          this.url = arrayBufferToDataURL(arrayBuffer, 'image/jpeg');
6563
6564          var _parseOrientation = parseOrientation(orientation);
6565
6566          rotate = _parseOrientation.rotate;
6567          scaleX = _parseOrientation.scaleX;
6568          scaleY = _parseOrientation.scaleY;
6569        }
6570
6571        if (options.rotatable) {
6572          imageData.rotate = rotate;
6573        }
6574
6575        if (options.scalable) {
6576          imageData.scaleX = scaleX;
6577          imageData.scaleY = scaleY;
6578        }
6579
6580        this.clone();
6581      }
6582    }, {
6583      key: 'clone',
6584      value: function clone() {
6585        var element = this.element,
6586            url = this.url;
6587
6588        var crossOrigin = void 0;
6589        var crossOriginUrl = void 0;
6590
6591        if (this.options.checkCrossOrigin && isCrossOriginURL(url)) {
6592          crossOrigin = element.crossOrigin;
6593
6594
6595          if (crossOrigin) {
6596            crossOriginUrl = url;
6597          } else {
6598            crossOrigin = 'anonymous';
6599
6600            // Bust cache when there is not a "crossOrigin" property
6601            crossOriginUrl = addTimestamp(url);
6602          }
6603        }
6604
6605        this.crossOrigin = crossOrigin;
6606        this.crossOriginUrl = crossOriginUrl;
6607
6608        var image = document.createElement('img');
6609
6610        if (crossOrigin) {
6611          image.crossOrigin = crossOrigin;
6612        }
6613
6614        image.src = crossOriginUrl || url;
6615
6616        var start = this.start.bind(this);
6617        var stop = this.stop.bind(this);
6618
6619        this.image = image;
6620        this.onStart = start;
6621        this.onStop = stop;
6622
6623        if (this.isImg) {
6624          if (element.complete) {
6625            // start asynchronously to keep `this.cropper` is accessible in `ready` event handler.
6626            this.timeout = setTimeout(start, 0);
6627          } else {
6628            addListener(element, EVENT_LOAD, start, {
6629              once: true
6630            });
6631          }
6632        } else {
6633          image.onload = start;
6634          image.onerror = stop;
6635          addClass(image, CLASS_HIDE);
6636          element.parentNode.insertBefore(image, element.nextSibling);
6637        }
6638      }
6639    }, {
6640      key: 'start',
6641      value: function start(event) {
6642        var _this2 = this;
6643
6644        var image = this.isImg ? this.element : this.image;
6645
6646        if (event) {
6647          image.onload = null;
6648          image.onerror = null;
6649        }
6650
6651        this.sizing = true;
6652
6653        var IS_SAFARI = WINDOW.navigator && /(Macintosh|iPhone|iPod|iPad).*AppleWeb
6653Kit/i.test(WINDOW.navigator.userAgent);
6654        var done = function done(naturalWidth, naturalHeight) {
6655          assign(_this2.imageData, {
6656            naturalWidth: naturalWidth,
6657            naturalHeight: naturalHeight,
6658            aspectRatio: naturalWidth / naturalHeight
6659          });
6660          _this2.sizing = false;
6661          _this2.sized = true;
6662          _this2.build();
6663        };
6664
6665        // Modern browsers (except Safari)
6666        if (image.naturalWidth && !IS_SAFARI) {
6667          done(image.naturalWidth, image.naturalHeight);
6668          return;
6669        }
6670
6671        var sizingImage = document.createElement('img');
6672        var body = document.body || document.documentElement;
6673
6674        this.sizingImage = sizingImage;
6675
6676        sizingImage.onload = function () {
6677          done(sizingImage.width, sizingImage.height);
6678
6679          if (!IS_SAFARI) {
6680            body.removeChild(sizingImage);
6681          }
6682        };
6683
6684        sizingImage.src = image.src;
6685
6686        // iOS Safari will convert the image automatically
6687        // with its orientation once append it into DOM (#279)
6688        if (!IS_SAFARI) {
6689          sizingImage.style.cssText = 'left:0;' + 'max-height:none!important;' + 'max-width:none!important;' + 'min-height:0!important;' + 'min-width:0!important;' + 'opacity:0;' + 'position:absolute;' + 'top:0;' + 'z-index:-1;';
6690          body.appendChild(sizingImage);
6691        }
6692      }
6693    }, {
6694      key: 'stop',
6695      value: function stop() {
6696        var image = this.image;
6697
6698
6699        image.onload = null;
6700        image.onerror = null;
6701        image.parentNode.removeChild(image);
6702        this.image = null;
6703      }
6704    }, {
6705      key: 'build',
6706      value: function build() {
6707        if (!this.sized || this.ready) {
6708          return;
6709        }
6710
6711        var element = this.element,
6712            options = this.options,
6713            image = this.image;
6714
6715        // Create cropper elements
6716
6717        var container = element.parentNode;
6718        var template = document.createElement('div');
6719
6720        template.innerHTML = TEMPLATE;
6721
6722        var cropper = template.querySelector('.' + NAMESPACE + '-container');
6723        var canvas = cropper.querySelector('.' + NAMESPACE + '-canvas');
6724        var dragBox = cropper.querySelector('.' + NAMESPACE + '-drag-box');
6725        var cropBox = cropper.querySelector('.' + NAMESPACE + '-crop-box');
6726        var face = cropBox.querySelector('.' + NAMESPACE + '-face');
6727
6728        this.container = container;
6729        this.cropper = cropper;
6730        this.canvas = canvas;
6731        this.dragBox = dragBox;
6732        this.cropBox = cropBox;
6733        this.viewBox = cropper.querySelector('.' + NAMESPACE + '-view-box');
6734        this.face = face;
6735
6736        canvas.appendChild(image);
6737
6738        // Hide the original image
6739        addClass(element, CLASS_HIDDEN);
6740
6741        // Inserts the cropper after to the current image
6742        container.insertBefore(cropper, element.nextSibling);
6743
6744        // Show the image if is hidden
6745        if (!this.isImg) {
6746          removeClass(image, CLASS_HIDE);
6747        }
6748
6749        this.initPreview();
6750        this.bind();
6751
6752        options.aspectRatio = Math.max(0, options.aspectRatio) || NaN;
6753        options.viewMode = Math.max(0, Math.min(3, Math.round(options.viewMode))) || 0;
6754
6755        addClass(cropBox, CLASS_HIDDEN);
6756
6757        if (!options.guides) {
6758          addClass(cropBox.getElementsByClassName(NAMESPACE + '-dashed'), CLASS_HIDDEN);
6759        }
6760
6761        if (!options.center) {
6762          addClass(cropBox.getElementsByClassName(NAMESPACE + '-center'), CLASS_HIDDEN);
6763        }
6764
6765        if (options.background) {
6766          addClass(cropper, NAMESPACE + '-bg');
6767        }
6768
6769        if (!options.highlight) {
6770          addClass(face, CLASS_INVISIBLE);
6771        }
6772
6773        if (options.cropBoxMovable) {
6774          addClass(face, CLASS_MOVE);
6775          setData(face, DATA_ACTION, ACTION_ALL);
6776        }
6777
6778        if (!options.cropBoxResizable) {
6779          addClass(cropBox.getElementsByClassName(NAMESPACE + '-line'), CLASS_HIDDEN);
6780          addClass(cropBox.getElementsByClassName(NAMESPACE + '-point'), CLASS_HIDDEN);
6781        }
6782
6783        this.render();
6784        this.ready = true;
6785        this.setDragMode(options.dragMode);
6786
6787        if (options.autoCrop) {
6788          this.crop();
6789        }
6790
6791        this.setData(options.data);
6792
6793        if (isFunction(options.ready)) {
6794          addListener(element, EVENT_READY, options.ready, {
6795            once: true
6796          });
6797        }
6798
6799        dispatchEvent(element, EVENT_READY);
6800      }
6801    }, {
6802      key: 'unbuild',
6803      value: function unbuild() {
6804        if (!this.ready) {
6805          return;
6806        }
6807
6808        this.ready = false;
6809        this.unbind();
6810        this.resetPreview();
6811        this.cropper.parentNode.removeChild(this.cropper);
6812        removeClass(this.element, CLASS_HIDDEN);
6813      }
6814    }, {
6815      key: 'uncreate',
6816      value: function uncreate() {
6817        var element = this.element;
6818
6819
6820        if (this.ready) {
6821          this.unbuild();
6822          this.ready = false;
6823          this.cropped = false;
6824        } else if (this.sizing) {
6825          this.sizingImage.onload = null;
6826          this.sizing = false;
6827          this.sized = false;
6828        } else if (this.reloading) {
6829          this.xhr.abort();
6830        } else if (this.isImg) {
6831          if (element.complete) {
6832            clearTimeout(this.timeout);
6833          } else {
6834            removeListener(element, EVENT_LOAD, this.onStart);
6835          }
6836        } else if (this.image) {
6837          this.stop();
6838        }
6839      }
6840
6841      /**
6842       * Get the no conflict cropper class.
6843       * @returns {Cropper} The cropper class.
6844       */
6845
6846    }], [{
6847      key: 'noConflict',
6848      value: function noConflict() {
6849        window.Cropper = AnotherCropper;
6850        return Cropper;
6851      }
6852
6853      /**
6854       * Change the default options.
6855       * @param {Object} options - The new default options.
6856       */
6857
6858    }, {
6859      key: 'setDefaults',
6860      value: function setDefaults(options) {
6861        assign(DEFAULTS, isPlainObject(options) && options);
6862      }
6863    }]);
6864    return Cropper;
6865  }();
6866
6867  assign(Cropper.prototype, render, preview, events, handlers, change, methods);
6868
6869  if ($.fn) {
6870    var AnotherCropper$1 = $.fn.cropper;
6871    var NAMESPACE$1 = 'cropper';
6872
6873    $.fn.cropper = function jQueryCropper(option) {
6874      for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
6875        args[_key - 1] = arguments[_key];
6876      }
6877
6878      var result = void 0;
6879
6880      this.each(function (i, element) {
6881        var $element = $(element);
6882        var isDestroy = option === 'destroy';
6883        var cropper = $element.data(NAMESPACE$1);
6884
6885        if (!cropper) {
6886          if (isDestroy) {
6887            return;
6888          }
6889
6890          var options = $.extend({}, $element.data(), $.isPlainObject(option) && option);
6891
6892          cropper = new Cropper(element, options);
6893          $element.data(NAMESPACE$1, cropper);
6894        }
6895
6896        if (typeof option === 'string') {
6897          var fn = cropper[option];
6898
6899          if ($.isFunction(fn)) {
6900            result = fn.apply(cropper, args);
6901
6902            if (result === cropper) {
6903              result = undefined;
6904            }
6905
6906            if (isDestroy) {
6907              $element.removeData(NAMESPACE$1);
6908            }
6909          }
6910        }
6911      });
6912
6913      return result !== undefined ? result : this;
6914    };
6915
6916    $.fn.cropper.Constructor = Cropper;
6917    $.fn.cropper.setDefaults = Cropper.setDefaults;
6918    $.fn.cropper.noConflict = function noConflict() {
6919      $.fn.cropper = AnotherCropper$1;
6920      return this;
6921    };
6922  }
6923
6924})));
6925;
6926/*!
6927 * Datepicker v0.5.4
6928 * https://github.com/fengyuanchen/datepicker
6929 *
6930 * Copyright (c) 2014-2017 Fengyuan Chen
6931 * Released under the MIT license
6932 *
6933 * Date: 2017-08-05T07:14:03.474Z
6934 */
6935
6936(function (factory) {
6937  if (typeof define === 'function' && define.amd) {
6938    // AMD. Register as anonymous module.
6939    define('datepicker', ['jquery'], factory);
6940  } else if (typeof exports === 'object') {
6941    // Node / CommonJS
6942    factory(require('jquery'));
6943  } else {
6944    // Browser globals.
6945    factory(jQuery);
6946  }
6947})(function ($) {
6948
6949  'use strict';
6950
6951  var $window = $(window);
6952  var document = window.document;
6953  var $document = $(document);
6954  var Number = window.Number;
6955  var NAMESPACE = 'datepicker';
6956
6957  // Events
6958  var EVENT_CLICK = 'click.' + NAMESPACE;
6959  var EVENT_KEYUP = 'keyup.' + NAMESPACE;
6960  var EVENT_FOCUS = 'focus.' + NAMESPACE;
6961  var EVENT_RESIZE = 'resize.' + NAMESPACE;
6962  var EVENT_SHOW = 'show.' + NAMESPACE;
6963  var EVENT_HIDE = 'hide.' + NAMESPACE;
6964  var EVENT_PICK = 'pick.' + NAMESPACE;
6965
6966  // RegExps
6967  var REGEXP_FORMAT = /(y|m|d)+/g;
6968  var REGEXP_DIGITS = /\d+/g;
6969  var REGEXP_YEAR = /^\d{2,4}$/;
6970
6971  // Classes
6972  var CLASS_INLINE = NAMESPACE + '-inline';
6973  var CLASS_DROPDOWN = NAMESPACE + '-dropdown';
6974  var CLASS_TOP_LEFT = NAMESPACE + '-top-left';
6975  var CLASS_TOP_RIGHT = NAMESPACE + '-top-right';
6976  var CLASS_BOTTOM_LEFT = NAMESPACE + '-bottom-left';
6977  var CLASS_BOTTOM_RIGHT = NAMESPACE + '-bottom-right';
6978  var CLASS_PLACEMENTS = [
6979        CLASS_TOP_LEFT,
6980        CLASS_TOP_RIGHT,
6981        CLASS_BOTTOM_LEFT,
6982        CLASS_BOTTOM_RIGHT
6983      ].join(' ');
6984  var CLASS_HIDE = NAMESPACE + '-hide';
6985
6986  // Views
6987  var VIEWS = {
6988    DAYS: 0,
6989    MONTHS: 1,
6990    YEARS: 2
6991  };
6992
6993  // Maths
6994  var min = Math.min;
6995
6996  // Utilities
6997  var toString = Object.prototype.toString;
6998
6999  function typeOf(obj) {
7000    return toString.call(obj).slice(8, -1).toLowerCase();
7001  }
7002
7003  function isString(str) {
7004    return typeof str === 'string';
7005  }
7006
7007  function isNumber(num) {
7008    return typeof num === 'number' && !isNaN(num);
7009  }
7010
7011  function isUndefined(obj) {
7012    return typeof obj === 'undefined';
7013  }
7014
7015  function isDate(date) {
7016    return typeOf(date) === 'date';
7017  }
7018
7019  function toArray(obj, offset) {
7020    var args = [];
7021
7022    if (Array.from) {
7023      return Array.from(obj).slice(offset || 0);
7024    }
7025
7026    // This is necessary for IE8
7027    if (isNumber(offset)) {
7028      args.push(offset);
7029    }
7030
7031    return args.slice.apply(obj, args);
7032  }
7033
7034  // Custom proxy to avoid jQuery's guid
7035  function proxy(fn, context) {
7036    var args = toArray(arguments, 2);
7037
7038    return function () {
7039      return fn.apply(context, args.concat(toArray(arguments)));
7040    };
7041  }
7042
7043  function isLeapYear(year) {
7044    return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
7045  }
7046
7047  function getDaysInMonth(year, month) {
7048    return [31, (isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
7049  }
7050
7051  function parseFormat(format) {
7052    var source = String(format).toLowerCase();
7053    var parts = source.match(REGEXP_FORMAT);
7054    var length;
7055    var i;
7056
7057    if (!parts || parts.length === 0) {
7058      throw new Error('Invalid date format.');
7059    }
7060
7061    format = {
7062      source: source,
7063      parts: parts
7064    };
7065
7066    length = parts.length;
7067
7068    for (i = 0; i < length; i++) {
7069      switch (parts[i]) {
7070        case 'dd':
7071        case 'd':
7072          format.hasDay = true;
7073          break;
7074
7075        case 'mm':
7076        case 'm':
7077          format.hasMonth = true;
7078          break;
7079
7080        case 'yyyy':
7081        case 'yy':
7082          format.hasYear = true;
7083          break;
7084
7085        // No default
7086      }
7087    }
7088
7089    return format;
7090  }
7091
7092  function Datepicker(element, options) {
7093    options = $.isPlainObject(options) ? options : {};
7094
7095    if (options.language) {
7096      // Priority: Datepicker.DEFAULTS < Datepicker.LANGUAGES < options
7097      options = $.extend({}, Datepicker.LANGUAGES[options.language], options);
7098    }
7099
7100    this.$element = $(element);
7101    this.options = $.extend({}, Datepicker.DEFAULTS, options);
7102    this.isBuilt = false;
7103    this.isShown = false;
7104    this.isInput = false;
7105    this.isInline = false;
7106    this.initialValue = '';
7107    this.initialDate = null;
7108    this.startDate = null;
7109    this.endDate = null;
7110    this.init();
7111  }
7112
7113  Datepicker.prototype = {
7114    constructor: Datepicker,
7115
7116    init: function () {
7117      var options = this.options;
7118      var $this = this.$element;
7119      var startDate = options.startDate;
7120      var endDate = options.endDate;
7121      var date = options.date;
7122
7123      this.$trigger = $(options.trigger);
7124      this.isInput = $this.is('input') || $this.is('textarea');
7125      this.isInline = options.inline && (options.container || !this.isInput);
7126      this.format = parseFormat(options.format);
7127      this.oldValue = this.initialValue = this.getValue();
7128      date = this.parseDate(date || this.initialValue);
7129
7130      if (startDate) {
7131        startDate = this.parseDate(startDate);
7132
7133        if (date.getTime() < startDate.getTime()) {
7134          date = new Date(startDate);
7135        }
7136
7137        this.startDate = startDate;
7138      }
7139
7140      if (endDate) {
7141        endDate = this.parseDate(endDate);
7142
7143        if (startDate && endDate.getTime() < startDate.getTime()) {
7144          endDate = new Date(startDate);
7145        }
7146
7147        if (date.getTime() > endDate.getTime()) {
7148          date = new Date(endDate);
7149        }
7150
7151        this.endDate = endDate;
7152      }
7153
7154      this.date = date;
7155      this.viewDate = new Date(date);
7156      this.initialDate = new Date(this.date);
7157
7158      this.bind();
7159
7160      if (options.autoShow || this.isInline) {
7161        this.show();
7162      }
7163
7164      if (options.autoPick) {
7165        this.pick();
7166      }
7167    },
7168
7169    build: function () {
7170      var options = this.options;
7171      var $this = this.$element;
7172      var $picker;
7173
7174      if (this.isBuilt) {
7175        return;
7176      }
7177
7178      this.isBuilt = true;
7179
7180      this.$picker = $picker = $(options.template);
7181      this.$week = $picker.find('[data-view="week"]');
7182
7183      // Years view
7184      this.$yearsPicker = $picker.find('[data-view="years picker"]');
7185      this.$yearsPrev = $picker.find('[data-view="years prev"]');
7186      this.$yearsNext = $picker.find('[data-view="years next"]');
7187      this.$yearsCurrent = $picker.find('[data-view="years current"]');
7188      this.$years = $picker.find('[data-view="years"]');
7189
7190      // Months view
7191      this.$monthsPicker = $picker.find('[data-view="months picker"]');
7192      this.$yearPrev = $picker.find('[data-view="year prev"]');
7193      this.$yearNext = $picker.find('[data-view="year next"]');
7194      this.$yearCurrent = $picker.find('[data-view="year current"]');
7195      this.$months = $picker.find('[data-view="months"]');
7196
7197      // Days view
7198      this.$daysPicker = $picker.find('[data-view="days picker"]');
7199      this.$monthPrev = $picker.find('[data-view="month prev"]');
7200      this.$monthNext = $picker.find('[data-view="month next"]');
7201      this.$monthCurrent = $picker.find('[data-view="month current"]');
7202      this.$days = $picker.find('[data-view="days"]');
7203
7204      if (this.isInline) {
7205        $(options.container || $this).append($picker.addClass(CLASS_INLINE));
7206      } else {
7207        $(document.body).append($picker.addClass(CLASS_DROPDOWN));
7208        $picker.addClass(CLASS_HIDE);
7209      }
7210
7211      this.fillWeek();
7212    },
7213
7214    unbuild: function () {
7215      if (!this.isBuilt) {
7216        return;
7217      }
7218
7219      this.isBuilt = false;
7220      this.$picker.remove();
7221    },
7222
7223    bind: function () {
7224      var options = this.options;
7225      var $this = this.$element;
7226
7227      if ($.isFunction(options.show)) {
7228        $this.on(EVENT_SHOW, options.show);
7229      }
7230
7231      if ($.isFunction(options.hide)) {
7232        $this.on(EVENT_HIDE, options.hide);
7233      }
7234
7235      if ($.isFunction(options.pick)) {
7236        $this.on(EVENT_PICK, options.pick);
7237      }
7238
7239      if (this.isInput) {
7240        $this.on(EVENT_KEYUP, $.proxy(this.keyup, this));
7241      }
7242
7243      if (!this.isInline) {
7244        if (options.trigger) {
7245          this.$trigger.on(EVENT_CLICK, $.proxy(this.toggle, this));
7246        } else if (this.isInput) {
7247          $this.on(EVENT_FOCUS, $.proxy(this.show, this));
7248        } else {
7249          $this.on(EVENT_CLICK, $.proxy(this.show, this));
7250        }�
7250
7251      }
7252    },
7253
7254    unbind: function () {
7255      var options = this.options;
7256      var $this = this.$element;
7257
7258      if ($.isFunction(options.show)) {
7259        $this.off(EVENT_SHOW, options.show);
7260      }
7261
7262      if ($.isFunction(options.hide)) {
7263        $this.off(EVENT_HIDE, options.hide);
7264      }
7265
7266      if ($.isFunction(options.pick)) {
7267        $this.off(EVENT_PICK, options.pick);
7268      }
7269
7270      if (this.isInput) {
7271        $this.off(EVENT_KEYUP, this.keyup);
7272      }
7273
7274      if (!this.isInline) {
7275        if (options.trigger) {
7276          this.$trigger.off(EVENT_CLICK, this.toggle);
7277        } else if (this.isInput) {
7278          $this.off(EVENT_FOCUS, this.show);
7279        } else {
7280          $this.off(EVENT_CLICK, this.show);
7281        }
7282      }
7283    },
7284
7285    showView: function (view) {
7286      var $yearsPicker = this.$yearsPicker;
7287      var $monthsPicker = this.$monthsPicker;
7288      var $daysPicker = this.$daysPicker;
7289      var format = this.format;
7290
7291      if (format.hasYear || format.hasMonth || format.hasDay) {
7292        switch (Number(view)) {
7293          case VIEWS.YEARS:
7294          case 'years':
7295            $monthsPicker.addClass(CLASS_HIDE);
7296            $daysPicker.addClass(CLASS_HIDE);
7297
7298            if (format.hasYear) {
7299              this.fillYears();
7300              $yearsPicker.removeClass(CLASS_HIDE);
7301              this.place();
7302            } else {
7303              this.showView(VIEWS.DAYS);
7304            }
7305
7306            break;
7307
7308          case VIEWS.MONTHS:
7309          case 'months':
7310            $yearsPicker.addClass(CLASS_HIDE);
7311            $daysPicker.addClass(CLASS_HIDE);
7312
7313            if (format.hasMonth) {
7314              this.fillMonths();
7315              $monthsPicker.removeClass(CLASS_HIDE);
7316              this.place();
7317            } else {
7318              this.showView(VIEWS.YEARS);
7319            }
7320
7321            break;
7322
7323          // case VIEWS.DAYS:
7324          // case 'days':
7325          default:
7326            $yearsPicker.addClass(CLASS_HIDE);
7327            $monthsPicker.addClass(CLASS_HIDE);
7328
7329            if (format.hasDay) {
7330              this.fillDays();
7331              $daysPicker.removeClass(CLASS_HIDE);
7332              this.place();
7333            } else {
7334              this.showView(VIEWS.MONTHS);
7335            }
7336        }
7337      }
7338    },
7339
7340    hideView: function () {
7341      if (!this.isInline && this.options.autoHide) {
7342        this.hide();
7343      }
7344    },
7345
7346    place: function () {
7347      if (this.isInline) {
7348        return;
7349      }
7350
7351      var options = this.options;
7352      var $this = this.$element;
7353      var $picker = this.$picker;
7354      var containerWidth = $document.outerWidth();
7355      var containerHeight = $document.outerHeight();
7356      var elementWidth = $this.outerWidth();
7357      var elementHeight = $this.outerHeight();
7358      var width = $picker.width();
7359      var height = $picker.height();
7360      var offsets = $this.offset();
7361      var left = offsets.left;
7362      var top = offsets.top;
7363      var offset = parseFloat(options.offset) || 10;
7364      var placement = CLASS_TOP_LEFT;
7365
7366      if (top > height && top + elementHeight + height > containerHeight) {
7367        top -= height + offset;
7368        placement = CLASS_BOTTOM_LEFT;
7369      } else {
7370        top += elementHeight + offset;
7371      }
7372
7373      if (left + width > containerWidth) {
7374        left = left + elementWidth - width;
7375        placement = placement.replace('left', 'right');
7376      }
7377
7378      $picker.removeClass(CLASS_PLACEMENTS).addClass(placement).css({
7379        top: top,
7380        left: left,
7381        zIndex: parseInt(options.zIndex, 10)
7382      });
7383    },
7384
7385    // A shortcut for triggering custom events
7386    trigger: function (type, data) {
7387      var e = $.Event(type, data);
7388
7389      this.$element.trigger(e);
7390
7391      return e;
7392    },
7393
7394    createItem: function (data) {
7395      var options = this.options;
7396      var itemTag = options.itemTag;
7397      var defaults = {
7398            text: '',
7399            view: '',
7400            muted: false,
7401            picked: false,
7402            disabled: false,
7403            highlighted: false
7404          };
7405      var classes = [];
7406
7407      $.extend(defaults, data);
7408
7409      if (defaults.muted) {
7410        classes.push(options.mutedClass);
7411      }
7412
7413      if (defaults.highlighted) {
7414        classes.push(options.highlightedClass);
7415      }
7416
7417      if (defaults.picked) {
7418        classes.push(options.pickedClass);
7419      }
7420
7421      if (defaults.disabled) {
7422        classes.push(options.disabledClass);
7423      }
7424
7425      return (
7426        '<' + itemTag + ' class="' + classes.join(' ') + '"' +
7427        (defaults.view ? ' data-view="' + defaults.view + '"' : '') +
7428        '>' +
7429        defaults.text +
7430        '</' + itemTag + '>'
7431      );
7432    },
7433
7434    fillAll: function () {
7435      this.fillYears();
7436      this.fillMonths();
7437      this.fillDays();
7438    },
7439
7440    fillWeek: function () {
7441      var options = this.options;
7442      var weekStart = parseInt(options.weekStart, 10) % 7;
7443      var days = options.daysMin;
7444      var list = '';
7445      var i;
7446
7447      days = $.merge(days.slice(weekStart), days.slice(0, weekStart));
7448
7449      for (i = 0; i <= 6; i++) {
7450        list += this.createItem({
7451          text: days[i]
7452        });
7453      }
7454
7455      this.$week.html(list);
7456    },
7457
7458    fillYears: function () {
7459      var options = this.options;
7460      var disabledClass = options.disabledClass || '';
7461      var suffix = options.yearSuffix || '';
7462      var filter = $.isFunction(options.filter) && options.filter;
7463      var startDate = this.startDate;
7464      var endDate = this.endDate;
7465      var viewDate = this.viewDate;
7466      var viewYear = viewDate.getFullYear();
7467      var viewMonth = viewDate.getMonth();
7468      var viewDay = viewDate.getDate();
7469      var now = new Date();
7470      var thisYear = now.getFullYear();
7471      var date = this.date;
7472      var year = date.getFullYear();
7473      var isPrevDisabled = false;
7474      var isNextDisabled = false;
7475      var isDisabled = false;
7476      var isPicked = false;
7477      var isMuted = false;
7478      var list = '';
7479      var start = -5;
7480      var end = 6;
7481      var i;
7482
7483      for (i = start; i <= end; i++) {
7484        date = new Date(viewYear + i, viewMonth, viewDay);
7485        isMuted = i === start || i === end;
7486        isPicked = (viewYear + i) === year;
7487        isDisabled = false;
7488
7489        if (startDate) {
7490          isDisabled = date.getFullYear() < startDate.getFullYear();
7491
7492          if (i === start) {
7493            isPrevDisabled = isDisabled;
7494          }
7495        }
7496
7497        if (!isDisabled && endDate) {
7498          isDisabled = date.getFullYear() > endDate.getFullYear();
7499
7500          if (i === end) {
7501            isNextDisabled = isDisabled;
7502          }
7503        }
7504
7505        if (!isDisabled && filter) {
7506          isDisabled = filter.call(this.$element, date) === false;
7507        }
7508
7509        list += this.createItem({
7510          text: viewYear + i,
7511          view: isDisabled ? 'year disabled' : isPicked ? 'year picked' : 'year',
7512          muted: isMuted,
7513          picked: isPicked,
7514          disabled: isDisabled,
7515          highlighted: date.getFullYear() === thisYear
7516        });
7517      }
7518
7519      this.$yearsPrev.toggleClass(disabledClass, isPrevDisabled);
7520      this.$yearsNext.toggleClass(disabledClass, isNextDisabled);
7521      this.$yearsCurrent.
7522        toggleClass(disabledClass, true).
7523        html((viewYear + start) + suffix + ' - ' + (viewYear + end) + suffix);
7524      this.$years.html(list);
7525    },
7526
7527    fillMonths: function () {
7528      var options = this.options;
7529      var disabledClass = options.disabledClass || '';
7530      var months = options.monthsShort;
7531      var filter = $.isFunction(options.filter) && options.filter;
7532      var startDate = this.startDate;
7533      var endDate = this.endDate;
7534      var viewDate = this.viewDate;
7535      var viewYear = viewDate.getFullYear();
7536      var viewDay = viewDate.getDate();
7537      var now = new Date();
7538      var thisYear = now.getFullYear();
7539      var thisMonth = now.getMonth();
7540      var date = this.date;
7541      var year = date.getFullYear();
7542      var month = date.getMonth();
7543      var isPrevDisabled = false;
7544      var isNextDisabled = false;
7545      var isDisabled = false;
7546      var isPicked = false;
7547      var list = '';
7548      var i;
7549
7550      for (i = 0; i <= 11; i++) {
7551        date = new Date(viewYear, i, viewDay);
7552        isPicked = viewYear === year && i === month;
7553        isDisabled = false;
7554
7555        if (startDate) {
7556          isPrevDisabled = date.getFullYear() === startDate.getFullYear();
7557          isDisabled = isPrevDisabled && date.getMonth() < startDate.getMonth();
7558        }
7559
7560        if (!isDisabled && endDate) {
7561          isNextDisabled = date.getFullYear() === endDate.getFullYear();
7562          isDisabled = isNextDisabled && date.getMonth() > endDate.getMonth();
7563        }
7564
7565        if (!isDisabled && filter) {
7566          isDisabled = filter.call(this.$element, date) === false;
7567        }
7568
7569        list += this.createItem({
7570          index: i,
7571          text: months[i],
7572          view: isDisabled ? 'month disabled' : isPicked ? 'month picked' : 'month',
7573          picked: isPicked,
7574          disabled: isDisabled,
7575          highlighted: viewYear === thisYear && date.getMonth() === thisMonth
7576        });
7577      }
7578
7579      this.$yearPrev.toggleClass(disabledClass, isPrevDisabled);
7580      this.$yearNext.toggleClass(disabledClass, isNextDisabled);
7581      this.$yearCurrent.
7582        toggleClass(disabledClass, isPrevDisabled && isNextDisabled).
7583        html(viewYear + options.yearSuffix || '');
7584      this.$months.html(list);
7585    },
7586
7587    fillDays: function () {
7588      var options = this.options;
7589      var disabledClass = options.disabledClass || '';
7590      var suffix = options.yearSuffix || '';
7591      var months = options.monthsShort;
7592      var weekStart = parseInt(options.weekStart, 10) % 7;
7593      var filter = $.isFunction(options.filter) && options.filter;
7594      var startDate = this.startDate;
7595      var endDate = this.endDate;
7596      var viewDate = this.viewDate;
7597      var viewYear = viewDate.getFullYear();
7598      var viewMonth = viewDate.getMonth();
7599      var prevViewYear = viewYear;
7600      var prevViewMonth = viewMonth;
7601      var nextViewYear = viewYear;
7602      var now = new Date();
7603      var thisYear = now.getFullYear();
7604      var thisMonth = now.getMonth();
7605      var today = now.getDate();
7606      var nextViewMonth = viewMonth;
7607      var date = this.date;
7608      var year = date.getFullYear();
7609      var month = date.getMonth();
7610      var day = date.getDate();
7611      var isPrevDisabled = false;
7612      var isNextDisabled = false;
7613      var isDisabled = false;
7614      var isPicked = false;
7615      var prevItems = [];
7616      var nextItems = [];
7617      var items = [];
7618      var total = 42; // 6 rows and 7 columns on the days picker
7619      var length;
7620      var i;
7621      var n;
7622
7623      // Days of previous month
7624      // -----------------------------------------------------------------------
7625
7626      if (viewMonth === 0) {
7627        prevViewYear -= 1;
7628        prevViewMonth = 11;
7629      } else {
7630        prevViewMonth -= 1;
7631      }
7632
7633      // The length of the days of previous month
7634      length = getDaysInMonth(prevViewYear, prevViewMonth);
7635
7636      // The first day of current month
7637      date = new Date(viewYear, viewMonth, 1);
7638
7639      // The visible length of the days of previous month
7640      // [0,1,2,3,4,5,6] - [0,1,2,3,4,5,6] => [-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6]
7641      n = date.getDay() - weekStart;
7642
7643      // [-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6] => [1,2,3,4,5,6,7]
7644      if (n <= 0) {
7645        n += 7;
7646      }
7647
7648      if (startDate) {
7649        isPrevDisabled = date.getTime() <= startDate.getTime();
7650      }
7651
7652      for (i = length - (n - 1); i <= length; i++) {
7653        date = new Date(prevViewYear, prevViewMonth, i);
7654        isPicked = prevViewYear === year && prevViewMonth === month && i === day;
7655        isDisabled = false;
7656
7657        if (startDate) {
7658          isDisabled = date.getTime() < startDate.getTime();
7659        }
7660
7661        if (!isDisabled && filter) {
7662          isDisabled = filter.call(this.$element, date) === false;
7663        }
7664
7665        prevItems.push(this.createItem({
7666          text: i,
7667          view: 'day prev',
7668          muted: true,
7669          picked: isPicked,
7670          disabled: isDisabled,
7671          highlighted: prevViewYear === thisYear && prevViewMonth === thisMonth && date.getDate() === today
7672        }));
7673      }
7674
7675      // Days of next month
7676      // -----------------------------------------------------------------------
7677
7678      if (viewMonth === 11) {
7679        nextViewYear += 1;
7680        nextViewMonth = 0;
7681      } else {
7682        nextViewMonth += 1;
7683      }
7684
7685      // The length of the days of current month
7686      length = getDaysInMonth(viewYear, viewMonth);
7687
7688      // The visible length of next month
7689      n = total - (prevItems.length + length);
7690
7691      // The last day of current month
7692      date = new Date(viewYear, viewMonth, length);
7693
7694      if (endDate) {
7695        isNextDisabled = date.getTime() >= endDate.getTime();
7696      }
7697
7698      for (i = 1; i <= n; i++) {
7699        date = new Date(nextViewYear, nextViewMonth, i);
7700        isPicked = nextViewYear === year && nextViewMonth === month && i === day;
7701        isDisabled = false;
7702
7703        if (endDate) {
7704          isDisabled = date.getTime() > endDate.getTime();
7705        }
7706
7707        if (!isDisabled && filter) {
7708          isDisabled = filter.call(this.$element, date) === false;
7709        }
7710
7711        nextItems.push(this.createItem({
7712          text: i,
7713          view: 'day next',
7714          muted: true,
7715          picked: isPicked,
7716          disabled: isDisabled,
7717          highlighted: nextViewYear === thisYear && nextViewMonth === thisMonth && date.getDate() === today
7718        }));
7719      }
7720
7721      // Days of current month
7722      // -----------------------------------------------------------------------
7723
7724      for (i = 1; i <= length; i++) {
7725        date = new Date(viewYear, viewMonth, i);
7726        isPicked = viewYear === year && viewMonth === month && i === day;
7727        isDisabled = false;
7728
7729        if (startDate) {
7730          isDisabled = date.getTime() < startDate.getTime();
7731        }
7732
7733        if (!isDisabled && endDate) {
7734          isDisabled = date.getTime() > endDate.getTime();
7735        }
7736
7737        if (!isDisabled && filter) {
7738          isDisabled = filter.call(this.$element, date) === false;
7739        }
7740
7741        items.push(this.createItem({
7742          text: i,
7743          view: isDisabled ? 'day disabled' : isPicked ? 'day picked' : 'day',
7744          picked: isPicked,
7745          disabled: isDisabled,
7746          highlighted: viewYear === thisYear && viewMonth === thisMonth && date.getDate() === today
7747        }));
7748      }
7749
7750      // Render days picker
7751      // -----------------------------------------------------------------------
7752
7753      this.$monthPrev.toggleClass(disabledClass, isPrevDisabled);
7754      this.$monthNext.toggleClass(disabledClass, isNextDisabled);
7755      this.$monthCurrent.
7756        toggleClass(disabledClass, isPrevDisabled && isNextDisabled).
7757        html(
7758          options.yearFirst ?
7759          viewYear + suffix + ' ' + months[viewMonth] :
7760          months[viewMonth] + ' ' + viewYear + suffix
7761        );
7762      this.$days.html(prevItems.join('') + items.join(' ') + nextItems.join(''));
7763    },
7764
7765    click: function (e) {
7766      var $target = $(e.target);
7767      var options = this.options;
7768      var viewDate = this.viewDate;
7769      var viewYear;
7770      var viewMonth;
7771      var viewDay;
7772      var isYear;
7773      var year;
7774      var view;
7775
7776      e.stopPropagation();
7777      e.preventDefault();
7778
7779      if ($target.hasClass('disabled')) {
7780        return;
7781      }
7782
7783      viewYear = viewDate.getFullYear();
7784      viewMonth = viewDate.getMonth();
7785      viewDay = viewDate.getDate();
7786      view = $target.data('view');
7787
7788      switch (view) {
7789        case 'years prev':
7790        case 'years next':
7791          viewYear = view === 'years prev' ? viewYear - 10 : viewYear + 10;
7792          year = $target.text();
7793          isYear = REGEXP_YEAR.test(year);
7794
7795          if (isYear) {
7796            viewYear = parseInt(year, 10);
7797            this.date = new Date(viewYear, viewMonth, min(viewDay, 28));
7798          }
7799
7800          this.viewDate = new Date(viewYear, viewMonth, min(viewDay, 28));
7801          this.fillYears();
7802
7803          if (isYear) {
7804            this.showView(VIEWS.MONTHS);
7805            this.pick('year');
7806          }
7807
7808          break;
7809
7810        case 'year prev':
7811        case 'year next':
7812          viewYear = view === 'year prev' ? viewYear - 1 : viewYear + 1;
7813          this.viewDate = new Date(viewYear, viewMonth, min(viewDay, 28));
7814          this.fillMonths();
7815          break;
7816
7817        case 'year current':
7818          if (this.format.hasYear) {
7819            this.showView(VIEWS.YEARS);
7820          }
7821
7822          break;
7823
7824        case 'year picked':
7825          if (this.format.hasMonth) {
7826            this.showView(VIEWS.MONTHS);
7827          } else {
7828            $target.addClass(options.pickedClass)
7829              .siblings()
7830                .removeClass(options.pickedClass);
7831            this.hideView();
7832          }
7833
7834          this.pick('year');
7835          break;
7836
7837        case 'year':
7838          viewYear = parseInt($target.text(), 10);
7839          this.date = new Date(viewYear, viewMonth, min(viewDay, 28));
7840
7841          if (this.format.hasMonth) {
7842            this.viewDate = new Date(viewYear, viewMonth, min(viewDay, 28));
7843            this.showView(VIEWS.MONTHS);
7844          } else {
7845            $target.addClass(options.pickedClass)
7846              .siblings()
7847                .removeClass(options.pickedClass);
7848            this.hideView();
7849          }
7850
7851          this.pick('year');
7852          break;
7853
7854        case 'month prev':
7855        case 'month next':
7856          viewMonth = view === 'month prev' ? viewMonth - 1 : view === 'month next' ? viewMonth + 1 : viewMonth;
7857          this.viewDate = new Date(viewYear, viewMonth, min(viewDay, 28));
7858          this.fillDays();
7859          break;
7860
7861        case 'month current':
7862          if (this.format.hasMonth) {
7863            this.showView(VIEWS.MONTHS);
7864          }
7865
7866          break;
7867
7868        case 'month picked':
7869          if (this.format.hasDay) {
7870            this.showView(VIEWS.DAYS);
7871          } else {
7872            $target.addClass(options.pickedClass)
7873              .siblings()
7874                .removeClass(options.pickedClass);
7875            this.hideView();
7876          }
7877
7878          this.pick('month');
7879          break;
7880
7881        case 'month':
7882          viewMonth = $.inArray($target.text(), options.monthsShort);
7883          this.date = new Date(viewYear, viewMonth, min(viewDay, 28));
7884
7885          if (this.format.hasDay) {
7886            this.viewDate = new Date(viewYear, viewMonth, min(viewDay, 28));
7887            this.showView(VIEWS.DAYS);
7888          } else {
7889            $target.addClass(options.pickedClass)
7890              .siblings()
7891                .removeClass(options.pickedClass);
7892            this.hideView();
7893          }
7894
7895          this.pick('month');
7896          break;
7897
7898        case 'day prev':
7899        case 'day next':
7900        case 'day':
7901          viewMonth = view === 'day prev' ? viewMonth - 1 : view === 'day next' ? viewMonth + 1 : viewMonth;
7902          viewDay = parseInt($target.text(), 10);
7903          this.date = new Date(viewYear, viewMonth, viewDay);
7904          this.viewDate = new Date(viewYear, viewMonth, viewDay);
7905          this.fillDays();
7906
7907          if (view === 'day') {
7908            this.hideView();
7909          }
7910
7911          this.pick('day');
7912          break;
7913
7914        case 'day picked':
7915          this.hideView();
7916          this.pick('day');
7917          break;
7918
7919        // No default
7920      }
7921    },
7922
7923    clickDoc: function (e) {
7924      var target = e.target;
7925      var element = this.$element[0];
7926      var trigger = this.$trigger[0];
7927      var ignored;
7928
7929      while (target !== document) {
7930        if (target === trigger || target === element) {
7931          ignored = true;
7932          break;
7933        }
7934
7935        target = target.parentNode;
7936      }
7937
7938      if (!ignored) {
7939        this.hide();
7940      }
7941    },
7942
7943    keyup: function () {
7944      this.update();
7945    },
7946
7947    keyupDoc: function (e) {
7948      if (this.isInput && e.target !== this.$element[0] &&
7949        this.isShown && (e.key === 'Tab' || e.keyCode === 9)) {
7950        this.hide();
7951      }
7952    },
7953
7954    getValue: function () {
7955      var $this = this.$element;
7956      var val = '';
7957
7958      if (this.isInput) {
7959        val = $this.val();
7960      } else if (this.isInline) {
7961        if (this.options.container) {
7962          val = $this.text();
7963        }
7964      } else {
7965        val = $this.text();
7966      }
7967
7968      return val;
7969    },
7970
7971    setValue: function (val) {
7972      var $this = this.$element;
7973
7974      val = isString(val) ? val : '';
7975
7976      if (this.isInput) {
7977        $this.val(val);
7978      } else if (this.isInline) {
7979        if (this.options.container) {
7980          $this.text(val);
7981        }
7982      } else {
7983        $this.text(val);
7984      }
7985    },
7986
7987
7988    // Methods
7989    // -------------------------------------------------------------------------
7990
7991    // Show the datepicker
7992    show: function () {
7993      if (!this.isBuilt) {
7994        this.build();
7995      }
7996
7997      if (this.isShown) {
7998        return;
7999      }
8000
8001      if (this.trigger(EVENT_SHOW).isDefaultPrevented()) {
8002        return;
8003      }
8004
8005      this.isShown = true;
8006      this.$picker.removeClass(CLASS_HIDE).on(EVENT_CLICK, $.proxy(this.click, this));
8007      this.showView(this.options.startView);
8008
8009      if (!this.isInline) {
8010        $window.on(EVENT_RESIZE, (this._place = proxy(this.place, this)));
8011        $document.on(EVENT_CLICK, (this._clickDoc = proxy(this.clickDoc, this)));
8012        $document.on(EVENT_KEYUP, (this._keyupDoc = proxy(this.keyupDoc, this)));
8013        this.place();
8014      }
8015    },
8016
8017    // Hide the datepicker
8018    hide: function () {
8019      if (!this.isShown) {
8020        return;
8021      }
8022
8023      if (this.trigger(EVENT_HIDE).isDefaultPrevented()) {
8024        return;
8025      }
8026
8027      this.isShown = false;
8028      this.$picker.addClass(CLASS_HIDE).off(EVENT_CLICK, this.click);
8029
8030      if (!this.isInline) {
8031        $window.off(EVENT_RESIZE, this._place);
8032        $document.off(EVENT_CLICK, this._clickDoc);
8033        $document.off(EVENT_KEYUP, this._keyupDoc);
8034      }
8035    },
8036
8037    toggle: function () {
8038      if (this.isShown) {
8039        this.hide();
8040      } else {
8041        this.show();
8042      }
8043    },
8044
8045    // Update the datepicker with the current input value
8046    update: function () {
8047      var value = this.getValue();
8048
8049      if (value === this.oldValue) {
8050        return;
8051      }
8052
8053      this.setDate(value, true);
8054      this.oldValue = value;
8055    },
8056
8057    /**
8058     * Pick the current date to the element
8059     *
8060     * @param {String} _view (private)
8061     */
8062    pick: function (_view) {
8063      var $this = this.$element;
8064      var date = this.date;
8065
8066      if (this.trigger(EVENT_PICK, {
8067        view: _view || '',
8068        date: date
8069      }).isDefaultPrevented()) {
8070        return;
8071      }
8072
8073      this.setValue(date = this.formatDate(this.date));
8074
8075      if (this.isInput) {
8076        $this.trigger('change');
8077      }
8078    },
8079
8080    // Reset the datepicker
8081    reset: function () {
8082      this.setDate(this.initialDate, true);
8083      this.setValue(this.initialValue);
8084
8085      if (this.isShown) {
8086        this.showView(this.options.startView);
8087      }
8088    },
8089
8090    /**
8091     * Get the month name with given argument or the current date
8092     *
8093     * @param {Number} month (optional)
8094     * @param {Boolean} short (optional)
8095     * @return {String} (month name)
8096     */
8097    getMonthName: function (month, short) {
8098      var options = this.options;
8099      var months = options.months;
8100
8101      if ($.isNumeric(month)) {
8102        month = Number(month);
8103      } else if (isUndefined(short)) {
8104        short = month;
8105      }
8106
8107      if (short === true) {
8108        months = options.monthsShort;
8109      }
8110
8111      return months[isNumber(month) ? month : this.date.getMonth()];
8112    },
8113
8114    /**
8115     * Get the day name with given argument or the current date
8116     *
8117     * @param {Number} day (optional)
8118     * @param {Boolean} short (optional)
8119     * @param {Boolean} min (optional)
8120     * @return {String} (day name)
8121     */
8122    getDayName: function (day, short, min) {
8123      var options = this.options;
8124      var days = options.days;
8125
8126      if ($.isNumeric(day)) {
8127        day = Number(day);
8128      } else {
8129        if (isUndefined(min)) {
8130          min = short;
8131        }
8132
8133        if (isUndefined(short)) {
8134          short = day;
8135        }
8136      }
8137
8138      days = min === true ? options.daysMin : short === true ? options.daysShort : days;
8139
8140      return days[isNumber(day) ? day : this.date.getDay()];
8141    },
8142
8143    /**
8144     * Get the current date
8145     *
8146     * @param {Boolean} formatted (optional)
8147     * @return {Date|String} (date)
8148     */
8149    getDate: function (formatted) {
8150      var date = this.date;
8151
8152      return formatted ? this.formatDate(date) : new Date(date);
8153    },
8154
8155    /**
8156     * Set the current date with a new date
8157     *
8158     * @param {Date} date
8159     * @param {Boolean} _isUpdated (private)
8160     */
8161    setDate: function (date, _isUpdated) {
8162      var filter = this.options.filter;
8163
8164      if (isDate(date) || isString(date)) {
8165        date = this.parseDate(date);
8166
8167        if ($.isFunction(filter) && filter.call(this.$element, date) === false) {
8168          return;
8169        }
8170
8171        this.date = date;
8172        this.viewDate = new Date(date);
8173
8174        if (!_isUpdated) {
8175          this.pick();
8176        }
8177
8178        if (this.isBuilt) {
8179          this.fillAll();
8180        }
8181      }
8182    },
8183
8184    /**
8185     * Set the start view date with a new date
8186     *
8187     * @param {Date} date
8188     */
8189    setStartDate: function (date) {
8190      if (isDate(date) || isString(date)) {
8191        this.startDate = this.parseDate(date);
8192
8193        if (this.isBuilt) {
8194          this.fillAll();
8195        }
8196      }
8197    },
8198
8199    /**
8200     * Set the end view date with a new date
8201     *
8202     * @param {Date} date
8203     */
8204    setEndDate: function (date) {
8205      if (isDate(date) || isString(date)) {
8206        this.endDate = this.parseDate(date);
8207
8208        if (this.isBuilt) {
8209          this.fillAll();
8210        }
8211      }
8212    },
8213
8214    /**
8215     * Parse a date string with the set date format
8216     *
8217     * @param {String} date
8218     * @return {Date} (parsed date)
8219     */
8220    parseDate: function (date) {
8221      var format = this.format;
8222      var parts = [];
8223      var length;
8224      var year;
8225      var day;
8226      var month;
8227      var val;
8228      var i;
8229
8230      if (isDate(date)) {
8231        return new Date(date.getFullYear(), date.getMonth(), date.getDate());
8232      } else if (isString(date)) {
8233        parts = date.match(REGEXP_DIGITS) || [];
8234      }
8235
8236      date = new Date();
8237      year = date.getFullYear();
8238      day = date.getDate();
8239      month = date.getMonth();
8240      length = format.parts.length;
8241
8242      if (parts.length === length) {
8243        for (i = 0; i < length; i++) {
8244          val = parseInt(parts[i], 10) || 1;
8245
8246          switch (format.parts[i]) {
8247            case 'dd':
8248            case 'd':
8249              day = val;
8250              break;
8251
8252            case 'mm':
8253            case 'm':
8254              month = val - 1;
8255              break;
8256
8257            case 'yy':
8258              year = 2000 + val;
8259              break;
8260
8261            case 'yyyy':
8262              year = val;
8263              break;
8264
8265            // No default
8266          }
8267        }
8268      }
8269
8270      return new Date(year, month, day);
8271    },
8272
8273    /**
8274     * Format a date object to a string with the set date format
8275     *
8276     * @param {Date} date
8277     * @return {String} (formatted date)
8278     */
8279    formatDate: function (date) {
8280      var format = this.format;
8281      var formatted = '';
8282      var length;
8283      var year;
8284      var part;
8285      var val;
8286      var i;
8287
8288      if (isDate(date)) {
8289        formatted = format.source;
8290        year = date.getFullYear();
8291        val = {
8292          d: date.getDate(),
8293          m: date.getMonth() + 1,
8294          yy: year.toString().substring(2),
8295          yyyy: year
8296        };
8297
8298        val.dd = (val.d < 10 ? '0' : '') + val.d;
8299        val.mm = (val.m < 10 ? '0' : '') + val.m;
8300        length = format.parts.length;
8301
8302        for (i = 0; i < length; i++) {
8303          part = format.parts[i];
8304          formatted = formatted.replace(part, val[part]);
8305        }
8306      }
8307
8308      return formatted;
8309    },
8310
8311    // Destroy the datepicker and remove the instance from the target element
8312    destroy: function () {
8313      this.unbind();
8314      this.unbuild();
8315      this.$element.removeData(NAMESPACE);
8316    }
8317  };
8318
8319  Datepicker.LANGUAGES = {};
8320
8321  Datepicker.DEFAULTS = {
8322    // Show the datepicker automatically when initialized
8323    autoShow: false,
8324
8325    // Hide the datepicker automatically when picked
8326    autoHide: false,
8327
8328    // Pick the initial date automatically when initialized
8329    autoPick: false,
8330
8331    // Enable inline mode
8332    inline: false,
8333
8334    // A element (or selector) for putting the datepicker
8335    container: null,
8336
8337    // A element (or selector) for triggering the datepicker
8338    trigger: null,
8339
8340    // The ISO language code (built-in: en-US)
8341    language: '',
8342
8343    // The date string format
8344    format: 'mm/dd/yyyy',
8345
8346    // The initial date
8347    date: null,
8348
8349    // The start view date
8350    startDate: null,
8351
8352    // The end view date
8353    endDate: null,
8354
8355    // The start view when initialized
8356    startView: 0, // 0 for days, 1 for months, 2 for years
8357
8358    // The start day of the week
8359    weekStart: 0, // 0 for Sunday, 1 for Monday, 2 for Tuesday, 3 for Wednesday, 4 for Thursday, 5 for Friday, 6 for Saturday
8360
8361    // Show year before month on the datepicker header
8362    yearFirst: false,
8363
8364    // A string suffix to the year number.
8365    yearSuffix: '',
8366
8367    // Days' name of the week.
8368    days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
8369
8370    // Shorter days' name
8371    daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
8372
8373    // Shortest days' name
8374    daysMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
8375
8376    // Months' name
8377    months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
8378
8379    // Shorter months' name
8380    monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
8381
8382    // A element tag for each item of years, months and days
8383    itemTag: 'li',
8384
8385    // A class (CSS) for muted date item
8386    mutedClass: 'muted',
8387
8388    // A class (CSS) for picked date item
8389    pickedClass: 'picked',
8390
8391    // A class (CSS) for disabled date item
8392    disabledClass: 'disabled',
8393
8394    // A class (CSS) for highlight date item
8395    highlightedClass: 'highlighted',
8396
8397    // The template of the datepicker
8398    template: (
8399      '<div class="datepicker-container">' +
8400        '<div class="datepicker-panel" data-view="years picker">' +
8401          '<ul>' +
8402            '<li data-view="years prev">&lsaquo;</li>' +
8403            '<li data-view="years current"></li>' +
8404            '<li data-view="years next">&rsaquo;</li>' +
8405          '</ul>' +
8406          '<ul data-view="years"></ul>' +
8407        '</div>' +
8408        '<div class="datepicker-panel" data-view="months picker">' +
8409          '<ul>' +
8410            '<li data-view="year prev">&lsaquo;</li>' +
8411            '<li data-view="year current"></li>' +
8412            '<li data-view="year next">&rsaquo;</li>' +
8413          '</ul>' +
8414          '<ul data-view="months"></ul>' +
8415        '</div>' +
8416        '<div class="datepicker-panel" data-view="days picker">' +
8417          '<ul>' +
8418            '<li data-view="month prev">&lsaquo;</li>' +
8419            '<li data-view="month current"></li>' +
8420            '<li data-view="month next">&rsaquo;</li>' +
8421          '</ul>' +
8422          '<ul data-view="week"></ul>' +
8423          '<ul data-view="days"></ul>' +
8424        '</div>' +
8425      '</div>'
8426    ),
8427
8428    // The offset top or bottom of the datepicker from the element
8429    offset: 10,
8430
8431    // The `z-index` of the datepicker
8432    zIndex: 1000,
8433
8434    // Filter each date item (return `false` to disable a date item)
8435    filter: null,
8436
8437    // Event shortcuts
8438    show: null,
8439    hide: null,
8440    pick: null
8441  };
8442
8443  Datepicker.setDefaults = function (options) {
8444    options = $.isPlainObject(options) ? options : {};
8445
8446    if (options.language) {
8447      options = $.extend({}, Datepicker.LANGUAGES[options.language], options);
8448    }
8449
8450    $.extend(Datepicker.DEFAULTS, options);
8451  };
8452
8453  // Save the other datepicker
8454  Datepicker.other = $.fn.datepicker;
8455
8456  // Register as jQuery plugin
8457  $.fn.datepicker = function (option) {
8458    var args = toArray(arguments, 1);
8459    var result;
8460
8461    this.each(function () {
8462      var $this = $(this);
8463      var data = $this.data(NAMESPACE);
8464      var options;
8465      var fn;
8466
8467      if (!data) {
8468        if (/destroy/.test(option)) {
8469          return;
8470        }
8471
8472        options = $.extend({}, $this.data(), $.isPlainObject(option) && option);
8473        $this.data(NAMESPACE, (data = new Datepicker(this, options)));
8474      }
8475
8476      if (isString(option) && $.isFunction(fn = data[option])) {
8477        result = fn.apply(data, args);
8478      }
8479    });
8480
8481    return isUndefined(result) ? this : result;
8482  };
8483
8484  $.fn.datepicker.Constructor = Datepicker;
8485  $.fn.datepicker.languages = Datepicker.LANGUAGES;
8486  $.fn.datepicker.setDefaults = Datepicker.setDefaults;
8487
8488  // No conflict
8489  $.fn.datepicker.noConflict = function () {
8490    $.fn.datepicker = Datepicker.other;
8491    return this;
8492  };
8493
8494});
8495;
8496/*!
8497 * cleave.js - 1.3.7
8498 * https://github.com/nosir/cleave.js
8499 * Apache License Version 2.0
8500 *
8501 * Copyright (C) 2012-2018 Max Huang https://github.com/nosir/
8502 */
8503!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Cleave=t():e.Cleave=t()}(this,function(){return function(e){function t(n){if(r[n])return r[n].exports;var i=r[n]={exports:{},id:n,loaded:!1};return e[n].call(i.exports,i,i.exports,t),i.loaded=!0,i.exports}var r={};return t.m=e,t.c=r,t.p="",t(0)}([function(e,t,r){(function(t){"use strict";var n=function(e,t){var r=this;if("string"==typeof e?r.element=document.querySelector(e):r.element="undefined"!=typeof e.length&&e.length>0?e[0]:e,!r.element)throw new Error("[cleave.js] Please check the element");t.initValue=r.element.value,r.properties=n.DefaultProperties.assign({},t),r.init()};n.prototype={init:function(){var e=this,t=e.properties;return t.numeral||t.phone||t.creditCard||t.date||0!==t.blocksLength||t.prefix?(t.maxLength=n.Util.getMaxLength(t.blocks),e.isAndroid=n.Util.isAndroid(),e.lastInputValue="",e.onChangeListener=e.onChange.bind(e),e.onKeyDownListener=e.onKeyDown.bind(e),e.onFocusListener=e.onFocus.bind(e),e.onCutListener=e.onCut.bind(e),e.onCopyListener=e.onCopy.bind(e),e.element.addEventListener("input",e.onChangeListener),e.element.addEventListener("keydown",e.onKeyDownListener),e.element.addEventListener("focus",e.onFocusListener),e.element.addEventListener("cut",e.onCutListener),e.element.addEventListener("copy",e.onCopyListener),e.initPhoneFormatter(),e.initDateFormatter(),e.initNumeralFormatter(),void((t.initValue||t.prefix&&!t.noImmediatePrefix)&&e.onInput(t.initValue))):void e.onInput(t.initValue)},initNumeralFormatter:function(){var e=this,t=e.properties;t.numeral&&(t.numeralFormatter=new n.NumeralFormatter(t.numeralDecimalMark,t.numeralIntegerScale,t.numeralDecimalScale,t.numeralThousandsGroupStyle,t.numeralPositiveOnly,t.stripLeadingZeroes,t.delimiter))},initDateFormatter:function(){var e=this,t=e.properties;t.date&&(t.dateFormatter=new n.DateFormatter(t.datePattern),t.blocks=t.dateFormatter.getBlocks(),t.blocksLength=t.blocks.length,t.maxLength=n.Util.getMaxLength(t.blocks))},initPhoneFormatter:function(){var e=this,t=e.properties;if(t.phone)try{t.phoneFormatter=new n.PhoneFormatter(new t.root.Cleave.AsYouTypeFormatter(t.phoneRegionCode),t.delimiter)}catch(r){throw new Error("[cleave.js] Please include phone-type-formatter.{country}.js lib")}},onKeyDown:function(e){var t=this,r=t.properties,i=e.which||e.keyCode,a=n.Util,o=t.element.value;return a.isAndroidBackspaceKeydown(t.lastInputValue,o)&&(i=8),t.lastInputValue=o,8===i&&a.isDelimiter(o.slice(-r.delimiterLength),r.delimiter,r.delimiters)?void(r.backspace=!0):void(r.backspace=!1)},onChange:function(){this.onInput(this.element.value)},onFocus:function(){var e=this,t=e.properties;n.Util.fixPrefixCursor(e.element,t.prefix,t.delimiter,t.delimiters)},onCut:function(e){this.copyClipboardData(e),this.onInput("")},onCopy:function(e){this.copyClipboardData(e)},copyClipboardData:function(e){var t=this,r=t.properties,i=n.Util,a=t.element.value,o="";o=r.copyDelimiter?a:i.stripDelimiters(a,r.delimiter,r.delimiters);try{e.clipboardData?e.clipboardData.setData("Text",o):window.clipboardData.setData("Text",o),e.preventDefault()}catch(l){}},onInput:function(e){var t=this,r=t.properties,i=n.Util;return r.numeral||!r.backspace||i.isDelimiter(e.slice(-r.delimiterLength),r.delimiter,r.delimiters)||(e=i.headStr(e,e.length-r.delimiterLength)),r.phone?(!r.prefix||r.noImmediatePrefix&&!e.length?r.result=r.phoneFormatter.format(e):r.result=r.prefix+r.phoneFormatter.format(e).slice(r.prefix.length),void t.updateValueState()):r.numeral?
vendor: 4,518 bytes, line 8503
8503(!r.prefix||r.noImmediatePrefix&&!e.length?r.result=r.numeralFormatter.format(e):r.result=r.prefix+r.numeralFormatter.format(e),void t.updateValueState()):(r.date&&(e=r.dateFormatter.getValidatedDate(e)),e=i.stripDelimiters(e,r.delimiter,r.delimiters),e=i.getPrefixStrippedValue(e,r.prefix,r.prefixLength,r.result),e=r.numericOnly?i.strip(e,/[^\d]/g):e,e=r.uppercase?e.toUpperCase():e,e=r.lowercase?e.toLowerCase():e,!r.prefix||r.noImmediatePrefix&&!e.length||(e=r.prefix+e,0!==r.blocksLength)?(r.creditCard&&t.updateCreditCardPropsByValue(e),e=i.headStr(e,r.maxLength),r.result=i.getFormattedValue(e,r.blocks,r.blocksLength,r.delimiter,r.delimiters,r.delimiterLazyShow),void t.updateValueState()):(r.result=e,void t.updateValueState()))},updateCreditCardPropsByValue:function(e){var t,r=this,i=r.properties,a=n.Util;a.headStr(i.result,4)!==a.headStr(e,4)&&(t=n.CreditCardDetector.getInfo(e,i.creditCardStrictMode),i.blocks=t.blocks,i.blocksLength=i.blocks.length,i.maxLength=a.getMaxLength(i.blocks),i.creditCardType!==t.type&&(i.creditCardType=t.type,i.onCreditCardTypeChanged.call(r,i.creditCardType)))},updateValueState:function(){var e=this,t=n.Util,r=e.properties;if(e.element){var i=e.element.selectionEnd,a=e.element.value,o=r.result;if(i=t.getNextCursorPosition(i,a,o,r.delimiter,r.delimiters),e.isAndroid)return void window.setTimeout(function(){e.element.value=o,t.setSelection(e.element,i,r.document,!1),e.callOnValueChanged()},1);e.element.value=o,t.setSelection(e.element,i,r.document,!1),e.callOnValueChanged()}},callOnValueChanged:function(){var e=this,t=e.properties;t.onValueChanged.call(e,{target:{value:t.result,rawValue:e.getRawValue()}})},setPhoneRegionCode:function(e){var t=this,r=t.properties;r.phoneRegionCode=e,t.initPhoneFormatter(),t.onChange()},setRawValue:function(e){var t=this,r=t.properties;e=void 0!==e&&null!==e?e.toString():"",r.numeral&&(e=e.replace(".",r.numeralDecimalMark)),r.backspace=!1,t.element.value=e,t.onInput(e)},getRawValue:function(){var e=this,t=e.properties,r=n.Util,i=e.element.value;return t.rawValueTrimPrefix&&(i=r.getPrefixStrippedValue(i,t.prefix,t.prefixLength,t.result)),i=t.numeral?t.numeralFormatter.getRawValue(i):r.stripDelimiters(i,t.delimiter,t.delimiters)},getISOFormatDate:function(){var e=this,t=e.properties;return t.date?t.dateFormatter.getISOFormatDate():""},getFormattedValue:function(){return this.element.value},destroy:function(){var e=this;e.element.removeEventListener("input",e.onChangeListener),e.element.removeEventListener("keydown",e.onKeyDownListener),e.element.removeEventListener("focus",e.onFocusListener),e.element.removeEventListener("cut",e.onCutListener),e.element.removeEventListener("copy",e.onCopyListener)},toString:function(){return"[Cleave Object]"}},n.NumeralFormatter=r(1),n.DateFormatter=r(2),n.PhoneFormatter=r(3),n.CreditCardDetector=r(4),n.Util=r(5),n.DefaultProperties=r(6),("object"==typeof t&&t?t:window).Cleave=n,e.exports=n}).call(t,function(){return this}())},function(e,t){"use strict";var r=function(e,t,n,i,a,o,l){var s=this;s.numeralDecimalMark=e||".",s.numeralIntegerScale=t>0?t:0,s.numeralDecimalScale=n>=0?n:2,s.numeralThousandsGroupStyle=i||r.groupStyle.thousand,s.numeralPositiveOnly=!!a,s.stripLeadingZeroes=o!==!1,s.delimiter=l||""===l?l:",",s.delimiterRE=l?new RegExp("\\"+l,"g"):""};r.groupStyle={thousand:"thousand",lakh:"lakh",wan:"wan",none:"none"},r.prototype={getRawValue:function(e){return e.replace(this.delimiterRE,"").replace(this.numeralDecimalMark,".")},format:function(e){var t,n,i=this,a="";switch(e=e.replace(/[A-Za-z]/g,"").replace(i.numeralDecimalMark,"M").replace(/[^\dM-]/g,"").replace(/^\-/,"N").replace(/\-/g,"").replace("N",i.numeralPositiveOnly?"":"-").replace("M",i.numeralDecimalMark),i.stripLeadingZeroes&&(e=e.replace(/^(-)?0+(?=\d)/,"$1")),n=e,e.indexOf(i.numeralDecimalMark)>=0&&(t=e.split(i.numeralDecimalMark),n=t[0],a=i.numeralDecimalMark+t[1].slice(0,i.numeralDecimalScale)),i.numeralIntegerScale>0&&(n=n.slice(0,i.numeralIntegerScale+("-"===e.slice(0,1)?1:0))),i.numeralThousandsGroupStyle){case r.groupStyle.lakh:n=n.replace(/(\d)(?=(\d\d)+\d$)/g,"$1"+i.delimiter);break;case r.groupStyle.wan:n=n.replace(/(\d)(?=(\d{4})+$)/g,"$1"+i.delimiter);break;case r.groupStyle.thousand:n=n.replace(/(\d)(?=(\d{3})+$)/g,"$1"+i.delimiter)}return n.toString()+(i.numeralDecimalScale>0?a.toString():"")}},e.exports=r},function(e,t){"use strict";var r=function(e){var t=this;t.date=[],t.blocks=[],t.datePattern=e,t.initBlocks()};r.prototype={initBlocks:function(){var e=this;
vendor: 2,199 bytes, line 8503
8503e.datePattern.forEach(function(t){"Y"===t?e.blocks.push(4):e.blocks.push(2)})},getISOFormatDate:function(){var e=this,t=e.date;return t[2]?t[2]+"-"+e.addLeadingZero(t[1])+"-"+e.addLeadingZero(t[0]):""},getBlocks:function(){return this.blocks},getValidatedDate:function(e){var t=this,r="";return e=e.replace(/[^\d]/g,""),t.blocks.forEach(function(n,i){if(e.length>0){var a=e.slice(0,n),o=a.slice(0,1),l=e.slice(n);switch(t.datePattern[i]){case"d":"00"===a?a="01":parseInt(o,10)>3?a="0"+o:parseInt(a,10)>31&&(a="31");break;case"m":"00"===a?a="01":parseInt(o,10)>1?a="0"+o:parseInt(a,10)>12&&(a="12")}r+=a,e=l}}),this.getFixedDateString(r)},getFixedDateString:function(e){var t,r,n,i=this,a=i.datePattern,o=[],l=0,s=0,u=0,c=0,d=0,m=0,p=!1;return 4===e.length&&"y"!==a[0].toLowerCase()&&"y"!==a[1].toLowerCase()&&(c="d"===a[0]?0:2,d=2-c,t=parseInt(e.slice(c,c+2),10),r=parseInt(e.slice(d,d+2),10),o=this.getFixedDate(t,r,0)),8===e.length&&(a.forEach(function(e,t){switch(e){case"d":l=t;break;case"m":s=t;break;default:u=t}}),m=2*u,c=l<=u?2*l:2*l+2,d=s<=u?2*s:2*s+2,t=parseInt(e.slice(c,c+2),10),r=parseInt(e.slice(d,d+2),10),n=parseInt(e.slice(m,m+4),10),p=4===e.slice(m,m+4).length,o=this.getFixedDate(t,r,n)),i.date=o,0===o.length?e:a.reduce(function(e,t){switch(t){case"d":return e+i.addLeadingZero(o[0]);case"m":return e+i.addLeadingZero(o[1]);default:return e+(p?i.addLeadingZeroForYear(o[2]):"")}},"")},getFixedDate:function(e,t,r){return e=Math.min(e,31),t=Math.min(t,12),r=parseInt(r||0,10),(t<7&&t%2===0||t>8&&t%2===1)&&(e=Math.min(e,2===t?this.isLeapYear(r)?29:28:30)),[e,t,r]},isLeapYear:function(e){return e%4===0&&e%100!==0||e%400===0},addLeadingZero:function(e){return(e<10?"0":"")+e},addLeadingZeroForYear:function(e){return(e<10?"000":e<100?"00":e<1e3?"0":"")+e}},e.exports=r},function(e,t){"use strict";var r=function(e,t){var r=this;r.delimiter=t||""===t?t:" ",r.delimiterRE=t?new RegExp("\\"+t,"g"):"",r.formatter=e};r.prototype={setFormatter:function(e){this.formatter=e},format:function(e){var t=this;t.formatter.clear(),e=e.replace(/[^\d+]/g,""),e=e.replace(t.delimiterRE,"");for(var r,n="",i=!1,a=0,o=e.length;a<o;a++)r=t.formatter.inputDigit(e.charAt(a)),/[\s()-]/g.test(r)?(n=r,
8503i=!0):i||(n=r);return n=n.replace(/[()]/g,""),n=n.replace(/[\s-]/g,t.delimiter)}},e.exports=r},function(e,t){"use strict";var r={blocks:{uatp:[4,5,6],amex:[4,6,5],diners:[4,6,4],discover:[4,4,4,4],mastercard:[4,4,4,4],dankort:[4,4,4,4],instapayment:[4,4,4,4],jcb15:[4,6,5],jcb:[4,4,4,4],maestro:[4,4,4,4],visa:[4,4,4,4],mir:[4,4,4,4],unionPay:[4,4,4,4],general:[4,4,4,4],generalStrict:[4,4,4,7]},re:{uatp:/^(?!1800)1\d{0,14}/,amex:/^3[47]\d{0,13}/,discover:/^(?:6011|65\d{0,2}|64[4-9]\d?)\d{0,12}/,diners:/^3(?:0([0-5]|9)|[689]\d?)\d{0,11}/,mastercard:/^(5[1-5]\d{0,2}|22[2-9]\d{0,1}|2[3-7]\d{0,2})\d{0,12}/,dankort:/^(5019|4175|4571)\d{0,12}/,instapayment:/^63[7-9]\d{0,13}/,jcb15:/^(?:2131|1800)\d{0,11}/,jcb:/^(?:35\d{0,2})\d{0,12}/,maestro:/^(?:5[0678]\d{0,2}|6304|67\d{0,2})\d{0,12}/,mir:/^220[0-4]\d{0,12}/,visa:/^4\d{0,15}/,unionPay:/^62\d{0,14}/},getInfo:function(e,t){var n=r.blocks,i=r.re;t=!!t;for(var a in i)if(i[a].test(e)){var o;return o=t?n.generalStrict:n[a],{type:a,blocks:o}}return{type:"unknown",blocks:t?n.generalStrict:n.general}}};e.exports=r},function(e,t){"use strict";var r={noop:function(){},strip:function(e,t){return e.replace(t,"")},isDelimiter:function(e,t,r){return 0===r.length?e===t:r.some(function(t){if(e===t)return!0})},getDelimiterREByDelimiter:function(e){return new RegExp(e.replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1"),"g")},getNextCursorPosition:function(e,t,r,n,i){return t.length===e?r.length:e+this.getPositionOffset(e,t,r,n,i)},getPositionOffset:function(e,t,r,n,i){var a,o,l;return a=this.stripDelimiters(t.slice(0,e),n,i),o=this.stripDelimiters(r.slice(0,e),n,i),l=a.length-o.length,0!==l?l/Math.abs(l):0},stripDelimiters:function(e,t,r){var n=this;if(0===r.length){var i=t?n.getDelimiterREByDelimiter(t):"";return e.replace(i,"")}return r.forEach(function(t){e=e.replace(n.getDelimiterREByDelimiter(t),"")}),e},headStr:function(e,t){return e.slice(0,t)},getMaxLength:function(e){return e.reduce(function(e,t){return e+t},0)},getPrefixStrippedValue:function(e,t,r,n){if(e.slice(0,r)!==t)if(e.length<n.length)e=e.length>r?n:t;else{var i=this.getFirstDiffIndex(t,e.slice(0,r));e=t+e.slice(i,i+1)+e.slice(r+1)}return e.slice(r)},getFirstDiffIndex:function(e,t){for(var r=0;e.charAt(r)===t.charAt(r);)if(""===e.charAt(r++))return-1;return r},getFormattedValue:function(e,t,r,n,i,a){var o,l="",s=i.length>0;return 0===r?e:(t.forEach(function(t,u){if(e.length>0){var c=e.slice(0,t),d=e.slice(t);o=s?i[a?u-1:u]||o:n,a?(u>0&&(l+=o),l+=c):(l+=c,c.length===t&&u<r-1&&(l+=o)),e=d}}),l)},fixPrefixCursor:function(e,t,r,n){if(e){var i=e.value,a=r||n[0]||" ";if(e.setSelectionRange&&t&&!(t.length+a.length<i.length)){var o=2*i.length;setTimeout(function(){e.setSelectionRange(o,o)},1)}}},setSelection:function(e,t,r){if(e===r.activeElement&&!(e&&e.value.length<=t))if(e.createTextRange){var n=e.createTextRange();n.move("character",t),n.select()}else try{e.setSelectionRange(t,t)}catch(i){console.warn("The input element type does not support selection")}},isAndroid:function(){return navigator&&/android/i.test(navigator.userAgent)},isAndroidBackspaceKeydown:function(e,t){return!!(this.isAndroid()&&e&&t)&&t===e.slice(0,-1)}};e.exports=r},function(e,t){(function(t){"use strict";var r={assign:function(e,r){return e=e||{},r=r||{},e.creditCard=!!r.creditCard,e.creditCardStrictMode=!!r.creditCardStrictMode,e.creditCardType="",e.onCreditCardTypeChanged=r.onCreditCardTypeChanged||function(){},e.phone=!!r.phone,e.phoneRegionCode=r.phoneRegionCode||"AU",e.phoneFormatter={},e.date=!!r.date,e.datePattern=r.datePattern||["d","m","Y"],e.dateFormatter={},e.numeral=!!r.numeral,e.numeralIntegerScale=r.numeralIntegerScale>0?r.numeralIntegerScale:0,e.numeralDecimalScale=r.numeralDecimalScale>=0?r.numeralDecimalScale:2,e.numeralDecimalMark=r.numeralDecimalMark||".",e.numeralThousandsGroupStyle=r.numeralThousandsGroupStyle||"thousand",e.numeralPositiveOnly=!!r.numeralPositiveOnly,e.stripLeadingZeroes=r.stripLeadingZeroes!==!1,e.numericOnly=e.creditCard||e.date||!!r.numericOnly,e.uppercase=!!r.uppercase,e.lowercase=!!r.lowercase,e.prefix=e.creditCard||e.date?"":r.prefix||"",e.noImmediatePrefix=!!r.noImmediatePrefix,e.prefixLength=e.prefix.length,e.rawValueTrimPrefix=!!r.rawValueTrimPrefix,e.copyDelimiter=!!r.copyDelimiter,e.initV
8503alue=void 0!==r.initValue&&null!==r.initValue?r.initValue.toString():"",e.delimiter=r.delimiter||""===r.delimiter?r.delimiter:r.date?"/":r.numeral?",":(r.phone," "),e.delimiterLength=e.delimiter.length,e.delimiterLazyShow=!!r.delimiterLazyShow,e.delimiters=r.delimiters||[],e.blocks=r.blocks||[],e.blocksLength=e.blocks.length,e.document=r.document||document,e.root="object"==typeof t&&t?t:window,e.maxLength=0,e.backspace=!1,e.result="",e.onValueChanged=r.onValueChanged||function(){},e}};e.exports=r}).call(t,function(){return this}())}])});;
8504if (!Object.assign) {
8505  Object.defineProperty(Object, 'assign', {
8506    enumerable: false,
8507    configurable: true,
8508    writable: true,
8509    value: function(target) {
8510      'use strict';
8511      if (target === undefined || target === null) {
8512        throw new TypeError('Cannot convert first argument to object');
8513      }
8514
8515      var to = Object(target);
8516      for (var i = 1; i < arguments.length; i++) {
8517        var nextSource = arguments[i];
8518        if (nextSource === undefined || nextSource === null) {
8519          continue;
8520        }
8521        nextSource = Object(nextSource);
8522
8523        var keysArray = Object.keys(Object(nextSource));
8524        for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
8525          var nextKey = keysArray[nextIndex];
8526          var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
8527          if (desc !== undefined && desc.enumerable) {
8528            to[nextKey] = nextSource[nextKey];
8529          }
8530        }
8531      }
8532      return to;
8533    }
8534  });
8535};
8536!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r():"function"==typeof define&&define.amd?define(r):(e="undefined"!=typeof globalThis?globalThis:e||self).Pristine=r()}(this,(function(){"use strict";var e={en:{required:"This field is required",email:"This field requires a valid e-mail address",number:"This field requires a number",integer:"This field requires an integer value",url:"This field requires a valid website URL",tel:"This field requires a valid telephone number",maxlength:"This fields length must be < ${1}",minlength:"This fields length must be > ${1}",min:"Minimum value for this field is ${1}",max:"Maximum value for this field is ${1}",pattern:"Please match the requested format",equals:"The two fields do not match"}};function r(e){var r=arguments;return this.replace(/\${([^{}]*)}/g,(function(e,t){return r[t]}))}function t(e){return e.pristine.self.form.querySelectorAll('input[name="'+e.getAttribute("name")+'"]:checked').length}var n={classTo:"form-group",errorClass:"has-danger",successClass:"has-success",errorTextParent:"form-group",errorTextTag:"div",errorTextClass:"text-help"},i=["required","min","max","minlength","maxlength","pattern"],s=/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,a=/-message(?:-([a-z]{2}(?:_[A-Z]{2})?))?/,o="en",l={},u=function(e,r){r.name=e,void 0===r.priority&&(r.priority=1),l[e]=r};function f(t,s,u){var f=this;function c(e,r,t,n){var i=l[t];
vendor: 1,836 bytes, line 8536
8536if(i&&(e.push(i),n)){var s="pattern"===t?[n]:n.split(",");s.unshift(null),r[t]=s}}function p(t){for(var n=[],i=!0,s=0;t.validators[s];s++){var a=t.validators[s],l=t.params[a.name]?t.params[a.name]:[];if(l[0]=t.input.value,!a.fn.apply(t.input,l)&&(i=!1,"function"==typeof a.msg?n.push(a.msg(t.input.value,l)):"string"==typeof a.msg?n.push(r.apply(a.msg,l)):a.msg===Object(a.msg)&&a.msg[o]?n.push(r.apply(a.msg[o],l)):t.messages[o]&&t.messages[o][a.name]?n.push(r.apply(t.messages[o][a.name],l)):e[o]&&e[o][a.name]&&n.push(r.apply(e[o][a.name],l)),!0===a.halt))break}return t.errors=n,i}function m(e){if(e.errorElements)return e.errorElements;var r=function(e,r){for(;(e=e.parentElement)&&!e.classList.contains(r););return e}(e.input,f.config.classTo),t=null,n=null;return(t=f.config.classTo===f.config.errorTextParent?r:r.querySelector("."+f.config.errorTextParent))&&((n=t.querySelector(".pristine-error"))||((n=document.createElement(f.config.errorTextTag)).className="pristine-error "+f.config.errorTextClass,t.appendChild(n),n.pristineDisplay=n.style.display)),e.errorElements=[r,n]}function d(e){var r=m(e),t=r[0],n=r[1];t&&(t.classList.remove(f.config.successClass),t.classList.add(f.config.errorClass)),n&&(n.innerHTML=e.errors.join("<br/>"),n.style.display=n.pristineDisplay||"")}function h(e){var r=function(e){var r=m(e),t=r[0],n=r[1];return t&&(t.classList.remove(f.config.errorClass),t.classList.remove(f.config.successClass)),n&&(n.innerHTML="",n.style.display="none"),r}(e)[0];r&&r.classList.add(f.config.successClass)}return function(e,r,t){e.setAttribute("novalidate","true"),f.form=e,f.config=function(e,r){for(var t in r)t in e||(e[t]=r[t]);return e}(r||{},n),f.live=!(!1===t),f.fields=Array.from(e.querySelectorAll("input:not([type^=hidden]):not([type^=submit]), select, textarea")).map(function(e){var r=[],t={},n={};
8536return[].forEach.call(e.attributes,(function(e){if(/^data-pristine-/.test(e.name)){var s=e.name.substr(14),o=s.match(a);if(null!==o){var l=void 0===o[1]?"en":o[1];return n.hasOwnProperty(l)||(n[l]={}),void(n[l][s.slice(0,s.length-o[0].length)]=e.value)}"type"===s&&(s=e.value),c(r,t,s,e.value)}else~i.indexOf(e.name)?c(r,t,e.name,e.value):"type"===e.name&&c(r,t,e.value)})),r.sort((function(e,r){return r.priority-e.priority})),f.live&&e.addEventListener(~["radio","checkbox"].indexOf(e.getAttribute("type"))?"change":"input",function(e){f.validate(e.target)}.bind(f)),e.pristine={input:e,validators:r,params:t,messages:n,self:f}}.bind(f))}(t,s,u),f.validate=function(e,r){r=e&&!0===r||!0===e;var t=f.fields;!0!==e&&!1!==e&&(e instanceof HTMLElement?t=[e.pristine]:(e instanceof NodeList||e instanceof(window.$||Array)||e instanceof Array)&&(t=Array.from(e).map((function(e){return e.pristine}))));for(var n=!0,i=0;t[i];i++){var s=t[i];p(s)?!r&&h(s):(n=!1,!r&&d(s))}return n},f.getErrors=function(e){if(!e){for(var r=[],t=0;t<f.fields.length;t++){var n=f.fields[t];n.errors.length&&r.push({input:n.input,errors:n.errors})}return r}return e.tagName&&"select"===e.tagName.toLowerCase()?e.pristine.errors:e.length?e[0].pristine.errors:e.pristine.errors},f.addValidator=function(e,r,t,n,i){e instanceof HTMLElement?(e.pristine.validators.push({fn:r,msg:t,priority:n,halt:i}),e.pristine.validators.sort((function(e,r){return r.priority-e.priority}))):console.warn("The parameter elem must be a dom element")},f.addError=function(e,r){(e=e.length?e[0]:e).pristine.errors.push(r),d(e.pristine)},f.reset=function(){for(var e=0;f.fields[e];e++)f.fields[e].errorElements=null;Array.from(f.form.querySelectorAll(".pristine-error")).map((function(e){e.parentNode.removeChild(e)})),Array.from(f.form.querySelectorAll("."+f.config.classTo)).map((function(e){e.classList.remove(f.config.successClass),e.classList.remove(f.config.errorClass)}))},f.destroy=function(){f.reset(),f.fields.forEach((function(e){delete e.input.pristine})),f.fields=[]},f.setGlobalConfig=function(e){n=e},f}return u("text",{fn:function(e){return!0},priority:0}),u("required",{fn:function(e){return"radio"===this.type||"checkbox"===this.type?t(this):void 0!==e&&""!==e},priority:99,halt:!0}),u("email",{fn:function(e){return!e||s.test(e)}}),u("number",{fn:function(e){return!e||!isNaN(parseFloat(e))},priority:2}),u("integer",{fn:function(e){return!e||/^\d+$/.test(e)}}),u("minlength",{fn:function(e,r){return!e||e.length>=parseInt(r)}}),u("maxlength",{fn:function(e,r){return!e||e.length<=parseInt(r)}}),u("min",{fn:function(e,r){return!e||("checkbox"===this.type?t(this)>=parseInt(r):parseFloat(e)>=parseFloat(r))}}),u("max",{fn:function(e,r){return!e||("checkbox"===this.type?t(this)<=parseInt(r):parseFloat(e)<=parseFloat(r))}}),u("pattern",{fn:function(e,r){var t=r.match(new RegExp("^/(.*?)/([gimy]*)$"));return!e||new RegExp(t[1],t[2]).test(e)}}),u("equals",{fn:function(e,r){var t=document.querySelector(r);return t&&(!e&&!t.value||t.value===e)}}),f.addValidator=function(e,r,t,n,i){u(e,{fn:r,msg:t,priority:n,halt:i})},f.addMessages=function(r,t){var n=e.hasOwnProperty(r)?e[r]:e[r]={};Object.keys(t).forEach((function(e,r){n[e]=t[e]}))},f.setLocale=function(e){o=e},f}));
8537;
8538/*! @license DOMPurify | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.2.2/LICENSE */
8539!(function (e, t) {
8540  "object" == typeof exports && "undefined" != typeof module
8541    ? (module.exports = t())
8542    : "function" == typeof define && define.amd
8543    ? define(t)
8544    : ((e = e || self).DOMPurify = t());
8545})(this, function () {
8546  "use strict";
8547  var e = Object.hasOwnProperty,
8548    t = Object.setPrototypeOf,
8549    n = Object.isFrozen,
8550    r = Object.freeze,
8551    o = Object.seal,
8552    i = Object.create,
8553    a = "undefined" != typeof Reflect && Reflect,
8554    l = a.apply,
8555    c = a.construct;
8556  l ||
8557    (l = function (e, t, n) {
8558      return e.apply(t, n);
8559    }),
8560    r ||
8561      (r = function (e) {
8562        return e;
8563      }),
8564    o ||
8565      (o = function (e) {
8566        return e;
8567      }),
8568    c ||
8569      (c = function (e, t) {
8570        return new (Function.prototype.bind.apply(
8571          e,
8572          [null].concat(
8573            (function (e) {
8574              if (Array.isArray(e)) {
8575                for (var t = 0, n = Array(e.length); t < e.length; t++)
8576                  n[t] = e[t];
8577                return n;
8578              }
8579              return Array.from(e);
8580            })(t)
8581          )
8582        ))();
8583      });
8584  var s,
8585    u = T(Array.prototype.forEach),
8586    d = T(Array.prototype.pop),
8587    f = T(Array.prototype.push),
8588    p = T(String.prototype.toLowerCase),
8589    m = T(String.prototype.match),
8590    y = T(String.prototype.replace),
8591    h = T(String.prototype.indexOf),
8592    g = T(String.prototype.trim),
8593    v = T(RegExp.prototype.test),
8594    b =
8595      ((s = TypeError),
8596      function () {
8597        for (var e = arguments.length, t = Array(e), n = 0; n < e; n++)
8598          t[n] = arguments[n];
8599        return c(s, t);
8600      });
8601  function T(e) {
8602    return function (t) {
8603      for (
8604        var n = arguments.length, r = Array(n > 1 ? n - 1 : 0), o = 1;
8605        o < n;
8606        o++
8607      )
8608        r[o - 1] = arguments[o];
8609      return l(e, t, r);
8610    };
8611  }
8612  function A(e, r) {
8613    t && t(e, null);
8614    for (var o = r.length; o--; ) {
8615      var i = r[o];
8616      if ("string" == typeof i) {
8617        var a = p(i);
8618        a !== i && (n(r) || (r[o] = a), (i = a));
8619      }
8620      e[i] = !0;
8621    }
8622    return e;
8623  }
8624  function x(t) {
8625    var n = i(null),
8626      r = void 0;
8627    for (r in t) l(e, t, [r]) && (n[r] = t[r]);
8628    return n;
8629  }
8630  var S = r([
8631      "a",
8632      "abbr",
8633      "acronym",
8634      "address",
8635      "area",
8636      "article",
8637      "aside",
8638      "audio",
8639      "b",
8640      "bdi",
8641      "bdo",
8642      "big",
8643      "blink",
8644      "blockquote",
8645      "body",
8646      "br",
8647      "button",
8648      "canvas",
8649      "caption",
8650      "center",
8651      "cite",
8652      "code",
8653      "col",
8654      "colgroup",
8655      "content",
8656      "data",
8657      "datalist",
8658      "dd",
8659      "decorator",
8660      "del",
8661      "details",
8662      "dfn",
8663      "dialog",
8664      "dir",
8665      "div",
8666      "dl",
8667      "dt",
8668      "element",
8669      "em",
8670      "fieldset",
8671      "figcaption",
8672      "figure",
8673      "font",
8674      "footer",
8675      "form",
8676      "h1",
8677      "h2",
8678      "h3",
8679      "h4",
8680      "h5",
8681      "h6",
8682      "head",
8683      "header",
8684      "hgroup",
8685      "hr",
8686      "html",
8687      "i",
8688      "img",
8689      "input",
8690      "ins",
8691      "kbd",
8692      "label",
8693      "legend",
8694      "li",
8695      "main",
8696      "map",
8697      "mark",
8698      "marquee",
8699      "menu",
8700      "menuitem",
8701      "meter",
8702      "nav",
8703      "nobr",
8704      "ol",
8705      "optgroup",
8706      "option",
8707      "output",
8708      "p",
8709      "picture",
8710      "pre",
8711      "progress",
8712      "q",
8713      "rp",
8714      "rt",
8715      "ruby",
8716      "s",
8717      "samp",
8718      "section",
8719      "select",
8720      "shadow",
8721      "small",
8722      "source",
8723      "spacer",
8724      "span",
8725      "strike",
8726      "strong",
8727      "style",
8728      "sub",
8729      "summary",
8730      "sup",
8731      "table",
8732      "tbody",
8733      "td",
8734      "template",
8735      "textarea",
8736      "tfoot",
8737      "th",
8738      "thead",
8739      "time",
8740      "tr",
8741      "track",
8742      "tt",
8743      "u",
8744      "ul",
8745      "var",
8746      "video",
8747      "wbr",
8748    ]),
8749    k = r([
8750      "svg",
8751      "a",
8752      "altglyph",
8753      "altglyphdef",
8754      "altglyphitem",
8755      "animatecolor",
8756      "animatemotion",
8757      "animatetransform",
8758      "audio",
8759      "canvas",
8760      "circle",
8761      "clippath",
8762      "defs",
8763      "desc",
8764      "ellipse",
8765      "filter",
8766      "font",
8767      "g",
8768      "glyph",
8769      "glyphref",
8770      "hkern",
8771      "image",
8772      "line",
8773      "lineargradient",
8774      "marker",
8775      "mask",
8776      "metadata",
8777      "mpath",
8778      "path",
8779      "pattern",
8780      "polygon",
8781      "polyline",
8782      "radialgradient",
8783      "rect",
8784      "stop",
8785      "style",
8786      "switch",
8787      "symbol",
8788      "text",
8789      "textpath",
8790      "title",
8791      "tref",
8792      "tspan",
8793      "video",
8794      "view",
8795      "vkern",
8796    ]),
8797    _ = r([
8798      "feBlend",
8799      "feColorMatrix",
8800      "feComponentTransfer",
8801      "feComposite",
8802      "feConvolveMatrix",
8803      "feDiffuseLighting",
8804      "feDisplacementMap",
8805      "feDistantLight",
8806      "feFlood",
8807      "feFuncA",
8808      "feFuncB",
8809      "feFuncG",
8810      "feFuncR",
8811      "feGaussianBlur",
8812      "feMerge",
8813      "feMergeNode",
8814      "feMorphology",
8815      "feOffset",
8816      "fePointLight",
8817      "feSpecularLighting",
8818      "feSpotLight",
8819      "feTile",
8820      "feTurbulence",
8821    ]),
8822    D = r([
8823      "math",
8824      "menclose",
8825      "merror",
8826      "mfenced",
8827      "mfrac",
8828      "mglyph",
8829      "mi",
8830      "mlabeledtr",
8831      "mmultiscripts",
8832      "mn",
8833      "mo",
8834      "mover",
8835      "mpadded",
8836      "mphantom",
8837      "mroot",
8838      "mrow",
8839      "ms",
8840      "mspace",
8841      "msqrt",
8842      "mstyle",
8843      "msub",
8844      "msup",
8845      "msubsup",
8846      "mtable",
8847      "mtd",
8848      "mtext",
8849      "mtr",
8850      "munder",
8851      "munderover",
8852    ]),
8853    E = r(["#text"]),
8854    L = r([
8855      "accept",
8856      "action",
8857      "align",
8858      "alt",
8859      "autocapitalize",
8860      "autocomplete",
8861      "autopictureinpicture",
8862      "autoplay",
8863      "background",
8864      "bgcolor",
8865      "border",
8866      "capture",
8867      "cellpadding",
8868      "cellspacing",
8869      "checked",
8870      "cite",
8871      "class",
8872      "clear",
8873      "color",
8874      "cols",
8875      "colspan",
8876      "controls",
8877      "controlslist",
8878      "coords",
8879      "crossorigin",
8880      "datetime",
8881      "decoding",
8882      "default",
8883      "dir",
8884      "disabled",
8885      "disablepictureinpicture",
8886      "disableremoteplayback",
8887      "download",
8888      "draggable",
8889      "enctype",
8890      "enterkeyhint",
8891      "face",
8892      "for",
8893      "headers",
8894      "height",
8895      "hidden",
8896      "high",
8897      "href",
8898      "hreflang",
8899      "id",
8900      "inputmode",
8901      "integrity",
8902      "ismap",
8903      "kind",
8904      "label",
8905      "lang",
8906      "list",
8907      "loading",
8908      "loop",
8909      "low",
8910      "max",
8911      "maxlength",
8912      "media",
8913      "method",
8914      "min",
8915      "minlength",
8916      "multiple",
8917      "muted",
8918      "name",
8919      "noshade",
8920      "novalidate",
8921      "nowrap",
8922      "open",
8923      "optimum",
8924      "pattern",
8925      "placeholder",
8926      "playsinline",
8927      "poster",
8928      "preload",
8929      "pubdate",
8930      "radiogroup",
8931      "readonly",
8932      "rel",
8933      "required",
8934      "rev",
8935      "reversed",
8936      "role",
8937      "rows",
8938      "rowspan",
8939      "spellcheck",
8940      "scope",
8941      "selected",
8942      "shape",
8943      "size",
8944      "sizes",
8945      "span",
8946      "srclang",
8947      "start",
8948      "src",
8949      "srcset",
8950      "step",
8951      "style",
8952      "summary",
8953      "tabindex",
8954      "title",
8955      "translate",
8956      "type",
8957      "usemap",
8958      "valign",
8959      "value",
8960      "width",
8961      "xmlns",
8962    ]),
8963    w = r([
8964      "accent-height",
8965      "accumulate",
8966      "additive",
8967      "alignment-baseline",
8968      "ascent",
8969      "attributename",
8970      "attributetype",
8971      "azimuth",
8972      "basefrequency",
8973      "baseline-shift",
8974      "begin",
8975      "bias",
8976      "by",
8977      "class",
8978      "clip",
8979      "clippathunits",
8980      "clip-path",
8981      "clip-rule",
8982      "color",
8983      "color-interpolation",
8984      "color-interpolation-filters",
8985      "color-profile",
8986      "color-rendering",
8987      "cx",
8988      "cy",
8989      "d",
8990      "dx",
8991      "dy",
8992      "diffuseconstant",
8993      "direction",
8994      "display",
8995      "divisor",
8996      "dur",
8997      "edgemode",
8998      "elevation",
8999      "end",
9000      "fill",
9001      "fill-opacity",
9002      "fill-rule",
9003      "filter",
9004      "filterunits",
9005      "flood-color",
9006      "flood-opacity",
9007      "font-family",
9008      "font-size",
9009      "font-size-adjust",
9010      "font-stretch",
9011      "font-style",
9012      "font-variant",
9013      "font-weight",
9014      "fx",
9015      "fy",
9016      "g1",
9017      "g2",
9018      "glyph-name",
9019      "glyphref",
9020      "gradientunits",
9021      "gradienttransform",
9022      "height",
9023      "href",
9024      "id",
9025      "image-rendering",
9026      "in",
9027      "in2",
9028      "k",
9029      "k1",
9030      "k2",
9031      "k3",
9032      "k4",
9033      "kerning",
9034      "keypoints",
9035      "keysplines",
9036      "keytimes",
9037      "lang",
9038      "lengthadjust",
9039      "letter-spacing",
9040      "kernelmatrix",
9041      "kernelunitlength",
9042      "lighting-color",
9043      "local",
9044      "marker-end",
9045      "marker-mid",
9046      "marker-start",
9047      "markerheight",
9048      "markerunits",
9049      "markerwidth",
9050      "maskcontentunits",
9051      "maskunits",
9052      "max",
9053      "mask",
9054      "media",
9055      "method",
9056      "mode",
9057      "min",
9058      "name",
9059      "numoctaves",
9060      "offset",
9061      "operator",
9062      "opacity",
9063      "order",
9064      "orient",
9065      "orientation",
9066      "origin",
9067      "overflow",
9068      "paint-order",
9069      "path",
9070      "pathlength",
9071      "patterncontentunits",
9072      "patterntransform",
9073      "patternunits",
9074      "points",
9075      "preservealpha",
9076      "preserveaspectratio",
9077      "primitiveunits",
9078      "r",
9079      "rx",
9080      "ry",
9081      "radius",
9082      "refx",
9083      "refy",
9084      "repeatcount",
9085      "repeatdur",
9086      "restart",
9087      "result",
9088      "rotate",
9089      "scale",
9090      "seed",
9091      "shape-rendering",
9092      "specularconstant",
9093      "specularexponent",
9094      "spreadmethod",
9095      "startoffset",
9096      "stddeviation",
9097      "stitchtiles",
9098      "stop-color",
9099      "stop-opacity",
9100      "stroke-dasharray",
9101      "stroke-dashoffset",
9102      "stroke-linecap",
9103      "stroke-linejoin",
9104      "stroke-miterlimit",
9105      "stroke-opacity",
9106      "stroke",
9107      "stroke-width",
9108      "style",
9109      "surfacescale",
9110      "systemlanguage",
9111      "tabindex",
9112      "targetx",
9113      "targety",
9114      "transform",
9115      "text-anchor",
9116      "text-decoration",
9117      "text-rendering",
9118      "textlength",
9119      "type",
9120      "u1",
9121      "u2",
9122      "unicode",
9123      "values",
9124      "viewbox",
9125      "visibility",
9126      "version",
9127      "vert-adv-y",
9128      "vert-origin-x",
9129      "vert-origin-y",
9130      "width",
9131      "word-spacing",
9132      "wrap",
9133      "writing-mode",
9134      "xchannelselector",
9135      "ychannelselector",
9136      "x",
9137      "x1",
9138      "x2",
9139      "xmlns",
9140      "y",
9141      "y1",
9142      "y2",
9143      "z",
9144      "zoomandpan",
9145    ]),
9146    M = r([
9147      "accent",
9148      "accentunder",
9149      "align",
9150      "bevelled",
9151      "close",
9152      "columnsalign",
9153      "columnlines",
9154      "columnspan",
9155      "denomalign",
9156      "depth",
9157      "dir",
9158      "display",
9159      "displaystyle",
9160      "encoding",
9161      "fence",
9162      "frame",
9163      "height",
9164      "href",
9165      "id",
9166      "largeop",
9167      "length",
9168      "linethickness",
9169      "lspace",
9170      "lquote",
9171      "mathbackground",
9172      "mathcolor",
9173      "mathsize",
9174      "mathvariant",
9175      "maxsize",
9176      "minsize",
9177      "movablelimits",
9178      "notation",
9179      "numalign",
9180      "open",
9181      "rowalign",
9182      "rowlines",
9183      "rowspacing",
9184      "rowspan",
9185      "rspace",
9186      "rquote",
9187      "scriptlevel",
9188      "scriptminsize",
9189      "scriptsizemultiplier",
9190      "selection",
9191      "separator",
9192      "separators",
9193      "stretchy",
9194      "subscriptshift",
9195      "supscriptshift",
9196      "symmetric",
9197      "voffset",
9198      "width",
9199      "xmlns",
9200    ]),
9201    O = r(["xlink:href", "xml:id", "xlink:title", "xml:space", "xmlns:xlink"]),
9202    N = o(/\{\{[\s\S]*|[\s\S]*\}\}/gm),
9203    R = o(/<%[\s\S]*|[\s\S]*%>/gm),
9204    F = o(/^data-[\-\w.\u00B7-\uFFFF]/),
9205    C = o(/^aria-[\-\w]+$/),
9206    H = o(
9207      /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i
9208    ),
9209    z = o(/^(?:\w+script|data):/i),
9210    I = o(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),
9211    j =
9212      "function" == typeof Symbol && "symbol" == typeof Symbol.iterator
9213        ? function (e) {
9214            return typeof e;
9215          }
9216        : function (e) {
9217            return e &&
9218              "function" == typeof Symbol &&
9219              e.constructor === Symbol &&
9220              e !== Symbol.prototype
9221              ? "symbol"
9222              : typeof e;
9223          };
9224  function U(e) {
9225    if (Array.isArray(e)) {
9226      for (var t = 0, n = Array(e.length); t < e.length; t++) n[t] = e[t];
9227      return n;
9228    }
9229    return Array.from(e);
9230  }
9231  var P = function () {
9232      return "undefined" == typeof window ? null : window;
9233    },
9234    W = function (e, t) {
9235      if (
9236        "object" !== (void 0 === e ? "undefined" : j(e)) ||
9237        "function" != typeof e.createPolicy
9238      )
9239        return null;
9240      var n = null;
9241      t.currentScript &&
9242        t.currentScript.hasAttribute("data-tt-policy-suffix") &&
9243        (n = t.currentScript.getAttribute("data-tt-policy-suffix"));
9244      var r = "dompurify" + (n ? "#" + n : "");
9245      try {
9246        return e.createPolicy(r, {
9247          createHTML: function (e) {
9248            return e;
9249          },
9250        });
9251      } catch (e) {
9252        return (
9253          console.warn("TrustedTypes policy " + r + " could not be created."),
9254          null
9255        );
9256      }
9257    };
9258  return (function e() {
9259    var t =
9260        arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : P(),
9261      n = function (t) {
9262        return e(t);
9263      };
9264    if (
9265      ((n.version = "2.2.2"),
9266      (n.removed = []),
9267      !t || !t.document || 9 !== t.document.nodeType)
9268    )
9269      return (n.isSupported = !1), n;
9270    var o = t.document,
9271      i = t.document,
9272      a = t.DocumentFragment,
9273      l = t.HTMLTemplateElement,
9274      c = t.Node,
9275      s = t.NodeFilter,
9276      T = t.NamedNodeMap,
9277      B = void 0 === T ? t.NamedNodeMap || t.MozNamedAttrMap : T,
9278      G = t.Text,
9279      q = t.Comment,
9280      K = t.DOMParser,
9281      V = t.trustedTypes;
9282    if ("function" == typeof l) {
9283      var Y = i.createElement("template");
9284      Y.content && Y.content.ownerDocument && (i = Y.content.ownerDocument);
9285    }
9286    var X = W(V, o),
9287      $ = X && Le ? X.createHTML("") : "",
9288      Z = i,
9289      J = Z.implementation,
9290      Q = Z.createNodeIterator,
9291      ee = Z.getElementsByTagName,
9292      te = Z.createDocumentFragment,
9293      ne = o.importNode,
9294      re = {};
9295    try {
9296      re = x(i).documentMode ? i.documentMode : {};
9297    } catch (e) {}
9298    var oe = {};
9299    n.isSupported = J && void 0 !== J.createHTMLDocument && 9 !== re;
9300    var ie = N,
9301      ae = R,
9302      le = F,
9303      ce = C,
9304      se = z,
9305      ue = I,
9306      de = H,
9307      fe = null,
9308      pe = A({}, [].concat(U(S), U(k), U(_), U(D), U(E))),
9309      me = null,
9310      ye = A({}, [].concat(U(L), U(w), U(M), U(O))),
9311      he = null,
9312      ge = null,
9313      ve = !0,
9314      be = !0,
9315      Te = !1,
9316      Ae = !1,
9317      xe = !1,
9318      Se = !1,
9319      ke = !1,
9320      _e = !1,
9321      De = !1,
9322      Ee = !0,
9323      Le = !1,
9324      we = !0,
9325      Me = !0,
9326      Oe = !1,
9327      Ne = {},
9328      Re = A({}, [
9329        "annotation-xml",
9330        "audio",
9331        "colgroup",
9332        "desc",
9333        "foreignobject",
9334        "head",
9335        "iframe",
9336        "math",
9337        "mi",
9338        "mn",
9339        "mo",
9340        "ms",
9341        "mtext",
9342        "noembed",
9343        "noframes",
9344        "plaintext",
9345        "script",
9346        "style",
9347        "svg",
9348        "template",
9349        "thead",
9350        "title",
9351        "video",
9352        "xmp",
9353      ]),
9354      Fe = null,
9355      Ce = A({}, ["audio", "video", "img", "source", "image", "track"]),
9356      He = null,
9357      ze = A({}, [
9358        "alt",
9359        "class",
9360        "for",
9361        "id",
9362        "label",
9363        "name",
9364        "pattern",
9365        "placeholder",
9366        "summary",
9367        "title",
9368        "value",
9369        "style",
9370        "xmlns",
9371      ]),
9372      Ie = null,
9373      je = i.createElement("form"),
9374      Ue = function (e) {
9375        (Ie && Ie === e) ||
9376          ((e && "object" === (void 0 === e ? "undefined" : j(e))) || (e = {}),
9377          (e = x(e)),
9378          (fe = "ALLOWED_TAGS" in e ? A({}, e.ALLOWED_TAGS) : pe),
9379          (me = "ALLOWED_ATTR" in e ? A({}, e.ALLOWED_ATTR) : ye),
9380          (He = "ADD_URI_SAFE_ATTR" in e ? A(x(ze), e.ADD_URI_SAFE_ATTR) : ze),
9381          (Fe = "ADD_DATA_URI_TAGS" in e ? A(x(Ce), e.ADD_DATA_URI_TAGS) : Ce),
9382          (he = "FORBID_TAGS" in e ? A({}, e.FORBID_TAGS) : {}),
9383          (ge = "FORBID_ATTR" in e ? A({}, e.FORBID_ATTR) : {}),
9384          (Ne = "USE_PROFILES" in e && e.USE_PROFILES),
9385          (ve = !1 !== e.ALLOW_ARIA_ATTR),
9386          (be = !1 !== e.ALLOW_DATA_ATTR),
9387          (Te = e.ALLOW_UNKNOWN_PROTOCOLS || !1),
9388          (Ae = e.SAFE_FOR_TEMPLATES || !1),
9389          (xe = e.WHOLE_DOCUMENT || !1),
9390          (_e = e.RETURN_DOM || !1),
9391          (De = e.RETURN_DOM_FRAGMENT || !1),
9392          (Ee = !1 !== e.RETURN_DOM_IMPORT),
9393          (Le = e.RETURN_TRUSTED_TYPE || !1),
9394          (ke = e.FORCE_BODY || !1),
9395          (we = !1 !== e.SANITIZE_DOM),
9396          (Me = !1 !== e.KEEP_CONTENT),
9397          (Oe = e.IN_PLACE || !1),
9398          (de = e.ALLOWED_URI_REGEXP || de),
9399          Ae && (be = !1),
9400          De && (_e = !0),
9401          Ne &&
9402            ((fe = A({}
9402, [].concat(U(E)))),
9403            (me = []),
9404            !0 === Ne.html && (A(fe, S), A(me, L)),
9405            !0 === Ne.svg && (A(fe, k), A(me, w), A(me, O)),
9406            !0 === Ne.svgFilters && (A(fe, _), A(me, w), A(me, O)),
9407            !0 === Ne.mathMl && (A(fe, D), A(me, M), A(me, O))),
9408          e.ADD_TAGS && (fe === pe && (fe = x(fe)), A(fe, e.ADD_TAGS)),
9409          e.ADD_ATTR && (me === ye && (me = x(me)), A(me, e.ADD_ATTR)),
9410          e.ADD_URI_SAFE_ATTR && A(He, e.ADD_URI_SAFE_ATTR),
9411          Me && (fe["#text"] = !0),
9412          xe && A(fe, ["html", "head", "body"]),
9413          fe.table && (A(fe, ["tbody"]), delete he.tbody),
9414          r && r(e),
9415          (Ie = e));
9416      },
9417      Pe = function (e) {
9418        f(n.removed, { element: e });
9419        try {
9420          e.parentNode.removeChild(e);
9421        } catch (t) {
9422          e.outerHTML = $;
9423        }
9424      },
9425      We = function (e, t) {
9426        try {
9427          f(n.removed, { attribute: t.getAttributeNode(e), from: t });
9428        } catch (e) {
9429          f(n.removed, { attribute: null, from: t });
9430        }
9431        t.removeAttribute(e);
9432      },
9433      Be = function (e) {
9434        var t = void 0,
9435          n = void 0;
9436        if (ke) e = "<remove></remove>" + e;
9437        else {
9438          var r = m(e, /^[\r\n\t ]+/);
9439          n = r && r[0];
9440        }
9441        var o = X ? X.createHTML(e) : e;
9442        try {
9443          t = new K().parseFromString(o, "text/html");
9444        } catch (e) {}
9445        if (!t || !t.documentElement) {
9446          var a = (t = J.createHTMLDocument("")).body;
9447          a.parentNode.removeChild(a.parentNode.firstElementChild),
9448            (a.outerHTML = o);
9449        }
9450        return (
9451          e &&
9452            n &&
9453            t.body.insertBefore(
9454              i.createTextNode(n),
9455              t.body.childNodes[0] || null
9456            ),
9457          ee.call(t, xe ? "html" : "body")[0]
9458        );
9459      },
9460      Ge = function (e) {
9461        return Q.call(
9462          e.ownerDocument || e,
9463          e,
9464          s.SHOW_ELEMENT | s.SHOW_COMMENT | s.SHOW_TEXT,
9465          function () {
9466            return s.FILTER_ACCEPT;
9467          },
9468          !1
9469        );
9470      },
9471      qe = function (e) {
9472        return (
9473          !(e instanceof G || e instanceof q) &&
9474          !(
9475            "string" == typeof e.nodeName &&
9476            "string" == typeof e.textContent &&
9477            "function" == typeof e.removeChild &&
9478            e.attributes instanceof B &&
9479            "function" == typeof e.removeAttribute &&
9480            "function" == typeof e.setAttribute &&
9481            "string" == typeof e.namespaceURI
9482          )
9483        );
9484      },
9485      Ke = function (e) {
9486        return "object" === (void 0 === c ? "undefined" : j(c))
9487          ? e instanceof c
9488          : e &&
9489              "object" === (void 0 === e ? "undefined" : j(e)) &&
9490              "number" == typeof e.nodeType &&
9491              "string" == typeof e.nodeName;
9492      },
9493      Ve = function (e, t, r) {
9494        oe[e] &&
9495          u(oe[e], function (e) {
9496            e.call(n, t, r, Ie);
9497          });
9498      },
9499      Ye = function (e) {
9500        var t = void 0;
9501        if ((Ve("beforeSanitizeElements", e, null), qe(e))) return Pe(e), !0;
9502        if (m(e.nodeName, /[\u0080-\uFFFF]/)) return Pe(e), !0;
9503        var r = p(e.nodeName);
9504        if (
9505          (Ve("uponSanitizeElement", e, { tagName: r, allowedTags: fe }),
9506          ("svg" === r || "math" === r) &&
9507            0 !== e.querySelectorAll("p, br, form, table").length)
9508        )
9509          return Pe(e), !0;
9510        if (
9511          !Ke(e.firstElementChild) &&
9512          (!Ke(e.content) || !Ke(e.content.firstElementChild)) &&
9513          v(/<[!/\w]/g, e.innerHTML) &&
9514          v(/<[!/\w]/g, e.textContent)
9515        )
9516          return Pe(e), !0;
9517        if (!fe[r] || he[r]) {
9518          if (Me && !Re[r] && "function" == typeof e.insertAdjacentHTML)
9519            try {
9520              var o = e.innerHTML;
9521              e.insertAdjacentHTML("AfterEnd", X ? X.createHTML(o) : o);
9522            } catch (e) {}
9523          return Pe(e), !0;
9524        }
9525        return ("noscript" !== r && "noembed" !== r) ||
9526          !v(/<\/no(script|embed)/i, e.innerHTML)
9527          ? (Ae &&
9528              3 === e.nodeType &&
9529              ((t = e.textContent),
9530              (t = y(t, ie, " ")),
9531              (t = y(t, ae, " ")),
9532              e.textContent !== t &&
9533                (f(n.removed, { element: e.cloneNode() }),
9534                (e.textContent = t))),
9535            Ve("afterSanitizeElements", e, null),
9536            !1)
9537          : (Pe(e), !0);
9538      },
9539      Xe = function (e, t, n) {
9540        if (we && ("id" === t || "name" === t) && (n in i || n in je))
9541          return !1;
9542        if (be && v(le, t));
9543        else if (ve && v(ce, t));
9544        else {
9545          if (!me[t] || ge[t]) return !1;
9546          if (He[t]);
9547          else if (v(de, y(n, ue, "")));
9548          else if (
9549            ("src" !== t && "xlink:href" !== t && "href" !== t) ||
9550            "script" === e ||
9551            0 !== h(n, "data:") ||
9552            !Fe[e]
9553          ) {
9554            if (Te && !v(se, y(n, ue, "")));
9555            else if (n) return !1;
9556          } else;
9557        }
9558        return !0;
9559      },
9560      $e = function (e) {
9561        var t = void 0,
9562          r = void 0,
9563          o = void 0,
9564          i = void 0;
9565        Ve("beforeSanitizeAttributes", e, null);
9566        var a = e.attributes;
9567        if (a) {
9568          var l = {
9569            attrName: "",
9570            attrValue: "",
9571            keepAttr: !0,
9572            allowedAttributes: me,
9573          };
9574          for (i = a.length; i--; ) {
9575            var c = (t = a[i]),
9576              s = c.name,
9577              u = c.namespaceURI;
9578            if (
9579              ((r = g(t.value)),
9580              (o = p(s)),
9581              (l.attrName = o),
9582              (l.attrValue = r),
9583              (l.keepAttr = !0),
9584              (l.forceKeepAttr = void 0),
9585              Ve("uponSanitizeAttribute", e, l),
9586              (r = l.attrValue),
9587              !l.forceKeepAttr && (We(s, e), l.keepAttr))
9588            )
9589              if (v(/\/>/i, r)) We(s, e);
9590              else {
9591                Ae && ((r = y(r, ie, " ")), (r = y(r, ae, " ")));
9592                var f = e.nodeName.toLowerCase();
9593                if (Xe(f, o, r))
9594                  try {
9595                    u ? e.setAttributeNS(u, s, r) : e.setAttribute(s, r),
9596                      d(n.removed);
9597                  } catch (e) {}
9598              }
9599          }
9600          Ve("afterSanitizeAttributes", e, null);
9601        }
9602      },
9603      Ze = function e(t) {
9604        var n = void 0,
9605          r = Ge(t);
9606        for (Ve("beforeSanitizeShadowDOM", t, null); (n = r.nextNode()); )
9607          Ve("uponSanitizeShadowNode", n, null),
9608            Ye(n) || (n.content instanceof a && e(n.content), $e(n));
9609        Ve("afterSanitizeShadowDOM", t, null);
9610      };
9611    return (
9612      (n.sanitize = function (e, r) {
9613        var i = void 0,
9614          l = void 0,
9615          s = void 0,
9616          u = void 0,
9617          d = void 0;
9618        if ((e || (e = "\x3c!--\x3e"), "string" != typeof e && !Ke(e))) {
9619          if ("function" != typeof e.toString)
9620            throw b("toString is not a function");
9621          if ("string" != typeof (e = e.toString()))
9622            throw b("dirty is not a string, aborting");
9623        }
9624        if (!n.isSupported) {
9625          if (
9626            "object" === j(t.toStaticHTML) ||
9627            "function" == typeof t.toStaticHTML
9628          ) {
9629            if ("string" == typeof e) return t.toStaticHTML(e);
9630            if (Ke(e)) return t.toStaticHTML(e.outerHTML);
9631          }
9632          return e;
9633        }
9634        if (
9635          (Se || Ue(r), (n.removed = []), "string" == typeof e && (Oe = !1), Oe)
9636        );
9637        else if (e instanceof c)
9638          (1 ===
9639            (l = (i = Be("\x3c!----\x3e")).ownerDocument.importNode(e, !0))
9640              .nodeType &&
9641            "BODY" === l.nodeName) ||
9642          "HTML" === l.nodeName
9643            ? (i = l)
9644            : i.appendChild(l);
9645        else {
9646          if (!_e && !Ae && !xe && -1 === e.indexOf("<"))
9647            return X && Le ? X.createHTML(e) : e;
9648          if (!(i = Be(e))) return _e ? null : $;
9649        }
9650        i && ke && Pe(i.firstChild);
9651        for (var f = Ge(Oe ? e : i); (s = f.nextNode()); )
9652          (3 === s.nodeType && s === u) ||
9653            Ye(s) ||
9654            (s.content instanceof a && Ze(s.content), $e(s), (u = s));
9655        if (((u = null), Oe)) return e;
9656        if (_e) {
9657          if (De)
9658            for (d = te.call(i.ownerDocument); i.firstChild; )
9659              d.appendChild(i.firstChild);
9660          else d = i;
9661          return Ee && (d = ne.call(o, d, !0)), d;
9662        }
9663        var p = xe ? i.outerHTML : i.innerHTML;
9664        return (
9665          Ae && ((p = y(p, ie, " ")), (p = y(p, ae, " "))),
9666          X && Le ? X.createHTML(p) : p
9667        );
9668      }),
9669      (n.setConfig = function (e) {
9670        Ue(e), (Se = !0);
9671      }),
9672      (n.clearConfig = function () {
9673        (Ie = null), (Se = !1);
9674      }),
9675      (n.isValidAttribute = function (e, t, n) {
9676        Ie || Ue({});
9677        var r = p(e),
9678          o = p(t);
9679        return Xe(r, o, n);
9680      }),
9681      (n.addHook = function (e, t) {
9682        "function" == typeof t && ((oe[e] = oe[e] || []), f(oe[e], t));
9683      }),
9684      (n.removeHook = function (e) {
9685        oe[e] && d(oe[e]);
9686      }),
9687      (n.removeHooks = function (e) {
9688        oe[e] && (oe[e] = []);
9689      }),
9690      (n.removeAllHooks = function () {
9691        oe = {};
9692      }),
9693      n
9694    );
9695  })();
9696});
9697;
9698; (function ($) {
9699
9700    window.vt = window.vt || {};
9701
9702    vt.initFormTools = (function () {
9703
9704        // Get parameters from the URL
9705        function getParameterByName(name, url) {
9706            if (!url) url = window.location.href;
9707            name = name.replace(/[\[\]]/g, "\\$&");
9708            var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
9709                results = regex.exec(url);
9710            if (!results) return null;
9711            if (!results[2]) return '';
9712            return decodeURIComponent(results[2].replace(/\+/g, " "));
9713        }
9714
9715        // initiate validate plugin...
9716
9717        if ($('body').is('.pageedit'))
9718            return;
9719
9720        // var form = $(elem),
9721        //     validate = form.find('.js-validate');
9722
9723        // validate inputs fields on submit
9724        var validateInitiate = function (e, form) {
9725            var element = e.target,
9726                $element = $(element),
9727                validate = form.find('.js-validate'),
9728
9729                item = {},
9730                jsonObj;
9731
9732            // check if request needs a token
9733            if (form.hasClass('js-token')) {
9734                var token = getParameterByName('token');
9735                if (token !== null && token !== '') {
9736                    item['token'] = token;
9737                }
9738            }
9739
9740            // on submit if js-repeat input field is empty
9741            $.each(form.find('.js-repeat'), function (index, repeats) {
9742                var input = $(repeats).find('input');
9743                vt.initFormTools.repeatValidate(input[0], form);
9744            });
9745
9746            // scroll to repeat error
9747            if (form.find('.ui-repeat-error').length > 0) {
9748                e.stopImmediatePropagation();
9749
9750                $('html, body').animate({
9751                    scrollTop: $(".ui-repeat-error").offset().top - 200
9752                }, 1000);
9753
9754                return;
9755            }
9756
9757            // scroll to phone and altphone error
9758            if (form.find('.invalid').length > 0) {
9759                e.stopImmediatePropagation();
9760
9761                $('html, body').animate({
9762                    scrollTop: $(".invalid").offset().top - 200
9763                }, 1000);
9764
9765                return;
9766            }
9767
9768            // get every elements value and error message
9769            $.each(validate, function (index, elm) {
9770                var inputsName = $(elm).find('input[type="text"], input[type="password"],input[type="hidden"], input[type="tel"], textarea, select').attr('name');
9771                var inputsValue = $(elm).find('input[type="text"], input[type="password"],input[type="hidden"], input[type="tel"], textarea, select').val();
9772                var radioName = $(elm).find('input[type="radio"]:checked').attr('name');
9773                var radioValue = $(elm).find('input[type="radio"]:checked').attr('value');
9774
9775                // check if its have a converted value
9776                if ($(elm).attr('data-js-initialize') && $(elm).find('input').attr('data-convertedData')) {
9777                    var convertedData = $(elm).find('input').attr('data-convertedData');
9778                    item[inputsName] = convertedData;
9779                } else if ($(elm).attr('data-js-select-other-values')) {
9780                    var selectText = $(elm).find('option[value="' + inputsValue + '"]').text();
9781                    item[inputsName] = selectText;
9782                    item[inputsName + "ID"] = inputsValue;
9783                }
9784                else {
9785                    item[inputsName] = inputsValue;
9786                }
9787
9788                // radio buttons
9789                if ($(elm).find('input[type="radio"]:checked').length) {
9790                    item[radioName] = radioValue;
9791                }
9792
9793                // checkboxes
9794                if ($(elm).find('input[type="checkbox"]').length) {
9795                    var array = [];
9796                    var checkboxName = $(elm).find('input[type="checkbox"]').attr('name');
9797
9798                    // if only one checkbox
9799                    if ($(elm).find('input[type="checkbox"]').length === 1) {
9800
9801                        if ($(elm).hasClass("js-use-value")) {
9802                            var checkboxValue = $(elm).find('input[type="checkbox"]')[0].value;
9803                            array.push(parseInt(checkboxValue));
9804                            item[checkboxName] = array;
9805
9806                        } else {
9807                            var checkboxValue = $(elm).find('input[type="checkbox"]')[0].checked;
9808                            item[checkboxName] = checkboxValue;
9809                        };
9810
9811                    } else {
9812                        $.each($(elm).find('input[type="checkbox"]'), function (index, elm) {
9813                            if ($(elm).hasClass("js-use-value")) {
9814                                if ($(elm)[0].checked) {
9815                                    var checkboxValue = $(elm)[0].value;
9816                                    array.push(parseInt(checkboxValue));
9817                                }
9818                            } else {
9819                                var checkboxValue = $(elm)[0].checked;
9820                                array.push(checkboxValue);
9821                            };
9822
9823
9824                        });
9825
9826                        item[checkboxName] = array;
9827                    }
9828                }
9829            });
9830
9831            // check if its a back button and instead save value to localstorage
9832            if ($element.hasClass('js-form-back')) {
9833                vt.initFormCookieData.saveStorage(item, form);
9834                window.location = e.currentTarget.href;
9835                return;
9836            }
9837
9838            vt.animations.feedbackEvaluate(form.find('.js-form-submit'));
9839
9840            if ($element.hasClass('js-form-repeating')) {
9841                var groups = [];
9842                $.each(item, function (currentItem, value) {
9843                    var index = currentItem.split("-").pop() - 1;
9844                    if (isNaN(index)) {
9845                        return;
9846                    }
9847                    var name = currentItem.substr(0, currentItem.indexOf('-')),
9848                        group = groups[index],
9849                        newSet = {};
9850
9851                    if (!group) {
9852                        group = groups[index] = {}
9853                    }
9854
9855                    group[name] = value;
9856                    group["Id"] = index + 1;
9857                });
9858
9859                groups = groups.filter(function (n) { return n != undefined });
9860
9861                item['deliverers'] = groups;
9862            }
9863
9864            // Filter function to clear out any undefined values
9865            var noUndefinedValues = function (val) {
9866                return typeof val !== 'undefined' && val !== null;
9867            }
9868
9869            // We allow users to type 24:00 - but the system stores this as 00:00
9870            var map24to00 = function (timeVal) {
9871                if (timeVal.From === '24:00') {
9872                    timeVal.From = '00:00';
9873                }
9874                if (timeVal.To === '24:00') {
9875                    timeVal.To = '00:00';
9876                }
9877                return timeVal;
9878            }
9879
9880            if ($element.hasClass('js-form-availability')) {
9881                var sections = {};
9882                $.each(item, function (currentItem, value) {
9883                    if (currentItem === "DefibrillatorId") {
9884                        sections[currentItem] = value;
9885                        return;
9886                    }
9887
9888                    var itemDetails = currentItem.split("-"),
9889                        itemSection = itemDetails[0],
9890                        itemSectionIndex = itemDetails[1],
9891                        itemName = itemDetails[2],
9892                        itemIndex = itemDetails[3];
9893
9894                    // if(isNaN(itemIndex)) {
9895                    //     return;
9896                    // }
9897                    var section = sections[itemSection];
9898
9899                    if (!section) {
9900                        section = sections[itemSection] = []
9901                    }
9902
9903                    if (itemSection === 'ClosingDays') {
9904
9905                        if (itemName === 'From' || itemName === 'To') {
9906                            var times = section[itemIndex];
9907                            if (!times) {
9908                                times = section[itemIndex] = {};
9909                            }
9910
9911                            times[itemName] = value;
9912                        }
9913                        return;
9914                    }
9915
9916                    if (itemSection === 'SpecialOpeningHours') {
9917                        var weekday = itemSectionIndex,
9918                            hourIndex = itemIndex;
9919
9920                        itemIndex = itemDetails[4] ? itemDetails[4] : "0";
9921
9922                        var sectionIndex = section[itemIndex];
9923
9924                        if (!sectionIndex) {
9925                            sectionIndex = section[itemIndex] = {};
9926                        }
9927                        //showExtraSection section is day 0
9928                        if (weekday === "0") {
9929                            sectionIndex[itemName] = value;
9930                            sectionIndex['ValidationId'] = itemIndex;
9931                            return;
9932                        }
9933
9934                        var weekdays = sectionIndex['Weekdays'];
9935
9936                        if (!weekdays) {
9937                            weekdays = sectionIndex['Weekdays'] = [];
9938                        }
9939
9940                        var weekdayIndex = weekdays[weekday - 1]; // Set to start of array as in special opening hours we have 0 as header
9941                        if (!weekdayIndex) {
9942                            weekdayIndex = weekdays[weekday - 1] = {};
9943                        }
9944                        weekdayIndex['typeId'] = parseInt(weekday);
9945                        if (itemName === 'Status') {
9946                            weekdayIndex['Status'] = value;
9947                            return;
9948                        }
9949                        var times = weekdayIndex['TimePeriods'];
9950                        if (!times) {
9951                            times = weekdayIndex['TimePeriods'] = [];
9952                        }
9953
9954                        if (itemName === 'From' || itemName === 'To') {
9955                            var timesIndex = times[hourIndex];
9956                            if (!timesIndex) {
9957                                timesIndex = times[hourIndex] = {};
9958                            }
9959                            timesIndex[itemName] = value;
9960                        }
9961                        return;
9962                    }
9963
9964                    var sectionIndex = section[itemSectionIndex];
9965
9966                    if (!sectionIndex) {
9967                        sectionIndex = section[itemSectionIndex] = {};
9968                    }
9969
9970                    sectionIndex['typeId'] = parseInt(itemSectionIndex);
9971
9972                    if (itemName === 'Status') {
9973                        sectionIndex['Status'] = value;
9974                        return;
9975                    }
9976
9977                    var times = sectionIndex['TimePeriods'];
9978                    if (!times) {
9979                        times = sectionIndex['TimePeriods'] = [];
9980                    }
9981
9982                    if (itemName === 'From' || itemName === 'To') {
9983                        var timesIndex = times[itemIndex];
9984                        if (!timesIndex) {
9985                            timesIndex = times[itemIndex] = {};
9986                        }
9987                        timesIndex[itemName] = value;
9988                    }
9989                });
9990
9991                sections['Holidays'] = sections['Holidays'].filter(noUndefinedValues);
9992                $.each(sections['Holidays'], function (currentItem, value) {
9993                    if (typeof value['TimePeriods'] !== 'undefined') {
9994                        value['TimePeriods'] = value['TimePeriods'].filter(noUndefinedValues);
9995                        value['TimePeriods'] = value['TimePeriods'].map(map24to00);
9996                    }
9997                });
9998
9999                sections['Weekdays'] = sections['Weekdays'].filter(noUndefinedValues);
10000
10001                $.each(sections['Weekdays'], function (currentItem, value) {
10002                    if (typeof value['TimePeriods'] !== 'undefined') {
10003                        value['TimePeriods'] = value['TimePeriods'].filter(noUndefinedValues);
10004                        value['TimePeriods'] = value['TimePeriods'].map(map24to00);
10005                    }
10006                });
10007
10008                sections['SpecialOpeningHours'] = typeof sections.SpecialOpeningHours !== 'undefined' ? sections.SpecialOpeningHours.filter(noUndefinedValues) : [];
10009                if (typeof sections['SpecialOpeningHours'] !== 'undefined') {
10010                    $.each(sections['SpecialOpeningHours'], function (currentItem, value) {
10011                        if (typeof value !== 'undefined') {
10012                            $.each(value['Weekdays'], function (currentItem, value) {
10013                                if (value['TimePeriods'] !== undefined) {
10014                                    value['TimePeriods'] = value['TimePeriods'].filter(noUndefinedValues);
10015                                    value['TimePeriods'] = value['TimePeriods'].map(map24to00);
10016                                }
10017                            });
10018                        }
10019                    });
10020                }
10021
10022                sections['ClosingDays'] = sections['ClosingDays'].filter(noUndefinedValues);
10023
10024
10025                item = sections;
10026            }
10027
10028            if ($element.hasClass('js-from-gdpr-terms')) {
10029                var terms = [];
10030
10031                $.each(item, function (currentItem, value) {
10032                    var name = currentItem.substr(0, currentItem.indexOf('-')) === 'TermsItem';
10033
10034                    if (name) {
10035                        terms.push(value);
10036                        delete item[currentItem];
10037                    }
10038                });
10039
10040                item['terms'] = terms;
10041            }
10042
10043            // push items to jsonObj as json
10044            jsonObj = JSON.stringify(item);
10045
10046            // check serversite if val is approved
10047            var url = form.attr('data-api');
10048
10049            // check if its has another request method
10050            var requestMethod = form.attr('data-request-method') || 'POST';
10051
10052            var opts = {
10053                type: requestMethod,
10054                contentType: "application/json;charset=utf-8",
10055                data: jsonObj
10056            }
10057
10058            var call = window.vt.ajax.call(url, opts, element);
10059            call.success(function (data) {
10060                vt.animations.feedbackApproved($element);
10061
10062                // Only track if validation passes (data.status != 400)
10063                if (data.status != 400) {
10064                    // Track form submission after successful validation
10065                    var defibrillatorId = new URLSearchParams(window.location.search).get("id") || "";
10066                    var isEditMode = requestMethod === "PATCH";
10067
10068                    // Helper function to determine if form is in create mode (for forms that don't support PATCH)
10069                    var isCreateForm = function () {
10070                        return form.attr('data-is-create-form') === 'true';
10071                    };
10072
10073                    // Helper function to track form events
10074                    var trackFormEvent = function (eventName) {
10075                        vt.functions.pushToDataLayer({
10076                            event: eventName,
10077                            defibrillator_id: defibrillatorId
10078                        });
10079                    };
10080
10081                    // Track defibrillator information form
10082                    if (url === "/api/defibrillator/defibrillatorinformation") {
10083                        trackFormEvent(
10084                            isEditMode ? "edit_defibrillator_information" : "register_defibrillator_information"
10085                        );
10086                    }
10087                    // Track location information form (API only supports POST)
10088                    else if (url === "/api/defibrillator/locationInformation") {
10089                        trackFormEvent(
10090                            isCreateForm() ? "register_defibrillator_location" : "edit_defibrillator_location"
10091                        );
10092                    }
10093                    // Track opening hours/availability form (API only supports POST)
10094                    else if (url === "/api/defibrillator/openingHours") {
10095                        trackFormEvent(
10096                            isCreateForm() ? "register_defibrillator_availablehours" : "edit_defibrillator_availablehours"
10097                        );
10098                    }
10099                    // Track images/picture form
10100                    else if (url === "/api/defibrillator/image" || url === "/api/defibrillator/submitImagesForm") {
10101                        trackFormEvent(
10102                            isEditMode ? "edit_defibrillator_picture" : "register_defibrillator_picture"
10103                        );
10104                    }
10105                    // Track summary submission (only on registration flow)
10106                    else if (url === "/api/defibrillator/submitSummaryForm" && window.location.pathname.includes("/opsummering")) {
10107                        trackFormEvent("register_defibrillator_submission");
10108                    }
10109                }
10110
10111                if (data.status == 400) {
10112                    vt.initFormTools.errorScenarios(data, validate);
10113                } else {
10114                    vt.initFormTools.serverSuccess(form);
10115
10116                    // Track forms with js-tracking class (only after successful validation)
10117                    if ($element.hasClass("js-tracking")) {
10118                        // Get form name and defibrillator ID for tracking
10119                        var formName = form.attr('data-formname');
10120                        var searchParam = new URLSearchParams(window.location.search);
10121                        var defibrillatorId = searchParam.get("id");
10122                        var requestMethod = form.attr('data-request-method');
10123                        var isEditMode = requestMethod === "PATCH" || (defibrillatorId !== null && defibr
10123illatorId !== "");
10124
10125                        // Track namephone form (responsible person) submission
10126                        if (formName === "namephone") {
10127                            vt.functions.pushToDataLayer({
10128                                event: isEditMode ? "edit_defibrillator_responsible" : "register_defibrillator_responsible",
10129                                defibrillator_id: defibrillatorId || ""
10130                            });
10131                        }
10132                        // Track defibrillatorinformation form (summary/submission) submission
10133                        // NOTE: register_defibrillator_submission tracking moved to button click handler in comp.forms.js
10134                        // Only track if this is not the summary form (submitSummaryForm is tracked on button click)
10135                        else if (formName === "defibrillatorinformation" && !$(element).closest('form').attr('data-api')?.includes('submitSummaryForm')) {
10136                            vt.functions.pushToDataLayer({
10137                                event: "register_defibrillator_submission",
10138                                defibrillator_id: defibrillatorId || ""
10139                            });
10140                        }
10141                        else {
10142                            // Default tracking for other forms with js-tracking class
10143                            vt.functions.pushToDataLayer({
10144                                event: "register_defibrillator_start"
10145                            });
10146                        }
10147                    }
10148                }
10149
10150                var waitTime = 4000;
10151
10152                if ($(form).find('.form-success').length <= 0) {
10153                    waitTime = 1000;
10154                }
10155
10156                // Wait for the animation + serverSuccess to finish
10157                setTimeout(function () {
10158                    const defibrilatorIdParameter = window.location.search.substring(1).split('&').find(p => p.split('=')[0] === 'id');
10159                    const defibrilatorId = defibrilatorIdParameter ? defibrilatorIdParameter.split('=')[1] : null;
10160
10161                    if ($element.hasClass("js-change-owner-submit")) {
10162                        vt.functions.pushToDataLayer({
10163                            event: "change_defibrillator_responsible_submit",
10164                            defibrillator_id: defibrilatorId || 'not_provided',
10165                        });
10166                        vt.changeOwnership(data, opts.data, element);
10167                    } else {
10168                        // All form tracking (edit/register) moved to button click handler in comp.forms.js
10169                        // Only redirect page after successful submission
10170                        vt.initFormTools.redirectPage(data, element);
10171                    }
10172                }, waitTime);
10173            });
10174            call.error(function (data) {
10175                if (data.status == 400) {
10176                    vt.initFormTools.errorScenarios(data, validate);
10177                    vt.animations.feedbackReset($element);
10178                } else {
10179                    vt.initFormTools.serverError(data);
10180                    vt.animations.feedbackReset($element);
10181                }
10182            });
10183        };
10184
10185        // add error messages to elements from callback
10186        var errorScenarios = function (data, validate) {
10187
10188            // clear up
10189            $('.ui-state-error').remove();
10190
10191            $.each(data.responseJSON, function (index, element) {
10192                if ($(validate).find('*[name="' + element + '"]').length === 0) {
10193                    var elm = $(validate).filter('*[data-name="' + element + '"]');
10194                    if (elm.length) {
10195                        elm.addClass('error');
10196                        elm.find('input, select').each(function () {
10197                            $(this).change(function () {
10198                                elm.removeClass('error').find('> .ui-state-error').remove();
10199                            });
10200                        });
10201
10202                        // If a validation error is inside a closed expandable section - then we expand it
10203                        var $expandableSection = elm.closest('.expandable-section');
10204                        if ($expandableSection.length) {
10205                            var $expandableSectionHeader = $expandableSection.find('.expandable-section--expand');
10206                            if ($expandableSectionHeader.length && !$expandableSectionHeader.hasClass('expanded')) {
10207                                $expandableSectionHeader.addClass('expanded');
10208                            }
10209                        }
10210                    }
10211                } else {
10212                    $(validate).find('*[name="' + element + '"]').addClass('invalid')
10213                    // input field error message
10214                    elm = $(validate).find('*[name="' + element + '"]').closest('.js-validate').last();
10215                }
10216
10217                var errorMessage = $(elm).attr('data-error');
10218                if (errorMessage == '') {
10219
10220                    if ($(elm).find('.help-text').length > 0) {
10221                        $('<p class="ui-state-error message">Noget gik galt, prøv igen senere</p>').insertBefore($(elm).find('.help-text'));
10222                    } else {
10223                        $(elm).append('<p class="ui-state-error message">Noget gik galt, prøv igen senere</p>');
10224                    }
10225                } else {
10226
10227                    if ($(elm).find('.help-text').length > 0) {
10228                        $('<p class="ui-state-error message">' + errorMessage + '</p>').insertBefore($(elm).find('.help-text'));
10229                    } else {
10230                        $(elm).append('<p class="ui-state-error message">' + errorMessage + '</p>');
10231                    }
10232                }
10233
10234            });
10235
10236            // scroll up to first error
10237            if ($('.ui-state-error').length) {
10238                $('html, body').animate({
10239                    scrollTop: $(".ui-state-error").offset().top - 200
10240                }, 1000);
10241            }
10242        };
10243
10244        // server side error popup
10245        var serverError = function (data) {
10246            // if error message isn't there then hardcode a text
10247            if (!data.responseJSON) {
10248                data.responseJSON = { "UserMessage": 'Noget gik galt, prøv igen senere' };
10249            }
10250
10251            // clear up
10252            var height;
10253            $('.ui-state-error').remove();
10254
10255            $('body').append('<div class="cookie-disclaimer form-element-popup-error">' + data.responseJSON.UserMessage + '</div>');
10256            $('.form-element-popup-error').css('top', '-1000px');
10257            height = $('.form-element-popup-error').height();
10258            $('.form-element-popup-error').css('top', height * (-1) + 'px');
10259            setTimeout(function () {
10260                $('.form-element-popup-error').css('top', '0px');
10261            }, 500);
10262            setTimeout(function () {
10263                $('.form-element-popup-error').css('top', height * (-1) + 'px');
10264            }, 5000);
10265        };
10266
10267        // server side success popup
10268        var serverSuccess = function (form) {
10269            var message;
10270
10271            // if error message isn't there then hardcode a text
10272            if ($(form).find('.form-success').length > 0) {
10273                message = $(form).find('.form-success').text();
10274
10275                // clear up
10276                $('.ui-state-error').remove();
10277
10278                $('body').append('<div class="cookie-disclaimer form-element-popup-success">' + message + '</div>');
10279                $('.form-element-popup-success').css('top', '-50px');
10280                setTimeout(function () {
10281                    $('.form-element-popup-success').css('top', '0px');
10282                }, 500);
10283                setTimeout(function () {
10284                    $('.form-element-popup-success').css('top', '-50px');
10285                }, 5000);
10286            }
10287        };
10288
10289        // for reloading page after ajax call
10290        var reloadPage = function (elem) {
10291            if ($(elem).closest('form').hasClass('js-reload')) {
10292                setTimeout(function () {
10293                    window.location.reload();
10294                }, 1000);
10295            }
10296        };
10297
10298        // for reloading page after ajax call
10299        var redirectPage = function (data, elem) {
10300            if ($(elem).closest('form[data-success-redirect]').length > 0) {
10301                if (data) {// redirect to has priority and should redirect there before returnTo
10302                    var url = data['RedirectTo'];
10303                    var hasPriority = data['HasPriority'];
10304                    if (hasPriority == true) { //we want specifically true, not just "non-empty"
10305                        window.location.href = url;
10306                        return;
10307                    }
10308                }
10309                if (window.location.href.indexOf('returnTo') > -1) {
10310
10311                    // Get returnTo string from URL
10312                    var url = window.location.href.split('returnTo=')[1];
10313
10314                    // Decode URL
10315                    url = unescape(url);
10316
10317                    // Redirect to returnTo string
10318                    document.location = url;
10319
10320
10321                } else if (data) {
10322
10323                    // Get redirectTo string from response
10324                    var url = data['RedirectTo'];
10325
10326                    // Redirect to redirectTo string
10327                    window.location.href = url;
10328
10329                } else {
10330
10331                    // Redirect to root domain
10332                    document.location = "/";
10333                }
10334            } else {
10335                vt.initFormTools.reloadPage(elem);
10336            }
10337        };
10338
10339        //Validate Phone field
10340        var phoneValidate = function (phone, isElm) {
10341            var numbers = /^[0-9]+$/;
10342
10343            if (phone.value.match(numbers) && phone.value.length == 8) {
10344                return false;
10345            }
10346            else if (isElm) {
10347                vt.initFormTools.setErrorMessage(phone);
10348            }
10349            return true;
10350        };
10351
10352        //Validate Alt Phone field
10353        var altPhoneValidate = function (altPhone, isElm) {
10354            var numbers = /^[0-9]+$/;
10355
10356            if (altPhone.value.length == 0 || (altPhone.value.length == 8 && altPhone.value.match(numbers))) {
10357                return false;
10358            }
10359            else if (isElm) {
10360                vt.initFormTools.setErrorMessage(altPhone);
10361            }
10362            return true;
10363        };
10364
10365        //Set error message when using js-livevalidate
10366        var setErrorMessage = function (elm) {
10367            var errorMessage = $(elm).closest('.js-livevalidate').attr('data-error');
10368            if ($(elm).closest('.js-livevalidate').find('.help-text').length > 0) {
10369                $('<p class="ui-state-error message">' + errorMessage + '</p>').insertBefore($(elm).closest('.js-livevalidate').find('.help-text'));
10370            } else {
10371                $(elm).closest('.js-livevalidate').append('<p class="ui-state-error message">' + errorMessage + '</p>');
10372            }
10373
10374            $(elm).closest('.js-livevalidate [name]').addClass('invalid');
10375        };
10376
10377        //Clean errors before doing validation on fields
10378        var cleanErrors = function (elm, form) {
10379
10380            $(elm).closest('.js-livevalidate').find('.ui-state-error').remove();
10381            form.find('.js-form-submit').prop('disabled', false);
10382            elm.classList.remove('invalid');
10383        };
10384
10385        // live validate input field when keyup
10386        var liveValidate = function (elm, form) {
10387            var validate = form.find('.js-validate');
10388
10389            //Special validation for phone and altphone fields
10390            if (elm.name === 'Phone') {
10391                cleanErrors(elm, form);
10392                var disableSubmitPhone = vt.initFormTools.phoneValidate(elm, true);
10393                var secElm = document.getElementsByName('Phonealternative');
10394                var disableSubmitAltPhone = vt.initFormTools.altPhoneValidate(secElm[0], false);
10395                if (disableSubmitPhone === true || disableSubmitAltPhone === true) {
10396                    form.find('.js-form-submit').prop('disabled', true);
10397                }
10398                return;
10399            }
10400            else if (elm.name === 'Phonealternative') {
10401                cleanErrors(elm, form);
10402                var disableSubmitAltPhone = vt.initFormTools.altPhoneValidate(elm, true);
10403                var secElm = document.getElementsByName('Phone');
10404                var disableSubmitPhone = vt.initFormTools.phoneValidate(secElm[0], false);
10405                if (disableSubmitPhone === true || disableSubmitAltPhone === true) {
10406                    form.find('.js-form-submit').prop('disabled', true);
10407                }
10408                return;
10409            }
10410
10411            // if first time typing then only validate on focus out
10412            if ($(elm).attr('data-validate-blur') == 0) {
10413                return;
10414            }
10415
10416            var jsonObj;
10417            var item = {};
10418
10419            // check if its has another request method
10420            var requestMethod;
10421            if ($(elm).closest('.js-validate').attr('data-request-method')) {
10422                requestMethod = $(elm).closest('.js-validate').attr('data-request-method');
10423            } else {
10424                requestMethod = 'POST';
10425            }
10426
10427            if (requestMethod != "GET") {
10428                // check serversite if val is approved
10429                item[$(elm).attr('name')] = elm.value;
10430
10431                jsonObj = JSON.stringify(item);
10432                var url = $(elm).closest('.js-validate').attr('data-api');
10433            } else {
10434                var url = $(elm).closest('.js-validate').attr('data-api');
10435                url = url + '/' + elm.value;
10436            }
10437            var opts = {
10438                type: requestMethod,
10439                contentType: "application/json;charset=utf-8",
10440                data: jsonObj
10441            }
10442            var call = window.vt.ajax.call(url, opts, elm);
10443            call.success(function (data) {
10444
10445                // clear up
10446                $(elm).closest('.js-livevalidate').find('.ui-state-error').remove();
10447
10448                if (data.IsValid == false) {
10449                    var errorMessage = $(elm).closest('.js-livevalidate').attr('data-error'),
10450                        errorMessageExisting = $(elm).closest('.js-livevalidate').attr('data-error-existing');
10451
10452                    if (errorMessageExisting != '' && data.IsExisting == true) {
10453
10454                        if ($(elm).closest('.js-livevalidate').find('.help-text').length > 0) {
10455                            $('<p class="ui-state-error message">' + errorMessageExisting + '</p>').insertBefore($(elm).closest('.js-livevalidate').find('.help-text'));
10456                        } else {
10457                            $(elm).closest('.js-livevalidate').append('<p class="ui-state-error message">' + errorMessageExisting + '</p>');
10458                        }
10459
10460                        $(elm).closest('.js-livevalidate [name]').addClass('invalid');
10461                    } else if (errorMessage == '') {
10462
10463                        if ($(elm).closest('.js-livevalidate').find('.help-text').length > 0) {
10464                            $('<p class="ui-state-error message">Noget gik galt, prøv igen senere</p>').insertBefore($(elm).closest('.js-livevalidate').find('.help-text'));
10465                        } else {
10466                            $(elm).closest('.js-livevalidate').append('<p class="ui-state-error message">Noget gik galt, prøv igen senere</p>');
10467                        }
10468
10469                        $(elm).closest('.js-livevalidate [name]').addClass('invalid');
10470                    } else {
10471
10472                        if ($(elm).closest('.js-livevalidate').find('.help-text').length > 0) {
10473                            $('<p class="ui-state-error message">' + errorMessage + '</p>').insertBefore($(elm).closest('.js-livevalidate').find('.help-text'));
10474                        } else {
10475                            $(elm).closest('.js-livevalidate').append('<p class="ui-state-error message">' + errorMessage + '</p>');
10476                        }
10477
10478                        $(elm).closest('.js-livevalidate [name]').addClass('invalid');
10479                    }
10480                }
10481            })
10482            call.error(function (data) {
10483                vt.initFormTools.serverError(data);
10484            });
10485        };
10486
10487        // check if input value is repeated
10488        var repeatValidate = function (elm, form) {
10489
10490            // clear up
10491            form.find('.js-form-submit').prop('disabled', false);
10492            $(elm).closest('.js-repeat').find('.ui-repeat-error').remove();
10493
10494            var elmValue = elm.value;
10495            var errorMessage = $(elm).closest('.js-repeat');
10496            elm = $(elm).attr('data-repeat');
10497            var repeatedInput = form.find('*[name="' + elm + '"]').val();
10498
10499            form.find('*[name="' + elm + '"]').on('keyup', function (e) {
10500                form.find('.js-form-submit').prop('disabled', false);
10501                $('.ui-repeat-error').remove();
10502
10503                if (elmValue != $(e.target).val()) {
10504
10505                    if ($(errorMessage).find('.help-text').length > 0) {
10506                        $('<p class="ui-repeat-error message">' + errorMessage.attr('data-error') + '</p>').insertBefore($(errorMessage).find('.help-text'));
10507                    } else {
10508                        $(errorMessage).append('<p class="ui-repeat-error message">' + errorMessage.attr('data-error') + '</p>');
10509                    }
10510
10511                    form.find('.js-form-submit').prop('disabled', true);
10512                }
10513            });
10514
10515            if (elmValue != repeatedInput) {
10516
10517                if ($(errorMessage).find('.help-text').length > 0) {
10518                    $('<p class="ui-repeat-error message">' + errorMessage.attr('data-error') + '</p>').insertBefore($(errorMessage).find('.help-text'));
10519                } else {
10520                    $(errorMessage).append('<p class="ui-repeat-error message">' + errorMessage.attr('data-error') + '</p>');
10521                }
10522
10523                form.find('.js-form-submit').prop('disabled', true);
10524            }
10525        };
10526
10527        var moreOpportunities = function (elm) {
10528
10529            // vars
10530            var id = $(elm).attr('id'),
10531                showFromId = $(elm).closest('.form-element').attr('data-showfromid');
10532            var formElements = $(elm).closest('.form-element').find('.form-element');
10533
10534
10535            // if radio button inside the new form-element then return
10536            if (showFromId === null) {
10537                return;
10538            }
10539
10540            // hide all
10541            hideAll(formElements);
10542
10543            // show element you selected
10544            showElement(formElements, id, showFromId);
10545
10546            function hideAll(formElements) {
10547
10548                if ($(elm).closest('.form-element').find('.form-element--twocolumns').length > 0) {
10549                    formElements = $(elm).closest('.form-element').find('.form-element--twocolumns');
10550                    $(formElements).addClass('hidden');
10551
10552                    $.each($(formElements).find('.form-element'), function (index, elem) {
10553                        $(elem).removeClass('js-validate');
10554                    });
10555                } else {
10556                    $.each(formElements, function (index, val) {
10557                        if (!$(this).hasClass('js-livevalidate')) {
10558                            $(this).addClass('hidden').removeClass('js-validate');
10559                        } else {
10560                            $(this).addClass('hidden');
10561                        }
10562                    });
10563                }
10564            };
10565
10566            function showElement(formElements, id, showFromId) {
10567
10568                // replace id name to nothing so its only the number left
10569                id = id.replace(showFromId, '');
10570
10571                // take the number and - 1
10572                id = parseInt(id) - 1;
10573
10574                if ($(elm).closest('.form-element').find('.form-element--twocolumns').length > 0) {
10575                    formElements = $(elm).closest('.form-element').find('.form-element--twocolumns[id="' + id + '"]');
10576                    $(formElements).removeClass('hidden');
10577
10578                    $.each($(formElements).find('.form-element'), function (index, elem) {
10579                        $(elem).addClass('js-validate');
10580                    });
10581                } else {
10582
10583                    // check if this form-element is a livevalidator
10584                    if (!$(formElements[id]).hasClass('js-livevalidate')) {
10585                        $(formElements[id]).addClass('js-validate').removeClass('hidden');
10586                    } else {
10587                        $(formElements[id]).removeClass('hidden');
10588                    }
10589                }
10590            };
10591        };
10592
10593        var getOpportunities = function (elm) {
10594
10595            // get next select
10596            var getopportunitiesValue = $(elm).closest('.js-getopportunities').attr('data-get');
10597            var getopportunitiesOther = $(elm).closest('.js-getopportunities').attr('data-other');
10598            var formElement = $(elm).parents('form').find('[data-getopportunities="' + getopportunitiesValue + '"]');
10599
10600            var encodedValue = elm.value.replace('/', '---');//escaping / in a custom way, as IIS decodes %2F that causes api to fail
10601
10602            // api call to get new options
10603            var url = $(elm).closest('.js-getopportunities').attr('data-api') + '/' + encodedValue;
10604            var opts = {
10605                type: 'GET',
10606                contentType: "application/json;charset=utf-8"
10607            }
10608            var call = window.vt.ajax.call(url, opts, elm);
10609            call.success(function (data) {
10610                var fromStrorage = formElement.attr("data-localStorage") !== undefined && formElement.attr("data-localStorage") === "true";
10611                var select = $(formElement).find('select');
10612                var value = $(select).val();
10613                var otherValue = $(select).find('[value="-1"]').html();
10614                var defaultValue = $(select).find('[value="0"]').html();
10615
10616                // if data is empty then hide select
10617                if (getopportunitiesOther !== 'true' && data.length <= 0 && !fromStrorage) {
10618                    $(formElement).find('select').closest('.form-element').removeClass('js-validate').addClass('hidden');
10619                    return;
10620                }
10621
10622                // remove all options from select
10623                $(select).html('');
10624
10625                // append defaultValue again
10626                if (defaultValue != undefined) {
10627                    $(select).append('<option value="0">' + defaultValue + '</option>');
10628                }
10629
10630                // each option from data
10631                $.each(data, function (index, option) {
10632
10633                    var optionBlock;
10634                    if ($(select).attr('data-key') === 'true') {
10635                        if (option === value) {
10636                            optionBlock = '<option selected value="' + index + '">' + option + '</option>';
10637                        } else {
10638                            optionBlock = '<option value="' + index + '">' + option + '</option>';
10639                        }
10640                    } else {
10641                        if (option === value) {
10642                            optionBlock = '<option selected value="' + option + '">' + option + '</option>';
10643                        } else {
10644                            optionBlock = '<option value="' + option + '">' + option + '</option>';
10645                        }
10646                    }
10647
10648                    // append option to select
10649                    $(select).append(optionBlock);
10650                });
10651
10652                // append otherValue again
10653                if (otherValue != undefined) {
10654                    $(select).append('<option value="-1">' + otherValue + '</option>');
10655                }
10656
10657                // if other (-1) is selected
10658                var parent = $(select).parents('.form-element');
10659                if (getopportunitiesOther === 'true') {
10660                    $(select).prop('disabled', true);
10661                    $(select).find('[value="-1"]').prop('selected', true);
10662
10663                    $(parent).find('.js-other').addClass('js-validate');
10664                    $(parent).find('.js-other').removeClass('hidden');
10665                }
10666                else if (getopportunitiesOther === 'false') {
10667                    $(select).prop('disabled', false);
10668                    $(parent).find('.js-other input[type="text"]').parent().addClass('hidden').removeClass('js-validate');
10669                    // show next select
10670                    //$(formElement).addClass('js-validate').removeClass('hidden');
10671                    $(parent).find('.js-other select').parent('form-element').removeClass('hidden').addClass('js-validate');
10672                }
10673
10674                if (fromStrorage) {
10675                    formElement.removeAttr("data-localStorage");
10676                }
10677
10678                formElement.removeClass('hidden').addClass('js-validate')
10679            })
10680            call.error(function (data) {
10681                vt.initFormTools.serverError(data);
10682            });
10683        };
10684
10685        var showExtraSection = function (initiator) {
10686            var targetSectionName = initiator.attr('data-target'),
10687                template = $('[data-template="' + targetSectionName + '"]').html(),
10688                sectionContainer = $('[data-container="' + targetSectionName + '"]')
10689            domTemplate = $(template),
10690                index = parseInt(initiator.attr('data-index')) + 1,
10691                count = parseInt(initiator.attr('data-count')) + 1;
10692
10693            initiator.attr('data-count', count).attr('data-index', index);
10694
10695            domTemplate.find(':input').each(function () {
10696                var $input = $(this);
10697                var name = $input.attr('name');
10698                $input.attr('name', name + '-' + index);
10699                if ($input.hasClass('input-type__time')) {
10700                    vt.initFormTools.initiateInputTimeFormating($input);
10701                }
10702            });
10703
10704            domTemplate.find('[data-name]').each(function () {
10705                var name = $(this).attr('data-name');
10706                $(this).attr('data-name', name + '-' + index);
10707            });
10708
10709            if (count <= 20) {
10710                domTemplate.find('[data-action="add-hours-interval"]').click(function (e) {
10711                    vt.initFormTools.initiateExtraHours(e, index);
10712                });
10713                domTemplate.find('.js-select-toggle select').change(function (e) {
10714                    var selectContainer = $(e.target).parent();
10715                    vt.initFormTools.toggleSectionOnSelect(selectContainer);
10716                });
10717                domTemplate.find('[data-action="remove-section"]').click(function (e) {
10718                    $(e.target).closest('.aed-availability').remove();
10719                    initiator.attr('data-count', parseInt(initiator.attr('data-count')) - 1);
10720                    initiator.attr('data-alteration', parseInt(initiator.attr('data-alteration')) - 1);
10721
10722                    if (parseInt(initiator.attr('data-count')) < 20) {
10723                        initiator.removeClass('hidden');
10724                    }
10725                });
10726
10727                vt.initDatePicker(domTemplate);
10728                var $timeInputs = domTemplate.find('.input-type__time');
10729                vt.initFormTools.initiateInputTimeFormating($timeInputs);
10730                sectionContainer.append(domTemplate);
10731            }
10732            if (count >= 20) {
10733                initiator.parent().addClass('hidden');
10734            }
10735        };
10736
10737        var positionTooltip = function (tooltip) {
10738            var tooltipBubble = $(tooltip).find('.tooltip'),
10739                tooltipTriangle = tooltipBubble.find('.tooltip__triangle'),
10740                bubblePosition = tooltipBubble.offset(),
10741                bubbleRightPosition = bubblePosition.left + tooltipBubble.outerWidth(),
10742                windowWidth = $(window).width();
10743
10744            if (bubbleRightPosition > windowWidth - 30) {
10745                var bubbleOffset = bubbleRightPosition - windowWidth + 30;
10746                tooltipBubble.offset({ left: bubblePosition.left - bubbleOffset });
10747                tooltipTriangle.offset({ left: tooltipTriangle.offset().left + bubbleOffset });
10748                // } else if (bubblePosition.left < 0) {
10749                //     var bubbleOffset = -bubblePosition.left + 30;
10750                //     tooltipBubble.offset({ left: bubblePosition.left + bubbleOffset });
10751                //     tooltipTriangle.offset({ left: tooltipTriangle.offset().left + bubbleOffset });
10752            } else {
10753                setTimeout(
10754                    function () {
10755                        tooltipBubble.css('left', '');
10756                        tooltipTriangle.css('left', '');
10757                    }, 300);
10758
10759            }
10760        };
10761
10762        var initiateExtraHours = function (e, sectionIndex) {
10763            e.preventDefault();
10764            var initiator = $(e.currentTarget),
10765                type = initiator.attr('data-type');
10766            container = initiator.parent().find('[data-container="hours-interval"]'),
10767                day = initiator.closest('[data-day]').attr('data-day'),
10768                index = parseInt(initiator.attr('data-interval-index')) + 1,
10769                count = parseInt(initiator.attr('data-interval-count')) + 1,
10770                from = container.find('input[name^="' + type + '-' + day + '-From"]').attr('placeholder'),
10771                to = container.find('input[name^="' + type + '-' + day + '-To"]').attr('placeholder'),
10772                sectionString = typeof sectionIndex !== "undefined" ? '-' + sectionIndex : '';
10773            initiator.attr('data-interval-count', count).attr('data-interval-index', index);
10774
10775            var template = $('<div class="hours-interval__item"><div class="form-element js-validate">'
10776                + '<input type="text" name="' + type + '-' + day + '-From" value="" class="input-type__time input-type__time--24" placeholder="' + from + '" maxlength="5" size="5">'
10777                + '</div><div class="form-element js-validate">'
10778                + '<input type="text" name="' + type + '-' + day + '-To" value="" class="input-type__time input-type__time--24" placeholder="' + to + '" maxlength="5" size="5">'
10779                + '</div><button class="circle icon icon-delete" data-action="remove-interval"></button></div>')
10780
10781            template.find(':input').each(function () {
10782                var $input = $(this);
10783                var name = $input.attr('name');
10784                $input.attr('name', name + '-' + index + sectionString);
10785                if ($input.hasClass('input-type__time')) {
10786                    vt.initFormTools.in
10786itiateInputTimeFormating($input);
10787                }
10788            });
10789
10790            if (count <= 2) {
10791                vt.initFormTools.initiateInputTimeFormating(template.find('.input-type__time'));
10792                template.find('[data-action="remove-interval"]').click(function (e) {
10793                    vt.initFormTools.removeInterval(e, initiator);
10794                });
10795                container.append(template);
10796
10797                var $inputs = container.find('.input-type__time');
10798                for (var i = 0; i < $inputs.length; i += 1) {
10799                    if ($($inputs[i]).val() === '') {
10800                        $inputs[i].focus();
10801                        break;
10802                    }
10803                }
10804            }
10805            if (count >= 2) {
10806                initiator.addClass('hidden');
10807            }
10808        };
10809
10810        var addZeros = function (timeValue) {
10811            var newTimeValue = timeValue;
10812            if (newTimeValue.length < 2) {
10813                newTimeValue = '0' + newTimeValue;
10814            }
10815            if (newTimeValue.length === 2) {
10816                newTimeValue = newTimeValue + ':';
10817            } else if (newTimeValue.length < 5) {
10818                newTimeValue = newTimeValue + '0';
10819            } else {
10820                return newTimeValue;
10821            }
10822            return addZeros(newTimeValue);
10823        }
10824
10825        var onlyNumbers = function (e) {
10826            // We also allow various system keys
10827            if (e.shiftKey && e.keyCode === 190) {
10828                var thisVal = $(this).val();
10829                if (thisVal.length === 0) {
10830                    $(this).val('00:');
10831                }
10832                if (thisVal.length < 2) {
10833                    $(this).val('0' + thisVal);
10834                }
10835            } else if (e.keyCode === 32 || (e.shiftKey && e.keyCode > 48 && e.keyCode < 57) || (e.keyCode > 58 && e.keyCode < 96) || e.keyCode > 105) {
10836                e.preventDefault();
10837            }
10838        };
10839
10840        var cleanUpTime = function (timeValue) {
10841            var sanitisedTime = timeValue.replace(/ /g, '');
10842            var timeArr = sanitisedTime.split(':');
10843            if (timeArr !== null && timeArr.length > 1) {
10844                timeArr = timeArr.map(function (str) {
10845                    return parseInt(str, 10);
10846                });
10847
10848                var firstDigit = timeArr[0];
10849                var secondDigit = timeArr[1];
10850
10851                // If we have any invalid numbers then return nothing
10852                if (isNaN(firstDigit) || isNaN(secondDigit)) {
10853                    return '';
10854                }
10855
10856                // Adjust down the hours to a max of 24 if over
10857                if (firstDigit >= 24) {
10858                    firstDigit = 24;
10859                }
10860
10861                // Adjust down the minutes to a max of 59 if over
10862                if (secondDigit >= 59) {
10863                    secondDigit = 59;
10864                }
10865
10866                // If we're setting the hour to 24 then the minutes must be 0
10867                if (firstDigit === 24) {
10868                    secondDigit = 0;
10869                }
10870
10871                firstDigit += '';
10872                secondDigit += '';
10873
10874                // Add some zeros for single digits
10875                if (firstDigit.length < 2) {
10876                    firstDigit = '0' + firstDigit;
10877                }
10878
10879                if (secondDigit.length < 2) {
10880                    secondDigit = '0' + secondDigit;
10881                }
10882
10883                // Return in the correct time format
10884                return firstDigit + ':' + secondDigit;
10885            }
10886            return timeValue;
10887        };
10888
10889        var timeAutoAdjustments = function (e) {
10890            var timeValue = $(this).val();
10891
10892            // Add any missing zeros
10893            if (timeValue !== null && timeValue !== '') {
10894                timeValue = addZeros(timeValue);
10895
10896                // Clean up time
10897                timeValue = cleanUpTime(timeValue);
10898                $(this).val(timeValue);
10899            }
10900        };
10901
10902        var initiateInputTimeFormating = function (input) {
10903            if (input !== null && input.length) {
10904                var timeInput = new Cleave(input[0], {
10905                    blocks: [2, 2],
10906                    delimiter: ':'
10907                });
10908                input.on('keydown', onlyNumbers);
10909                input.on('blur', timeAutoAdjustments);
10910            }
10911        };
10912
10913        var toggleSectionOnSelect = function (initiator) {
10914            var target = initiator.attr('data-select-toggle-target'),
10915                value = initiator.attr('data-select-toggle-value'),
10916                targetContainer = initiator.parent().find('[data-select-toggle-container="' + target + '"]');
10917
10918            if (initiator.find('select option:selected').attr('data-value') === value) {
10919                targetContainer.addClass('hidden');
10920                targetContainer.find('.form-element').removeClass('js-validate');
10921            } else {
10922                targetContainer.removeClass('hidden');
10923                targetContainer.find('.form-element').addClass('js-validate');
10924            }
10925        };
10926
10927        var removeInterval = function (e, initiator) {
10928            e.preventDefault();
10929            $(e.target).parent().remove();
10930            var reducedCount = parseInt(initiator.attr('data-interval-count')) - 1;
10931            // initiator.attr('data-interval-index', parseInt(initiator.attr('data-interval-index')) - 1);
10932            initiator.attr('data-interval-count', parseInt(initiator.attr('data-interval-count')) - 1);
10933
10934            if (reducedCount < 2) {
10935                initiator.removeClass('hidden');
10936            }
10937        };
10938
10939        return {
10940            validateInitiate: validateInitiate,
10941            errorScenarios: errorScenarios,
10942            serverError: serverError,
10943            serverSuccess: serverSuccess,
10944            reloadPage: reloadPage,
10945            redirectPage: redirectPage,
10946            phoneValidate: phoneValidate,
10947            altPhoneValidate: altPhoneValidate,
10948            setErrorMessage: setErrorMessage,
10949            cleanErrors: cleanErrors,
10950            liveValidate: liveValidate,
10951            repeatValidate: repeatValidate,
10952            moreOpportunities: moreOpportunities,
10953            getOpportunities: getOpportunities,
10954            showExtraSection: showExtraSection,
10955            positionTooltip: positionTooltip,
10956            initiateExtraHours: initiateExtraHours,
10957            initiateInputTimeFormating: initiateInputTimeFormating,
10958            toggleSectionOnSelect: toggleSectionOnSelect,
10959            removeInterval: removeInterval
10960        };
10961
10962    })();
10963
10964})(jQuery);
10965;
10966(function ($) {
10967  window.vt = window.vt || {};
10968
10969  var FlipCountDigit = function (initVal, index, length, $container, isIE) {
10970    this.$container = $container;
10971    this.index = index;
10972    this.length = length;
10973    this.digit = initVal;
10974    this.isIE = isIE;
10975
10976    this.$digitContainer = this.$container.find(
10977      ".c-flip-count__number:eq(" + this.index + ")"
10978    );
10979    this.$digitCurrentTop = this.$digitContainer.find(
10980      ".c-flip-count__current .c-flip-count__digit-top"
10981    );
10982    this.$digitCurrentBottom = this.$digitContainer.find(
10983      ".c-flip-count__current .c-flip-count__digit-bottom"
10984    );
10985    this.$digitNextTop = this.$digitContainer.find(
10986      ".c-flip-count__next .c-flip-count__digit-top"
10987    );
10988    this.$digitNextBottom = this.$digitContainer.find(
10989      ".c-flip-count__next .c-flip-count__digit-bottom"
10990    );
10991  };
10992
10993  FlipCountDigit.prototype.set = function (num) {
10994    this.digit = parseInt(num, 10);
10995    // this.updateCurrentDigit();
10996    this.updateNextDigit();
10997
10998    if (!this.isIE) {
10999      this.animate();
11000    } else {
11001      this.updateCurrentDigit();
11002    }
11003  };
11004
11005  FlipCountDigit.prototype.reset = function () {
11006    this.$digitContainer.addClass("c-flip-count__number--no-animation");
11007    this.$digitContainer[0].offsetHeight;
11008    this.$digitContainer.removeClass("c-flip-count__number--flip");
11009    this.updateCurrentDigit();
11010    this.$digitContainer[0].offsetHeight;
11011    this.$digitContainer.removeClass("c-flip-count__number--no-animation");
11012  };
11013
11014  FlipCountDigit.prototype.animate = function () {
11015    var $digitContainer = this.$digitContainer;
11016    this.timeout = setTimeout(function () {
11017      $digitContainer.addClass("c-flip-count__number--flip");
11018    }, 300 * (this.length - this.index));
11019  };
11020
11021  FlipCountDigit.prototype.updateCurrentDigit = function () {
11022    this.$digitCurrentTop.html(this.digit);
11023    this.$digitCurrentBottom.html(this.digit);
11024  };
11025
11026  FlipCountDigit.prototype.updateNextDigit = function () {
11027    var nextDigit = this.digit;
11028    this.$digitNextTop.html(nextDigit);
11029    this.$digitNextBottom.html(nextDigit);
11030  };
11031
11032  var FlipCount = function (elem) {
11033    this.$elem = $(elem);
11034    this.numberOfDigits = 5;
11035    this.digits = [];
11036    // The old IE browsers can't have nice things
11037    this.isIE = navigator.appVersion.indexOf("Trident/") > 0;
11038
11039    for (var i = 0; i < this.numberOfDigits; i += 1) {
11040      this.digits.push(
11041        new FlipCountDigit(0, i, this.numberOfDigits, this.$elem, this.isIE)
11042      );
11043    }
11044  };
11045
11046  FlipCount.prototype.set = function (val) {
11047    var stringVal = typeof val !== "undefined" ? val.toString() : "";
11048    var stringDigitArray = stringVal.split("");
11049
11050    // Make sure we have the right number of digits
11051    while (stringDigitArray.length < this.numberOfDigits) {
11052      stringDigitArray.unshift("0");
11053    }
11054
11055    for (var i = 0; i < stringDigitArray.length; i += 1) {
11056      this.digits[i].set(stringDigitArray[i]);
11057    }
11058
11059    var flipCount = this;
11060    this.timeout = setTimeout(function () {
11061      flipCount.resetAfterAnimation();
11062    }, this.numberOfDigits * 300 + 1000);
11063  };
11064
11065  FlipCount.prototype.resetAfterAnimation = function () {
11066    for (var i = 0; i < this.numberOfDigits; i += 1) {
11067      this.digits[i].reset();
11068    }
11069  };
11070
11071  vt.FlipCount = FlipCount;
11072})(jQuery);
11073;
11074; (function ($) {
11075
11076    window.vt = window.vt || {};
11077
11078    var FormElement = function (elem) {
11079        this.id = elem.attr('id');
11080        this.elem = elem;
11081        this.isValid = true;
11082        this.redirectPage = this.elem.data('success-redirect');
11083        this.shouldDisplayConfirmation = this.elem.data('show-confirmation') === true;
11084        this.validationMessage = this.elem.data('validation-msg');
11085        this.validationMessageElem = $('#' + this.elem.data('validation-msg-id'));
11086    };
11087
11088    FormElement.prototype.checkValidity = function() {
11089        this.validate();
11090        return this.isValid;
11091    }
11092
11093    FormElement.prototype.hasValue = function() {
11094        var htmlElem = this.elem[0];
11095        if (htmlElem.type === 'radio' || htmlElem.type === 'checkbox') {
11096            return htmlElem.checked;
11097        } else {
11098            return htmlElem.value !== null && htmlElem.value !== '';
11099        }
11100    };
11101
11102    FormElement.prototype.validate = function() {
11103        this.isValid = this.elem[0].checkValidity();
11104
11105        if (this.isValid) {
11106            this.resetValidationMessage();
11107        } else {
11108            this.setValidationMessage();
11109        }
11110    };
11111
11112    FormElement.prototype.setValidationMessage = function() {
11113        this.validationMessageElem.addClass('validation-error--visible');
11114        this.validationMessageElem.html(this.validationMessage);
11115    };
11116
11117    FormElement.prototype.resetValidationMessage = function() {
11118        this.validationMessageElem.removeClass('validation-error--visible');
11119        this.validationMessageElem.html('');
11120    };
11121
11122    // Form class
11123    var Form = function(elem) {
11124        this.form = $(elem);
11125        this.url = this.form.data('api');
11126        this.opts = {
11127            type: 'POST',
11128            contentType: 'application/json; charset=utf-8',
11129        };
11130        this.setRequestMethod();
11131        this.inputs = [];
11132        var inputElements = this.form.find('input[type="text"], input[type="password"], input[type="hidden"], input[type="tel"], input[type="radio"], textarea, select');
11133        for (var i = 0; i < inputElements.length; i += 1) {
11134            this.inputs.push(new FormElement($(inputElements[i])));
11135        };
11136
11137        var modalData = this.form.data('modal');
11138        this.hasModal = typeof modalData !== 'undefined' && modalData !== null;
11139        // this.modal = this.hasModal ? new vt.Modal($('#' + this.form.data('modal')), this.makeCall.bind(this)) : null;
11140        this.fetchModals();
11141        this.activeModal = null;
11142        this.shouldDisplayConfirmation = false;
11143        
11144        this.backButton = this.form.find('.js-form-back');
11145        this.submitButton = new vt.SubmitButton(this.form.find('.js-form-submit')[0], this);
11146    };
11147
11148    Form.prototype.setRequestMethod = function () {
11149        var requestMethod = this.form.attr('data-request-method');
11150        if (requestMethod !== null && requestMethod !== '') {
11151            this.opts.type = requestMethod;
11152        }
11153    };
11154
11155    Form.prototype.fetchModals = function() {
11156        this.modals = [];
11157        var modals = this.form.find('.c-modal');
11158        for (var i = 0; i < modals.length; i += 1) {
11159            this.modals.push(new vt.Modal($(modals[i]), this.makeCall.bind(this)));
11160        }
11161    };
11162
11163    Form.prototype.getModalById = function (id) {
11164        for (var i = 0; i < this.modals.length; i += 1) {
11165            if (this.modals[i].id === id) {
11166                return this.modals[i];
11167            }
11168        }
11169        return null;
11170    };
11171
11172    // The action performed to submit the form
11173    Form.prototype.submit = function() {
11174        this.validate();
11175        
11176        if (this.isValid) {
11177            if (this.shouldDisplayConfirmation && this.activeModal !== null) {
11178                this.activeModal.open();
11179            } else {
11180                this.makeCall();
11181            }
11182        }
11183    };
11184
11185    Form.prototype.makeCall = function() {
11186        var submitButton = this.submitButton;
11187        var self = this;
11188        submitButton.setLoading();
11189        var ajaxCall = window.vt.ajax.call(this.url, this.getFormOptions(), submitButton);
11190        ajaxCall.success(function(data) {
11191            submitButton.setSuccess();
11192            self.actionSuccess(data);
11193        });
11194        ajaxCall.error(function(data) {
11195            submitButton.setError();
11196            self.actionError(data);
11197        });
11198    };
11199
11200    // A method to retrieve the data needed to submit
11201    Form.prototype.gatherData = function() {
11202        return {};
11203    };
11204
11205    // Generates the options to be passed to the AJAX call
11206    Form.prototype.getFormOptions = function() {
11207        return Object.assign({
11208            data: JSON.stringify(this.gatherData()),
11209        }, this.opts);
11210    };
11211
11212    // Validates the form
11213    Form.prototype.validate = function() {
11214        this.isValid = true;
11215        this.shouldDisplayConfirmation = false;
11216        for(var i = 0; i < this.inputs.length; i++) {
11217            if (!this.inputs[i].checkValidity()) {
11218                this.isValid = false;
11219            }
11220            if (this.inputs[i].hasValue() && this.inputs[i].shouldDisplayConfirmation) {
11221                this.shouldDisplayConfirmation = true;
11222                this.activeModal = this.getModalById(this.inputs[i].elem.data('confirmation-modal-id'));
11223            }
11224        }
11225    };
11226
11227    // Called on success of the form submit
11228    Form.prototype.actionSuccess = function (data) {
11229        var waitTime = 4000;
11230        var thisForm = this.form;
11231
11232        if (thisForm.find('.form-success').length <= 0) {
11233            waitTime = 1000;
11234        }
11235
11236        // Wait for the animation + serverSuccess to finish
11237        setTimeout(function () {
11238            vt.initFormTools.redirectPage(data, thisForm);
11239        }, waitTime);
11240    };
11241
11242    // Called on error of the form submit
11243    Form.prototype.actionError = function(data) {
11244        if (data.status == 400) {
11245            vt.initFormTools.errorScenarios(data, null);
11246        } else {
11247            vt.initFormTools.serverError(data);
11248        }
11249    };
11250
11251    vt.Form = Form;
11252})(jQuery);;
11253; (function ($) {
11254    window.vt = window.vt || {};
11255
11256    var Modal = function(elem, actionCallback) {
11257        this.elem = elem;
11258        this.id = elem.attr('id');
11259        this.closeButton = this.elem.find('.c-modal__close');
11260        this.content = this.elem.find('c-modal__content');
11261        this.actionButton = this.elem.find('.c-modal__action');
11262        this.cancelButton = this.elem.find('.c-modal__cancel');
11263        this.actionCallback = actionCallback;
11264
11265        this.cancelButton.click(this.closeClick.bind(this));
11266        this.actionButton.click(this.actionClick.bind(this));
11267    }
11268
11269    Modal.prototype.action = function () {
11270        if (this.actionCallback !== null) {
11271            this.close();
11272            this.actionCallback();
11273        }
11274    };
11275
11276    Modal.prototype.open = function() {
11277        this.elem.addClass('c-modal--open');
11278    };
11279
11280    Modal.prototype.close = function() {
11281        this.elem.removeClass('c-modal--open');
11282    }
11283
11284    Modal.prototype.actionClick = function(e) {
11285        e.preventDefault();
11286        this.action();
11287    }
11288
11289    Modal.prototype.closeClick = function(e) {
11290        e.preventDefault();
11291        this.close();
11292    }
11293
11294    vt.Modal = Modal;
11295})(jQuery);;
11296; (function ($) {
11297
11298    window.vt = window.vt || {};
11299
11300    var SubmitButton = function(elem, form) {
11301        this.form = form;
11302        this.button = $(elem);
11303        this.button.click(this.click.bind(this));
11304    };
11305
11306    SubmitButton.prototype.click = function(e) {
11307        e.preventDefault();
11308        this.form.submit();
11309    };
11310
11311    SubmitButton.prototype.setLoading = function() {
11312        vt.animations.feedbackEvaluate(this.button);
11313    };
11314
11315    SubmitButton.prototype.setSuccess = function() {
11316        vt.animations.feedbackApproved(this.button);
11317    };
11318
11319    SubmitButton.prototype.setError = function() {
11320        vt.animations.feedbackError(this.button);
11321    };
11322
11323    SubmitButton.prototype.reset = function() {
11324        vt.animations.feedbackReset(this.button);
11325    };
11326
11327    vt.SubmitButton = SubmitButton;
11328})(jQuery);;
11329(function ($) {
11330  window.vt = window.vt || {};
11331
11332  var SwipableConfig = function () {
11333    this.opts = {};
11334  };
11335
11336  SwipableConfig.prototype.addConfig = function (
11337    key,
11338    containerModifierClass,
11339    bodyModifierClass,
11340    callback
11341  ) {
11342    var opts = {
11343      containerModifierClass: containerModifierClass,
11344      bodyModifierClass: bodyModifierClass,
11345      callback: callback,
11346    };
11347    this.opts[key] = opts;
11348  };
11349
11350  SwipableConfig.prototype.addUpConfig = function (
11351    containerModifierClass,
11352    bodyModifierClass,
11353    callback
11354  ) {
11355    this.addConfig("up", containerModifierClass, bodyModifierClass, callback);
11356  };
11357
11358  SwipableConfig.prototype.addDownConfig = function (
11359    containerModifierClass,
11360    bodyModifierClass,
11361    callback
11362  ) {
11363    this.addConfig("down", containerModifierClass, bodyModifierClass, callback);
11364  };
11365
11366  SwipableConfig.prototype.outputConfig = function () {
11367    return this.opts;
11368  };
11369
11370  var Swipable = function (
11371    elem,
11372    containerSelector,
11373    transformDirection,
11374    directionOpts
11375    /*
11376    direction,
11377    containerSelector,
11378    modifierClassName,
11379    bodyModifierClassName,
11380    callback,*/
11381  ) {
11382    this.transformDirection = transformDirection;
11383    this.elem = elem;
11384    this.body = document.body;
11385    this.container = document.querySelector(containerSelector);
11386    this.directionOpts = directionOpts;
11387    this.row = document.querySelector(".c-pin-previews__row");
11388    /*
11389    this.modifierClassName = modifierClassName;
11390    this.bodyModifierClassName = bodyModifierClassName;
11391    this.callback = callback;*/
11392
11393    this.init();
11394    if (Modernizr.touchevents) {
11395      this.elem.addEventListener("touchstart", this.onTouchStart.bind(this), {
11396        passive: true,
11397      });
11398      this.elem.addEventListener("touchmove", this.onTouchMove.bind(this), {
11399        passive: true,
11400      });
11401      this.elem.addEventListener("touchend", this.onTouchEnd.bind(this), {
11402        passive: true,
11403      });
11404    } else {
11405      this.elem.addEventListener("click", this.click.bind(this));
11406    }
11407  };
11408
11409  Swipable.prototype.init = function () {
11410    this.startingPointY = 0;
11411    this.startingPointX = 0;
11412    this.differenceY = 0;
11413    this.differenceX = 0;
11414    this.lastY = 0;
11415    this.lastX = 0;
11416    this.directionY = [null, null, null, null, null];
11417    this.directionX = [null, null, null, null, null];
11418  };
11419
11420  Swipable.prototype.updateDirection = function () {
11421    if (this.differenceY > this.lastY) {
11422      this.directionY.push(1);
11423    } else {
11424      this.directionY.push(0);
11425    }
11426    this.directionY.shift();
11427    if (this.differenceX > this.lastX) {
11428      this.directionX.push(1);
11429    } else {
11430      this.directionX.push(0);
11431    }
11432  };
11433
11434  Swipable.prototype.transform = function () {
11435    if (this.transformDirection === "both") {
11436      this.elem.style.transform = "translateY(" + this.differenceY + "px)";
11437    } else if (this.transformDirection === "down") {
11438      if (this.differenceY > 0 && this.differenceY < this.startingPointY) {
11439        this.elem.style.transform = "translateY(" + this.differenceY + "px)";
11440      }
11441    } else if (this.transformDirection === "up") {
11442      if (this.differenceY < 0 && this.differenceY < this.startingPointY) {
11443        this.elem.style.transform = "translateY(" + this.differenceY + "px)";
11444      }
11445    }
11446  };
11447
11448  Swipable.prototype.click = function (e) {
11449    this.container.classList.add(this.directionOpts.up.containerModifierClass);
11450    this.body.classList.add(this.directionOpts.up.bodyModifierClass);
11451    this.directionOpts.up.callback();
11452    this.elem.removeAttribute("style");
11453    this.body.removeAttribute("style");
11454    this.container.removeAttribute("style");
11455  };
11456
11457  Swipable.prototype.onTouchStart = function (e) {
11458    this.init();
11459    this.startingPointY = e.changedTouches[0].clientY;
11460    this.startingPointX = e.changedTouches[0].clientX;
11461    this.elem.style.transition = ".05s transform ease";
11462    this.body.style.overflow = "hidden";
11463    this.container.style.overflow = "hidden";
11464  };
11465
11466  Swipable.prototype.onTouchMove = function (e) {
11467    this.differenceY = e.changedTouches[0].clientY - this.startingPointY;
11468    this.differenceX = e.changedTouches[0].clientX - this.startingPointX;
11469    this.updateDirection();
11470    if (!this.isHorizontalSwipe()) {
11471      this.transform();
11472    }
11473    this.lastY = this.differenceY;
11474    this.lastX = this.differenceX;
11475  };
11476
11477  Swipable.prototype.isHorizontalSwipe = function (e) {
11478    return Math.abs(this.differenceX) > Math.abs(this.differenceY);
11479  };
11480
11481  Swipable.prototype.isUpSwipe = function () {
11482    var directionYVals = this.directionY.filter(function (x) {
11483      return x !== null;
11484    });
11485    if (directionYVals.length) {
11486      var upOrDownSum = directionYVals.reduce(function (a, b) {
11487        return a + b;
11488      });
11489      return upOrDownSum < (directionYVals.length + 1) / 2;
11490    }
11491    return true;
11492  };
11493
11494  Swipable.prototype.onTouchEnd = function (e) {
11495    if (!this.isHorizontalSwipe()) {
11496      if (this.isUpSwipe()) {
11497        // UP
11498        if (typeof this.directionOpts.up !== "undefined") {
11499          this.container.classList.add(
11500            this.directionOpts.up.containerModifierClass
11501          );
11502          this.body.classList.add(this.directionOpts.up.bodyModifierClass);
11503          this.directionOpts.up.callback();
11504        }
11505        if (typeof this.directionOpts.down !== "undefined") {
11506          this.container.classList.remove(
11507            this.directionOpts.down.containerModifierClass
11508          );
11509          this.body.classList.remove(this.directionOpts.down.bodyModifierClass);
11510        }
11511      } else {
11512        // DOWN
11513        if (typeof this.directionOpts.up !== "undefined") {
11514          this.container.classList.remove(
11515            this.directionOpts.up.containerModifierClass
11516          );
11517          this.body.classList.remove(this.directionOpts.up.bodyModifierClass);
11518        }
11519        if (typeof this.directionOpts.down !== "undefined") {
11520          this.container.classList.add(
11521            this.directionOpts.down.containerModifierClass
11522          );
11523          this.body.classList.add(this.directionOpts.down.bodyModifierClass);
11524          this.directionOpts.down.callback();
11525        }
11526      }
11527    }
11528    /*
11529    if (!this.isUpSwipe()) {
11530      this.container.classList.remove(this.modifierClassName);
11531      this.body.classList.remove(this.bodyModifierClassName);
11532    } else {
11533      this.container.classList.add(this.modifierClassName);
11534      this.body.classList.add(this.bodyModifierClassName);
11535      this.callback();
11536    }*/
11537    this.elem.removeAttribute("style");
11538    this.body.removeAttribute("style");
11539    this.container.removeAttribute("style");
11540  };
11541
11542  window.vt.Swipable = Swipable;
11543  window.vt.SwipableConfig = SwipableConfig;
11544})(jQuery);
11545;
11546(function ($) {
11547  window.vt = window.vt || {};
11548
11549  vt.initAedFilter = function (container) {
11550    var $container = $(container);
11551    var $parent = $container.parent();
11552
11553    // Get elements
11554    var $form = $container.find("form");
11555    var $items = $parent.find("[data-itemid]");
11556    var $select = $container.find("[data-aed-filters]");
11557    var $input = $container.find("[data-aed-filter-input]");
11558    var $reset = $parent.find("[data-reset]");
11559    var $feedback = $container.find("[data-feedback]");
11560
11561    // Stop form from reloading page
11562    $form.on("submit", function (e) {
11563      e.preventDefault();
11564    });
11565
11566    // Get feedback text
11567    var seeAllText = $feedback[0].dataset.messageSeeAll;
11568    var seeAllFilteredText = $feedback[0].dataset.messageSeeAllFiltered;
11569    var seeAllWithActiveFilterText =
11570      $feedback[0].dataset.messageSeeAllWithActiveFilter;
11571
11572    // Get filter values
11573    var filters = $select
11574      .children("option")
11575      .toArray()
11576      .map(function (x) {
11577        return x.value;
11578      });
11579    var filterAttributes = filters.filter(function (x) {
11580      return x !== "all";
11581    });
11582
11583    // Get active filter
11584    var activeFilter = $select.val();
11585    var activeFilterText = $select.children("option:selected").text();
11586    var filterText = "";
11587
11588    var inputChange = function (value) {
11589      filterText = DOMPurify.sanitize(value);
11590    };
11591    inputChange($input.val());
11592
11593    var getFieldsToFilterOn = function () {
11594      if (activeFilter === "all") return filterAttributes.slice(0);
11595      return [activeFilter];
11596    };
11597
11598    var isItemAMatch = function ($item, fieldsToFilterOn) {
11599      for (var i = 0; i < fieldsToFilterOn.length; i += 1) {
11600        var dataKey = fieldsToFilterOn[i];
11601        var dataValue = $item.dataset[dataKey];
11602        if (
11603          typeof dataValue !== "undefined" &&
11604          dataValue.toLowerCase().indexOf(filterText.toLowerCase()) > -1
11605        ) {
11606          return true;
11607        }
11608      }
11609      return false;
11610    };
11611
11612    var tokenReplacement = function (
11613      str,
11614      totalCount,
11615      itemsCount,
11616      filterText,
11617      activeFilterText
11618    ) {
11619      return str
11620        .replace(/\${totalCount}/g, totalCount)
11621        .replace(/\${itemsCount}/g, itemsCount)
11622        .replace(/\${sanitizedFilterText}/g, filterText)
11623        .replace(/\${sanitizedActiveFilterText}/g, activeFilterText);
11624    };
11625
11626    var filterAeds = function () {
11627      if (filterText !== "") {
11628        var fieldsToFilterOn = getFieldsToFilterOn();
11629        var totalCount = 0;
11630
11631        $items.each(function (itemIndex, $item) {
11632          if (isItemAMatch($item, fieldsToFilterOn)) {
11633            totalCount++;
11634            $item.classList.add("filter-match");
11635          } else {
11636            $item.classList.remove("filter-match");
11637          }
11638        });
11639
11640        $parent[0].classList.add("filtering-aeds");
11641        var tokenizedStringToReplace =
11642          activeFilter !== "all"
11643            ? seeAllWithActiveFilterText
11644            : seeAllFilteredText;
11645        var feedbackTextForFilters = tokenReplacement(
11646          tokenizedStringToReplace,
11647          totalCount,
11648          $items.length,
11649          DOMPurify.sanitize(filterText),
11650          DOMPurify.sanitize(activeFilterText)
11651        );
11652        $feedback[0].innerHTML = feedbackTextForFilters;
11653      } else {
11654        $items.removeClass("filter-match");
11655        $parent.removeClass("filtering-aeds");
11656        var feebackTextWithNumOfItems = seeAllText.replace(
11657          /\${itemsCount}/g,
11658          $items.length
11659        );
11660        $feedback[0].innerHTML = feebackTextWithNumOfItems;
11661      }
11662    };
11663
11664    var resetFilters = function (e) {
11665      filterText = "";
11666      $input.val("");
11667      $select.val("all");
11668      selectChange("all");
11669      $input.focus();
11670    };
11671
11672    var selectChange = function (value) {
11673      activeFilter = value;
11674      activeFilterText = $select.children("option:selected").text();
11675      filterAeds();
11676    };
11677
11678    var selectChangeEvent = function (e) {
11679      selectChange(e.target.value);
11680    };
11681
11682    var inputKeyUp = function (e) {
11683      inputChange(e.target.value);
11684      filterAeds();
11685    };
11686
11687    // Wire up events
11688    $reset.click(resetFilters);
11689
11690    $select.change(selectChangeEvent);
11691
11692    $input.keyup(inputKeyUp);
11693
11694    filterAeds();
11695  };
11696})(jQuery);
11697;
11698
11699; (function ($) {
11700
11701    window.vt = window.vt || {};
11702
11703    vt.initAEDList = function (container) {
11704        var $container = $(container);
11705        var confirm = false;
11706        var confirmActivation = false;
11707
11708        if ($('body').is('.pageedit'))
11709            return;
11710
11711        var confirmDelete = function(button) {
11712
11713            // warning message
11714            var warningMessage = $container.attr('data-warning');
11715
11716            // make html element from template
11717            var htmlOutput = vt.templates.renderTemplate('warningModal', warningMessage);
11718
11719            // append modal to body
11720            $('body').append(htmlOutput);
11721
11722
11723            var confirmed = setInterval(function () {
11724                if (confirm === true) {
11725                    deleteAed(button);
11726
11727                    // remove modal and stop interval
11728                    $('.modal.modal--confirm').remove();
11729                    clearInterval(confirmed);
11730
11731                    // set confirm back to false
11732                    confirm = false;
11733                }
11734            }, 1000);
11735
11736            // button click events
11737            $('.modal.modal--confirm').find('button').on('click', function(e) {
11738                var method = $(e.target).attr('data-method');
11739
11740                if (method === 'delete') {
11741                    confirm = true;
11742                } else {
11743
11744                    // remove modal and stop interval
11745                    $(e.target).parents('.modal').remove();
11746                    clearInterval(confirmed);
11747                }
11748            });
11749        };
11750
11751        var deleteAed = function(button) {
11752
11753            vt.animations.feedbackEvaluate($(button));
11754
11755            // get api url and item id
11756            var url = $container.attr('data-api') + $container.attr('data-itemid');
11757            var opts = {
11758                type: 'DELETE'
11759            }
11760
11761            var call = window.vt.ajax.call(url, opts, $container);
11762            call.success(function (data) {
11763
11764                // if success then remove item
11765                vt.animations.feedbackApproved($(button));
11766                var containerParent = $container.parent(),
11767                    count = parseInt(containerParent.attr('data-aed-count'));
11768
11769                containerParent.attr('data-aed-count', count - 1 );
11770                
11771                if ( count == 1) {
11772                    containerParent.remove();
11773                } else {
11774                    $container.remove();
11775                }
11776            });
11777            call.error(function (data) {
11778                vt.animations.feedbackError($(button));
11779                vt.initFormTools.serverError(data);
11780            });
11781        }
11782
11783        var deactivateAED = function(button) {
11784            
11785            $formContainer = $(button).parents('.aed-list__actions');
11786            $formContainerDeactivate = $formContainer.siblings('.aed-list__deactivate');
11787            $formContainerDeactivate.find('.js-validate').on('change', function() {
11788                $formContainerDeactivate.find('.js-form-submit').prop("disabled", false);
11789            });
11790
11791            // display form
11792            $formContainerDeactivate.addClass('active');
11793
11794            //Hide notificaiton buttons
11795            $formContainer.find('.aed-list__actions-bottom').hide();
11796
11797            // on click Annuller then hide form
11798            $formContainerDeactivate.find('.btn--back').on('click', function(e) {
11799                e.preventDefault()
11800                e.stopImmediatePropagation();
11801                $formContainerDeactivate.removeClass('active');
11802                $formContainer.find('.aed-list__actions-bottom').show();
11803            });
11804        };
11805
11806        var confirmActivate = function (button) {
11807            $formContainer = $(button).parents('.aed-list__actions');
11808            $formContainer = $formContainer.next('.aed-list__activate');
11809            // warning message
11810            var activateTitle = $formContainer.find('p[data-type="title"]').text();
11811            var activateContent = $formContainer.find('p[data-type="content"]').text();
11812            var activateButtonTxt = $formContainer.find('p[data-type="buttonTxt"]').text();
11813            var data = {};
11814            data.title = activateTitle;
11815            data.message = activateContent;
11816            data.buttonTxt = activateButtonTxt;
11817            // make html element from template
11818            var htmlOutput = vt.templates.renderTemplate('activateModal', data);
11819
11820            // append modal to body
11821            $('body').append(htmlOutput);
11822
11823
11824            var confirmed = setInterval(function () {
11825                if (confirmActivation === true) {
11826                    activateAED(button);
11827
11828                    // remove modal and stop interval
11829                    $('.modal.modal--confirm').remove();
11830                    clearInterval(confirmed);
11831
11832                    // set confirm back to false
11833                    confirmActivation = false;
11834                }
11835            }, 1000);
11836
11837            // button click events
11838            $('.modal.modal--confirm').find('button').on('click', function (e) {
11839                var method = $(e.target).attr('data-method');
11840
11841                if (method === 'post') {
11842                    confirmActivation = true;
11843                } else {
11844
11845                    // remove modal and stop interval
11846                    $(e.target).parents('.modal').remove();
11847                    clearInterval(confirmed);
11848                }
11849            });
11850        };
11851
11852        var activateAED = function (button) {
11853            vt.animations.feedbackEvaluate($(button));
11854
11855            // get api url and item id
11856            var url = $container.attr('data-activate-api') + $container.attr('data-itemid');
11857            var opts = {
11858                type: 'POST'
11859            }
11860
11861            var call = window.vt.ajax.call(url, opts, $container);
11862            call.success(function (data) {
11863
11864                // if success then remove item
11865                vt.animations.feedbackApproved($(button));
11866                var containerParent = $container.parent(),
11867                    count = parseInt(containerParent.attr('data-aed-count'));
11868
11869                containerParent.attr('data-aed-count', count - 1);
11870
11871                if (count == 1) {
11872                    containerParent.remove();
11873                } else {
11874                    $container.remove();
11875                }
11876
11877                //reload page
11878                window.location.reload();
11879            });
11880            call.error(function (data) {
11881                vt.animations.feedbackError($(button));
11882                vt.initFormTools.serverError(data);
11883            });
11884        };
11885
11886        $container.find('.js-aed-delete').on('click', function(e) {
11887            confirmDelete(e.target);
11888        });
11889
11890        $container.find('.js-aed-deactivate').on('click', function(e) {
11891            deactivateAED(e.target);
11892        });
11893
11894        $container.find('.js-aed-activate').on('click', function (e) {
11895            confirmActivate(e.target);
11896        });
11897    };
11898
11899})(jQuery);
11900;
11901; (function ($) {
11902
11903    window.vt = window.vt || {};
11904
11905    window.vt.initAedMap = (function () {
11906        var geocoder;
11907        var map;
11908        var marker;
11909        var isGoogleMapsLoaded = false;
11910        var pendingInitializations = [];
11911
11912        // map settings
11913        var settings = {
11914            fullscreenControl: false,
11915            streetViewControl: false,
11916            mapTypeControl: false,
11917            zoomControl: true,
11918            gestureHandling: 'cooperative',
11919            clickableIcons: false,
11920            disableDefaultUI: false,
11921            tilt: 0,
11922            styles: [
11923                {
11924                    featureType: 'poi',
11925                    elementType: 'labels',
11926                    stylers: [{ visibility: 'off' }]
11927                }
11928            ]
11929        };
11930
11931        // Initialize Google Maps objects once API is loaded
11932        var initializeGoogleMaps = function () {
11933            if (typeof google !== 'undefined' && typeof google.maps !== 'undefined') {
11934                geocoder = new google.maps.Geocoder();
11935                isGoogleMapsLoaded = true;
11936                
11937                // Process any pending initializations
11938                while (pendingInitializations.length > 0) {
11939                    var pending = pendingInitializations.shift();
11940                    pending.fn.apply(null, pending.args);
11941                }
11942                
11943                return true;
11944            }
11945            return false;
11946        };
11947
11948        // Wait for Google Maps to be ready
11949        var waitForGoogleMaps = function (callback, args) {
11950            if (initializeGoogleMaps()) {
11951                callback.apply(null, args);
11952            } else {
11953                pendingInitializations.push({ fn: callback, args: args });
11954            }
11955        };
11956
11957        var aedMap = function (container, details) {
11958            // Wait for Google Maps to be ready
11959            if (!isGoogleMapsLoaded) {
11960                waitForGoogleMaps(aedMap, [container, details]);
11961                return;
11962            }
11963
11964            var $container = $(container);
11965            var $parent = $container.parents('.form-element--address');
11966            var $radioButtons = $parent.find('[data-maptext]');
11967            var $latInput = $parent.find('.aed-map__input[id="latitude"]');
11968            var $lngInput = $parent.find('.aed-map__input[id="longitude"]');
11969
11970            // if not any radio buttons have been checked then return;
11971            if ($radioButtons.filter(':checked').length <= 0) {
11972
11973                // on change radio button run code again (remove old handlers first)
11974                $radioButtons.off('change.aedMap').on('change.aedMap', function() {
11975                    vt.initAedMap.aedMap(container, details);
11976                });
11977
11978                return;
11979            }
11980
11981            // reset lat and long input fields
11982            $latInput.val('');
11983            $lngInput.val('');
11984
11985            // show map
11986            $container.show();
11987            $container.parent('.js-formmap').removeClass('hidden');
11988
11989            // get address
11990            var address;
11991            address = details.address + ' ' + details.number + ', ';
11992            if (details.floor) { address += details.floor + '. '}
11993            if (details.door) { address += details.door + ', '}
11994            address += details.postalcode + ' ' + details.city;
11995
11996            // add map
11997            map = new google.maps.Map($container[0], settings);
11998
11999            // go to address on the map
12000            geocoder.geocode({'address': address}, function(results, status) {
12001                if (status === google.maps.GeocoderStatus.OK) {
12002                    map.setCenter(results[0].geometry.location);
12003                    map.fitBounds(results[0].geometry.viewport);
12004                } else {
12005                    geocoder.geocode({'address': 'Danmark'}, function(results, status) {
12006                        if (status === google.maps.GeocoderStatus.OK) {
12007
12008                            map.setCenter(results[0].geometry.location);
12009                            map.fitBounds(results[0].geometry.viewport);
12010                        }
12011                    });
12012                }
12013            });
12014
12015            // get checkbox:checked map text to overlay
12016            var overlayText = $radioButtons.filter(':checked').attr('data-maptext');
12017            var $overlay = $('<div class="aed-map__overlay"></div>');
12018            var $content = $('<div class="aed-map__content"></div>');
12019            var $text = $('<p></p>').text(overlayText);
12020            var $button = $('<button class="btn colored">OK</button>');
12021            
12022            $content.append($text).append($button);
12023            $overlay.append($content);
12024            $container.append($overlay);
12025
12026            // on map overlay button click remove overlay
12027            $button.on('click', function(e) {
12028                e.preventDefault();
12029                $overlay.remove();
12030            });
12031
12032            // on change radio button run code again (remove old handlers first)
12033            $radioButtons.off('change.aedMap').on('change.aedMap', function() {
12034                vt.initAedMap.aedMap(container, details);
12035            });
12036
12037            // add marker click event
12038            google.maps.event.addListener(map, 'click', function(e) {
12039               addMarker($container, e.latLng);
12040            });
12041        };
12042
12043        var addMarker = function ($container, location) {
12044            var locationInput = $container.parents('.form-element--address');
12045
12046            // clear current marker and locationInput value
12047            if (marker) {
12048                marker.setMap(null);
12049                $(locationInput).val('');
12050            }
12051
12052            // set marker on map
12053            marker = new google.maps.Marker({
12054                map: map,
12055                position: location,
12056                animation: google.maps.Animation.DROP,
12057                optimized: true
12058            });
12059
12060            // inset location to hidden input field
12061            $(locationInput).find('.aed-map__input[id="latitude"]').val(marker.position.lat()).trigger("change");
12062            $(locationInput).find('.aed-map__input[id="longitude"]').val(marker.position.lng()).trigger("change");
12063        };
12064
12065        var initializeExistingLocation = function () {
12066            // Wait for Google Maps to be ready
12067            if (!isGoogleMapsLoaded) {
12068                waitForGoogleMaps(initializeExistingLocation, []);
12069                return;
12070            }
12071
12072            // if already has a location
12073            var $locationInput = $('.form-element--address');
12074            if ($locationInput.length > 0) {
12075                var $latInput = $locationInput.find('.aed-map__input[id="latitude"]');
12076                var $lngInput = $locationInput.find('.aed-map__input[id="longitude"]');
12077                var $addressInput = $locationInput.find('input[id="address"]');
12078                
12079                var latitude = $latInput.val();
12080                var longitude = $lngInput.val();
12081                var address = $addressInput.val();
12082
12083                if (latitude.length > 0 && longitude.length > 0 || address.length > 0) {
12084                    var $aedMap = $locationInput.find('.aed-map');
12085                    var $formMap = $locationInput.find('.js-formmap');
12086
12087                    // show map
12088                    $aedMap.show();
12089                    $formMap.removeClass('hidden');
12090
12091                    // if no marker go to Danmark
12092                    if (latitude === '' && longitude === '') {
12093
12094                        // add map
12095                        map = new google.maps.Map($aedMap[0], settings);
12096
12097                        geocoder.geocode({'address': 'Denmark'}, function(results, status) {
12098                            if (status === google.maps.GeocoderStatus.OK) {
12099                                map.setCenter(results[0].geometry.location);
12100                                map.fitBounds(results[0].geometry.viewport);
12101                            }
12102                        });
12103
12104                    } else {
12105
12106                        // add location to settings
12107                        settings.zoom = 18;
12108                        settings.center = {lat: Number(latitude), lng: Number(longitude)};
12109
12110                        // add map
12111                        map = new google.maps.Map($aedMap[0], settings);
12112
12113                        // set marker on map
12114                        marker = new google.maps.Marker({
12115                            map: map,
12116                            position: settings.center,
12117                            animation: google.maps.Animation.DROP,
12118                            optimized: true
12119                        });
12120                    }
12121
12122                    // add marker click event
12123                    google.maps.event.addListener(map, 'click', function(e) {
12124                       addMarker($aedMap, e.latLng);
12125                    });
12126                }
12127            }
12128        };
12129
12130        // Initialize on document ready or after timeout
12131        setTimeout(initializeExistingLocation, 100);
12132
12133        return {
12134            aedMap: aedMap,
12135            initializeGoogleMaps: initializeGoogleMaps
12136        };
12137    })();
12138})(jQuery);
12139;
12140/**
12141 * AJAX helper for all UI -> API calls
12142 * call(): returns a promise from the AJAX call
12143 * The following methods are available on the response for specific use cases
12144 * always / complete / done / error / fail / success / then
12145 *
12146 * AJAX call runs animations by default
12147 */
12148
12149; (function ($) {
12150
12151    window.vt = window.vt || {};
12152
12153    window.vt.ajax = (function () {
12154        /* Makes an AJAX call with the url and opts provided 
12155         * @param {string} url - The URL to submit the request to
12156         * @param {object} opts - AJAX options object that can be passed to $.ajax
12157         * @returns {object} - Promise object returned by the AJAX call
12158         */
12159        var call = function (url, opts) {
12160            var defaultOpts = {
12161                url: url
12162            };
12163
12164            // Extend default options with user-defined options
12165            var reqOpts = $.extend(defaultOpts, opts);
12166
12167            // Make request
12168            var response = $.ajax(reqOpts);
12169
12170            // On success set positive animation state
12171            response.done(function () {
12172                //console.info('Ajax success', response.status, response.statusText, '(' + response.responseText + ') -', response);
12173            });
12174
12175            // On error set error animation state
12176            response.fail(function () {
12177                //console.warn('Ajax error:', response.status, response.statusText, '(' + response.responseText + ') -', response);
12178            });
12179            
12180            return response;
12181        };
12182
12183        // Make call function public
12184        return {
12185            call: call
12186        };
12187    })();
12188})(jQuery);;
12189; (function ($) {
12190
12191    window.vt = window.vt || {};
12192
12193    vt.animations = (function (elem) {
12194
12195        function feedbackEvaluate($elem, callback) {
12196            var $spinner = $elem.siblings('.spinner'),
12197                $spinnerContainer = $elem.closest('.spinner-container');
12198
12199            // Initiate the spinner
12200            $spinnerContainer.addClass('spinning');
12201            $spinner.addClass('js-evaluate');
12202
12203            // Wait for the element to get the right width
12204            setTimeout(function () {
12205
12206                // If the evaluate has not already ended
12207                if ($spinner.hasClass('js-evaluate')) {
12208
12209                    // Then start rotation
12210                    $spinner.addClass('js-rotate');
12211                }
12212            }, 300);
12213
12214        };
12215
12216        function feedbackApproved($elem) {
12217            var $spinner = $elem.siblings('.spinner');
12218
12219            // Update spinner classes
12220            $spinner.removeClass('js-evaluate').addClass('js-approved');
12221        }
12222
12223        function feedbackError($elem) {
12224            var $spinner = $elem.siblings('.spinner');
12225
12226            // Error message display handled by component that calls the animation library
12227
12228            // Remove spinner class
12229            $spinner.removeClass('js-evaluate');
12230            feedbackReset($elem);
12231        }
12232
12233        function feedbackReset($elem) {
12234            var $spinner = $elem.siblings('.spinner'),
12235                $spinnerContainer = $elem.closest('.spinner-container');
12236
12237            // Remove spinner classes
12238            $spinnerContainer.removeClass('spinning');
12239            $spinner.removeClass('js-approved js-rotate js-evaluate');
12240        }
12241
12242        return {
12243            feedbackEvaluate: feedbackEvaluate,
12244            feedbackApproved: feedbackApproved,
12245            feedbackError: feedbackError,
12246            feedbackReset: feedbackReset
12247        }
12248    })();
12249
12250})(jQuery);
12251;
12252; (function ($) {
12253
12254    window.vt = window.vt || {};
12255
12256    vt.initApplicationEndDate = function (elem) {
12257
12258        console.log('Edit end date...');
12259
12260        $('.date-picker').pickadate({ // Initiate datepickers
12261            format: 'dddd, d. mmmm yyyy',
12262            formatSubmit: 'yyyy/mm/dd',
12263            firstDay: 1,
12264            monthsFull: ['januar', 'februar', 'marts', 'april', 'maj', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'december'],
12265            monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'],
12266            weekdaysFull: ['Søndag', 'Mandag', 'Tirsdag', 'Onsdag', 'Torsdag', 'Fredag', 'Lørdag'],
12267            weekdaysShort: ['Søn', 'Man', 'Tir', 'Ons', 'Tor', 'Fre', 'Lør'],
12268            today: 'I dag',
12269            clear: 'Ryd',
12270            close: 'Luk',
12271            labelMonthNext: 'Næste måned',
12272            labelMonthPrev: 'Forrige måned',
12273            labelMonthSelect: 'Vælg en måned',
12274            labelYearSelect: 'Vælg et år',
12275            selectMonths: true,
12276            selectYears: 10,
12277            min: -1,
12278        })
12279        .on('change', function () {
12280
12281            console.log('date changed...')
12282
12283            if ($(this).val() != '') {
12284                $('#btnEditEndDate').removeAttr('disabled');
12285            }
12286            else {
12287                $('#btnEditEndDate').attr('disabled', 'disabled');
12288            }
12289
12290        });
12291
12292        $('#btnEditEndDate').on('click', function () {
12293
12294            console.log('date submitted...');
12295
12296        });
12297    };
12298
12299})(jQuery);;
12300; (function ($) {
12301
12302    window.vt = window.vt || {};
12303
12304    vt.initApplicationForm = function (elem) {
12305        vt.applicationForm.$element = $(elem); //set application element
12306        vt.applicationForm.init(); //Initiate application form
12307    };
12308
12309    vt.applicationForm = {
12310        $element: null, //The application-form dom element
12311        $menu: $('#subnav'), //The application form menu element
12312        currentSection: 0, //Index value of the active/visible section
12313        isDraft: true, //If application is still in draft mode
12314        isSafari: navigator.userAgent.indexOf('Mac') > 1 && /^((?!chrome).)*safari/i.test(navigator.userAgent), //Used for Safari hack only
12315        isTouch: ('ontouchstart' in document.documentElement), //Used for Safari hack only
12316        isBusy: false, //When in autosave mode this is true to avoid saving the application form with empty fields
12317        deadline: null, //The application deadline is set,
12318        applicationReady: false,
12319
12320        init: function () {
12321            //$.cookie('applicationID', value, { expires: null });
12322            Cookies.set('applicationID', $('#ApplicationId').val());
12323
12324            vt.applicationForm.isDraft = $('#IsDraft').val().toLowerCase() == "true"; //Read from hidden field dom element
12325
12326            $('div[data-isrequired="True"] > .form-element-file > .form-file-name-app:gt(0)').removeAttr('required'); //Remove required attribute from file input elements except form the first
12327
12328            vt.applicationForm.menu.init(); //Initiate the menu
12329            vt.applicationForm.dependencies.init(); //Initiate dependent form elements
12330            vt.applicationForm.print.init(); //Init print
12331
12332            if (vt.applicationForm.isDraft) { //If application is in draft mode...
12333                vt.showCookieMessage('applicationPageMsg', true); //Any cookie messages to show? If any show them and delete them afterwards
12334
12335                vt.applicationForm.autoSave.init(); //... initiate autosave
12336                vt.applicationForm.sections.testCompletion(); //... test if application is complete
12337                vt.applicationForm.deadline.init(); //... show the right deadline, depending on location and apllication type
12338            }
12339            else {
12340                $('.application-form').addClass('completed'); //If not a draft apply the right css styles
12341            }
12342
12343            window.onhashchange = function () { vt.applicationForm.hashChange() }; //When hash changes, get pagenumber
12344
12345            vt.applicationForm.hashChange(); //Start by listening to the hash, if any
12346
12347            $('.auto-numeric').autoNumeric('init', { aSep: '.', aDec: ',', mDec: 0}); //Initiate auto numeric fields
12348            $('input[type="number"], .auto-numeric').keydown(function (e) { //Allow only numeric values
12349                if ($.inArray(e.keyCode, [46, 8, 9, 27, 13, 110, 190]) !== -1 ||
12350                    (e.keyCode == 65 && e.ctrlKey === true) ||
12351                    (e.keyCode == 67 && e.ctrlKey === true) ||
12352                    (e.keyCode == 88 && e.ctrlKey === true) ||
12353                    (e.keyCode >= 35 && e.keyCode <= 39)) {
12354                    return;
12355                }
12356                if ((e.shiftKey || (e.keyCode < 48 || e.keyCode > 57)) && (e.keyCode < 96 || e.keyCode > 105)) {
12357                    e.preventDefault();
12358                }
12359            });
12360
12361            $('.date-picker').pickadate({ // Initiate datepickers
12362                format: 'dddd, d. mmmm yyyy',
12363                formatSubmit: 'yyyy/mm/dd',
12364                hiddenName: true,
12365                firstDay: 1,
12366                monthsFull: ['januar', 'februar', 'marts', 'april', 'maj', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'december'],
12367                monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'],
12368                weekdaysFull: ['Søndag', 'Mandag', 'Tirsdag', 'Onsdag', 'Torsdag', 'Fredag', 'Lørdag'],
12369                weekdaysShort: ['Søn', 'Man', 'Tir', 'Ons', 'Tor', 'Fre', 'Lør'],
12370                today: 'I dag',
12371                clear: 'Ryd',
12372                close: 'Luk',
12373                labelMonthNext: 'Næste måned',
12374                labelMonthPrev: 'Forrige måned',
12375                labelMonthSelect: 'Vælg en måned',
12376                labelYearSelect: 'Vælg et år',
12377                selectMonths: true,
12378                selectYears: 10,
12379                min: new Date()
12380            });
12381
12382            $(document).on('keyup focus', 'textarea', function (event) { //Auto expand textareas on keyup and focus
12383                var el = $(this);
12384                el.height(el.prop('scrollHeight') - 20);
12385            });
12386
12387            $(document).on('focus', 'textarea', function (event) { //Set current scroll position in data attribute
12388                $(this).data('top-position', $(window).scrollTop());
12389            });
12390
12391            $(document).on('blur', 'textarea', function (event) { //On textarea blur...
12392                var $element = $(this);
12393                if ($element.height() < 111) { //... if no expanding, do nothing
12394                    return true;
12395                };
12396
12397                if (event.relatedTarget) { //...if focus on a new input element...
12398                    setTimeout(function () { // ... wait for blurred element to animate back to normal height and do a scroll to the new target
12399                        $('html, body').animate({ scrollTop: $(event.relatedTarget).offset().top - 150 }, 1);
12400                    }, 350);
12401                    return true;
12402                };
12403
12404                if ($element.data('top-position') != '' && $element.data('top-position') != $(window).scrollTop() && $(':input:focus').length == 0) { //If blurred element is scrolled out of original scroll position, scroll back to original
12405                    $('html, body').animate({ scrollTop: $element.data('top-position') }, 1)
12406                }
12407            });
12408
12409            //Safari has problems with autosave and form validation and needs a little help...
12410            //Instead of validating when autosaving, wait for a blur event and validate afterwards...
12411            if (vt.applicationForm.isSafari && !vt.applicationForm.isTouch) { //Hack to help stupid Safari browser behave right...
12412                $(document).on('blur', 'input.sfr-validate', function (event) {
12413                    window.setTimeout(function () {
12414                        vt.applicationForm.sections.validateSection()
12415                    }, 400);
12416                })
12417            };
12418
12419            //Nice print of textareas.
12420            $(document).on('click', '.btn-print', function (event) { //When clicking the printbutton...
12421                vt.applicationForm.setHash(0); //Go to first application page...
12422                var $textAreas = $('#applicationForm textarea'); //Find all textareas...
12423                $textAreas.each(function () {
12424                    $element = $(this);
12425                    var val = $element.val();
12426                    $element.next('.textarea-print').html(val); //Copy the textarea value to hidden element, only visible on print
12427                });
12428                setTimeout(function () { //Wait a while and then call print dialog
12429                    window.print();
12430                }, 700);
12431                event.preventDefault();
12432            });
12433
12434            //Click the Next button
12435            $(document).on('click', '.btnNext', function (event) {
12436                var $currentMenuItem = $('ul > li.selected', $('#subnav')); //Find current menu item...
12437                $currentMenuItem.next().find('a').trigger('click'); //Trigger click on the next menu item
12438                event.preventDefault();
12439            });
12440
12441            //Click on 'Send ansøgning' button, show confirm user message.
12442            $(document).on('click', '.btnSubmit', function (event) { 
12443                var msgConfig = {
12444                    container: $('body'),
12445                    content: $('#applicationSubmitConfirm').html() + '<p class="text-align-right"><button class="btn colored black btnClose">Fortryd</button><button ' + ((vt.applicationForm.isBusy) ? 'disabled="disabled"' : '') + ' class="btn colored progress-button btnSendApp" data-loading="Sender" data-finished="Færdig" data-type="background-bar">Send</button></p>', //Get usermessage from dom element
12446                    addOverlay: true
12447                };
12448                vt.userMessage(msgConfig); //Use usermessage plugin
12449                vt.applicationForm.setSubmitStatus(); //Test if application is not busy and enable submit
12450                event.preventDefault();
12451            });
12452
12453            //Click on 'Fortryd' button, show previous 'Send ansøgning' user message
12454            $(document).on('click', '.btnCancel', function (event) {
12455                vt.applicationForm.showSubmit();
12456                event.preventDefault();
12457            });
12458
12459            //Click on 'Send' button, remove draft attribute and submit form
12460            $(document).on('click', '.btnSendApp', function (event) { 
12461                $('.isDraft').val("False");
12462                var $element = vt.applicationForm.$element,
12463                    $btn = $(this);
12464
12465                $btn.progressInitialize().progressStart(); //Use button progress plugin to fake progress
12466
12467                $('form', $element).ajaxSubmit({
12468                    type: 'POST',
12469                    url: $element.data('service-url'),
12470                    error: function (response) { //Error on submit, show error user mesage
12471                        var msgConfig = {
12472                            container: $('body'),
12473                            content: $('#applicationSubmitError').html() + '<p class="text-align-right"><button class="btn colored black btnCancel">Luk</button></p>', //Get usermessage from dom element
12474                            addOverlay: true
12475                        };
12476                        vt.userMessage(msgConfig);
12477                        $('.isDraft').val("True");
12478                    },
12479                    success: function (response) { //Success!! Application is submitted
12480
12481                        $btn.progressFinish(); //Show progress button
12482
12483                        //Add cookie message
12484                        var cookieConfig = {
12485                            title: $('#applicationSubmitSuccess h1').text(),
12486                            message: $('#applicationSubmitSuccess p').text(),
12487                            addOverlay: true
12488                        };
12489
12490                        //Show cookie message on MyPage
12491                        vt.setCookieMessage('myPageMsg', cookieConfig);
12492                        
12493                        vt.applicationForm.disableInputs(); //Disable on inputs
12494                        vt.applicationForm.$element.addClass('completed'); //get the right styles
12495
12496                        var redirectUrl = $('body').data('mypage-url'); //Get MyPpage url...
12497                        window.location.href = redirectUrl; // redirect to My Page
12498                    }
12499                });
12500                event.preventDefault();
12501            });
12502
12503            //When uploading a file attachments...
12504            $(document).on('change', 'input.form-file-name-app', function () {
12505
12506                var $input = $(this),
12507                    $element = vt.applicationForm.$element,
12508                    url = $element.data('service-url') + '/' + $('#ApplicationId').val() + $element.data('attachment-url'), // build unique url for the application
12509                    $container = $(this).parent(),
12510                    $deleteAttachment = $container.find('input.visuallyhidden');
12511
12512                $('#autosaveInfo').removeClass('off savedmode').addClass('savemode'); //Show autosave
12513
12514                $container.wrap('<form></form>'); //Make a custom form
12515                $container.parent().ajaxSubmit({ //Submit the form
12516                    type: 'POST',
12517                    url: url,
12518                    error: function (response) { //Ups! An error...
12519                        $('#autosaveInfo').removeClass('savemode').addClass('savedmode'); // Update autosave
12520
12521                        $input.val(''); // Reset file input
12522
12523                        var data = $.parseJSON(response.responseText); //Show the error message from server in a growl
12524                        if (data.Message)
12525                            $.growl.error({ title: 'Der er sket en fejl', message: data.Message, duration: 15000 });
12526
12527                        if ($container.parent().is('form'))
12528                            $container.unwrap(); //Remove the form again
12529
12530                        setTimeout(function () { //Wait a while...
12531                            $('#autosaveInfo').addClass('off'); //Hide autosave..
12532                            vt.applicationForm.sections.validateSection(); //Validate the section...
12533                        }, 1000);
12534                    },
12535                    success: function (response) { //Success!
12536                        $('#autosaveInfo').removeClass('savemode').addClass('savedmode'); //Update autosave
12537                        $input.val(response); // Maybe the file was renamed on the server? Show the new name
12538                        $container.addClass('form-element-file--selected').find('.btn-delete-file').fadeIn(); //Make it possible to delete the file
12539                        if ($container.parent().is('form'))
12540                            $container.unwrap(); //Remove the form
12541
12542                        setTimeout(function () { //Wait a while...
12543                            $('#autosaveInfo').addClass('off'); //Hide autosave..
12544                            vt.applicationForm.sections.validateSection(); //Validate the section...
12545                        }, 1000);
12546                    }
12547                });
12548            });
12549
12550            //When deleting an attachment...
12551            $(document).on('click', '.btn-delete-file', function (e) {
12552                var $deletebutton = $(this),
12553                    $element = vt.applicationForm.$element,
12554                    $container = $(this).parent(),
12555                    $fileElement = $container.find('.btn-file :file'),
12556                    $fileName = $container.find('input.form-file-name-app'),
12557                    url = $element.data('service-url') + '/' + $('#ApplicationId').val() + $element.data('attachment-url') + '/' + $fileName.val() + '/'; //Build unique url
12558
12559                $('#autosaveInfo').removeClass('off savedmode').addClass('savemode'); //Show autosave
12560
12561                $container.wrap('<form></form>'); //Make a custom form
12562                $container.parent().ajaxSubmit({ //Submit the form
12563                    type: 'DELETE',
12564                    url: url,
12565                    error: function (response) { //An error!!...
12566                        var data = $.parseJSON(response.responseText);
12567                        $('#autosaveInfo').removeClass('savemode').addClass('savedmode'); //Update autosave
12568                        $.growl.error({ title: 'Der er sket en fejl', message: data.Message, duration: 5000 }); //Show the error message
12569
12570                        if ($container.parent().is('form'))
12571                            $container.unwrap(); //Remove the form
12572
12573                        setTimeout(function () { //Wait a while...
12574                            $('#autosaveInfo').addClass('off'); //Hide autosave..
12575                            vt.applicationForm.sections.validateSection(); //Validate the section...
12576                        }, 1000);
12577                    },
12578                    success: function (response) {
12579                        $('#autosaveInfo').removeClass('savemode').addClass('savedmode');//Update autosave
12580
12581                        $fileElement.val(''); //Reset form fields
12582                        $fileName.val('');
12583                        $('input.delete-attachment').val(false);
12584
12585                        $container.removeClass('form-element-file--selected'); //Update styles
12586
12587                        if ($container.parent().is('form'))
12588                            $container.unwrap(); //Remove the form
12589
12590                        setTimeout(function () { //Wait a while...
12591                            $('#autosaveInfo').addClass('off'); //Hide autosave..
12592                            vt.applicationForm.sections.validateSection(); //Validate the section...
12593                        }, 1000);
12594                    }
12595                });
12596
12597                e.preventDefault();
12598            });
12599        },
12600
12601        setApplicationStatus: function () { //Function to check if all application inputs are correct and ready to send. Show/hide submit message if all good/bad
12602            var $applicationMenu = vt.applicationForm.$menu,
12603                steps = $('ul > li', $applicationMenu).length, //Number of application steps
12604                stepsDone = $('ul > li.done', $applicationMenu).length, //Number of application steps completed
12605                submitInvisible = $('#userMessage.visible').length == 0; //Is the submit user message allready visible?
12606
12607            if (!vt.applicationForm.isDraft) { //If not in draft mode add the right styles
12608                vt.applicationForm.$element.addClass('completed');
12609            }
12610
12611            if (steps == stepsDone && vt.applicationForm.isDraft) { //If all is done and still a draft...
12612                if (submitInvisible) { //... and the the submit message is not visible...
12613                    vt.applicationForm.showSubmit(); //... show submit message
12614                }
12615                vt.applicationForm.enableSubmit();
12616            }
12617            else {
12618                vt.userMessageHide(); //The application is not completed, hide the submit user message
12619                vt.applicationForm.disableSubmit();
12620            }
12621        },
12622
12623        disableSubmit: function () {
12624            $('.btnSubmit').attr('disabled', 'disabled');
12625        },
12626
12627        enableSubmit: function () {
12628            $('.btnSubmit').removeAttr('disabled');
12629        },
12630
12631        showSubmit: function () { //Application is ready, show submit message
12632
12633            if (!vt.applicationForm.applicationReady) {
12634                var msgConfig = {
12635                    container: $('body'),
12636                    content: $('#applicationSubmit').html() + '<p class="text-align-right"><button class="btn colored black btnClose">OK</button></p>', // Get submit message from dom element
12637                    addOverlay: true
12638                };
12639                vt.userMessage(msgConfig); //Use use message plugin to show message
12640                vt.applicationForm.applicationReady = true;
12641            }
12642        },
12643
12644        autoSave: { //Initiate autosave
12645            init: function () {
12646                var $element = vt.applicationForm.$element;
12647
12648                //add autosave-dom-elements...
12649                $('body').append('<div id="autosaveInfo" class="off"><div class="autosaving">Gemmer<span>.</span><span>.</span><span>.</span></div><div class="autosaved">Gemt!</div></div>');
12650
12651                //Use autosave plugin...
12652                vt.autoSave({
12653                    url: $element.data('service-url'),
12654                    form: $('#applicationForm'),
12655                    timer: 1000,
12656                    beforeSendHandling: function () {
12657                        vt.applicationForm.isBusy = true; //The application is now busy
12658
12659                        $('#autosaveInfo').removeClass('off savedmode').addClass('savemode'); //Show autosave
12660
12661                        //If not focus on input-text-field in desktop Safari - validate section. Safari waits for a blur event
12662                        if (!vt.applicationForm.isSafari || vt.applicationForm.isTouch || ($('input.sfr-validate:focus').length == 0 && vt.applicationForm.isSafari)) {
12663                            window.setTimeout(function () {
12664                                vt.applicationForm.sections.validateSection()
12665                            }, 400);
12666                        }
12667                    },
12668                    afterSendHandling: function () {
12669                        $('#autosaveInfo').addClass('off'); //Hide autosave
12670                    },
12671                    callback: function (data) {
12672                        $('#autosaveInfo').removeClass('savemode').addClass('savedmode'); //Update autosave
12673                        vt.applicationForm.isBusy = false; // Not busy
12674                        vt.applicationForm.setSubmitStatus(); //Maybe the form is completed? Check submit status...
12675                    },
12676                    errorHandling: function (data) {
12677                        $('#autosaveInfo').removeClass('savemode').addClass('savedmode'); //Update autosave
12678                        vt.applicationForm.isBusy = false; // Not busy
12679
12680                        $.growl.error({ title: 'Der er sket en fejl', message: 'Din ansøgning kunne ikke gemmes', duration: 5000 }); //Show growl error message.
12681
12682                        //If not focus on input-text-field in desktop Safari - validate section. Safari waits for a blur event
12683                        if (!vt.applicationForm.isSafari || vt.applicationForm.isTouch || ($('input.sfr-validate:focus').length == 0 && vt.applicationForm.isSafari)) {
12684                            window.setTimeout(function () {
12685                                vt.applicationForm.sections.validateSection()
12686                            }, 400);
12687                        }
12688                        //window.location.href = $element.data('error-page-url');
12689                    },
12690                    specialForm: function () {
12691                        return {
12692                            form: $('form.testform').clone(), //Build special form to submit only the current section
12693                            inputs: $(':input:not(:file)', $('#applicationForm .application-page:visible').clone(true)).removeAttr('id') //Avoid file inputs and remove id's
12694                        }
12695                    }
12696                });
12697            }
12698        },
12699
12700        menu: { //Initiate application menu
12701            init: function () {
12702                var $applicationMenu = vt.applicationForm.$menu;
12703                $('ul > li:first-child', $applicationMenu).addClass('selected'); //Select first menu item
12704
12705                $('ul li a', $applicationMenu).on('click', function (event) { //Click a menu item
12706                    var $parent = $(this).parent();
12707
12708                    if ($parent.hasClass('selected')) { //If allready selected, abort
12709                        return false;
12710                        event.preventDefault();
12711                    }
12712
12713                    $('html, body').animate({ scrollTop: 0 }, 300, function () { //Scroll to top..
12714                        vt.applicationForm.setHash($parent.index()); //update hash value with new index
12715                    });
12716                    event.preventDefault();
12717                });
12718            },
12719            setSelected: function (sectionIndex) { //Select menu item by index
12720                var $applicationMenu = vt.applicationForm.$menu;
12721                $('ul li.selected', $applicationMenu).removeClass('selected'); //Remove style..
12722                $('ul li', $applicationMenu).eq(sectionIndex).addClass('selected'); //... add styles to selected menu item
12723            }
12724        },
12725
12726        disableInputs: function () {//Function to disable all inputs
12727            $(':input', vt.applicationForm.$element).not('button').prop({ 'disabled': true })
12728        },
12729
12730        setSubmitStatus: function () { //Function to enable final submit button
12731            if ($('.btnSendApp').length > 0) { //Is the button visible at all...
12732                if (vt.applicationForm.isBusy) //is the application form busy..
12733                    $('.btnSendApp').attr('disabled', 'disabled'); //... disable button
12734                else
12735                    $('.btnSendApp').removeAttr('disabled'); // ...enable button
12736            }
12737        },
12738
12739        sections: {
12740
12741            showSection: function (sectionIndex) { //Function to show section called by index
12742                var $currentSection = $('#applicationForm > section.selected'), //current visible section
12743                    $applicationMenu = vt.applicationForm.$menu,
12744                    $nextSection = $('#applicationForm > section').eq(sectionIndex); //next section
12745
12746                $nextSection.show(); //Show next section
12747                $currentSection.addClass('deselected'); //animate previous section
12748
12749                window.setTimeout(function () { //Wait for css-animation...
12750                    $nextSection.removeClass('deselected').addClass('selected'); // ... add new styles...
12751
12752                    window.setTimeout(function () { //... wait again...
12753                        $currentSection.removeClass('deselected').removeClass('selected').hide(); //... hide previous section
12754                        $nextSection.find(':input:first').trigger('focus'); //Focus the first input
12755                        vt.applicationForm.currentSection = sectionIndex; //Update application
12756                    }, 200);
12757
12758                }, 200);
12759            },
12760
12761            validateSection: function () { //Validate current visible section.
12762                var $section = $('#applicationForm > section').eq(vt.applicationForm.currentSection), //Get current section
12763                    $form = $('<form class="testform"></form>'), // Start a custom form
12764                    $currentMenuItem = $('li', vt.applicationForm.$menu).eq(vt.applicationForm.currentSection), //Find related menu item
12765                    dependentError = vt.applicationForm.dependencies.validateDependencies($section), //Validate dependencies
12766                    requiredCheckboxes = $('div[data-isdependent="False"][data-isrequired="True"] input[type=checkbox]', $section), //Array of required and not dependent checkboxes
12767                    requiredCheckboxError = false; //Default no error
12768
12769                if (requiredCheckboxes.length > 0) // If any required and not dependent checkboxes...
12770                    requiredCheckboxError = requiredCheckboxes.filter(':checked').length == 0; // ... there's an error if none are checked. 
12771
12772                $currentMenuItem.removeClass('done'); //remove 'done' of current menu item
12773
12774                if (!dependentError && !requiredCheckboxError) { //If valid dependencies and valid required checkboxes, validate section
12775                    var $inputs = $('div[data-isrequired="True"]:visible :input[required]', $section).clone(true); //Clone the required inputs in the section and add to custom form
12776                    $inputs.appendTo($form);
12777
12778                    $form.insertAfter('#applicationForm').h5Validate(); //HTML5 validate custom form
12779
12780                    if ($form.h5Validate('allValid')) { //Validation is OK, do custom radio button validation
12781
12782                        var customRadioValidationValid = true,
12783                            $requiredRadioButtons = $('input[type=radio][required]', $form);
12784
12785                        if ($requiredRadioButtons.length > 0)
12786                            customRadioValidationValid = $('input[type=radio][required]', $form).h5Validate('isValid');
12787
12788                        if (customRadioValidationValid) //Everything is perfect and completed...
12789                            $currentMenuItem.addClass('done'); // section done..
12790                    }
12791                    $form.remove();//remove custom form
12792                };
12793
12794                vt.applicationForm.setApplicationStatus(); //Test the completion of the form, set status
12795            },
12796
12797            testCompletion: function () { //Test completion on each section
12798                var $element = vt.applicationForm.$element,
12799                    $applicationMenu = vt.applicationForm.$menu,
12800                    $sections = $('#applicationForm > section.application-page', $element);
12801
12802                $sections.each(function () { //Run through sections
12803                    var $form = $('<form class="testform"></form>'),
12804                        $section = $(this),
12805                        dependentError = vt.applicationForm.dependencies.validateDependencies($section), //Validate dependencies
12806                    requiredCheckboxes = $('[data-isrequired="True"] input[type=checkbox]', $section), //Array of required and not dependent checkboxes
12807                    requiredCheckboxError = false; //Default no error
12808
12809                    if (requiredCheckboxes.length > 0) // If any required and not dependent checkboxes...
12810                        requiredCheckboxError = requiredCheckboxes.filter(':checked').length == 0; // ... there's an error if none are checked. 
12811                    if (!dependentError && !requiredCheckboxError) { //If valid depen
12811dencies and valid required checkboxes, validate section
12812                        $section.clone(true).show().appendTo($form);//Clone the section and add to custom form
12813                        $form.insertAfter('#applicationForm').h5Validate();//HTML5 validate custom form
12814
12815                        var customRadioValidationValid = true,
12816                            $requiredRadioButtons = $('input[type=radio][required]', $form);
12817
12818                        if ($requiredRadioButtons.length > 0)
12819                            customRadioValidationValid = $('input[type=radio][required]', $form).h5Validate('isValid');
12820
12821                        if ($form.h5Validate('allValid') && customRadioValidationValid)//Everything is perfect and completed...
12822                            $('li', $applicationMenu).eq($sections.index($section)).addClass('done');// section done..
12823                    }
12824                    $form.remove();
12825                });
12826                vt.applicationForm.setApplicationStatus();
12827            }
12828        },
12829
12830        setHash: function (page) {
12831            document.location.hash = 'page=' + page; //Update page hash value...
12832        },
12833
12834        hashChange: function () { //If hash value changes...
12835            if (document.location.hash != '') {
12836                var hashFilter = document.location.hash.slice(1).split('&');
12837                var page = hashFilter[0].split('='); //find the page requested
12838
12839                if (typeof page[1] !== 'undefined' && page[0] == 'page') {
12840                    vt.applicationForm.sections.showSection(page[1]); //show the requested page
12841                    vt.applicationForm.menu.setSelected(page[1]); //set selected
12842                }
12843            }
12844            else { //if no page hash value
12845                vt.applicationForm.sections.showSection(0); // show first page
12846                vt.applicationForm.menu.setSelected(0);
12847            }
12848            return;
12849        },
12850
12851        dependencies: {
12852
12853            $dependents: $("div[data-isdependent='True']", $('#applicationForm')),
12854            $dependencies: $('input[data-dependencyidentifier]', $('#applicationForm')),
12855
12856            init: function () {
12857                vt.applicationForm.dependencies.hideDependents(vt.applicationForm.dependencies.$dependents, false); //Hide all dependents by default
12858
12859                vt.applicationForm.dependencies.$dependencies.on('change', function () { //If change on a dependency
12860
12861                    var $dependencygroupid = $(this).data().dependencygroup; //Get the group id
12862                    var $dependencygroupmembers = vt.applicationForm.dependencies.$dependents.filter(function () { return $(this).data().dependencygroup == $dependencygroupid; }); // get all the dependecies in the group
12863
12864                    var $dependencyidentifier = $(this).data().dependencyidentifier;
12865                    var $dependentsonthis = $dependencygroupmembers.filter(function () { return $(this).data().dependencyvalue == $dependencyidentifier; }); //Find all dependencies filtered by identifier
12866
12867                    vt.applicationForm.dependencies.hideDependents($dependencygroupmembers, true); //Hide all groupmembers
12868
12869                    if ($(this).is(':checked')) {
12870                        $dependentsonthis.show(); //Show only current dependencies
12871                        return;
12872                    }
12873                });
12874
12875                var $checkedElements = vt.applicationForm.dependencies.$dependencies.filter(':checked'); //Open on pageload
12876
12877                if ($checkedElements.length > 0) {
12878
12879                    $checkedElements.each(function () {
12880                        $element = $(this);
12881
12882                        var $dependencygroupid = $element.data().dependencygroup;
12883                        var $dependencygroupmembers = vt.applicationForm.dependencies.$dependents.filter(function () { return $element.data().dependencygroup == $dependencygroupid; });
12884
12885                        var $dependencyidentifier = $element.data().dependencyidentifier;
12886                        var $dependentsonthis = $dependencygroupmembers.filter(function () { return $(this).data().dependencyvalue == $dependencyidentifier; });
12887
12888                        $dependentsonthis.show();
12889
12890                    });
12891                };
12892
12893            },
12894            hideDependents: function (dependents, reset) { //function to hide dependents
12895                $(dependents).each(function () {
12896                    $(this).hide();
12897                    if (reset) {
12898                        vt.applicationForm.dependencies.resetDependents($(this)); //reset dependents?
12899                    }
12900                });
12901            },
12902            resetDependents: function ($section) { //reset dependents
12903                $section.find('input:text, input:password, input[type="email"], input[type="number"], input:file, select, textarea').val('');
12904                $section.find('input:radio, input:checkbox').removeAttr('checked').removeAttr('selected');
12905            },
12906
12907            validateDependencies: function ($section) { //Validate dependencies
12908                var $dependents = $("div[data-isdependent='True']", $section),
12909                    $dependencies = $('input[data-dependencyidentifier]', $section),
12910                    dependentError = false;
12911
12912                var $checkedElements = $dependencies.filter(':checked');
12913
12914                if ($checkedElements.length == 0)
12915                    return false;
12916
12917                if ($checkedElements.length > 0) {
12918
12919                    $checkedElements.each(function () {
12920                        $element = $(this);
12921
12922                        var $dependencygroupid = $element.data().dependencygroup;
12923                        var $dependencygroupmembers = $dependents.filter(function () { return $element.data().dependencygroup == $dependencygroupid; });
12924
12925                        var $dependencyidentifier = $element.data().dependencyidentifier;
12926                        var $dependentsonthis = $dependencygroupmembers.filter(function () { return $(this).data().dependencyvalue == $dependencyidentifier; });
12927
12928                        var $dependentInputs = $(':input', $dependentsonthis).not(':hidden');
12929
12930                        if ($dependentInputs.length) {
12931                            dependentError = $dependentInputs.serializeArray().length == 0;
12932                        }
12933                    });
12934                }
12935
12936                return dependentError;
12937            }
12938        },
12939
12940        deadline: { //Show/hide deadline dom elements based on input field selection
12941            nationalDeadlineString: (typeof ($('#appDeadline').data('national')) !== 'undefined') ? $('#appDeadline').data('national') : 'Landsdækkende',
12942
12943            init: function () {
12944
12945                if ($('input[name="tfxcouncil"]').length > 0) {
12946
12947                    // TFSLA-957: don't show application deadline initially
12948                    // vt.applicationForm.deadline.setDeadline();
12949
12950                    $('input[name="tfxcouncil"]').on('change', function () {
12951                        vt.applicationForm.deadline.setDeadline();
12952                    });
12953                }
12954
12955                $('#appDeadline').show();
12956
12957            },
12958
12959            setDeadline: function () {
12960                var value = $('input[name="tfxcouncil"]:checked').val();
12961
12962                if (typeof (value) != 'undefined') {
12963                    if (value == vt.applicationForm.deadline.nationalDeadlineString || value == '') {
12964                        $('#deadlineNational').show();
12965                        $('#deadlineRegional').hide();
12966                    }
12967                    else {
12968                        $('#deadlineNational').hide();
12969                        $('#deadlineRegional').show();
12970                    }
12971                }
12972            }
12973
12974        },
12975
12976        print: {
12977
12978            init: function () {
12979                var $textAreas = $('#applicationForm textarea'); //get all textareas
12980
12981                $textAreas.each(function () {
12982                    var $element = $(this);
12983                    $('<div class="textarea-print"></div>').insertAfter($element); //Insert print div after each textarea for print only
12984                });
12985
12986                var beforePrint = function () { //Before print...
12987                    $textAreas.each(function () {
12988                        $element = $(this);
12989                        var val = $element.val();
12990                        $element.next('.textarea-print').html(val); //copy textarea value print div.
12991                    });
12992                };
12993
12994                var afterPrint = function () {
12995                    $textAreas.each(function () {
12996                        $element = $(this);
12997                        $element.next('.textarea-print').text(''); //remove print div content
12998                    });
12999                };
13000
13001                if (window.matchMedia) { //Listen for print events
13002                    var mediaQueryList = window.matchMedia('print');
13003                    mediaQueryList.addListener(function (mql) {
13004                        if (mql.matches) {
13005                            beforePrint();
13006                        } else {
13007                            afterPrint();
13008                        }
13009                    });
13010                }
13011
13012                window.onbeforeprint = beforePrint;
13013                window.onafterprint = afterPrint;
13014            }
13015        }
13016    }
13017
13018})(jQuery);
13019
13020
13021
13022//Textarea clone() bug workaround | Spencer Tipping
13023//Licensed under the terms of the MIT source code license
13024
13025//Motivation.
13026//jQuery's clone() method works in most cases, but it fails to copy the value of textareas. This patch replaces jQuery's clone() method with a wrapper that fills in textarea values.
13027
13028(function (original) {
13029    jQuery.fn.clone = function () {
13030        var result = original.apply(this, arguments),
13031            my_textareas = (this.is('textarea')) ? this : this.find('textarea'),
13032        result_textareas = (result.is('textarea')) ? result : result.find('textarea');
13033
13034        for (var i = 0, l = my_textareas.length; i < l; ++i)
13035            $(result_textareas[i]).val($(my_textareas[i]).val());
13036
13037        return result;
13038    };
13039})(jQuery.fn.clone);;
13040; (function ($) {
13041
13042    // Declaring namespace if not already declared
13043    window.vt = window.vt || {};
13044
13045    // Wrapping methods in a module
13046    vt.initApplicationWizard = function (elem) {
13047
13048        // Variables
13049        var $element = $(elem),
13050            $radios = $(':radio', $element),
13051            $togglers = $('.toggler', $element),
13052            $button = $('button', $element),
13053            //storage = $.sessionStorage,
13054            appID = Cookies.get('applicationID'),
13055            isBrowserBack = window.location.hash.indexOf('appID') > 0;
13056
13057        console.log('isBrowserBack', isBrowserBack);
13058
13059        //if (isBrowserBack) {
13060        if (appID && isBrowserBack) {
13061            showRunningApplicationMessage();
13062        }
13063        else {
13064            vt.showCookieMessage('wizardPageMsg', true); //Any cookie messages to show? If any show them and delete them afterwards
13065        }
13066
13067
13068        var togglers = function () {
13069            var $toggler = $(this),
13070                $container = $toggler.parents('.toggle-item'),
13071                isToggled = $container.hasClass('toggle-on'),
13072                hasSubTypes = $toggler.siblings('.content').children('.form-element').length > 0;
13073
13074            $radios.each(function () {
13075                $(this).prop('checked', false);
13076            });
13077
13078            if (isToggled) {
13079                $(':radio', $toggler).prop('checked', false);
13080                $button.fadeOut();
13081            }
13082            else {
13083                moveNextButton($container);
13084                $(':radio', $toggler).prop('checked', true);
13085
13086                if (hasSubTypes) {
13087                    $button.fadeOut();
13088                } else {
13089                    $button.fadeIn();
13090                }
13091            }
13092        }
13093
13094        var toggleMakeButton = function () {
13095            if ($('input:checked').length >= 1) {
13096                if ($('input.dependent').is(':checked')) {
13097                    if ($('.dependence').val() == '') {
13098                        $button.fadeOut();
13099                        return;
13100                    }
13101                }
13102                $button.fadeIn();
13103                return;
13104            }
13105            $button.fadeOut();
13106            return;
13107        };
13108
13109        var triggerChange = function () {
13110            $('input.dependent').prop('checked', true).trigger('change');
13111        };
13112
13113        var newApp = function () {
13114            vt.userMessageHide();
13115            vt.deleteCookieMessage('wizardPageMsg');
13115 // In edge cases, a double cookie message may be stored.
13116            window.location = window.location.pathname; // Failsafe: Reload page to get rid of applicationId in querystring, if any
13117            event.preventDefault();
13118        };
13119
13120        var appNext = function () {
13121            window.location.hash = 'appID=set';
13122
13123            return true;
13124        };
13125
13126        var continueApp = function () {
13127            window.location.href = $element.data('applicationform-url') + '?applicationId=' + appID;
13128            event.preventDefault();
13129        };
13130
13131        var moveNextButton = function ($container) {
13132            $button.hide().appendTo($container);
13133        };
13134
13135        function showRunningApplicationMessage() {
13136            var msgConfig = {
13137                container: $('body'),
13138                content: '<h1>Hov, du har allerede valgt ansøgningstype</h1><p>Du kan ikke ændre ansøgningstypen, når først ansøgningen er oprettet. Du er velkommen til at <a href="#" class="btnNewApp">oprette en ny ansøgning</a>, hvis du har behov for en anden type ansøgning.</p><p class="text-align-right"><button class="btn colored btnContinueApp">Tilbage til ansøgning</button></p>',
13139                addOverlay: true
13140            };
13141            vt.userMessage(msgConfig);
13142        };
13143
13144
13145        // Event listensers
13146        // Mouse click
13147        $togglers.on('click', togglers);
13148        $(document).on('click', '.btnNewApp', newApp);
13149        $(document).on('click', '.btnAppNext', appNext);
13150        $(document).on('click', '.btnContinueApp', continueApp);
13151
13152        // Keyboard
13153        $('input.dependence', $element).on('keyup', triggerChange);
13154
13155        // Change
13156        $('input', $element).on('change', toggleMakeButton);
13157
13158
13159        // Making methods public
13160        return {
13161
13162        };
13163
13164    };
13165
13166    // Naming the module
13167    //vt.initApplicationWizard = initApplicationWizard;
13168
13169})(jQuery);;
13170; (function ($) {
13171
13172    window.vt = window.vt || {};
13173
13174    vt.initApplication = function (elem) {
13175        var $element = $(elem);
13176
13177        $.ajax({
13178            url: '/assets/vendor/knockout-3.3.0.js',
13179            dataType: 'script',
13180            cache: true,
13181            success: function () {
13182                initApplication($element);
13183            }
13184        });
13185
13186        function initApplication($element) {
13187
13188            //add autosave-dom-elements...
13189            $element.prepend('<div id="autosaveInfo" class="off"><div class="autosaving">Gemmer<span>.</span><span>.</span><span>.</span></div><div class="autosaved">Gemt!</div></div>');
13190
13191            //initiate autosave...
13192            vt.autoSave({
13193                url: $element.data('service-url'),
13194                form: $('#applicationForm'),
13195                timer: 1000,
13196                beforeSendHandling: function () {
13197                    //console.log('beforeSendHandling...');
13198                    $('#autosaveInfo').removeClass('off savedmode').addClass('savemode');
13199                },
13200                afterSendHandling: function () {
13201                    //console.log('afterSendHandling...');
13202                    $('#autosaveInfo').addClass('off');
13203                },
13204                callback: function (data) {
13205                    //console.log('success...', data);
13206                    $('#autosaveInfo').removeClass('savemode').addClass('savedmode');
13207                    if ($('#IsDraft').val() == 'false')
13208                        window.location.href = $element.data('succes-page-url');
13209                },
13210                errorHandling: function (data) {
13211                    //console.log('error, redirect...', data);
13212                    $('#autosaveInfo').removeClass('savemode').addClass('savedmode');
13213                    window.location.href = $element.data('error-page-url');
13214                }
13215            });
13216
13217            console.log()
13218        }
13219    };
13220
13221})(jQuery);;
13222; (function ($) {
13223
13224	window.vt = window.vt || {};
13225
13226	function debounce(func, wait, immediate) {
13227        var timeout;
13228        return function() {
13229            var context = this, args = arguments;
13230            var later = function() {
13231                timeout = null;
13232                if (!immediate) func.apply(context, args);
13233            };
13234            var callNow = immediate && !timeout;
13235            clearTimeout(timeout);
13236            timeout = setTimeout(later, wait);
13237            if (callNow) func.apply(context, args);
13238        };
13239    };
13240
13241	vt.initAutoComplete = function (container) {
13242        var $container = $(container);
13243        var $autoCompleteList = $container.find('.search-autocomplete__list');
13244        var $aedMap = $container.find('.aed-map');
13245
13246        // if in experience editor
13247        if ($('body').is('.pageedit')) return;
13248
13249        var mf = {};
13250
13251        mf.autoComplete = function(searchValue) {
13252            var autoCompleteService = 'https://adresser.trygfonden.dk/api/adresser/autocomplete?q=' + searchValue;
13253
13254            // if searchValue is empty
13255            if (searchValue.length <= 0) return;
13256
13257            // clear autocomple list
13258            if ($autoCompleteList.find('li, p').length > 0) {
13259                $autoCompleteList.html('');
13260            }
13261
13262            // show list
13263            $autoCompleteList.show();
13264
13265            // get results from search
13266            $.ajax({
13267            	url: autoCompleteService,
13268            	type: 'GET'
13269            })
13270            .done(function(data) {
13271
13272                // if autoCompleteList already is active
13273                if ($autoCompleteList.hasClass('active')) return;
13274
13275                // set $autoCompleteList to active
13276                $autoCompleteList.addClass('active');
13277
13278                if (data.length > 0) {
13279
13280                    // each results add them to $autoCompleteList
13281                    $.each(data, function(index, val) {
13282                        var $li = $('<li></li>')
13283                            .attr({
13284                                'data-door': this.adresse.dør || '',
13285                                'data-city': this.adresse.postnrnavn || '',
13286                                'data-postalcode': this.adresse.postnr || '',
13287                                'data-floor': this.adresse.etage || '',
13288                                'data-number': this.adresse.husnr || '',
13289                                'data-address': this.adresse.vejnavn || ''
13290                            })
13291                            .text(this.tekst);
13292
13293                        $autoCompleteList.append($li);
13294                    });
13295
13296                    mf.autoCompleteSplitUp();
13297                } else {
13298
13299                    // check if there already is an error message
13300                    if ($autoCompleteList.find('.lifesaver__search-autocomplete-error').length > 0) return;
13301
13302                    // add error message if it can't find any
13303                    var $errorMsg = $('<p></p>')
13304                        .addClass('lifesaver__search-autocomplete-error')
13305                        .text('Der kan ikke findes nogen adresse ud fra de oplysninger, der er indtastet. Prøv igen.');
13306                    $autoCompleteList.append($errorMsg);
13307                }
13308            })
13309            .fail(function(data) {
13310                vt.initFormTools.serverError(data);
13311            });
13312        }
13313
13314        mf.autoCompleteSplitUp = function() {
13315
13316            // click event on results
13317            $($autoCompleteList).find('li, p').off('click').on('click', function(e) {
13318                e.stopImmediatePropagation();
13319                var inputs = $('.form-element--address').find('input[type="text"]');
13320                var details = $(e.target).data();
13321
13322                $.each(inputs, function(index, input) {
13323
13324					// clear all inputs
13325					$(input).val('');
13326					$(input).removeClass('invalid');
13327
13328                    var id = $(input).attr('id');
13329                    $(input).val(details[id])
13330                });
13331
13332				// removeClassmove active from $autoCompleteList
13333				$autoCompleteList.removeClass('active');
13334				$('.search-autocomplete__field').removeClass('active');
13335
13336				// clear autocomplete list
13337                if ($autoCompleteList.find('li, p').length > 0) {
13338                    $autoCompleteList.html('');
13339                }
13340
13341				vt.initAedMap.aedMap($aedMap, details);
13342            });
13343        }
13344
13345        // on every keyup
13346        $container.find('.search-autocomplete__field').on('keyup', debounce(function (e) {
13347            e.stopImmediatePropagation();
13348            var searchValue = $(e.target).val();
13349
13350            // remove active from $autoCompleteList
13351            $autoCompleteList.removeClass('active');
13352
13353            mf.autoComplete(searchValue);
13354
13355			$(e.target).addClass('active');
13356
13357			// uncheck radio buttons
13358			$('[data-maptext]').prop('checked', false);
13359
13360			// hide map again
13361			$aedMap.hide();
13362			$container.parent('.js-formmap').addClass('hidden');
13363
13364            // remove attr data-clicked
13365            $autoCompleteList.removeAttr('data-clicked');
13366        }, 200));
13367
13368        // set/remove class active when focused
13369        $container.find('.search-autocomplete__field').on('focus', function (e) {
13370            var searchValue = $(e.target).val();
13371
13372			if (searchValue.length > 0) {
13373				$(e.target).addClass('active');
13374			}
13375
13376            mf.autoComplete(searchValue);
13377        });
13378
13379        $container.find('.search-autocomplete__field').on('focusout', function (e) {
13380
13381			// if focus out but are hovering list then return
13382            if ($autoCompleteList.is(":hover") === true) return;
13383
13384            setTimeout(function() {
13385
13386				// removeClassmove active from $autoCompleteList
13387				$autoCompleteList.removeClass('active');
13388
13389				$(e.target).removeClass('active');
13390
13391                // clear autocomple list
13392                if ($autoCompleteList.find('li, p').length > 0) {
13393                    $autoCompleteList.html('');
13394                }
13395            }, 100);
13396        });
13397
13398		// on change radio button run code again
13399		$container.find('[data-maptext]').on('change', function(e) {
13400            e.stopImmediatePropagation();
13401			if ($container.find('.search-autocomplete__field').val() !== '') {
13402				var details = {};
13403
13404				// get address from input
13405				$.each($('.form-element--address').find('input'), function(index, input) {
13406					var id = $(input).attr('id');
13407					details[id] = $(input).val();
13408				});
13409
13410				vt.initAedMap.aedMap($aedMap, details);
13411			}
13412		});
13413	};
13414})(jQuery);
13415;
13416; (function ($) {
13417
13418    window.vt = window.vt || {};
13419
13420    vt.autoSave = function (autosaveArgs) {
13421        //autosaveArgs shold be as follows: { url: [url to autosave-service], form: [formelement to serialize], timer: [autosave-interval in millisec, callback: function () {}, errorHandling: function () {}, afterSendHandling: function () {}, beforeSendHandling: function () { }...
13422        //console.log('autosave...', autosaveArgs);
13423        if ($('body').is('.pageedit'))
13424            return;
13425
13426        //default configuration...
13427        var autoSaveConfig = {
13428            timer: 1000,
13429            callback: function () {
13430                console.log('default success...');
13431            },
13432            errorHandling: function () {
13433                console.log('default error...');
13434            },
13435            beforeSendHandling: function () {
13436                console.log('default beforeSendHandling...');
13437            },
13438            afterSendHandling: function () {
13439                console.log('default afterSendHandling...');
13440            },
13441            specialForm: null
13442        }
13443
13444        //merge with autosave arguments...
13445        $.extend(autoSaveConfig, autosaveArgs);
13446
13447        //if form element exists start autosave...
13448        if (typeof autoSaveConfig.form != 'undefined') {
13449            var autosaveTimer,
13450        		autosaveHideTimer;
13451
13452            //run test...
13453            autosave();
13454
13455            //set autosave actions on form fields...
13456            $('input[type="checkbox"], input[type="radio"], select', autoSaveConfig.form).on('change', function () {
13457                showAutosave();
13458            });
13459            $('input.form-file-name, input.date-picker').on('change', function () {
13460                showAutosave();
13461            });
13462            $('textarea, input[type="text"], input[type="number"], input[type="email"]', autoSaveConfig.form).on('keyup', function () {
13463                showAutosave();
13464            });
13465        };
13466
13467        function showAutosave() {
13468            clearTimeout(autosaveTimer);
13469            clearTimeout(autosaveHideTimer);
13470            autoSaveConfig.beforeSendHandling();
13471            autosaveTimer = setTimeout(function () {
13472                autosave();
13473            }, autoSaveConfig.timer);
13474        }
13475
13476        function HideAutosave() {
13477            autosaveHideTimer = setTimeout(function () { autoSaveConfig.afterSendHandling(); }, 5000);
13478        }
13479
13480        function autosave() {
13481
13482            if ($.isFunction(autoSaveConfig.specialForm)) {
13483
13484                var specialForm = autoSaveConfig.specialForm(),
13485                    $inputs = specialForm.inputs,
13486                    $form = specialForm.form;
13487
13488                $inputs.clone(true).appendTo($form);
13489
13490                $form.ajaxSubmit({
13491                    type: 'POST',
13492                    url: autoSaveConfig.url,
13493                    error: function (response) {
13494                        autoSaveConfig.errorHandling(response);
13495                        HideAutosave();
13496                    },
13497                    success: function (response) {
13498                        autoSaveConfig.callback(response);
13499                        HideAutosave();
13500                    }
13501                });
13502
13503                setTimeout(function () { $form.remove(); }, 1000);
13504            }
13505            else {
13506
13507                autoSaveConfig.form.ajaxSubmit({
13508                    type: 'POST',
13509                    url: autoSaveConfig.url,
13510                    error: function (response) {
13511                        autoSaveConfig.errorHandling(response);
13512                        HideAutosave();
13513                    },
13514                    success: function (response) {
13515                        autoSaveConfig.callback(response);
13516                        HideAutosave();
13517                    }
13518                });
13519            }
13520
13521            return true;
13522        };
13523
13524        setStickyAutosafe();
13525
13526        $(window).scroll(function () {
13527            setStickyAutosafe();
13528        });
13529
13530        function setStickyAutosafe() {
13531            var $element = $("#autosaveInfo");
13532            var pos = $('#autosaveInfo').parent().offset();
13533            $(window).scroll(function () {
13534                var windowpos = $(window).scrollTop();
13535                if (windowpos >= pos.top) {
13536                    $element.addClass("stick");
13537                } else {
13538                    $element.removeClass("stick");
13539                }
13540            });
13541        }
13542
13543    }
13544
13545})(jQuery);
13546;
13547; (function ($) {
13548
13549    window.vt = window.vt || {};
13550
13551    if ($('.pageedit').length > 0) {
13552        return;
13553    }
13554
13555    var $win = $(window),
13556        $spot = {},
13557        opacityVal = $win.scrollTop() / 250,
13558        breakpoint = 1023;
13559
13560    var fader = function () {
13561        var screenSize = $(window).width()
13562        if (screenSize > breakpoint) {
13563            opacityVal = $win.scrollTop() / 250;
13564            $spot.css('opacity', opacityVal);
13565        }
13566    };
13567
13568    
13569
13570    vt.initBlurSpot = function (element) {
13571
13572        $spot = $(element).find('.blur');
13573        if ($spot.length) {
13574            $(document).bind('scroll', fader);
13575        }
13576    };
13577
13578})(jQuery);;
13579;(function ($) {
13580
13581	window.vt = window.vt || {};
13582
13583	vt.initCandidateList = function (elem) {
13584
13585	    var currFilter = '*',
13586            $list,
13587            $filters;
13588
13589	    var toggleFilter = function () {
13590	        var $this = $(this),
13591                filter = $this.attr('data-filter');
13592
13593	        if ($this.hasClass('active')) {
13594	            $this.removeClass('active');
13595	            currFilter = '*';
13596	        }
13597	        else {
13598	            $this.siblings().removeClass('active');
13599	            $this.addClass('active');
13600	            currFilter = filter;
13601	        }
13602
13603	        $list.isotope({ filter: currFilter });
13604	    };
13605
13606	    $list = $(elem);
13607	    $filters = $list.siblings('.candidate-filters').find('li');
13608		$filters.on('click', toggleFilter);
13609		$list.isotope({ filter: currFilter });
13610	};
13611
13612})(jQuery);
13613;
13614(function ($) {
13615
13616    let ownershipModal = $("#changeOwnershipModal"),
13617        desicionStep = $("#decisionStep"),
13618        ownerInformationStep = $("#ownerInformationStep"),
13619        moreDefibrillatorsStep = $("#moreDefibrillatorsStep"),
13620        confimationProfileExists = $("#changeOwnerConfimation-profile-exists"),
13621        confimationProfileNotExists = $("#changeOwnerConfimation-profile-not-exists"),
13622        firstName = $("#firstName"),
13623        lastName = $("#lastName"),
13624        email = $("#email"),
13625        mobilePhone = $("#mobilePhone"),
13626        alternativePhone = $("#alternativePhone"),
13627        movedDefibrilatorsList = $(".moved-defibrilators-list"),
13628        newOwner = $(".js-new-owner"),
13629        profileExists = false,
13630        countText = $("#move-count").text(),
13631        currentDefibrillator = $("#current-defibrillator"),
13632        currentId = 0,
13633        redirectUrl = '';
13634
13635    vt.initChangeOwnership = function (elem) {
13636
13637        currentId = getParameter("id");
13638        currentDefibrillator.val(currentId);
13639
13640        $('.js-edit-ownership').on('click', function () {
13641            ownershipModal.addClass("c-modal--open");
13642            vt.functions.pushToDataLayer({
13643                event: "edit_defibrillator_responsible_modal",
13644                defibrillator_id: currentId || "",
13645            });
13646        });
13647        $('.js-edit-ownership-current').on('click', function () {
13648            vt.functions.pushToDataLayer({
13649                event: "edit_defibrillator_responsible",
13650                defibrillator_id: currentId || "",
13651            });
13652        });
13653
13654        $('.js-change-ownership').on('click', function () {
13655            desicionStep.hide();
13656            ownerInformationStep.show();
13657            vt.functions.pushToDataLayer({
13658                event: "change_defibrillator_responsible_form",
13659                defibrillator_id: currentId || "",
13660            });
13661        });
13662
13663        $('.js-change-owner-close').on('click', function () {
13664            $(this).closest(".c-modal").removeClass("c-modal--open");
13665            resetForm();
13666            if (redirectUrl) {
13667                window.location.href = redirectUrl;
13668            }
13669        });
13670
13671        $('.js-change-owner-skip-step').on('click', function (e) {
13672            e.preventDefault();
13673            moreDefibrillatorsStep.hide();
13674            if (profileExists) {
13675                confimationProfileExists.show()
13676            } else {
13677                confimationProfileNotExists.show()
13678            }
13679
13680        });
13681
13682        $(".js-move-other-list input").on('change', function () {
13683            let count = $('.js-move-other-list input:checked').length;
13684            $("#move-count").text(count + countText);
13685            if (count > 0) {
13686                $(".js-move-other-submit").prop('disabled', false);
13687            } else {
13688                $(".js-move-other-submit").prop('disabled', true);
13689            }
13690        });
13691
13692        function resetForm() {
13693            desicionStep.show();
13694            ownerInformationStep.hide();
13695            moreDefibrillatorsStep.hide();
13696            confimationProfileExists.hide();
13697            confimationProfileNotExists.hide();
13698            movedDefibrilatorsList.html("");
13699            $(".spinner-container").removeClass("spinning");
13700            $(".spinner").removeClass("js-rotate js-approved");
13701        };
13702
13703
13704        function getParameter(p) {
13705            var url = window.location.search.substring(1);
13706            var varUrl = url.split('&');
13707            for (var i = 0; i < varUrl.length; i++) {
13708                var parameter = varUrl[i].split('=');
13709                if (parameter[0] == p) {
13710                    return parameter[1];
13711                }
13712            }
13713        }
13714    };
13715
13716    vt.changeOwnership = function (data, input, element) {
13717        var userInput = JSON.parse(input)
13718        profileExists = data.ProfileExists;
13719        redirectUrl = data.RedirectTo;
13720        if (moreDefibrillatorsStep.length > 0 && !$(element).hasClass("js-move-other-submit")) {
13721            ownerInformationStep.hide();
13722            moreDefibrillatorsStep.show();
13723            firstName.val(userInput.FirstName);
13724            lastName.val(userInput.LastName);
13725            email.val(userInput.Email);
13726            mobilePhone.val(userInput.Phone);
13727            alternativePhone.val(userInput.Phonealternative);
13728        } else {
13729            ownerInformationStep.hide();
13730            moreDefibrillatorsStep.hide();
13731
13732            if (profileExists) {
13733                confimationProfileExists.show()
13734            } else {
13735                confimationProfileNotExists.show()
13736            }
13737        }
13738
13739        $(data.Defibrillators).each(function (item) {
13740            movedDefibrilatorsList.append('<li>' + this.Street + " " + this.Number + '</li>');
13741        });
13742        newOwner.text(userInput.FirstName + " " + userInput.LastName);
13743    };
13744
13745})(jQuery);;
13746; (function ($) {
13747
13748    // Declaring namespace if not already declared
13749    window.vt = window.vt || {};
13750
13751    // Wrapping methods in a module
13752    vt.initChangePasswordForm = function (elem) {
13753
13754        vt.showCookieMessage('changePasswordPageMsg', true); //Any cookie messages to show? If any show them and delete them afterwards
13755
13756        // Variables
13757        var username = Cookies.get('loginUserName');
13758
13759        $('.btn-action').progressInitialize();
13760
13761        // Initiate validation
13762        var validateInitiate = function ($element) {
13763            $element.addClass('validationInitiated');
13764        };
13765
13766        // Stop validation
13767        var validateStop = function ($element) {
13768            $element.removeClass('validationInitiated');
13769            validateEmpty($element);
13770        };
13771
13772        // Form has error
13773        var validateError = function ($element) {
13774            $element.addClass('ui-state-error');
13775            $element.removeClass("ui-state-valid");
13776            $element.siblings(".message").css("display", "block");
13777        };
13778
13779        // Form is valid
13780        var validateAccept = function ($element) {
13781            $element.removeClass('ui-state-error');
13782            $element.addClass("ui-state-valid");
13783            $element.siblings(".message").css("display", "none");
13784        };
13785
13786        // Form is empty
13787        var validateEmpty = function ($element) {
13788            $element.removeClass('ui-state-error');
13789            $element.removeClass("ui-state-valid");
13790            $element.siblings(".message").css("display", "none");
13791        };
13792
13793        // Ajax request done
13794        var ajaxDone = function ($element, msg) {
13795            if ($element.hasClass("validationInitiated")) {
13796                if (!msg.IsValid) {
13797                    validateError($element);
13798                } else {
13799                    validateAccept($element);
13800                    return "success!";
13801                }
13802            }
13803        };
13804
13805        // Validate username or password
13806        var ajaxValidate = function (url, $element) {
13807            return $.ajax({
13808                type: 'GET',
13809                url: url + $element.val()
13810            })
13811            .done(function (msg) { ajaxDone($element, msg) })
13812            .fail(function (msg) { validateError($element) })
13813            //.always(function (msg) { console.log(msg) });
13814        };
13815
13816        var fieldPassword = function () {
13817            var $element = $(this);
13818
13819            $element.blur(function () {
13820                if ($element.val().length === 0) {
13821                    validateStop($element);
13822                } else {
13823                    validateInitiate($element);
13824                    ajaxValidate("/api/sag/isvalidpassword/", $element);
13825                }
13826            });
13827
13828            $element.on('keyup', function () {
13829                $(".passwordrepeated").keyup();
13830                if ($element.hasClass("validationInitiated") && $element.val().length > 0) {
13831                    ajaxValidate("/api/sag/isvalidpassword/", $element);
13832                } else if ($element.val().length === 0) {
13833                    validateStop($element);
13834                }
13835            });
13836        };
13837
13838        var fieldPasswordRepeated = function () {
13839            var $element = $(this);
13840
13841            $element.blur(function () {
13842                validateInitiate($element);
13843
13844                if ($element.val() != $('.password', elem).val() && $element.val().length > 0) {
13845                    validateError($element);
13846                } else if ($element.val().length === 0) {
13847                    validateStop($element);
13848                } else {
13849                    validateAccept($element);
13850                }
13851            });
13852
13853            $element.on('keyup', function () {
13854                if ($element.hasClass("validationInitiated") && $element.val().length > 0) {
13855
13856                    if ($element.val() != $('.password', elem).val()) {
13857                        validateError($element);
13858                    } else {
13859                        validateAccept($element);
13860                    }
13861                } else if ($element.val().length === 0) {
13862                    validateStop($element);
13863                }
13864            });
13865        };
13866
13867        var fieldCurrentPassword = function () {
13868            var $element = $(this);
13869
13870            $element.blur(function () {
13871                if ($element.val().length === 0) {
13872                    validateStop($element);
13873                } else {
13874                    validateInitiate($element);
13875                    ajaxValidate('/api/sag/isvalidlogin/?username=' + username + '&password=', $element);
13876                }
13877            });
13878
13879            $element.on('keyup', function () {
13880                if ($element.hasClass("validationInitiated") && $element.val().length > 0) {
13881                    ajaxValidate('/api/sag/isvalidlogin/?username=' + username + '&password=', $element);
13882                } else if ($element.val().length === 0) {
13883                    validateStop($element);
13884                }
13885            });
13886        };
13887
13888        // Validate all fields
13889        var validate = function () {
13890            if ($("input:not(.ui-state-valid):required").length) {
13891                var $invalid = $("input:not(.ui-state-valid):required");
13892                $invalid.blur();
13893
13894                $invalid = $("input:not(.ui-state-valid):required"); // needs to wait for server validation before running
13895                validateError($invalid);
13896                return false;
13897            } else {
13898                createAction();
13899            }
13900        }
13901
13902        // Trigger btn-action
13903        var simClickBtnAction = function () {
13904            $(".btn-action").click();
13905        };
13906
13907        // Submit form
13908        var createAction = function () {
13909
13910            //Fake progressbutton
13911            $('.btn-action').progressStart();
13912
13913            $.ajax({
13914                type: 'POST',
13915                url: '/api/sag/changepassword/',
13916                data: JSON.stringify({ CurrentPassword: $('input.currentpassword').val(), NewPassword: $('input.password').val() }),
13917                contentType: "application/json;charset=utf-8",
13918                success: function () {
13919                    //add cookieMessage
13920                    var cookieConfig = {
13921                        title: $('#myPageMsg strong').html(),
13922                        message: $('#myPageMsg p').html(),
13923                        button: 'OK',
13924                        addOverlay: true
13925                    },
13926                        redirectUrl = $('body').data('mypage-url');
13927
13928                    //Show cookie message on MyPage
13929                    vt.setCookieMessage('myPageMsg', cookieConfig);
13930
13931                    //Redirect to My Page
13932                    window.location.href = redirectUrl;
13933                },
13934                error: function (response) {
13935                    $.growl.error({ title: 'Der er sket en fejl', message: 'Din adgangskode blev ikke ændret.', duration: 5000 })
13936                    //console.log(response);
13937                }
13938            })
13939        };
13940        
13941        // Event listensers
13942        $('.password', elem).each(fieldPassword);
13943        $('.passwordrepeated', elem).each(fieldPasswordRepeated);
13944        $('.currentpassword', elem).each(fieldCurrentPassword);
13945
13946        // Mouse click
13947        $('.btn-action').click(function (e) {
13948            e.preventDefault();
13949            validate();
13950        });
13951
13952        // Keyboard
13953        $("input").keydown(function (e) {
13954            if (e.which === 13) { // Enter key
13955                simClickBtnAction();
13956            }
13957        });
13958
13959        // Making methods public
13960        return {
13961
13962        };
13963    };
13964
13965    // Naming the module
13966    //vt.initLoginPanel = initLoginPanel;
13967
13968})(jQuery);
13969;
13970; (function ($) {
13971
13972    window.vt = window.vt || {};
13973
13974    vt.initContenGallery = function (elem) {
13975
13976        $('.step-slider').slick({
13977            slidesToShow: 1,
13978            slidesToScroll: 1,
13979            accessibility: false,
13980            infinite: false,
13981            dots: true,
13982            centerMode: false,
13983            focusOnSelect: true,
13984            prevArrow: $('.step-list .prev'),
13985            nextArrow: $('.step-list .next'),
13986            adaptiveHeight: true,
13987        });
13988
13989        $('#content-gallery .slider-for').slick({
13990            slidesToShow: 1,
13991            slidesToScroll: 1,
13992            accessibility: false,
13993            arrows: false,
13994            fade: true,
13995            asNavFor: '#content-gallery .slider-nav',
13996            responsive: [{
13997                breakpoint: 690,
13998                settings: {
13999                    slidesToShow: 1,
14000                    slidesToScroll: 1,
14001                    infinite: true,
14002                    dots: true
14003                }
14004            }]
14005        });
14006        $('#content-gallery .slider-nav').slick({
14007            slidesToShow: 4,
14008            slidesToScroll: 1,
14009            accessibility: false,
14010            asNavFor: '#content-gallery .slider-for',
14011            dots: true,
14012            centerMode: false,
14013            focusOnSelect: true
14014        });
14015
14016        // --- Accessibility fixes for Slick pagination dots (WCAG 4.1.2) -------
14017        // With accessibility:false Slick leaves two problems on the dots:
14018        //   1. inactive dots get aria-hidden="true" while their <button> stays
14019        //      keyboard-focusable ("Hidden element has focusable content").
14020        //      Dots are navigation controls, so re-expose them.
14021        //   2. the dot <button> is rendered with aria-required="false", which is
14022        //      not a supported attribute on role="button" ("ARIA attribute
14023        //      unsupported or prohibited"). Strip it.
14024        // Slick re-renders the dots on every change, so this runs on each event.
14025        function fixSlickDotsA11y() {
14026            // 'this' is the slider element when called as an event handler.
14027            var $dots = $(this).find('.slick-dots');
14028            $dots.children('li[aria-hidden]').removeAttr('aria-hidden');
14029            $dots.find('button[aria-required]').removeAttr('aria-required');
14030        }
14031
14032        var $gallerySliders = $('.step-slider, #content-gallery .slider-for, #content-gallery .slider-nav');
14033        $gallerySliders.each(fixSlickDotsA11y);
14034        $gallerySliders.on('setPosition afterChange reInit', fixSlickDotsA11y);
14035
14036        // Only the active step-slider slide should keep playing its video.
14037        // Pause any other slide's video before the slide transition starts.
14038        $('.step-slider').on('beforeChange', function (event, slick, currentSlide, nextSlide) {
14039            slick.$slides.each(function (index) {
14040                if (index === nextSlide) {
14041                    return;
14042                }
14043
14044                var pausePlayback = $(this).data('videoPlayerPause');
14045                if (typeof pausePlayback === 'function') {
14046                    pausePlayback();
14047                }
14048            });
14049        });
14050
14051        // Videos in Gallery
14052
14053        var allVideos = $(elem).find('.js-video');
14054
14055        // reset videos after slide change
14056        $('#content-gallery').on('afterChange', function (event, slick, currentSlide) {
14057
14058            allVideos.each(function () {
14059                var cleanupPlayback = $(this).data('videoPlayerCleanup');
14060                if (typeof cleanupPlayback === 'function') {
14061                    cleanupPlayback();
14062                }
14063            });
14064
14065            allVideos.find('.embed-container').removeClass('playing');
14066            allVideos.find('.js-video-play').removeClass('is-playing');
14067            allVideos.find('iframe').remove();
14068            allVideos.find('.js-embed').remove();
14069
14070        });
14071
14072        /*
14073        if (allVideos.length) {
14074
14075            
14076
14077            allVideos.each(function (element) {
14078
14079                var video,
14080                    videoData,
14081                    videoUrl,
14082                    videoId,
14083                    playerUrl,
14084                    isYoutube,
14085                    isVimeo;
14086
14087                video = $(this).find('.embed-container');
14088                videoData = video.data();
14089
14090                if (!videoData.videoUrl) {
14091                    return;
14092                }
14093
14094                isYoutube = videoData.videoUrl.indexOf('youtube'),
14095                isVimeo = videoData.videoUrl.indexOf('vimeo');
14096
14097                if (isYoutube >= 0) {
14098                    playerUrl = "//youtube.com/embed/";
14099                    // Get videoId from url
14100                    videoId = videoData.videoUrl.match(/(?:https?:\/{2})?(?:w{3}\.)?youtu(?:be)?\.(?:com|be)(?:\/watch\?v=|\/)([^\s&]+)/);
14101                    videoId = videoId[1];
14102                } else if (isVimeo >= 0) {
14103                    playerUrl = "//player.vimeo.com/video/";
14104                    videoId = videoData.videoUrl.match(/https?:\/\/(?:www\.|player\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/([^\/]*)\/videos\/|album\/(\d+)\/video\/|video\/|)(\d+)(?:$|\/|\?)/);
14105                    videoId = videoId[3];
14106                } else {
14107                    return;
14108                }
14109
14110                video.on("click", function () {
14111                    if (videoData.playMode === 'inline') {
14112
14113                        $('<iframe frameborder="0" type="text/html" src="' + playerUrl + videoId + '?autoplay=1" frameborder="0"  webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>').appendTo(video);
14114                        video.addClass("playing");
14115                        //video.off("click");
14116
14117                    } else {
14118
14119                        console.log(playerUrl + videoId);
14120
14121                    }
14122
14123                });
14124
14125            });
14126        }
14127        */
14128    };
14129
14130})(jQuery);
14131
14132
14133;
14134; (function ($) {
14135
14136    window.vt = window.vt || {};
14137
14138    vt.initCookieDisclaimer = function (elem) {
14139
14140        var $disclaimer = $(elem);
14141
14142        var setCookieAndHideDisclaimer = function (e) {
14143            $disclaimer.hide();
14144            document.cookie = "vtAcceptCookies=true; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/";
14145            e.preventDefault();
14146        };
14147
14148        if (!$disclaimer.length || document.cookie.search('vtAcceptCookies') > -1) {
14149            return;
14150        }
14151        else {
14152            $disclaimer.show();
14153            $disclaimer.find('.btn').on('click', setCookieAndHideDisclaimer);
14154        }
14155    };
14156
14157})(jQuery);;
14158; (function ($) {
14159
14160    // Declaring namespace if not already declared
14161    window.vt = window.vt || {};
14162
14163    // Wrapping methods in a module
14164    vt.initCreateProfileForm = function (elem) {
14165
14166        // Variables
14167        $('.create-action').progressInitialize();
14168
14169        // Initiate validation
14170        var validateInitiate = function ($element) {
14171            $element.addClass('validationInitiated');
14172        };
14173
14174        // Stop validation
14175        var validateStop = function ($element) {
14176            $element.removeClass('validationInitiated');
14177            validateEmpty($element);
14178        };
14179
14180        // Form has error
14181        var validateError = function ($element) {
14182            $element.addClass('ui-state-error');
14183            $element.removeClass("ui-state-valid");
14184            $element.siblings(".message").css("display", "block");
14185        };
14186
14187        // Form is valid
14188        var validateAccept = function ($element) {
14189            $element.removeClass('ui-state-error');
14190            $element.addClass("ui-state-valid");
14191            $element.siblings(".message").css("display", "none");
14192        };
14193
14194        // Form is empty
14195        var validateEmpty = function ($element) {
14196            $element.removeClass('ui-state-error');
14197            $element.removeClass("ui-state-valid");
14198            $element.siblings(".message").css("display", "none");
14199        };
14200
14201        // Ajax request done
14202        var ajaxDone = function ($element, msg) {
14203            if ($element.hasClass("validationInitiated")) {
14204                if (!msg.IsValid) {
14205                    validateError($element);
14206                } else {
14207                    validateAccept($element);
14208                    return "success!";
14209                }
14210            }
14211        };
14212
14213        // Validate username or password
14214        var ajaxValidate = function (url, $element) {
14215            return $.ajax({
14216                type: 'GET',
14217                url: url + $element.val()
14218            })
14219            .done(function (msg) { ajaxDone($element, msg) })
14220            .fail(function (msg) { validateError($element) })
14221            .always(function (msg) { console.log(msg) });
14222        };
14223
14224        var fieldUsername = function () {
14225            var $element = $(this);
14226
14227            $element.blur(function () {
14228                if ($element.val().length === 0) {
14229                    validateStop($element);
14230                } else {
14231                    validateInitiate($element);
14232                    ajaxValidate("/api/sag/isvalidusername/", $element);
14233                }
14234            });
14235            $element.on('keyup', function () {
14236                if ($element.hasClass("validationInitiated") && $element.val().length > 0) {
14237                    ajaxValidate("/api/sag/isvalidusername/", $element);
14238                } else if ($element.val().length === 0) {
14239                    validateStop($element);
14240                }
14241            });
14242            $element.blur();
14243        };
14244
14245        var fieldPassword = function () {
14246            var $element = $(this);
14247
14248            $element.blur(function () {
14249                if ($element.val().length === 0) {
14250                    validateStop($element);
14251                } else {
14252                    validateInitiate($element);
14253                    ajaxValidate("/api/sag/isvalidpassword/", $element);
14254                }
14255            });
14256
14257            $element.on('keyup', function () {
14258                $(".passwordrepeated").keyup();
14259                if ($element.hasClass("validationInitiated") && $element.val().length > 0) {
14260                    ajaxValidate("/api/sag/isvalidpassword/", $element);
14261                } else if ($element.val().length === 0) {
14262                    validateStop($element);
14263                }
14264            });
14265        };
14266
14267        var fieldPasswordRepeated = function () {
14268            var $element = $(this);
14269
14270            $element.blur(function () {
14271                validateInitiate($element);
14272
14273                if ($element.val() != $('.password', elem).val() && $element.val().length > 0) {
14274                    validateError($element);
14275                } else if ($element.val().length === 0) {
14276                    validateStop($element);
14277                } else {
14278                    validateAccept($element);
14279                }
14280            });
14281
14282            $element.on('keyup', function () {
14283                if ($element.hasClass("validationInitiated") && $element.val().length > 0) {            
14284                    if ($element.val() != $('.password', elem).val()) {
14285                        validateError($element);
14286                    } else {
14287                        validateAccept($element);
14288                    }
14289                } else if ($element.val().length === 0) {
14290                    validateStop($element);
14291                }
14292            });
14293        };
14294        
14295        var fieldTerms = function () {
14296            var $element = $(this);
14297            $element.blur(function () {
14298                if ($element.is(':checked')) {
14299                    validateAccept($element);
14300                } else {
14301                    validateError($element);
14302                }
14303            });
14304
14305            $element.change(function () {
14306                if ($element.is(':checked')) {
14307                    validateAccept($element);
14308                } else {
14309                    validateError($element);
14310                }
14311            });
14312
14313            if ($element.is(':checked')) {
14314                $element.blur();
14315            }
14316        };
14317
14318        // Validate all fields
14319        var validate = function () {
14320            if ($("input:not(.ui-state-valid):required").length) {
14321                console.log("element required not approved");
14322                var $invalid = $("input:not(.ui-state-valid):required");
14323                $invalid.blur();
14324
14325                $invalid = $("input:not(.ui-state-valid):required"); // needs to wait for server validation before running
14326                validateError($invalid);
14327                return false;
14328            //} else if (!$("#acceptterms:checked").length) {
14329            //    console.log("terms not accepted");
14330            } else {
14331                console.log("valid form");
14332                return createAction();
14333            }
14334        }
14335
14336        // Trigger create-action
14337        var simClickCreateAction = function () {
14338            $(".create-action").click();
14339        };
14340
14341        // Submit form
14342        var createAction = function (e) {
14343            var errorElements = $('input.ui-state-error');
14344            if (typeof errorElements[0] != 'undefined') {
14345                errorElements[0].focus();
14346                return false;
14347            }
14348
14349            //Fake progressbutton
14350            $('.create-action').progressStart();
14351
14352            //add cookieMessage
14353            var cookieConfig = {
14354                title: 'Profil oprettet',
14355                message: 'Din profil er blevet oprettet, og du kan nu fortsætte med din ansøgning til TrygFonden',
14356                addOverlay: true
14357            };
14358
14359            //Show cookie message on MyPage
14360            vt.setCookieMessage('wizardPageMsg', cookieConfig);
14361            
14362            return true;
14363        };
14364
14365
14366        // Event listensers
14367        $('.username', elem).each(fieldUsername);
14368        $('.password', elem).each(fieldPassword);
14369        $('.passwordrepeated', elem).each(fieldPasswordRepeated);
14370        $('#acceptterms', elem).each(fieldTerms);
14371
14372        // Mouse click
14373        $('.create-action').click(function (e) {
14374            //validateH5();
14375            if (!validate())
14376            { e.preventDefault(); }
14377        });
14378
14379        // Keyboard
14380        $("input").keydown(function (e) {
14381            if (e.which === 13) { // Enter key
14382                simClickCreateAction();
14383            }
14384        });
14385
14386        // Making methods public
14387        return {
14388
14389        };
14390    };
14391
14392    // Naming the module
14393    //vt.initLoginPanel = initLoginPanel;
14394
14395})(jQuery);
14396;
14397; (function ($) {
14398
14399    window.vt = window.vt || {};
14400
14401    vt.initDatePicker = function (elem) {
14402
14403        // onload if input have value then convert that
14404        var onloadValue = $(elem).find('input');
14405        if ($(onloadValue).val() != '') {
14406            $(elem).attr('data-convertedData', $(onloadValue).val() + 'T00:00:00+01:00');
14407        }
14408
14409        var d = new Date(),
14410            currentDate = d.getDate();
14411
14412        // Options
14413        var options = {};
14414        options = {
14415            'startView': '2',
14416            'weekStart': '1',
14417            'autoHide': 'true',
14418            'days': ['Søndag', 'Mandag', 'Tirsdag', 'Onsdag', 'Torsdag', 'Fredag', 'Lørdag'],
14419            'daysShort': ['Søn', 'Man', 'Tir', 'Ons', 'Tor', 'Fre', 'Lør'],
14420            'daysMin': ['Sø', 'Ma', 'Ti', 'On', 'To', 'Fr', 'Lø'],
14421            'months': ['Januar', 'Februar', 'Marts', 'April', 'Maj', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'December'],
14422            'monthsShort': ['Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'],
14423            'startDate' : currentDate
14424        };
14425
14426        // Convert data
14427        var convertData = function(format, inputValue) {
14428            if ($('.datepicker-container').hasClass('datepicker-hide')) {
14429                var dateValue = inputValue;
14430                dateValue = dateValue.split('-');
14431
14432                // check if its a dayMonthYear or just monthYear
14433                if (format == 'dd-mm-yyyy') {
14434                    dateValue = (dateValue[0] + '-' + dateValue[1] + '-' + dateValue[2] + 'T00:00:00+01:00');
14435                } else {
14436                    dateValue = ('01' + '-' + dateValue[0] + '-' + dateValue[1] + 'T00:00:00+01:00');
14437                }
14438
14439                $(elem).attr('data-convertedData', dateValue);
14440            }
14441        };
14442
14443        // if change input
14444        var datepickerInput = $(elem).find('[data-toggle="datepicker"] input');
14445        $(datepickerInput.context).on('change', function(e) {
14446            var inputValue = $(e.target).val();
14447
14448            // if inputvalue is empty or not having '-'
14449            if (inputValue == '' || inputValue.indexOf('-') <= -1) {
14450                $(datepickerInput.context).attr('data-convertedData', '');
14451            } else {
14452                // check if its a dd-mm-yyyy or mm-yyyy
14453                if ($(elem).hasClass('js-datepickerDay')) {
14454                    convertData('dd-mm-yyyy', inputValue);
14455                } else {
14456                    convertData('mm-yyyy', inputValue);
14457                }
14458            }
14459        });
14460
14461        // check if datepicker is a full date
14462        if ($(elem).hasClass('js-datepickerDay')) {
14463            $(elem).find('[data-toggle="datepicker"]').datepicker({
14464                'format': 'dd-mm-yyyy',
14465                'startView': options.startView,
14466                'weekStart': options.weekStart,
14467                'autoHide': options.autoHide,
14468                'days': options.days,
14469                'daysShort': options.daysShort,
14470                'daysMin': options.daysMin,
14471                'months': options.months,
14472                'monthsShort': options.monthsShort,
14473                'startDate' : options.startDate
14474            });
14475        } else {
14476            $(elem).find('[data-toggle="datepicker"]').datepicker({
14477                'format': 'mm-yyyy',
14478                'startView': options.startView,
14479                'weekStart': options.weekStart,
14480                'autoHide': options.autoHide,
14481                'months': options.months,
14482                'monthsShort': options.monthsShort,
14483                'startDate' : options.startDate
14484            });
14485        }
14486    };
14487
14488})(jQuery);
14489;
14490; (function ($) {
14491
14492    window.vt = window.vt || {};
14493
14494    vt.initEvaluation = function (elem) {
14495
14496        var $questionnaireElement = $('#evaluationQuestionnaire', elem),
14497            $infoElement = $('#evaluationInfo', elem),
14498            $resumeElements = $('.evaluation-block--resume', $questionnaireElement),
14499            timer = 0,
14500            windowWidth = $(window).width();
14501
14502        if ($resumeElements.length > 0) {
14503            
14504            $resumeElements.each(function () {
14505                var $element = $(this);
14506                $element.append('<div class="evaluation-block--toggle__toggler"><span></span></div>');
14507            });
14508
14509            $(window).resize(function () {
14510                clearTimeout(timer);
14511                startTimer();
14512            });
14513
14514            function startTimer() {                
14515                if ($(window).width() !== windowWidth) {
14516                    timer = window.setTimeout(function () {
14517                        windowWidth = $(window).width();
14518                        setMaxHeight();
14519                    }, 500);
14520                }
14521            };
14522
14523            setMaxHeight();
14524        };
14525
14526        $(document).on('click', '.evaluation-block--toggle .evaluation-block--toggle__toggler span', function () {
14527            var $parent = $(this).parents('.evaluation-block--toggle');
14528            $parent.toggleClass('evaluation-block--toggle--on');
14529            $('html, body').animate({ scrollTop: $parent.offset().top }, 400)
14530        });
14531
14532        $(document).on('click', '#evaluationSubmit', function () {
14533
14534            if ($('form', $questionnaireElement).h5Validate('allValid')) {
14535                if (confirm('Du er ved at indsende din evaluering. Når den er sendt, kan den ikke ændres længere.')) {
14536                    $('#IsDraft').val(false);
14537
14538                    $('form', $questionnaireElement).ajaxSubmit({
14539                        type: 'POST',
14540                        url: $(elem).data('service-url'),
14541                        error: function (response) {
14542                            //console.log('submit error', response);
14543                            window.location.href = $(elem).data('error-page-url');
14544                        },
14545                        success: function (response) {
14546                            //console.log('submit success', response);
14547                            window.location.href = $(elem).data('succes-page-url');
14548                        }
14549                    });
14550
14551                    return false;
14552                }
14553                else {
14554                    return false;
14555                }
14556            }
14557            else {
14558                var errorElements = $('input.ui-state-error, textarea.ui-state-error');
14559                if (typeof errorElements[0] != 'undefined')
14560                    errorElements[0].focus();
14561
14562                return false;
14563            }
14564        });
14565
14566        $(document).on('change', 'input.form-file-name', function () {
14567            //console.log('input change...');
14568            var input = $(this),
14569                numFiles = input.get(0).files ? input.get(0).files.length : 1,
14570                label = input.val().replace(/\\/g, '/').replace(/.*\//, '');
14571
14572            var $container = $(this).parent(),
14573                $deleteAttachment = $container.find('input.visuallyhidden');
14574            if ($(this).val() == '') {
14575                $deleteAttachment.val("true");
14576                $container.removeClass('form-element-file--selected');
14577            }
14578            else {
14579                $deleteAttachment.val("false");
14580                $container.addClass('form-element-file--selected');
14581            }
14582        });
14583
14584        $(document).on('click', '.btn-delete-file', function (e) {
14585            $(this).fadeOut();
14586            var $fileElement = $(this).parent().find('.btn-file :file');
14587            clearFileInput($fileElement);
14588            $fileElement.trigger('fileselect');
14589            e.preventDefault();
14590        });
14591
14592        window.onload = hashChange();
14593        window.onhashchange = function () { hashChange(); };
14594
14595        function hashChange() {
14596            if (location.hash === "#evaluationQuestionnaire") {
14597                setQuestionnaireMode();
14598                window.scrollTo(0, 0);
14599            }
14600            else {
14601                $questionnaireElement.hide();
14602                $infoElement.show();
14603            };
14604        };
14605
14606        function setQuestionnaireMode() {
14607            $questionnaireElement.show();
14608            $infoElement.hide();
14609
14610            //add autosave-dom-elements...
14611            $('body').append('<div id="autosaveInfo" class="off"><div class="autosaving">Gemmer<span>.</span><span>.</span><span>.</span></div><div class="autosaved">Gemt!</div></div>');
14612
14613            setMaxHeight();
14614
14615            //initiate autosave...
14616            vt.autoSave({
14617                url: $(elem).data('service-url'),
14618                form: $('#evaluationForm'),
14619                timer: 1000,
14620                beforeSendHandling: function () {
14621                    //console.log('beforeSendHandling...');
14622                    $('#autosaveInfo').removeClass('off savedmode').addClass('savemode');
14623                },
14624                afterSendHandling: function () {
14625                    //console.log('afterSendHandling...');
14626                    $('#autosaveInfo').addClass('off');
14627                },
14628                callback: function (data) {
14629                    //console.log('autosave success...', data);
14630                    $('#autosaveInfo').removeClass('savemode').addClass('savedmode');
14631                    if ($('#IsDraft').val() == 'false')
14632                        window.location.href = $(elem).data('succes-page-url');
14633
14634                    $('.form-element-file--selected').each(function () {
14635                        $(this).find('.btn-delete-file').fadeIn();
14636                    });
14637
14638                    $('input.delete-attachment').val(false);
14639                },
14640                errorHandling: function (data) {
14641                    //console.log('autosave error, redirect...', data);
14642                    $('#autosaveInfo').removeClass('savemode').addClass('savedmode');
14643                    //return false;
14644                    window.location.href = $(elem).data('error-page-url');
14645                }
14646            })
14647        };
14648
14649        function setMaxHeight() {
14650            $resumeElements.each(function () {
14651                var $element = $(this);
14652                $element.removeClass('evaluation-block--toggle--on').css('max-height', 'inherit');
14653
14654                elementHeight = $element.outerHeight();
14655
14656                if (elementHeight > 370)
14657                    $element.addClass('evaluation-block--toggle evaluation-block--toggle--on').css('max-height', elementHeight + 400);
14658                else
14659                    $element.removeClass('evaluation-block--toggle evaluation-block--toggle--on');
14660            });
14661        };
14662
14663        function clearFileInput($input) {
14664            if ($input.val() == '') {
14665                return;
14666            }
14667            // Fix for IE ver < 11, that does not clear file inputs
14668            // Requires a sequence of steps to prevent IE crashing but
14669            // still allow clearing of the file input.
14670            //if (/MSIE/.test(navigator.userAgent)) {
14671            //    var $frm1 = $input.closest('form');
14672            //    if ($frm1.length) { // check if the input is already wrapped in a form
14673            //        $input.wrap('<form>');
14674            //        var $frm2 = $input.closest('form'), // the wrapper form
14675            //            $tmpEl = $(document.createElement('div')); // a temporary placeholder element
14676            //        $frm2.before($tmpEl).after($frm1).trigger('reset');
14677            //        $input.unwrap().appendTo($tmpEl).unwrap();
14678            //    } else { // no parent form exists - just wrap a form element
14679            //        $input.wrap('<form>').closest('form').trigger('reset').unwrap();
14680            //    }
14681            //} else { // normal reset behavior for other sane browsers
14682                $input.val('');
14683            //}
14684        };
14685    };
14686
14687})(jQuery);;
14688; (function ($) {
14689
14690    window.vt = window.vt || {};
14691
14692    vt.initFaqList = function (elem) {
14693
14694        var faqTrigger = $(elem).find('.question');
14695
14696        //show faq content clicking on faqTrigger
14697        faqTrigger.on('click', function (event) {
14698            event.preventDefault();
14699            var isOpening = !$(this).parent('li').hasClass('content-visible');
14700            $(this).next('.answer').slideToggle(200).end().parent('li').toggleClass('content-visible');
14701            vt.functions.pushToDataLayer({
14702                event: "accordion_click",
14703                accordion_topic: $(this).text().trim(),
14704                accordion_action: isOpening ? "open" : "close"
14705            });
14706        });
14707
14708    };
14709
14710})(jQuery);
14711;
14712; (function ($) {
14713
14714    window.vt = window.vt || {};
14715
14716    vt.initFileUpload = function (elem) {
14717        var container = $(elem);
14718        var input = container.find("input");
14719        var containerShow = container.find(".custom-fileupload__show");
14720        var containerShowInput = containerShow.find("span");
14721        var containerFile = container.parent().find(".custom-fileupload__file");
14722        var containerShowInput = containerFile.find("span");
14723
14724
14725        input.on("change", function () {
14726            var item = $(this);
14727            var file = item.val().replace("C:\\fakepath\\", "");
14728
14729            if (file != "") {
14730                containerFile.show();
14731                containerShowInput.html(file);
14732            } else {
14733                containerFile.hide();
14734            }
14735        });
14736
14737        containerShowInput.on("click", function () {
14738            input.val("");
14739            containerFile.hide();
14740            containerShowInput.val("");
14741        })
14742    };
14743
14744})(jQuery);
14745;
14746; (function ($) {
14747
14748    window.vt = window.vt || {};
14749
14750    vt.initFooter = function (element) {
14751
14752        var initCardboard = function () {
14753            var cardboardNumber = Math.floor(Math.random() * (7 - 1 + 1)) + 1,
14754                cardboardImage = "/assets/img/du-kan-redde-liv-0" 
14754+ cardboardNumber + ".png";
14755            $(".cardboard-figure img").attr("src", cardboardImage);
14756        }
14757
14758        var initCardboardPosition = function () {
14759            var cardboardPosition = Math.floor(Math.random() * (1 - 0 + 1)) + 0;
14760
14761            if (cardboardPosition === 0) {
14762                $(".cardboard-figure").addClass("cardboard-right");
14763            } else {
14764                $(".cardboard-figure").removeClass("cardboard-right");
14765            }
14766        }
14767
14768        initCardboard();
14769        initCardboardPosition();
14770
14771        $(".cardboard-figure").click(function () {
14772            $(".cardboard-figure").addClass("scram");
14773            setTimeout(function () {
14774                initCardboard();
14775                initCardboardPosition();
14776                $(".cardboard-figure").removeClass("scram");
14777                $(".cardboard-figure").addClass("easter-egg");
14778            }, 1300);
14779
14780        });
14781    };
14782
14783})(jQuery);
14784;
14785; (function ($) {
14786
14787    window.vt = window.vt || {};
14788
14789    vt.initFormCookieData = (function () {
14790
14791        if ($('body').is('.pageedit'))
14792            return;
14793
14794            function saveStorage(list, form) {
14795                var formName = form.attr('data-formname');
14796
14797                // timeoutdate obj to today + 1 day
14798                var date = new Date();
14799                date.setDate(date.getDate() + 1);
14800
14801                // if list is array
14802                if (Array.isArray(list) === true) {
14803                    $.each(list, function(index, val) {
14804
14805                        // add timeOutDate to barsket array
14806                        list[index]['timeOutDate'] = date;
14807                    });
14808
14809                    // change array to string so its can be in localstorage
14810                    list = JSON.stringify(list);
14811                } else {
14812                    // change array to string so its can be in localstorage
14813                    list = JSON.stringify(list);
14814
14815                    // add timeoutdate to array
14816                    list = list.replace('}', ',"timeOutDate":"' + date + '"}');
14817                }
14818
14819                if (localStorage.getItem(formName)) {
14820                    var oldList = localStorage.getItem(formName);
14821                    oldList = JSON.parse(oldList);
14822                    list = JSON.parse(list);
14823
14824                    $.each(list, function(index, item) {
14825                        oldList[index] = item;
14826                    });
14827
14828                    oldList = JSON.stringify(oldList);
14829                    localStorage.setItem(formName, oldList);
14830                } else {
14831                    localStorage.setItem(formName, list);
14832                }
14833            }
14834
14835            function deleteStorage(form) {
14836
14837                // delete localstorage item
14838                localStorage.removeItem(form);
14839            }
14840
14841            var getStorage = function() {
14842                var form = $('[data-formname]')
14843
14844                // each item there is in localstorage
14845                $.each(form, function(index, form) {
14846                    form = $(form).attr('data-formname');
14847
14848                    if (!localStorage.getItem(form)) {
14849                        return;
14850                    }
14851
14852                    // get/convert json string to json array
14853                    var items = localStorage.getItem(form);
14854                    items = JSON.parse(items);
14855
14856                    var currentDate = new Date();
14857
14858                    // if localstorge item is older than 1 day then delete it
14859                    var expired = currentDate > new Date(items["timeOutDate"]);
14860                    if (expired) {
14861                        vt.initFormCookieData.deleteStorage(form);
14862                        return;
14863                    }
14864
14865                    vt.initFormCookieData.insetStorage(items, form);
14866                });
14867            };
14868
14869            var insetStorage = function(items, form) {
14870                var formItem = $('[data-formname="'+ form +'"]');
14871
14872                // each saved item in localstorage
14873                $.each(items, function(index, value) {
14874                    var inputItem = formItem.find(':input[name="'+ index +'"]');
14875                    var parent = $(inputItem).parents('.form-element').attr("data-localStorage", true);
14876                    // checkboxes are sent as array and need to be targeted by value
14877                    if (Array.isArray(value) === true) {
14878                        $.each(value, function(index, val) {
14879                            formItem.find(':input[value="'+ val +'"]').prop('checked', true);
14880                        });
14881                    } else if (inputItem.attr('type') === "radio") {    
14882                        //check if input is radio as radions share same name and need to be targeted by value
14883                        $.each(inputItem, function(index, item) {
14884                            if($(item).val() === value){
14885                                var radioItem = $(item);
14886                                radioItem.prop('checked', true).trigger('click');
14887                                setTimeout(
14888                                    function() 
14889                                    {
14890                                        radioItem.trigger('change');
14891                                    }, 350);
14892                            }
14893                        });
14894                    } else if ($(parent[0]).hasClass('js-getopportunities') === true) {   
14895                            $(inputItem[0]).val(value);
14896                            vt.initFormTools.getOpportunities(inputItem[0]);                        
14897
14898                    } else if ($(parent[1]).hasClass('js-getopportunities') === true || $(parent[1]).hasClass('js-opportunities')) {
14899
14900                            var selectInput = $(inputItem[0]),
14901                                    selectedOption = selectInput.find('option:contains('+value+')'),
14902                                    otherOption = selectInput.find('option[value="-1"]'),
14903                                    otherInput = formItem.find(':input[name="'+ index +'"][type="text"]');
14904
14905                            //handle Producer selection and Other Producer
14906                            if (index === "Producer") {
14907                                if(selectedOption.length) {
14908                                    selectedOption.prop('selected', true);
14909                                } else {
14910                                    otherOption.prop('selected', true);
14911                                    otherInput.val(value);
14912                                }
14913                                selectInput.trigger('change');                                    
14914                            }
14915                            //handle Model selection and Other Model
14916                            else if (index === "Model") {
14917                                //Check if Model select dropdown has value from LocalStorage
14918                                var checkExist = setInterval(function() { 
14919                                    var selectedOption = selectInput.find('option:contains('+value+')');
14920                                    if (selectedOption.length) {
14921                                        selectedOption.prop('selected', true);
14922                                        selectInput.prop('disabled', false);
14923                                        clearInterval(checkExist);
14924                                        clearTimeout(checkTimeout);
14925                                    }
14926                                    
14927                                }, 10);
14928
14929                                //keep checking for 0.5 sec max
14930                                var checkTimeout = setTimeout(function () {
14931                                    if (checkExist) {
14932                                        clearInterval(checkExist);
14933                                    }
14934                                    var otherOption = selectInput.find('option[value="-1"]'),
14935                                        otherInput = formItem.find(':input[name="'+ index +'"][type="text"]');
14936                                    otherOption.prop('selected', true);
14937                                    otherInput.val(value).parent().removeClass('hidden').addClass('js-validate');
14938                                }, 500);
14939                            }
14940                            else{
14941                                // Create option element safely to prevent XSS
14942                                var optionBlock = $('<option></option>').attr('selected', true).val(value).text(value);
14943                                // append option to select
14944                                inputItem.append(optionBlock);
14945                            }
14946                    } else if (form.substr(0, form.indexOf('-')) === 'delivererinformation') {                        
14947                        if (inputItem.length === 0 && index !== 'timeOutDate' && value !== '') {
14948                            formItem.find('[data-action="show-extra-section"]').click();
14949                            var inputItem = formItem.find(':input[name="'+ index +'"]');
14950                        }
14951                        inputItem.val(value);
14952                    }                    
14953                    else {
14954                        inputItem.val(value);
14955                    }
14956                });
14957
14958                vt.initFormCookieData.deleteStorage(form);
14959            };
14960
14961            return {
14962                saveStorage: saveStorage,
14963                deleteStorage: deleteStorage,
14964                getStorage: getStorage,
14965                insetStorage: insetStorage                
14966            };
14967    })();
14968
14969    
14970    $(function() {
14971        // run this on document ready
14972        if ($('body').hasClass('registrationformpage')) {
14973            window.vt.initFormCookieData.getStorage();
14974        }
14975    });
14976})(jQuery);
14977;
14978; (function ($) {
14979
14980    window.vt = window.vt || {};
14981
14982    var IntroductionForm = function (elem) {
14983        vt.Form.call(this, elem);
14984        this.radios = this.form.find('.c-podium__radio');
14985        this.radios.on('change', this.radioChange.bind(this));
14986        this.modalActions = this.form.find('.c-modal__action');
14987        this.modalActions.on('click', this.handleSubmit.bind(this));
14988    };
14989
14990    IntroductionForm.prototype = Object.create(vt.Form.prototype);
14991    IntroductionForm.prototype.constructor = IntroductionForm;
14992
14993    IntroductionForm.prototype.radioChange = function (e) {
14994        if (e.target.checked) {
14995            this.radios.closest('.c-podium').removeClass('c-podium--active');
14996            $(e.target).closest('.c-podium').addClass('c-podium--active');
14997        }
14998    };
14999
15000    IntroductionForm.prototype.gatherData = function () {
15001        var is247 = null;
15002        var isBehindLock = null;
15003        var radioVal = $('input[type=radio]:checked').val();
15004        var defibrillatorIdVal = $('input[name="DefibrillatorId"]').val();
15005
15006        if (!radioVal) {
15007            throw new Error('No option selected');
15008        }
15009
15010        switch (radioVal) {
15011            case '1':
15012                is247 = true;
15013                isBehindLock = false;
15014                break;
15015            case '2':
15016                is247 = false;
15017                isBehindLock = false;
15018                break;
15019            case '3':
15020                is247 = false;
15021                isBehindLock = true;
15022                break;
15023            default:
15024                throw new Error('Invalid selection: ' + radioVal);
15025        }
15026
15027        var data = {
15028            Is247: is247,
15029            IsBehindLock: isBehindLock,
15030            SelectedOption: radioVal
15031        };
15032
15033        if (defibrillatorIdVal && defibrillatorIdVal !== '') {
15034            data.DefibrillatorId = defibrillatorIdVal;
15035        }
15036
15037        return data;
15038    };
15039
15040    IntroductionForm.prototype.handleSubmit = function (e) {
15041        try {
15042            var formData = this.gatherData();
15043            console.log('Form submitted with data:', formData);
15044
15045            // Determine if this is edit mode
15046            var isEditMode = formData.DefibrillatorId && formData.DefibrillatorId !== '';
15047
15048            // Track form submission with complete data
15049            vt.functions.pushToDataLayer({
15050                event: isEditMode ? 'edit_defibrillator_availability' : 'register_defibrillator_availability',
15051                defibrillator_id: formData.DefibrillatorId || '',
15052            });
15053
15054            // Add additional logic for form submission, e.g., AJAX request
15055        } catch (error) {
15056            console.error('Form submission error:', error);
15057
15058            // Track error event
15059            vt.functions.pushToDataLayer({
15060                event: 'form_error',
15061                form_name: 'defibrillator_introduction',
15062                error_message: error.message
15063            });
15064        }
15065    };
15066
15067    vt.initDefibrillatorForms = function (elem) {
15068        var forms = [];
15069        var formName = elem.getAttribute('data-formname');
15070        var searchParam = new URLSearchParams(window.location.search);
15071        var defibrillatorId = searchParam.get("id");
15072        var isEditMode = defibrillatorId !== null && defibrillatorId !== "";
15073
15074        switch (formName) {
15075            case 'defibrillatorintroduction':
15076                var form = new IntroductionForm(elem);
15077                forms.push(form);
15078                break;
15079            default:
15080                console.warn('Unknown form type:', formName);
15081        }
15082
15083        return forms;
15084    };
15085})(jQuery);;
15086; (function ($) {
15087    window.vt = window.vt || {};
15088
15089    var formContainer = document.querySelector(".page-form-container");
15090
15091    var formChecked = false;
15092    var formSubmitted = false;
15093    var formCheck = function () {
15094        if (formContainer !== null && formContainer !== undefined) {
15095            if (document.querySelector('.wffm-custom-file-input') !== null && document.querySelector('.wffm-custom-file-input') !== undefined) {
15096                var group = document.querySelector('.wffm-custom-file-input');
15097                var container = '<label class="custom-fileupload"/>';
15098                var label = '<span class="custom-fileupload__show"><span class="text">Upload fil</span></span>';
15099                var file = '<div class="custom-fileupload__file"><span class="text"></span></div>'
15100                var input = group.querySelector('input[type="file"]');
15101                input.insertAdjacentHTML('afterend', container);
15102                var box = group.querySelector('.custom-fileupload');
15103                var newInput = input.outerHTML;
15104                input.remove();
15105                box.insertAdjacentHTML('beforeend', newInput);
15106                box.insertAdjacentHTML('beforeend', label);
15107                box.insertAdjacentHTML('afterend', file);
15108                vt.initFileUpload(group.querySelector(".custom-fileupload"));
15109                formChecked = true;
15110            }
15111        }
15112    };
15113    vt.initFormCheck = function () {
15114        if (!formChecked) {
15115            $('document').ready(function () { formCheck(); });
15116        }
15117
15118        if (formContainer !== null && formContainer !== undefined) {
15119            var pristine = new Pristine(formContainer);
15120
15121            var pristineError = document.querySelector(".pristine-error");
15122            if (pristineError) {
15123                formContainer.scrollIntoView();
15124            }
15125            formContainer.addEventListener('submit', function (e) {
15126
15127                var valid = pristine.validate();
15128
15129                if (!valid) {
15130                    e.preventDefault();
15131                    formContainer.scrollIntoView();
15132                }
15133                if (formSubmitted) {
15134                    e.preventDefault();
15135                }
15136                if (valid) {
15137                    formSubmitted = true;
15138                }
15139            });
15140        }
15141    };
15142})(jQuery);;
15143(function ($) {
15144  window.vt = window.vt || {};
15145
15146  vt.initForm = function (elem) {
15147    // All tracking events moved to button click handler - no pageload tracking
15148
15149    if ($("body").is(".pageedit")) return;
15150
15151    var form = $(elem);
15152    var confirmActivation = false;
15153    // click and keyup events
15154    $.each(form.find(".js-livevalidate"), function (index, elm) {
15155      $(elm).find("input").attr("data-validate-blur", "0");
15156
15157      // every letter
15158      $(elm).on(
15159        "keyup",
15160        vt.functions.debounce(function (e) {
15161          vt.initFormTools.liveValidate(e.target, form);
15162        }, 1000)
15163      );
15164
15165      // focus out
15166      $(elm).on("focusout", function (e) {
15167        if ($(e.target).val().length > 0) {
15168          $(elm).find("input").attr("data-validate-blur", "1");
15169
15170          vt.initFormTools.liveValidate(e.target, form);
15171        }
15172      });
15173
15174      // radio button change
15175      $(elm)
15176        .find('input[type="radio"]')
15177        .on(
15178          "change",
15179          vt.functions.debounce(function (e) {
15180            $(elm).find("input").attr("data-validate-blur", "1");
15181
15182            vt.initFormTools.liveValidate(e.target, form);
15183          }, 1000)
15184        );
15185    });
15186
15187    // repeat input field
15188    form.find(".js-repeat").on("keyup", function (e) {
15189      // if val is bigger than 2
15190      if ($(e.target).val().length >= 1) {
15191        vt.initFormTools.repeatValidate(e.target, form);
15192      }
15193    });
15194
15195    // back button
15196    form.find(".js-form-back").on("click", function (e) {
15197      e.preventDefault();
15198      vt.initFormTools.validateInitiate(e, form);
15199    });
15200
15201    // submit button
15202    form.find(".js-form-submit").on("click", function (e) {
15203      e.preventDefault();
15204      vt.initFormTools.validateInitiate(e, form);
15205    });
15206
15207    // opportunities input field
15208    form
15209      .find('.js-opportunities input[type="radio"]')
15210      .on("click", function (e) {
15211        vt.initFormTools.moreOpportunities(e.target);
15212      });
15213
15214    // opportunities select field
15215    form
15216      .find(".js-opportunities .select-styled select")
15217      .on("change", function (e) {
15218        var formElement = vt.functions.closestByClass(e.target, "form-element");
15219        formElements = $(formElement).find(".form-element");
15220        var formOtherElement = formElements.find('input[type="text"]').parent();
15221
15222        //make sure validation is added
15223        if (!$(formElements).hasClass("js-livevalidate")) {
15224          $(formElements).addClass("js-validate");
15225        }
15226        // if value is -1 then set other to true and show the other input
15227        var closestFormElement = vt.functions.closestByClass(
15228          e.target,
15229          "form-element"
15230        );
15231        if ($(e.target).val() === "-1") {
15232          $(closestFormElement).attr("data-other", "true");
15233          $(formOtherElement).removeClass("hidden").addClass("js-validate");
15234        } else {
15235          $(closestFormElement).attr("data-other", "false");
15236          $(formOtherElement).addClass("hidden").removeClass("js-validate");
15237        }
15238
15239        //make sure validation removed and field hidden if we go back to default selection
15240        if ($(e.target).val() === "0") {
15241          $(formElements).addClass("hidden").removeClass("js-validate");
15242          var targetName = $(formElements)
15243            .parents(".js-getopportunities")
15244            .attr("data-get"),
15245            targetFormElement = $(formElements)
15246              .parents("form")
15247              .find('[data-getopportunities="' + targetName + '"]'),
15248            targetSelect = targetFormElement.find(".select-styled select");
15249          targetSelect
15250            .prop("disabled", true)
15251            .find('[value="0"]')
15252            .prop("selected", true);
15253          targetFormElement
15254            .find(".js-other")
15255            .addClass("hidden")
15256            .removeClass("js-validate");
15257        }
15258      });
15259
15260    // get more opportunities with ajax call
15261    form
15262      .find(".js-getopportunities .getopportunities-container select")
15263      .on("change", function (e) {
15264        if ($(e.target).val() === "0") {
15265          return;
15266        }
15267        vt.initFormTools.getOpportunities(e.target);
15268      });
15269
15270    // Submit on 'enter'
15271    // $(document).on("keyup", form, function (e) {
15272    //     var code = e.keyCode || e.which;
15273    //     if (code == 13) {
15274    //         e.preventDefault();
15275    //         e.stopImmediatePropagation();
15276
15277    //         form = $(e.target).closest('form')[0];
15278    //         form = $(form);
15279
15280    //         //vt.initFormTools.validateInitiate(e, form);
15281    //         //Instead of calling function with the initiator we programatically click the button
15282    //         form.find('.js-form-submit').trigger('click');
15283    //         return false;
15284    //     }
15285    // });
15286
15287    // on change then clear error field
15288    form.find("input, select, textarea").on("change", function (e) {
15289      var linkedInputs = form.find('*[name="' + e.target.name + '"]');
15290      linkedInputs.removeClass("invalid");
15291
15292      linkedInputs.each(function () {
15293        var closestError = vt.functions.closestByClass(this, "form-element");
15294        closestError = $(closestError).find(".ui-state-error");
15295        $(closestError).remove();
15296      });
15297    });
15298
15299    $(".expandable-section--expand").on("click", function (e) {
15300      e.stopImmediatePropagation();
15301      $(e.target).toggleClass("expanded");
15302    });
15303
15304    form.find('[data-action="show-extra-section"]').on("click", function (e) {
15305      e.preventDefault();
15306      var initiator = $(e.currentTarget);
15307      initiator.parent().removeClass("no-form-gap--top");
15308      vt.initFormTools.showExtraSection(initiator);
15309    });
15310
15311    form.find('[data-action="add-days-interval"]').click(function (e) {
15312      e.preventDefault();
15313
15314      var initiator = $(e.currentTarget),
15315        container = initiator.parent().find('[data-container="days-interval"]'),
15316        index = parseInt(initiator.attr("data-interval-index")) + 1,
15317        count = parseInt(initiator.attr("data-interval-count")) + 1,
15318        from = container
15319          .find('input[name="ClosingDays-0-From-0"]')
15320          .attr("placeholder"),
15321        to = container
15322          .find('input[name="ClosingDays-0-To-0"]')
15323          .attr("placeholder");
15324
15325      initiator
15326        .attr("data-interval-count", count)
15327        .attr("data-interval-index", index);
15328
15329      var template = $(
15330        '<div class="days-interval__item"><div class="form-element datepicker-styled js-validate js-datepickerDay">' +
15331        '<input type="text" name="ClosingDays-0-From" value="" placeholder="' +
15332        from +
15333        '" maxlength="5" size="5" data-toggle="datepicker">' +
15334        "</div>" +
15335        '<div class="form-element datepicker-styled js-validate js-datepickerDay">' +
15336        '<input type="text" name="ClosingDays-0-To" value="" placeholder="' +
15337        to +
15338        '" maxlength="5" size="5" data-toggle="datepicker">' +
15339        '</div><button class="circle icon icon-delete" data-action="remove-interval"></button></div>'
15340      );
15341
15342      template.find(":input").each(function () {
15343        var $input = $(this);
15344        var name = $input.attr("name");
15345        $input.attr("name", name + "-" + index);
15346        if ($input.hasClass("input-type__time")) {
15347          vt.initFormTools.initiateInputTimeFormating($input);
15348        }
15349      });
15350
15351      // if(count <= 2) {
15352      var elements = template.find(".form-element");
15353      elements.each(function (index, elm) {
15354        vt.initDatePicker(elm);
15355      });
15356      template.find('[data-action="remove-interval"]').click(function (e) {
15357        vt.initFormTools.removeInterval(e, initiator);
15358      });
15359      container.append(template);
15360      // }
15361      // if (count >= 20)  {
15362      //     initiator.addClass('hidden');
15363      // }
15364    });
15365
15366    form.find(".input-type__time").each(function () {
15367      var $input = $(this);
15368      vt.initFormTools.initiateInputTimeFormating($input);
15369    });
15370
15371    form.find('[data-action="add-hours-interval"]').click(function (e) {
15372      var sectionIndex = $(this).attr("data-sectionIndex"),
15373        sectionIndexInt = sectionIndex
15374          ? parseInt(sectionIndex.replace("-", ""))
15375          : undefined;
15376      vt.initFormTools.initiateExtraHours(e, sectionIndexInt);
15377    });
15378
15379    form.find(".js-select-toggle select").change(function (e) {
15380      var selectContainer = $(e.target).parent();
15381      vt.initFormTools.toggleSectionOnSelect(selectContainer);
15382    });
15383
15384    form.find('[data-action="remove-section"]').click(function (e) {
15385      var initiator = form.find('[data-action="show-extra-section"]');
15386      $(e.target).closest(".aed-availability").remove();
15387      initiator.attr("data-count", parseInt(initiator.attr("data-count")) - 1);
15388
15389      if (parseInt(initiator.attr("data-index")) < 20) {
15390        initiator.removeClass("hidden");
15391      }
15392    });
15393
15394    form.find('[data-action="remove-interval"]').click(function (e) {
15395      var initiator = $(e.currentTarget)
15396        .parents(".aed-availability")
15397        .find(
15398          '[data-action="add-hours-interval"], [data-action="add-days-interval"]'
15399        );
15400      vt.initFormTools.removeInterval(e, initiator);
15401    });
15402
15403    //$.each(form.find('.icon.icon-tooltip'), function(i, tooltip){
15404    form.find(".icon.icon-tooltip").hover(function (e) {
15405      e.preventDefault();
15406      var initiator = $(e.currentTarget);
15407      vt.initFormTools.positionTooltip(initiator);
15408    });
15409    //});
15410
15411    // form.keypress(function(event) {
15412    //     return event.keyCode != 13;
15413    // });
15414    $(".delete-profile").on("click", function (e) {
15415      e.preventDefault();
15416      e.stopImmediatePropagation();
15417      // make html element from template
15418
15419      var data = "Er du sikker på, du vil slette din profil?";
15420      //need to get the warning message here
15421      var warningMessage = $(e.target).closest("form").find("[data-warning]");
15422      if (warningMessage != undefined && warningMessage.length != 0) {
15423        data = warningMessage.text();
15424      }
15425      var htmlOutput = vt.templates.renderTemplate("warningModal", data);
15426      // append modal to body
15427      $("body").append(htmlOutput);
15428
15429      var confirmed = setInterval(function () {
15430        if (confirmActivation === true) {
15431          // get api url
15432          var url = $(e.target).parents("form").attr("data-api");
15433          var opts = {
15434            type: "DELETE",
15435          };
15436
15437          var call = window.vt.ajax.call(url, opts, e);
15438          vt.animations.feedbackEvaluate($(e.target));
15439
15440          call.success(function (data) {
15441            // if success then remove item
15442            vt.animations.feedbackApproved($(e.target));
15443
15444            setTimeout(function () {
15445              //If we have Redirect URL
15446              if (data["RedirectTo"]) {
15447                var RedirectUrl = data["RedirectTo"];
15448                window.location.href = RedirectUrl;
15449              } else {
15450                window.location.href = "/";
15451              }
15452            }, 1000);
15453          });
15454          call.error(function (data) {
15455            vt.animations.feedbackError($(e.target));
15456            vt.initFormTools.serverError(data);
15457          });
15458
15459          // remove modal and stop interval
15460          $(".modal.modal--confirm").remove();
15461          clearInterval(confirmed);
15462
15463          // set confirm back to false
15464          confirmActivation = false;
15465        }
15466      }, 1000);
15467
15468      // button click events
15469      $(".modal.modal--confirm")
15470        .find("button")
15471        .on("click", function (e) {
15472          var method = $(e.target).attr("data-method");
15473
15474          if (method === "delete") {
15475            confirmActivation = true;
15476          } else {
15477            // remove modal and stop interval
15478            $(e.target).parents(".modal").remove();
15479            clearInterval(confirmed);
15480          }
15481        });
15482    });
15483
15484    var showOrHideWarningMessage = function (show, $dynamicWarningMessage) {
15485      if (show) {
15486        $dynamicWarningMessage.removeClass("c-warning-inline--hidden");
15487      } else {
15488        $dynamicWarningMessage.addClass("c-warning-inline--hidden");
15489      }
15490    };
15491
15492    var isDateInThePast = function ($datePicker) {
15493      var value = $datePicker[0].value;
15494      if (
15495        $datePicker.closest("[data-js-expiration]").hasClass("js-datepickerDay")
15496      ) {
15497        convertedValue = value.split("-");
15498        value =
15499          convertedValue[2] + "-" + convertedValue[1] + "-" + convertedValue[0];
15500      }
15501      var dateValue = new Date(value);
15502      var currentDate = new Date();
15503      return dateValue < currentDate;
15504    };
15505
15506    var updateExpirationDateWarning = function (
15507      $datePicker,
15508      $dynamicWarningMessage
15509    ) {
15510      var hasDatePassed = isDateInThePast($datePicker);
15511      showOrHideWarningMessage(hasDatePassed, $dynamicWarningMessage);
15512    };
15513
15514    $("[data-js-expiration]").each(function (index, elem) {
15515      var $elem = $(elem);
15516      var $datePicker = $elem.find("input[name='ExpirationDate']");
15517      var $dynamicWarningMessage = $elem.find(".c-warning-inline");
15518      updateExpirationDateWarning($datePicker, $dynamicWarningMessage);
15519
15520      $datePicker.change(function (e) {
15521        console.log("CHANGE", e);
15522        updateExpirationDateWarning($datePicker, $dynamicWarningMessage);
15523      });
15524    });
15525  };
15526
15527  // Logout
15528  $(".logout-action").on("click", function (e) {
15529    e.preventDefault();
15530    var $element = $(e.currentTarget),
15531      logoutUrl = $element.attr("data-url");
15532
15533    var ajaxResponse = window.vt.ajax.call(
15534      logoutUrl,
15535      {
15536        type: "POST",
15537        contentType: "application/json;charset=utf-8",
15538      },
15539      e
15540    );
15541
15542    // Success actions
15543    ajaxResponse.success(function (data) {
15544      vt.animations.feedbackApproved($element);
15545
15546      // Track logout event
15547      vt.functions.pushToDataLayer({
15548        event: "logout",
15549        user_id: window.vt.userId || ""
15550      });
15551
15552      setTimeout(function () {
15553        //If we have Redirect URL
15554        if (data["RedirectTo"]) {
15555          var RedirectUrl = data["RedirectTo"];
15556          window.location.href = RedirectUrl;
15557        } else {
15558          location.reload();
15559        }
15560      }, 1000);
15561    });
15562
15563    // Error actions
15564    ajaxResponse.error(function (data) {
15565      console.log("Error logging out");
15566    });
15567  });
15568})(jQuery);;
15569; (function ($) {
15570
15571    window.vt = window.vt || {};
15572
15573    vt.functions = (function () {
15574
15575        // This is also present in comp.autocomplete.js, we should build them together, so that they use the same code. Not duplicate code.
15576        function debounce(func, wait, immediate) {
15577            var timeout;
15578            return function () {
15579                var context = this, args = arguments;
15580                var later = function () {
15581                    timeout = null;
15582                    if (!immediate) func.apply(context, args);
15583                };
15584                var callNow = immediate && !timeout;
15585                clearTimeout(timeout);
15586                timeout = setTimeout(later, wait);
15587                if (callNow) func.apply(context, args);
15588            };
15589        };
15590
15591        function evaluator(str) {
15592            var fArr = str.split('.');
15593            var start = window;
15594            for (var i = 0; i < fArr.length; i++) {
15595                var fName = fArr[i];
15596                start = start[fName];
15597            }
15598
15599            return start;
15600        }
15601
15602        function closestByClass(el, clazz) {
15603            while (!el.classList.contains(clazz)) {
15604                el = el.parentNode;
15605                if (!el) {
15606                    return null;
15607                }
15608            }
15609
15610            return el;
15611        }
15612
15613        function googleMapImage(staticImage) {
15614            var key = 'AIzaSyBzQYAoayOgvHm0zXyCBsiT54v_RfqCzv4',
15615                zoom = '18',
15616                size = '350x350',
15617                maptype = 'hybrid';
15618
15619            var image = $(staticImage);
15620                latitude = image.attr('data-latitude'),
15621                longitude = image.attr('data-longitude');
15622
15623            if (latitude != '' && longitude != '') {
15624                var mapUrl = 'https://maps.googleapis.com/maps/api/staticmap?zoom=' + zoom + '&size=' + size + '&maptype=' + maptype + '&markers=' + latitude + ',' + longitude + '&key=' + key;
15625                image.attr('src', mapUrl);
15626            }
15627        };
15628
15629        // if find any google map image
15630        if ($('[data-role="google-map-image"]').length > 0) {
15631            $.each($('[data-role="google-map-image"]'), function(index, staticImage) {
15632                googleMapImage($(staticImage));
15633            });
15634        }
15635
15636        function pushToDataLayer(eventData) {
15637            if (typeof window.dataLayer !== 'undefined' && Array.isArray(window.dataLayer)) {
15638                try {
15639                    window.dataLayer.push(eventData);
15640                } catch (error) {
15641                    console.error('GTM dataLayer push error:', error, eventData);
15642                }
15643            } else {
15644                console.warn('GTM dataLayer not available');
15645            }
15646        }
15647
15648        return {
15649            debounce: debounce,
15650            evaluator: evaluator,
15651            closestByClass: closestByClass,
15652            googleMapImage: googleMapImage,
15653            pushToDataLayer: pushToDataLayer
15654        };
15655
15656    })();
15657
15658})(jQuery);
15659;
15660; (function ($) {
15661
15662    window.vt = window.vt || {};
15663
15664    vt.initGroupToggler = function (elem) {
15665
15666        var toggleGroup = false;
15667
15668        if ($(elem).data('toggle-group'))
15669            toggleGroup = true;
15670
15671        var $togglers = $('.toggle-item', elem);
15672
15673        $togglers.each(function () {
15674            var $toggleItem = $(this);
15675            var $toggler = $('.toggler', $toggleItem);
15676
15677
15678            $toggleItem.on('click', '.toggler', function () {
15679                if ($toggleItem.hasClass('toggle-on')) {
15680                    $togglers.removeClass('toggle-on');
15681                }
15682                else {
15683                    if (toggleGroup)
15684                        $togglers.removeClass('toggle-on')
15685
15686                    $toggleItem.addClass('toggle-on');
15687                };
15688            });
15689        });
15690    };
15691
15692})(jQuery);;
15693/* Google Maps documentation:
15694 *  https://developers.google.com/maps/documentation/javascript/3.exp/reference
15695 */
15696
15697(function ($) {
15698  window.vt = window.vt || {};
15699  var MAP = ".map";
15700  var MAP_CONTAINER = ".map__container";
15701  var MAP_SEARCH = ".c-map-search";
15702  var MAP_SEARCH_INPUT = MAP_SEARCH + "__input";
15703  var MAP_SEARCH_BACK = MAP_SEARCH + "__back";
15704  var MAP_SEARCH_CLEAR = MAP_SEARCH + "__clear";
15705  var MAP_OVERLAY = ".c-map-overlay";
15706  // var MAP_OVERLAY_GEO = MAP_OVERLAY + "__geo";
15707  var MAP_OVERLAY_SEARCH = MAP_OVERLAY + "__search";
15708  var MAP_FILTER_TOGGLE = ".c-map-filter-button";
15709  var MAP_FILTERS = ".map-filter";
15710  var MAP_HUNT_URL = "";
15711  var DesktopView = "1024";
15712  if (document.querySelector(MAP)) {
15713    MAP_HUNT_URL = document.querySelector(MAP).getAttribute("data-hunt-url");
15714  }
15715
15716  var ENV = document.querySelector(MAP)
15717    ? document.querySelector(MAP).getAttribute("data-url") || ""
15718    : "https://cdn.hjertestarterdata.dk";
15719
15720  vt.mapCounts = {
15721    regions: {
15722      all: { title: "Alle regioner" },
15723      1081: { title: "Nordjylland" },
15724      1082: { title: "Midtjylland" },
15725      1083: { title: "Syddanmark" },
15726      1084: { title: "Hovedstaden" },
15727      1085: { title: "Sjælland" },
15728    },
15729    municipalities: {},
15730  };
15731
15732  vt.getMapCounts = function () {
15733    return new Promise(function (resolve) {
15734      $.get(ENV + "/api/v4/aedspublic/count?app=web", function (countData) {
15735        var counts = vt.mapCounts;
15736        counts.municipalities = countData;
15737        var array = Object.keys(countData)
15738          .filter(function (key) {
15739            return key !== "total"; // return countData[key].regionId;
15740          })
15741          .map(function (key) {
15742            var itm = countData[key];
15743            itm.id = key;
15744            return itm;
15745          });
15746        array.sort(function (a, b) {
15747          return a.municipality > b.municipality ? 1 : -1;
15748        });
15749        counts.municipalities = array.filter(function (obj) {
15750          return !!obj.regionId;
15751        });
15752
15753        // Make regions
15754        var regionKeys = Object.keys(counts.regions);
15755
15756        for (var i = 0; i < regionKeys.length; i += 1) {
15757          var id = regionKeys[i];
15758          counts.regions[id].countAlways = 0;
15759          counts.regions[id].countOpeningHours = 0;
15760          counts.regions[id].countBehindLock = 0;
15761          counts.regions[id].countGreyAed = 0;
15762          counts.regions[id].countTotal = 0;
15763        }
15764
15765        for (var index = 0; index < array.length; index++) {
15766          var itm = array[index];
15767          itm.title = itm.municipality;
15768          if (counts.regions[itm.regionId]) {
15769            counts.regions[itm.regionId].countAlways += itm.countAlways;
15770            counts.regions[itm.regionId].countOpeningHours +=
15771              itm.countOpeningHours;
15772            counts.regions[itm.regionId].countBehindLock += itm.countBehindLock;
15773            counts.regions[itm.regionId].countGreyAed += itm.countGreyAed;
15774            counts.regions[itm.regionId].countTotal +=
15775              itm.countAlways +
15776              itm.countOpeningHours +
15777              itm.countBehindLock +
15778              itm.countGreyAed;
15779          }
15780
15781          counts.regions.all.countAlways += itm.countAlways;
15782          counts.regions.all.countOpeningHours += itm.countOpeningHours;
15783          counts.regions.all.countBehindLock += itm.countBehindLock;
15784          counts.regions.all.countGreyAed += itm.countGreyAed;
15785          counts.regions.all.countTotal +=
15786            itm.countAlways +
15787            itm.countOpeningHours +
15788            itm.countBehindLock +
15789            itm.countGreyAed;
15790        }
15791
15792        vt.mapCounts = counts;
15793
15794        $(".c-aed-map__count--247").html(counts.regions.all.countAlways);
15795        $(".c-aed-map__count--opening-hours").html(
15796          counts.regions.all.countOpeningHours
15797        );
15798        $(".c-aed-map__count--behind-lock").html(
15799          counts.regions.all.countBehindLock
15800        );
15801        $(".c-aed-map__count--unavailable").html(
15802          counts.regions.all.countGreyAed
15803        );
15804
15805        resolve(vt.mapCounts);
15806      });
15807    });
15808  };
15809
15810  vt.mapCountsData = vt.getMapCounts();
15811
15812  var regions$;
15813
15814  vt.mapCountsData.then(function (counts) {
15815    // Update filter options
15816    regions$ = $(".map-filter-regions");
15817    var regionKeys = ["all"].concat(
15818      Object.keys(counts.regions)
15819        .filter(function (key) {
15820          return key !== "all";
15821        })
15822        .sort(function (a, b) {
15823          return counts.regions[a].title > counts.regions[b].title ? 1 : -1;
15824        })
15825    );
15826
15827    for (var i = 0; i < regionKeys.length; i += 1) {
15828      var id = regionKeys[i];
15829      var itm = counts.regions[id];
15830      var title = itm.title;
15831      var o = new Option(title, id);
15832      o.setAttribute("data-id", id);
15833      o.setAttribute("data-name", title);
15834      o.setAttribute("data-aed", itm.countTotal);
15835      o.setAttribute("data-247-aed", itm.countAlways);
15836      o.setAttribute("data-opening-hours-aed", itm.countOpeningHours);
15837      o.setAttribute("data-behind-lock-aed", itm.countBehindLock);
15838      o.setAttribute("data-unavailable-aed", itm.countGreyAed);
15839      regions$.append(o);
15840    }
15841  });
15842
15843  function registerEvent(data) {
15844    if (window.dataLayer) {
15845      window.dataLayer.push(data);
15846    }
15847  }
15848
15849  // Debounce utility function
15850  function debounce(func, wait) {
15851    var timeout;
15852    return function () {
15853      var context = this, args = arguments;
15854      clearTimeout(timeout);
15855      timeout = setTimeout(function () {
15856        func.apply(context, args);
15857      }, wait);
15858    };
15859  }
15860
15861  vt.mapSearch = function () {
15862    var mapElem = document.querySelector(MAP);
15863    var inputElem = document.querySelector(MAP_SEARCH_INPUT);
15864    var backElem = document.querySelector(MAP_SEARCH_BACK);
15865    var clearElem = document.querySelector(MAP_SEARCH_CLEAR);
15866    var searchElem = document.querySelector(MAP_OVERLAY_SEARCH);
15867    var filterButtonElem = document.querySelector(MAP_FILTER_TOGGLE);
15868    var mapFiltersElem = document.querySelector(MAP_FILTERS);
15869
15870    if (inputElem) {
15871      inputElem.addEventListener("focus", function () {
15872        mapElem.classList.add("map--search");
15873        vt.deselectMarker();
15874      });
15875      inputElem.addEventListener("change", function () {
15876        if ($(this).val() != "") {
15877          $(clearElem).show();
15878        } else {
15879          $(clearElem).hide();
15880        }
15881      });
15882    }
15883
15884    if (backElem) {
15885      backElem.addEventListener("click", function () {
15886        mapElem.classList.remove("map--search");
15887      });
15888    }
15889    if (clearElem) {
15890      clearElem.addEventListener("click", function () {
15891        $(inputElem).val("").trigger("change");
15892        $(clearElem).hide();
15893      });
15894    }
15895
15896    if (filterButtonElem) {
15897      filterButtonElem.addEventListener("click", function (e) {
15898        e.stopPropagation();
15899      });
15900    }
15901
15902    if (searchElem) {
15903      searchElem.addEventListener("click", function () {
15904        // If we have a value in the text field perform a SEARCH
15905        if (inputElem.value !== "") {
15906          vt.hjertestarterMapMethods.search();
15907        } else {
15908          // If we have filter values apply FILTERS
15909          vt.hjertestarterMapMethods.applyFilters(true);
15910        }
15911
15912        // Hide the overlay
15913        mapElem.classList.remove("map--search");
15914      });
15915    }
15916  };
15917
15918  function debounce(func, wait, immediate) {
15919    var timeout;
15920    return function () {
15921      var context = this,
15922        args = arguments;
15923      var later = function () {
15924        timeout = null;
15925        if (!immediate) func.apply(context, args);
15926      };
15927      var callNow = immediate && !timeout;
15928      clearTimeout(timeout);
15929      timeout = setTimeout(later, wait);
15930      if (callNow) func.apply(context, args);
15931    };
15932  }
15933
15934  String.prototype.splice = function (idx, rem, str) {
15935    return this.slice(0, idx) + str + this.slice(idx + Math.abs(rem));
15936  };
15937
15938  vt.viewportCalc = function () {
15939    var vhInPixels = window.innerHeight / 100;
15940    document.documentElement.style.setProperty("--vh", vhInPixels + "px");
15941  };
15942
15943  vt.pinOverlay = function () {
15944    var mapElem = document.querySelector(".map");
15945    var cardOverlayElem = document.createElement("div");
15946    cardOverlayElem.classList.add("c-card-overlay");
15947    document.body.appendChild(cardOverlayElem);
15948    // CLOSE BUTTON OVERLAY
15949    var closeButtonElem = document.createElement("button");
15950    closeButtonElem.classList.add("c-card-overlay__close");
15951    var svgCloseIcon =
15952      '<svg class="c-icon c-icon-close" aria-hidden="true"><use xlink:href="/assets/img/svg-sprite/symbol-defs.svg#icon-close"></use></svg>';
15953    closeButtonElem.innerHTML = svgCloseIcon;
15954    closeButtonElem.addEventListener("click", function () {
15955      document.body.classList.remove("o-body--card-overlay");
15956      mapElem.classList.remove("map--detail");
15957    });
15958    var overlayContentElem = document.createElement("div");
15959    overlayContentElem.classList.add("c-card-overlay__content");
15960    cardOverlayElem.appendChild(closeButtonElem);
15961    cardOverlayElem.appendChild(overlayContentElem);
15962  };
15963
15964  vt.createPinDetails = function (data) {
15965    var title = data.address.organisation;
15966    var address =
15967      data.address.street.trim() +
15968      " " +
15969      data.address.number.trim() +
15970      (data.address.floor.trim() != ""
15971        ? ", " + data.address.floor.trim()
15972        : "") +
15973      "" +
15974      ", " +
15975      (data.address.postalCode + "").trim();
15976    var availabilityHtml = "";
15977    var show247 = data.availability.availability === "Always";
15978    var showOpeningHours = data.availability.availability === "OpeningHours";
15979    var showBehindLock = data.availability.availability === "BehindLock";
15980    var unavailable = data.status.status === "Deactivated";
15981    var iconClass = "";
15982    var statusLabel = "";
15983    if (show247 && !unavailable) {
15984      iconClass = "open--247";
15985      statusLabel = "Døgnåbent";
15986      availabilityHtml =
15987        '<p class="map-modal-open--247">Tilgængelig døgnet rundt</p>';
15988    } else if (showOpeningHours && !unavailable) {
15989      iconClass = "open--hours";
15990      statusLabel = "Se åbningstider";
15991      availabilityHtml =
15992        '<p class="map-modal-open--hours">Tilgængelig i åbningstiden</p>';
15993    } else if (showBehindLock && !unavailable) {
15994      iconClass = "open--lock";
15995      statusLabel = "Lukket";
15996      availabilityHtml = '<p class="map-modal-open--lock">LÃ¥st inde</p>';
15997    } else if (unavailable) {
15998      iconClass = "open--unavailable";
15999      statusLabel = "Ude af drift";
16000      availabilityHtml =
16001        '<p class="map-modal--unavailable">Pt utilgængelig</p>';
16002    }
16003    var html = "";
16004    html += '<div class="c-pin-card__content">';
16005    html += '<div class="c-pin-card__content-top">';
16006    if (iconClass === "open--247") {
16007      html +=
16008        '<svg class="c-pin-card__icon" width="40" height="41" viewBox="0 0 40 41" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="20" cy="20.3145" r="20" fill="#35A845"/><mask id="mask0_5227_4617" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="8" y="8" width="24" height="25"><rect x="8" y="8.31445" width="24" height="24" fill="white"/></mask><g mask="url(#mask0_5227_4617)"><path d="M17.5497 23.4645L26.0247 14.9895C26.2247 14.7895 26.4581 14.6895 26.7247 14.6895C26.9914 14.6895 27.2247 14.7895 27.4247 14.9895C27.6247 15.1895 27.7247 15.427 27.7247 15.702C27.7247 15.977 27.6247 16.2145 27.4247 16.4145L18.2497 25.6145C18.0497 25.8145 17.8164 25.9145 17.5497 25.9145C17.2831 25.9145 17.0497 25.8145 16.8497 25.6145L12.5497 21.3145C12.3497 21.1145 12.2539 20.877 12.2622 20.602C12.2706 20.327 12.3747 20.0895 12.5747 19.8895C12.7747 19.6895 13.0122 19.5895 13.2872 19.5895C13.5622 19.5895 13.7997 19.6895 13.9997 19.8895L17.5497 23.4645Z" fill="white"/></g></svg>';
16009    }
16010    if (iconClass === "open--hours") {
16011      html +=
16012        '<svg class="map-modal__icon" width="40" height="41" viewBox="0 0 40 41" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="20" cy="20.3145" r="20" fill="#FFCE00"/><mask id="mask0_5227_4625" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="8" y="8" width="24" height="25"><rect x="8" y="8.31445" width="24" height="24" fill="#D9D9D9"/></mask><g mask="url(#mask0_5227_4625)">' +
16013        '<path d="M21 19.9148L24 22.9148C24.1833 23.0982 24.275 23.3315 24.275 23.6148C24.275 23.8982 24.1833 24.1315 24 24.3148C23.8167 24.4982 23.5833 24.5898 23.3 24.5898C23.0167 24.5898 22.7833 24.4982 22.6 24.3148L19.3 21.0148C19.2 20.9148 19.125 20.8023 19.075 20.6773C19.025 20.5523 19 20.4232 19 20.2898V16.3148C19 16.0315 19.0958 15.794 19.2875 15.6023C19.4792 15.4107 19.7167 15.3148 20 ' +
16014        '15.3148C20.2833 15.3148 20.5208 15.4107 20.7125 15.6023C20.9042 
1601415.794 21 16.0315 21 16.3148V19.9148ZM23.25 13.3148C22.9 13.3148 22.6042 13.194 22.3625 12.9523C22.1208 12.7107 22 12.4148 22 12.0648C22 11.7148 22.1208 11.419 22.3625 11.1773C22.6042 10.9357 22.9 10.8148 23.25 10.8148C23.6 10.8148 23.8958 10.9357 24.1375 11.1773C24.3792 11.419 24.5 11.7148 24.5 12.0648C24.5 12.4148 24.3792 ' +
16015        '12.7107 24.1375 12.9523C23.8958 13.194 23.6 13.3148 23.25 13.3148ZM23.25 29.8148C22.9 29.8148 22.6042 29.694 22.3625 29.4523C22.1208 29.2107 22 28.9148 22 28.5648C22 28.2148 22.1208 27.919 22.3625 27.6773C22.6042 27.4357 22.9 27.3148 23.25 27.3148C23.6 27.3148 23.8958 27.4357 24.1375 27.6773C24.3792 27.919 24.5 28.2148 24.5 28.5648C24.5 28.9148 24.3792 29.2107 24.1375 29.4523C23.8958 29.694 23.6 29.8148 23.25 29.8148ZM27.25 16.8148C26.9 16.8148 26.6042 16.694 26.3625 16.4523C26.1208 16.2107 26 15.9148 26 15.5648C26 15.2148 26.1208 14.919 26.3625 14.6773C26.6042 14.4357 26.9 14.3148 27.25 14.3148C27.6 14.3148 27.8958 14.4357 28.1375 14.6773C28.3792 14.919 28.5 15.2148 28.5 15.5648C28.5 15.9148 28.3792 16.2107 28.1375 16.4523C27.8958 16.694 27.6 16.8148 27.25 16.8148ZM27.25 26.3148C26.9 26.3148 26.6042 26.194 26.3625 25.9523C26.1208 25.7107 26 25.4148 26 25.0648C26 24.7148 26.1208 24.419 26.3625 24.1773C26.6042 23.9357 26.9 23.8148 27.25 23.8148C27.6 23.8148 27.8958 23.9357 28.1375 24.1773C28.3792 24.419 28.5 24.7148 28.5 25.0648C28.5 25.4148 28.3792 25.7107 28.1375 25.9523C27.8958 26.194 27.6 26.3148 27.25 26.3148ZM28.75 21.5648C28.4 21.5648 28.1042 21.444 27.8625 21.2023C27.6208 20.9607 27.5 20.6648 27.5 20.3148C27.5 19.9648 27.6208 19.669 27.8625 19.4273C28.1042 19.1857 28.4 19.0648 28.75 19.0648C29.1 19.0648 29.3958 19.1857 29.6375 19.4273C29.8792 19.669 30 19.9648 30 20.3148C30 20.6648 29.8792 20.9607 29.6375 21.2023C29.3958 21.444 29.1 21.5648 28.75 21.5648ZM10 20.3148C10 17.6982 10.8708 15.4482 12.6125 13.5648C14.3542 11.6815 16.4917 10.6148 19.025 10.3648C19.2917 10.3315 19.5208 10.4107 19.7125 10.6023C19.9042 10.794 20 11.0315 20 11.3148C20 11.5815 19.9125 11.8148 19.7375 12.0148C19.5625 12.2148 19.3417 12.3315 19.075 12.3648C17.0583 12.5982 15.375 13.4648 14.025 14.9648C12.675 16.4648 12 18.2482 12 20.3148C12 22.3982 12.675 24.1857 14.025 25.6773C15.375 27.169 17.0583 28.0315 19.075 28.2648C19.3417 28.2982 19.5625 28.4148 19.7375 28.6148C19.9125 28.8148 20 29.0482 20 29.3148C20 29.5982 19.9042 29.8357 19.7125 30.0273C19.5208 30.219 19.2917 30.2982 19.025 30.2648C16.475 30.0148 14.3333 28.9482 12.6 27.0648C10.8667 25.1815 10 22.9315 10 20.3148Z" fill="white"/></g></svg>';
16016    }
16017    if (iconClass === "open--lock") {
16018      html +=
16019        '<svg class="map-modal__icon" width="40" height="41" viewBox="0 0 40 41" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="20" cy="20.3145" r="20" fill="#DE0000"/><mask id="mask0_5227_4633" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="8" y="8" width="24" height="25"><rect x="8" y="8.31445" width="24" height="24" fill="#D9D9D9"/></mask><g mask="url(#mask0_5227_4633)"><path d="M14 30.3145C13.45 30.3145 12.9792 30.1186 12.5875 29.727C12.1958 29.3353 12 28.8645 12 28.3145V18.3145C12 17.7645 12.1958 17.2936 12.5875 16.902C12.9792 16.5103 13.45 16.3145 14 16.3145H15V14.3145C15 12.9311 15.4875 11.752 16.4625 10.777C17.4375 9.80195 18.6167 9.31445 20 9.31445C21.3833 9.31445 22.5625 9.80195 23.5375 10.777C24.5125 11.752 25 12.9311 25 14.3145V16.3145H26C26.55 16.3145 27.0208 16.5103 27.4125 16.902C27.8042 17.2936 28 17.7645 28 18.3145V28.3145C28 28.8645 27.8042 29.3353 27.4125 29.727C27.0208 30.1186 26.55 30.3145 26 30.3145H14ZM14 28.3145H26V18.3145H14V28.3145ZM20 25.3145C20.55 25.3145 21.0208 25.1186 21.4125 24.727C21.8042 24.3353 22 23.8645 22 23.3145C22 22.7645 21.8042 22.2936 21.4125 21.902C21.0208 21.5103 20.55 21.3145 20 21.3145C19.45 21.3145 18.9792 21.5103 18.5875 21.902C18.1958 22.2936 18 22.7645 18 23.3145C18 23.8645 18.1958 24.3353 18.5875 24.727C18.9792 25.1186 19.45 25.3145 20 25.3145ZM17 16.3145H23V14.3145C23 13.4811 22.7083 12.7728 22.125 12.1895C21.5417 11.6061 20.8333 11.3145 20 11.3145C19.1667 11.3145 18.4583 11.6061 17.875 12.1895C17.2917 12.7728 17 13.4811 17 14.3145V16.3145Z" fill="white"/></g></svg>';
16020    }
16021    if (iconClass === "open--unavailable") {
16022      html +=
16023        '<svg class="map-modal__icon" width="40" height="41" viewBox="0 0 40 41" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="20" cy="20.3145" r="20" fill="#D1D1D1"/><g opacity="0.6"><mask id="mask0_5227_4641" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="8" y="8" width="24" height="25"><rect x="8" y="8.31445" width="24" height="24" fill="#D9D9D9"/></mask><g mask="url(#mask0_5227_4641)"><path d="M20 30.3145C18.6167 30.3145 17.3167 30.052 16.1 29.527C14.8833 29.002 13.825 28.2895 12.925 27.3895C12.025 26.4895 11.3125 25.4311 10.7875 24.2145C10.2625 22.9978 10 21.6978 10 20.3145C10 18.9311 10.2625 17.6311 10.7875 16.4145C11.3125 15.1978 12.025 14.1395 12.925 13.2395C13.825 12.3395 14.8833 11.627 16.
160231 11.102C17.3167 10.577 18.6167 10.3145 20 10.3145C21.3833 10.3145 22.6833 10.577 23.9 11.102C25.1167 11.627 26.175 12.3395 27.075 13.2395C27.975 14.1395 28.6875 15.1978 29.2125 16.4145C29.7375 17.6311 30 18.9311 30 20.3145C30 21.6978 29.7375 22.9978 29.2125 24.2145C28.6875 25.4311 27.975 26.4895 27.075 27.3895C26.175 28.2895 25.1167 29.002 23.9 29.527C22.6833 30.052 21.3833 30.3145 20 30.3145ZM20 28.3145C20.9 28.3145 21.7667 28.1686 22.6 27.877C23.4333 27.5853 24.2 27.1645 24.9 26.6145L13.7 15.4145C13.15 16.1145 12.7292 16.8811 12.4375 17.7145C12.1458 18.5478 12 19.4145 12 20.3145C12 22.5478 12.775 24.4395 14.325 25.9895C15.875 27.5395 17.7667 28.3145 20 28.3145ZM26.3 25.2145C26.85 24.5145 27.2708 23.7478 27.5625 22.9145C27.8542 22.0811 28 21.2145 28 20.3145C28 18.0811 27.225 16.1895 25.675 14.6395C24.125 13.0895 22.2333 12.3145 20 12.3145C19.1 12.3145 18.2333 12.4603 17.4 12.752C16.5667 13.0436 15.8 13.4645 15.1 14.0145L26.3 25.2145Z" fill="black"/></g></g></svg>';
16024    }
16025    html += '<div class="c-pin-card__address-wrap">';
16026    html +=
16027      '<h3 class="c-card-overlay__title c-pin-card__title">' + title + "</h3>";
16028    html +=
16029      '<address class="c-card-overlay__address c-pin-card__address">' +
16030      address +
16031      "</address>";
16032    html += "</div>";
16033    html += "</div>";
16034    html += " " + availabilityHtml;
16035    html += '<div class="c-pin-card__open-status">';
16036    html +=
16037      '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none"><mask id="mask0_1763_5777" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="20" height="20"><rect width="20" height="20" fill="#D9D9D9"/></mask><g mask="url(#mask0_1763_5777)"><path d="M10.5429 9.45641V5.54293C10.5429 5.38947 10.4914 5.26083 10.3884 5.15701C10.2854 5.05319 10.1577 5.00128 10.0054 5.00128C9.85308 5.00128 9.72405 5.05319 9.61828 5.15701C9.51252 5.26083 9.45964 5.38947 9.45964 5.54293V9.64872C9.45964 9.74682 9.4778 9.83491 9.51411 9.91299C9.55043 9.99107 9.60065 10.0622 9.66476 10.1263L12.5574 13.0189C12.6749 13.1364 12.8036 13.193 12.9436 13.1888C13.0836 13.1845 13.2139 13.122 13.3346 13.0013C13.4553 12.8806 13.5157 12.7516 13.5157 12.6143C13.5157 12.477 13.453 12.3462 13.3276 12.222L10.5429 9.45641ZM10.0027 17.5846C8.95808 17.5846 7.97619 17.3872 7.05701 16.9925C6.13783 16.5978 5.33232 16.0547 4.64047 15.3631C3.94861 14.6716 3.40523 13.8654 3.01032 12.9447C2.61542 12.0239 2.41797 11.04 2.41797 9.99314C2.41797 8.94622 2.61533 7.96187 3.01005 7.04009C3.40477 6.1183 3.9479 5.31495 4.63943 4.63005C5.33097 3.94514 6.13713 3.40523 7.05791 3.01032C7.97868 2.61542 8.96252 2.41797 10.0094 2.41797C11.0563 2.41797 12.0407 2.617 12.9626 3.01505C13.8845 3.41311 14.6864 3.95332 15.3683 4.63568C16.0502 5.31805 16.5901 6.12048 16.9879 7.04295C17.3857 7.96541 17.5846 8.95105 17.5846 9.99989C17.5846 11.0445 17.3872 12.0264 16.9925 12.9456C16.5978 13.8647 16.0581 14.6702 15.3736 15.3621C14.689 16.054 13.8851 16.5973 12.9619 16.9922C12.0387 17.3871 11.0523 17.5846 10.0027 17.5846ZM10.0112 16.5013C11.8095 16.5013 13.3408 15.8659 14.605 14.595C15.8692 13.3242 16.5013 11.7896 16.5013 9.99134C16.5013 8.19305 15.8692 6.6618 14.605 5.39759C13.3408 4.13339 11.8095 3.50128 10.0112 3.50128C8.21293 3.50128 6.67836 4.13339 5.40753 5.39759C4.1367 6.6618 3.50128 8.19305 3.50128 9.99134C3.50128 11.7896 4.1367 13.3242 5.40753 14.595C6.67836 15.8659 8.21293 16.5013 10.0112 16.5013Z" fill="#081D3F"/></g></svg>';
16038    html += '<div class="c-pin-card__open-status-label">';
16039    html += "" + statusLabel;
16040    html += "</div>";
16041    html += "</div>";
16042    html += "</div>";
16043    html += '<div class="c-card-overlay__placement-section">';
16044    html += '<h3 class="c-card-overlay__subtitle">Placering</h3>';
16045    html +=
16046      '<p class="c-card-overlay__placement">' +
16047      data.placement.description +
16048      "</p>";
16049
16050    if (data.pictures.length > 0) {
16051      var firstCaptionContent = "";
16052      html += '<div class="c-card-overlay__slider">';
16053      html += '<div class="c-card-overlay__slides">';
16054      $.each(data.pictures, function (i, imageElement) {
16055        var captionContent =
16056          "<div>" +
16057          imageElement.name +
16058          "</div><div>" +
16059          imageElement.text +
16060          "</div>";
16061        if (i === 0) {
16062          firstCaptionContent = captionContent;
16063        }
16064        html +=
16065          '<figure class="c-card-overlay__slide"><img src="' +
16066          ENV +
16067          imageElement.imageUrl +
16068          '"/><figcaption class="c-card-overlay__slide-caption">' +
16069          captionContent +
16070          "</figcaption></figure>";
16071      });
16072      html += "</div>";
16073      html +=
16074        '<div class="c-card-overlay__caption"><div class="c-card-overlay__caption-content">';
16075      html += firstCaptionContent;
16076      html += "</div></div>";
16077      html += "</div>";
16078    }
16079    html += "</div>";
16080
16081    /* Start opening hours accordion */
16082    if (showOpeningHours) {
16083      html += '<div class="c-card-overlay__times modal-opening-hours">';
16084
16085      html +=
16086        '<h3 class="c-card-overlay__subtitle c-card-overlay__times-title">Åbningstider</h3>';
16087
16088      /* Start access exceptions */
16089      html += '<div class="map-modal-access-exceptions">';
16090
16091      var periods = vt.createModalHelpers.flattenAvailability(
16092        data.availability
16093      );
16094      var showSpecialOpeningHeadline = true;
16095      $.each(periods, function (i, item) {
16096        // Container div with indexed classname
16097        html += '<div class="map-modal-opening-period period' + i + '">';
16098
16099        //if startdate is set then make a period headline
16100        if (item.begin && item.end) {
16101          var startDate = vt.createModalHelpers.returnDate(item.begin);
16102          var endDate = vt.createModalHelpers.returnDate(item.end);
16103          var specialOpeningHeadline = showSpecialOpeningHeadline
16104            ? '<h3 class="c-card-overlay__subtitle c-card-overlay__times-title">SÆRLIGE ÅBNINGSTIDER</h3>'
16105            : "";
16106          html +=
16107            specialOpeningHeadline +
16108            '<div class="map-modal-period-headline">' +
16109            vt.createModalHelpers.formatDate(startDate) +
16110            " - " +
16111            vt.createModalHelpers.formatDate(endDate) +
16112            "</div>";
16113          showSpecialOpeningHeadline = false;
16114        }
16115
16116        // If it's even
16117        if (item.OnlyEvenWeeks) {
16118          html += "<p>(kun lige uger)</p>";
16119        }
16120        // If it's odd
16121        if (item.OnlyOddWeeks) {
16122          html += "<p>(kun ulige uger)</p>";
16123        }
16124
16125        //go through all days and show the information about the time it's opened
16126        $.each(item.weekDays, function (a, weekday) {
16127          html += '<p class="map-modal-day-container">';
16128
16129          html +=
16130            '<span class="map-modal-dayName">
16130' + weekday.name + ": </span>";
16131
16132          if (weekday.timeSlots == null || weekday.timeSlots.length === 0) {
16133            html += '<span class="map-modal-timeslot">Lukket</span>';
16134          } else {
16135            $.each(weekday.timeSlots, function (b, timeslot) {
16136              var startTime = vt.createModalHelpers.returnTime(timeslot.begin);
16137              var endTime = vt.createModalHelpers.returnTime(timeslot.end);
16138
16139              if (startTime == "" && endTime == "") {
16140                html += '<span class="map-modal-timeslot">Lukket</span>';
16141              } else {
16142                html +=
16143                  '<span class="map-modal-timeslot">' +
16144                  startTime +
16145                  " - " +
16146                  endTime +
16147                  "</span>";
16148              }
16149            });
16150          }
16151
16152          html += "</p>";
16153        });
16154
16155        // Container div with indexed classname end
16156        html += "</div>";
16157      });
16158
16159      if (data.accessexceptions && data.accessexceptions.length > 0) {
16160        var showExceptionPeriod = false;
16161        $.each(data.accessexceptions, function (i, item) {
16162          var startDate = vt.createModalHelpers.toDate(item.begin);
16163          var endDate = vt.createModalHelpers.toDate(item.end);
16164          if (!showExceptionPeriod) {
16165            html +=
16166              '<h3 class="c-card-overlay__subtitle c-card-overlay__times-title">FERIE OG LUKKEDAGE</h3>';
16167            showExceptionPeriod = true;
16168          }
16169          if (item.begin == item.end) {
16170            html +=
16171              '<div class="map-modal-access-exception period' +
16172              i +
16173              '"><strong>' +
16174              vt.createModalHelpers.formatDate(startDate) +
16175              "</strong><br/>" +
16176              item.Note +
16177              "</div>";
16178          } else {
16179            html +=
16180              '<div class="map-modal-access-exception period' +
16181              i +
16182              '"><strong>' +
16183              vt.createModalHelpers.formatDate(startDate) +
16184              " - " +
16185              vt.createModalHelpers.formatDate(endDate) +
16186              "</strong><br/>" +
16187              item.Note +
16188              "</div>";
16189          }
16190        });
16191      }
16192
16193      html += "</div>";
16194    }
16195    /* End access exceptions */
16196
16197    /* Start public holidays */
16198    if (showOpeningHours) {
16199      html += '<div class="map-modal-public-holidays">';
16200      $.each(data.availability.holidays, function (key, value) {
16201        if ($.type(value) === "null" || value.status === "Closed") {
16202          html +=
16203            '<p class="map-modal-holiday-timeslot"><span>' +
16204            vt.createModalHelpers.publicHolidays[key] +
16205            "</span><span>Lukket</span></p>";
16206        } else {
16207          $.each(value.timespans.reverse(), function (i, timeslot) {
16208            var startTime = vt.createModalHelpers.returnTime(timeslot.begin),
16209              endTime = vt.createModalHelpers.returnTime(timeslot.end);
16210            html +=
16211              '<p class="map-modal-holiday-timeslot"><span>' +
16212              vt.createModalHelpers.publicHolidays[key] +
16213              "</span><span>" +
16214              startTime +
16215              " - " +
16216              endTime +
16217              "</span></p>";
16218          });
16219        }
16220      });
16221
16222      html += "</div>";
16223    }
16224    /* End public holidays */
16225    html += "</div>";
16226    if ($(".hunt").length > 0) {
16227      html +=
16228        "<div class='map-modal__btn-wrap'><button class='btn colored btn--small js-pick-this' data-address='" +
16229        address +
16230        "'>Vælg denne hjertestarter</button></div>";
16231    } else {
16232      html +=
16233        "<div class='map-modal__btn-wrap'><div class='map-modal__cta-text'>Er der fejl på hjertestarteren?</div><a href='" +
16234        MAP_HUNT_URL +
16235        data.huntProperties +
16236        "' class='btn colored btn--small'>Tip os</a></div>";
16237    }
16238
16239    return html;
16240  };
16241
16242  vt.deselectMarker = function () {
16243    var previews = document.querySelector(".c-pin-previews");
16244    if (previews !== null) {
16245      previews.classList.remove("c-pin-previews--active");
16246      previews.classList.remove("c-pin-previews--multiple");
16247    }
16248    var activeMarker = document.querySelector(".active-marker");
16249    if (activeMarker !== null) {
16250      activeMarker.classList.remove("active-marker");
16251    }
16252  };
16253
16254  vt.pinPreview = function (data) {
16255    var cardElems = document.querySelectorAll(".c-pin-card__content-top");
16256    var cards = [];
16257    var upCallback = function () {
16258      var overlayContentElem = document.querySelector(
16259        ".c-card-overlay__content"
16260      );
16261
16262      overlayContentElem.innerHTML = vt.createPinDetails(this);
16263
16264      // Add handler to cta
16265      $(".js-pick-this").on("click", function () {
16266        $(".js-placement").val($(this).data("address")).trigger("change");
16267        $(".c-card-overlay__close").click();
16268        vt.closePreview();
16269      })
16270      var $caption = $(".c-card-overlay__caption-content");
16271      $(overlayContentElem)
16272        .find(".c-card-overlay__slides")
16273        .slick({
16274          dots: true,
16275        })
16276        .on("afterChange", function (slick, currentSlide) {
16277          $caption.html("");
16278          var $slide = $(currentSlide.$slides[currentSlide.currentSlide]);
16279          var $newCaption = $slide.find(".c-card-overlay__slide-caption");
16280
16281          $caption.html($newCaption.html());
16282        });
16283    };
16284
16285    for (var i = 0; i < cardElems.length; i++) {
16286      var config = new vt.SwipableConfig();
16287      config.addUpConfig(
16288        "map--detail",
16289        "o-body--card-overlay",
16290        upCallback.bind(data[i])
16291      );
16292      config.addDownConfig(null, null, vt.deselectMarker.bind(data[i]));
16293
16294      cards.push(
16295        new vt.Swipable(cardElems[i], ".map", "both", config.outputConfig())
16296      );
16297    }
16298  };
16299
16300  // Close preview
16301  vt.closePreview = function () {
16302    var previews = document.querySelector(".c-pin-previews");
16303    var modal = document.querySelector(".map-modal-container");
16304    if (previews !== null) {
16305      previews.classList.remove("c-pin-previews--active");
16306      previews.classList.remove("c-pin-previews--multiple");
16307    }
16308    if (modal !== null) {
16309      modal.classList.remove("active");
16310    }
16311  }
16312
16313  vt.createModalHelpers = {
16314    toDate: function (inputDate) {
16315      if (!inputDate) {
16316        return null;
16317      }
16318      var components = inputDate.split(/[-\s]/);
16319
16320      if (components.length >= 3) {
16321        var day = parseInt(components[2]),
16322          month = parseInt(components[1]) - 1,
16323          year = parseInt(components[0]);
16324        return new Date(year, month, day);
16325      } else {
16326        var stringDate = inputDate.match(/[\d.]+/g),
16327          intDate = parseInt(stringDate);
16328        return new Date(intDate);
16329      }
16330    },
16331    formatDate: function (date) {
16332      if (!date) {
16333        return "";
16334      }
16335      var dateDay = date.getDate();
16336      var dateMonth = vt.createModalHelpers.months[date.getMonth()];
16337      var dateYear = date.getFullYear();
16338      return dateDay + ". " + dateMonth + " " + dateYear;
16339    },
16340    isEarlier: function (inputDate) {
16341      return new Date() > inputDate;
16342    },
16343    isWithinYearFromNow: function (checkDt) {
16344      var now = new Date();
16345      if (now <= checkDt) {
16346        now.setFullYear(now.getFullYear() + 1);
16347        return now >= checkDt;
16348      }
16349      return false;
16350    },
16351    returnDate: function (inputDate) {
16352      if (!inputDate) {
16353        return null;
16354      }
16355      var components = inputDate.split(/[-\s]/);
16356      if (components.length >= 3) {
16357        var day = parseInt(components[2]);
16358        var month = parseInt(components[1]) - 1;
16359        var year = parseInt(components[0]);
16360        return new Date(year, month, day);
16361      }
16362      return inputDate;
16363    },
16364    returnTime: function (inputTime) {
16365      var time = (inputTime || "").split(":"),
16366        hours = time[0],
16367        minutes = time[1];
16368      return hours + ":" + minutes;
16369    },
16370    mapAvailabilityObject: function (obj) {
16371      if (!obj) {
16372        return null;
16373      }
16374      var result = {
16375        weekDays: [],
16376      };
16377
16378      $.each(Object.keys(vt.createModalHelpers.weekDays), function (
16379        _index,
16380        key
16381      ) {
16382        var itm = obj[key];
16383        var name = vt.createModalHelpers.weekDays[key];
16384
16385        if (itm.begin || itm.end) {
16386          result.begin = itm.begin;
16387          result.end = itm.end;
16388        }
16389
16390        result.weekDays.push({
16391          name: name,
16392          key: key,
16393          timeSlots: itm.timespans,
16394          status: itm.status,
16395        });
16396      });
16397
16398      return result;
16399    },
16400    flattenAvailability: function (availability) {
16401      var results = [
16402        vt.createModalHelpers.mapAvailabilityObject(availability.weekdays),
16403      ].concat(
16404        availability.special.map(function (x) {
16405          return vt.createModalHelpers.mapAvailabilityObject(x);
16406        })
16407      );
16408      return results.filter(function (x) {
16409        return !!x;
16410      });
16411    },
16412
16413    publicHolidays: {
16414      PalmSunday: "Palmesøndag",
16415      NewYearsDay: "Nytårsdag",
16416      EasterDay: "1. påskedag",
16417      EasterMonday: "2. påskedag",
16418      HolyThursday: "Skærtorsdag",
16419      GoodFriday: "Langfredag",
16420      FirstOfMay: "1. maj",
16421      WhitSunday: "1. pinsedag",
16422      WhitMonday: "2. pinsedag",
16423      AscensionDay: "Kristi Himmelfartsdag",
16424      FridayAfterAscensionDay: "Fredag efter Kr. himmelfart",
16425      ConstitutionDay: "Grundlovsdag",
16426      ChristmasEve: "Juleaftensdag",
16427      ChristmasDay: "1. juledag",
16428      BoxingDay: "2. juledag",
16429      NewYearsEve: "Nytårsaften",
16430    },
16431    months: [
16432      "januar",
16433      "februar",
16434      "marts",
16435      "april",
16436      "maj",
16437      "juni",
16438      "juli",
16439      "august",
16440      "september",
16441      "oktober",
16442      "november",
16443      "december",
16444    ],
16445    weekDays: {
16446      Monday: "Mandag",
16447      Tuesday: "Tirsdag",
16448      Wednesday: "Onsdag",
16449      Thursday: "Torsdag",
16450      Friday: "Fredag",
16451      Saturday: "Lørdag",
16452      Sunday: "Søndag",
16453    },
16454  };
16455
16456  vt.hjertestarterMap = function (elem) {
16457    // Wait for Google Maps to be available
16458    if (typeof google === 'undefined' || typeof google.maps === 'undefined') {
16459      setTimeout(function () {
16460        vt.hjertestarterMap(elem);
16461      }, 50);
16462      return;
16463    }
16464
16465    vt.viewportCalc();
16466
16467    vt.mapCountsData.then(function () {
16468      vt.mapSearch();
16469    });
16470
16471    vt.pinOverlay();
16472
16473    // Debounce resize events for better performance
16474    var resizeTimer;
16475    var debouncedViewportCalc = function () {
16476      clearTimeout(resizeTimer);
16477      resizeTimer = setTimeout(vt.viewportCalc, 150);
16478    };
16479
16480    window.addEventListener("resize", debouncedViewportCalc);
16481    window.addEventListener("orientationchange", vt.viewportCalc);
16482
16483    // Custom map functions object
16484    var mf = {};
16485    mf.settings = {
16486      switchToRealMarkers: 13,
16487      clusterLevel: 150,
16488      currentMarker: 0,
16489      searchLocation: null,
16490      searchLatLng: null,
16491    };
16492
16493    mf.centerDenmark = new google.maps.LatLng(56.26392, 9.50178500000004);
16494    mf.denmarkSW = new google.maps.LatLng(54.4317001, 7.855200100000047);
16495    mf.denmarkNE = new google.maps.LatLng(58.02846, 15.229800000000068);
16496    mf.boundsDenmark = new google.maps.LatLngBounds(mf.denmarkSW, mf.denmarkNE);
16497
16498    mf.bounds = new google.maps.LatLngBounds(null);
16499
16500    // Cache filter checkbox selectors for performance
16501    var $filter247 = null;
16502    var $filterOpeningHours = null;
16503    var $filterBehindLock = null;
16504    var $filterUnavailable = null;
16505    var $regionSelect = null;
16506    var $municipalitiesSelect = null;
16507
16508    // Initialize cached selectors (called after DOM is ready)
16509    var initFilterCache = function () {
16510      $filter247 = $("#filter-247");
16511      $filterOpeningHours = $("#filter-opening-hours");
16512      $filterBehindLock = $("#filter-behind-lock");
16513      $filterUnavailable = $("#filter-unavailable");
16514      $regionSelect = $(".map-filter-regions");
16515      $municipalitiesSelect = $(".map-filter-municipalities");
16516    };
16517
16518    // Debounced tracking function for filter changes (300ms delay)
16519    var trackFilterChange = debounce(function () {
16520      var activeFilters = [];
16521      var activeFiltersValues = [];
16522
16523      // Add region selection if not "all"
16524      if ($regionSelect && $regionSelect.length > 0) {
16525        var regionVal = $regionSelect.val();
16526        var regionText = $regionSelect.find("option:selected").data("name") || $regionSelect.find("option:selected").text();
16527        if (regionVal && regionVal !== "all") {
16528          activeFilters.push("Region");
16529          activeFiltersValues.push(regionText);
16530        }
16531      }
16532
16533      // Add municipality selection if not "all"
16534      if ($municipalitiesSelect && $municipalitiesSelect.length > 0) {
16535        var municipalityVal = $municipalitiesSelect.val();
16536        var municipalityText = $municipalitiesSelect.find("option:selected").data("name") || $municipalitiesSelect.find("option:selected").text();
16537        if (municipalityVal && municipalityVal !== "all") {
16538          activeFilters.push("Kommune");
16539          activeFiltersValues.push(municipalityText);
16540        }
16541      }
16542
16543      if ($filter247 && $filter247.length > 0 && $filter247[0].checked) {
16544        activeFilters.push("24/7");
16545        activeFiltersValues.push("24/7");
16546      }
16547      if ($filterOpeningHours && $filterOpeningHours.length > 0 && $filterOpeningHours[0].checked) {
16548        activeFilters.push("Åbningstider");
16549        activeFiltersValues.push("Åbningstider");
16550      }
16551      if ($filterBehindLock && $filterBehindLock.length > 0 && $filterBehindLock[0].checked) {
16552        activeFilters.push("LÃ¥st");
16553        activeFiltersValues.push("LÃ¥st");
16554      }
16555      if ($filterUnavailable && $filterUnavailable.length > 0 && $filterUnavailable[0].checked) {
16556        activeFilters.push("Utilgængelig");
16557        activeFiltersValues.push("Utilgængelig");
16558      }
16559
16560      vt.functions.pushToDataLayer({
16561        event: "map_filters_click",
16562        active_map_filters: activeFilters.join(", ") || "",
16563        active_map_filters_value: activeFiltersValues.join(", ") || ""
16564      });
16565    }, 300);
16566
16567    // Deeplinking
16568    mf.deepLink = function () {
16569      var hash = window.location.hash.replace("#", "").split("/");
16570
16571      if (hash[0] === "id") {
16572        var ids = hash[1],
16573          zoomLevel = hash[2];
16574
16575        mf.openMarker(ids, zoomLevel);
16576      } else if (hash[0] === "filter") {
16577        var filters = [
16578          {
16579            filter: hash[1],
16580            val: hash[2],
16581          },
16582          {
16583            filter: hash[3],
16584            val: hash[4],
16585          },
16586        ];
16587
16588        mf.setFilters(filters);
16589      }
16590    };
16591
16592    mf.typeControl = function (controlDiv, map) {
16593      var controlUI = document.createElement("button");
16594      controlUI.classList.add("c-type-button");
16595      controlDiv.appendChild(controlUI);
16596      var controlIcon = document.createElement("div");
16597      controlIcon.classList.add("c-type-button__bg");
16598      controlUI.appendChild(controlIcon);
16599      var satelliteText = document.createElement("span");
16600      satelliteText.classList.add("c-type-button__satellite");
16601      satelliteText.innerHTML = "Satellit";
16602      var streetText = document.createElement("span");
16603      streetText.classList.add("c-type-button__street");
16604      streetText.innerHTML = "Kort";
16605      controlUI.appendChild(satelliteText);
16606      controlUI.appendChild(streetText);
16607
16608      controlUI.addEventListener("click", function (e) {
16609        if (e.target.classList.contains("c-type-button--satellite")) {
16610          e.target.classList.remove("c-type-button--satellite");
16611          map.setMapTypeId("roadmap");
16612        } else {
16613          e.target.classList.add("c-type-button--satellite");
16614          map.setMapTypeId("satellite");
16615        }
16616      });
16617    };
16618
16619    mf.geoControl = function (controlDiv, map) {
16620      // Set CSS for the control border.
16621      var controlUI = document.createElement("button");
16622      controlUI.classList.add("c-geo-button");
16623      controlUI.title = "Brug min placering";
16624      controlDiv.appendChild(controlUI);
16625      // Set CSS for the control interior.
16626      var controlIcon = document.createElement("div");
16627      controlIcon.classList.add("c-geo-button__icon");
16628      controlUI.appendChild(controlIcon);
16629
16630      if (navigator.geolocation) {
16631        mf.customerLocationMarker = null;
16632
16633        controlUI.addEventListener("click", function (e) {
16634          e.target.setAttribute("disabled", "disabled");
16635          registerEvent({
16636            event: "GTM_Event",
16637            eventAction: "My position",
16638            eventCategory: "Google Maps",
16639            eventLabel: "Click",
16640          });
16641
16642          // HTML5 geolocation.
16643          navigator.geolocation.getCurrentPosition(function (position) {
16644            var pos = {
16645              lat: position.coords.latitude,
16646              lng: position.coords.longitude,
16647            };
16648
16649            mf.closeModal();
16650
16651            //set marker on user location
16652            var image = "/assets/img/marker-home.png";
16653            if (mf.customerLocationMarker !== null) {
16654              mf.customerLocationMarker.setMap(null);
16655            }
16656            mf.customerLocationMarker = new google.maps.Marker({
16657              position: pos,
16658              map: map,
16659              icon: image,
16660            });
16661
16662            // Center map to current marker
16663            mf.map.setZoom(18);
16664            mf.map.panTo(pos);
16665            controlUI.removeAttribute("disabled");
16666            // func(mf);
16667          });
16668        });
16669      }
16670    };
16671
16672    // Get markers when map has changed
16673    mf.mapChanged = function () {
16674      if (mf.map.getZoom() >= mf.settings.switchToRealMarkers) {
16675        // Clear static png tiles from map
16676        mf.map.overlayMapTypes.clear();
16677
16678        // Get new markser
16679        mf.getMarkers();
16680      } else {
16681        // HACK: Remove marker-overlay class. See line 26 in FastMarkerOverlay.js
16682        $(".marker-overlay").removeClass("marker-overlay");
16683
16684        // Clear already existing static png tiles from map
16685        mf.map.overlayMapTypes.clear();
16686
16687        // Clear all markers on map
16688        mf.clearMarkers("mapChanged");
16689
16690        // Generate static png tiles
16691        mf.generateTiles();
16692      }
16693    };
16694
16695    // Get markers
16696    // -----------
16697    // Usage:
16698    // /api​/v4​/aedspublic​/maps​/clustered​/{profile}​/{includeIs247}​/{includeIsOpeningHours}​/{includeIsBehindLock}​/{northEastLat}​/{northEastLng}​/{southWestLat}​/{southWestLng}​/{zoomLevel}.json
16699    // Example:
16700    // /api/maps/aedmarkers/clustered/55.681512832124625/12.610063823350401/55.67828866855793/12.601909907945128/17/6/5/21
16700/true/false/true
16701    mf.getMarkers = function () {
16702      var ne = mf.map.getBounds().getNorthEast(),
16703        sw = mf.map.getBounds().getSouthWest(),
16704        is247 =
16705          $filter247 && $filter247.length > 0 ? $filter247[0].checked : "true",
16706        isOpeningHours =
16707          $filterOpeningHours && $filterOpeningHours.length > 0
16708            ? $filterOpeningHours[0].checked
16709            : "true",
16710        isBehindLock =
16711          $filterBehindLock && $filterBehindLock.length > 0
16712            ? $filterBehindLock[0].checked
16713            : "true",
16714        unavailable =
16715          $filterUnavailable && $filterUnavailable.length > 0
16716            ? $filterUnavailable[0].checked
16717            : "true";
16718      //If all filters are deselected then we show all
16719      if (
16720        is247 === false &&
16721        isOpeningHours === false &&
16722        isBehindLock === false &&
16723        unavailable === false
16724      ) {
16725        is247 = "true";
16726        isOpeningHours = "true";
16727        isBehindLock = "true";
16728        unavailable = "true";
16729      }
16730      var service =
16731        ENV +
16732        "/api/v4/aedspublic/maps/clustered/web" +
16733        "/" +
16734        is247 +
16735        "/" +
16736        isOpeningHours +
16737        "/" +
16738        isBehindLock +
16739        "/" +
16740        unavailable +
16741        "/" +
16742        ne.lat() +
16743        "/" +
16744        ne.lng() +
16745        "/" +
16746        sw.lat() +
16747        "/" +
16748        sw.lng() +
16749        "/" +
16750        mf.map.getZoom() +
16751        ".json";
16752
16753      $.get(service, function (data) {
16754        // Set markers
16755        mf.setMarkers(data);
16756      });
16757    };
16758
16759    // Clear markers and overlays
16760    mf.clearMarkers = function () {
16761      var i = mf.overlays.length;
16762      while (i--) {
16763        var overlay = mf.overlays[i];
16764        if (overlay) {
16765          overlay.setMap(null);
16766          delete mf.overlays[i];
16767        }
16768      }
16769
16770      // Clean up array
16771      var newOverlays = new Array();
16772      for (var j = 0; j < mf.overlays.length; j++) {
16773        if (mf.overlays[j]) {
16774          newOverlays.push(mf.overlays[j]);
16775        }
16776      }
16777      mf.overlays = newOverlays;
16778    };
16779
16780    // set home marker
16781    mf.setMarkersList = function (markers) {
16782      mf.markers = markers;
16783
16784      // Add loading overlay before clearing markers
16785      $(MAP_CONTAINER).addClass("loading");
16786
16787      // Clear markers on map
16788      // TODO: Need to perform a diff on markers
16789      // Clearing all markers makes all markers blink
16790      // mf.clearMarkers('setMarkersList', markersToRemove);
16791
16792      // Remove old overlay and delete
16793      mf.clearMarkers("setMarkersList");
16794
16795      // Set marker on map
16796      mf.overlays.push(
16797        new com.redfin.FastMarkerOverlay(mf.map, markers, function () {
16798          // Remove loading overlay when markers are appended
16799          $(MAP_CONTAINER).removeClass("loading");
16800          // Set active class on selected marker
16801          setTimeout(function () {
16802            $('.marker div[data-ids="' + mf.settings.currentMarker + '"]')
16803              .parent()
16804              .addClass("active-marker");
16805          }, 30);
16806        })
16807      );
16808    };
16809
16810    mf.setMarkersSearched = function (markers) {
16811      // At this point we should already have a latlng - no need to geocode
16812      var latitude =
16813        typeof mf.settings.searchLatLng.lat === "function"
16814          ? mf.settings.searchLatLng.lat()
16815          : mf.settings.searchLatLng.lat;
16816      var longitude =
16817        typeof mf.settings.searchLatLng.lng === "function"
16818          ? mf.settings.searchLatLng.lng()
16819          : mf.settings.searchLatLng.lng;
16820      mf.markers = markers;
16821      var markerHtml = new Array();
16822      markerHtml.push(
16823        '<div data-ids="' +
16824        0 +
16825        '" data-count="' +
16826        0 +
16827        '" data-lat="' +
16828        latitude +
16829        '" data-lng="' +
16830        longitude +
16831        '">'
16832      );
16833      markerHtml.push('<span class="marker-pin"></span>');
16834      markerHtml.push("</div>");
16835
16836      var latlng = new google.maps.LatLng(latitude, longitude);
16837
16838      var newMarker = new com.redfin.FastMarker(
16839        0,
16840        latlng,
16841        [markerHtml.join("")],
16842        "marker",
16843        1000
16844      );
16845
16846      mf.markers.push(newMarker);
16847      mf.setMarkersList(mf.markers);
16848    };
16849
16850    // Set markers on maps
16851    mf.setMarkers = function (data) {
16852      // Empty markers array before inserting new ones
16853      mf.oldMarkers = mf.markers;
16854      mf.markers = new Array();
16855
16856      // Loop through new markser
16857      $.each(data, function (i) {
16858        var latlng = new google.maps.LatLng(this.latitude, this.longitude),
16859          ids = this.ids.join(","),
16860          markerHtml = new Array();
16861
16862        if (this.c > 1) {
16863          // Show cluster icon if more than 8
16864          if (this.is247Aed && !this.isBehindLock) {
16865            // Add 24/7 icon
16866            markerHtml.push(
16867              '<div class="marker-cluster marker-cluster--is247" data-ids="' +
16868              ids +
16869              '" data-count="' +
16870              this.c +
16871              '" data-lat="' +
16872              this.latitude +
16873              '" data-lng="' +
16874              this.longitude +
16875              '">' +
16876              this.c +
16877              "</div>"
16878            );
16879          } else if (!this.is247Aed && !this.isBehindLock && !this.isGreyAed) {
16880            markerHtml.push(
16881              '<div class="marker-cluster marker-cluster--isOpeningHours" data-ids="' +
16882              ids +
16883              '" data-count="' +
16884              this.c +
16885              '" data-lat="' +
16886              this.latitude +
16887              '" data-lng="' +
16888              this.longitude +
16889              '">' +
16890              this.c +
16891              "</div>"
16892            );
16893          } else if (this.isGreyAed) {
16894            markerHtml.push(
16895              '<div class="marker-cluster marker-cluster--unavailable" data-ids="' +
16896              ids +
16897              '" data-count="' +
16898              this.c +
16899              '" data-lat="' +
16900              this.latitude +
16901              '" data-lng="' +
16902              this.longitude +
16903              '">' +
16904              this.c +
16905              "</div>"
16906            );
16907          } else {
16908            markerHtml.push(
16909              '<div class="marker-cluster marker-cluster--isBehindLock" data-ids="' +
16910              ids +
16911              '" data-count="' +
16912              this.c +
16913              '" data-lat="' +
16914              this.latitude +
16915              '" data-lng="' +
16916              this.longitude +
16917              '">' +
16918              this.c +
16919              "</div>"
16920            );
16921          }
16922        } else {
16923          // Generate marker html
16924          markerHtml.push(
16925            '<div class="marker" data-ids="' +
16926            ids +
16927            '" data-count="' +
16928            this.c +
16929            '" data-lat="' +
16930            this.latitude +
16931            '" data-lng="' +
16932            this.longitude +
16933            '"></span>'
16934          );
16935          if (this.is247Aed && !this.isBehindLock) {
16936            // Add 24/7 icon
16937            markerHtml.push('<span class="marker-247"></span>');
16938          } else if (!this.is247Aed && !this.isBehindLock && !this.isGreyAed) {
16939            markerHtml.push('<span class="marker-opening-hours"></span>');
16940          } else if (this.isGreyAed) {
16941            markerHtml.push('<span class="marker-unavailable"></span>');
16942          } else {
16943            markerHtml.push('<span class="marker-behind-lock"></span>');
16944          }
16945          markerHtml.push("</div>");
16946        }
16947
16948        // Join marker html
16949        markerHtml = markerHtml.join("");
16950
16951        // Create FastMarker
16952        var newMarker = new com.redfin.FastMarker(
16953          i,
16954          latlng,
16955          [markerHtml],
16956          "marker",
16957          1000
16958        );
16959
16960        newMarker.comparisonIDs = ids;
16961
16962        // Add new markers to array
16963        mf.markers.push(newMarker);
16964      });
16965
16966      // Add home marker
16967      if (mf.settings.searchLocation !== null) {
16968        mf.setMarkersSearched(mf.markers);
16969      } else {
16970        mf.setMarkersList(mf.markers);
16971      }
16972    };
16973
16974    // Get static png tiles
16975    mf.getTile = function (coord, zoom, ownerDocument) {
16976      var div = ownerDocument.createElement("div");
16977      div.innerHTML =
16978        '<img src="' +
16979        ENV +
16980        "/api/v4/aedspublic/maps/tiles/web" +
16981        "/" +
16982        mf.color247 +
16983        "/" +
16984        mf.colorOpeningHours +
16985        "/" +
16986        mf.colorBehindLock +
16987        "/" +
16988        mf.colorUnavailable +
16989        "/" +
16990        coord.x +
16991        "/" +
16992        coord.y +
16993        "/" +
16994        zoom +
16995        '.png" class="map-tile-image" alt="" />';
16996      div.style.width = this.tileSize.width + "px";
16997      div.style.height = this.tileSize.height + "px";
16998      div.style.fontSize = "10";
16999      div.style.textAlign = "center";
17000      div.style.lineHeight = this.tileSize.height + "px";
17001
17002      // Hack to prevent tile layer container from being on top of the other layers
17003      //$('.map-tile-image').parent().parent().parent().parent().parent().addClass('tile-layer');
17004
17005      return div;
17006    };
17007
17008    // Generate static png tiles
17009    mf.generateTiles = function () {
17010      mf.map.overlayMapTypes.push({
17011        index: 0,
17012        tileSize: new google.maps.Size(256, 256),
17013        getTile: mf.getTile,
17014      });
17015    };
17016
17017    mf.setRegionBoundsDawa = function (type, selectedRegionID, callback) {
17018      if (!isNaN(selectedRegionID)) {
17019        mf.hideBorders();
17020        mf.loadDawaBorder(type + "/" + selectedRegionID, callback);
17021      }
17022    };
17023
17024    mf.hideBorders = function () {
17025      (mf.highlightedBorders || []).map(function (x) {
17026        return x.setMap(null);
17027      });
17028    };
17029
17030    mf.loadDawaBorder = function (segment, callback) {
17031      $.getJSON("https://dawa.aws.dk/" + segment + "?format=geojson", function (
17032        data
17033      ) {
17034        mf.handleDawaPolygon(data);
17035        if (callback) {
17036          callback();
17037        }
17038      });
17039    };
17040
17041    mf.handleDawaPolygon = function (result) {
17042      var coordinates = result.geometry.coordinates;
17043      mf.bounds = new google.maps.LatLngBounds(null);
17044
17045      // Map MultiPolygon to Polygons
17046      mf.highlightedBorders = coordinates.map(mf.multiPolygonToPolygon);
17047
17048      mf.highlightedBorders.map(function (x) {
17049        return x.setMap(mf.map);
17050      });
17051
17052      mf.map.fitBounds(mf.bounds, -100);
17053    };
17054
17055    mf.multiPolygonToPolygon = function (array) {
17056      var paths = array[0].map(function (point) {
17057        return new google.maps.LatLng(point[1], point[0]);
17058      });
17059
17060      paths.map(function (p) {
17061        return mf.bounds.extend(p);
17062      });
17063
17064      return new google.maps.Polygon({
17065        paths: paths,
17066        strokeColor: "#333333",
17067        strokeOpacity: 0.8,
17068        strokeWeight: 3,
17069        fillColor: "#333333",
17070        fillOpacity: 0,
17071        clickable: false,
17072      });
17073    };
17074
17075    mf.updateAreaName = function (
17076      $municipalitiesSelect,
17077      $municipalitiesSelectedOption,
17078      $regionsSelectedOption
17079    ) {
17080      var $areaName = $(".map-filter-region");
17081      if ($municipalitiesSelect.val() !== "all") {
17082        $areaName.html($municipalitiesSelectedOption.data("name"));
17083      } else {
17084        $areaName.html($regionsSelectedOption.data("name"));
17085      }
17086    };
17087
17088    mf.setCountsFromSelectedOptions = function () {
17089      // If municipalities is set
17090      var $municipalitiesSelect = $(
17091        ".map-filter-municipalities:not([data-filter-on-submit])"
17092      ).first();
17093      var $regionsSelect = $(
17094        ".map-filter-regions:not([data-filter-on-submit])"
17095      ).first();
17096
17097      var $municipalitiesSelectedOption = $municipalitiesSelect.find(
17098        "option:selected"
17099      );
17100      var $regionsSelectedOption = $regionsSelect.find("option:selected");
17101      if ($regionsSelectedOption.length === 0) {
17102        $regionsSelectedOption = $regionsSelect.find("option").eq(0);
17103      }
17104
17105      if ($municipalitiesSelectedOption.val() !== "all") {
17106        mf.setCountsFromSelectedOption($municipalitiesSelectedOption);
17107      } else {
17108        mf.setCountsFromSelectedOption($regionsSelectedOption);
17109      }
17110      mf.updateAreaName(
17111        $municipalitiesSelect,
17112        $municipalitiesSelectedOption,
17113        $regionsSelectedOption
17114      );
17115    };
17116
17117    // Takes a selected option and updates the corresponding counts
17118    mf.setCountsFromSelectedOption = function ($selectedOption) {
17119      // Count elements
17120      var $countTotal = $(".map-filter-shown-count.count-total");
17121      var $count247 = $(".c-aed-map__count--247");
17122      var $countOpeningHours = $(".c-aed-map__count--opening-hours");
17123      var $countBehindLock = $(".c-aed-map__count--behind-lock");
17124      var $countUnavailable = $(".c-aed-map__count--unavailable");
17125
17126      // New values
17127      var countNewTotal = $selectedOption.data("aed");
17128      var countNew247 = $selectedOption.data("247-aed");
17129      var countNewOpeningHours = $selectedOption.data("opening-hours-aed");
17130      var countNewBehindLock = $selectedOption.data("behind-lock-aed");
17131      var countUnavailable = $selectedOption.data("unavailable-aed");
17132
17133      // Update counts
17134      $countTotal.html(countNewTotal);
17135      $count247.html(countNew247);
17136      $countOpeningHours.html(countNewOpeningHours);
17137      $countBehindLock.html(countNewBehindLock);
17138      $countUnavailable.html(countUnavailable);
17139    };
17140
17141    mf.clearMunicipalityList = function ($municipalitiesSelect) {
17142      $municipalitiesSelect.val("all");
17143      $municipalitiesSelect.find("option[data-region]").remove();
17144    };
17145
17146    mf.resetMunicipalityList = function ($municipalitiesSelect) {
17147      $.each(mf.regions, function () {
17148        mf.populateMunicipalityDropdown(
17149          this.municipalities,
17150          $municipalitiesSelect
17151        );
17152      });
17153    };
17154
17155    mf.getUpdatedMunicipalities = function (val) {
17156      var newMunicipalities;
17157      $.each(mf.regions, function () {
17158        if (typeof this.data !== "undefined") {
17159          if (this.data.id == val) {
17160            newMunicipalities = this.municipalities;
17161          }
17162        }
17163      });
17164      return newMunicipalities;
17165    };
17166
17167    mf.populateMunicipalityDropdown = function (
17168      municipalities,
17169      $municipalitiesSelect
17170    ) {
17171      $.each(municipalities, function (index, itm) {
17172        var option = new Option(itm.title, itm.id);
17173        option.setAttribute("data-aed", itm.countTotal);
17174        option.setAttribute("data-247-aed", itm.countAlways);
17175        option.setAttribute("data-opening-hours-aed", itm.countOpeningHours);
17176        option.setAttribute("data-behind-lock-aed", itm.countBehindLock);
17177        option.setAttribute("data-unavailable-aed", itm.countGreyAed);
17178        option.setAttribute("data-region", itm.regionId);
17179        option.setAttribute("data-name", itm.title);
17180        $municipalitiesSelect.append(option);
17181      });
17182    };
17183
17184    mf.updateMunicipalityList = function (municipalities, $select) {
17185      mf.clearMunicipalityList($select);
17186
17187      if (municipalities && municipalities.length > 0) {
17188        mf.populateMunicipalityDropdown(municipalities, $select);
17189      } else {
17190        mf.resetMunicipalityList($select);
17191      }
17192    };
17193
17194    mf.updateMunicipalityLists = function (
17195      val,
17196      $municipalitiesSelect,
17197      $municipalitiesSelectMobile
17198    ) {
17199      var newMunicipalities = mf.getUpdatedMunicipalities(val);
17200      mf.updateMunicipalityList(newMunicipalities, $municipalitiesSelect);
17201      mf.updateMunicipalityList(newMunicipalities, $municipalitiesSelectMobile);
17202    };
17203
17204    mf.applyRegionFilter = function (
17205      takeValuesFromMobile,
17206      $regionSelect,
17207      $regionSelectMobile,
17208      $municipalitiesSelect,
17209      $municipalitiesSelectMobile,
17210      applyMunicipalityFilterAfterwards
17211    ) {
17212      var $regionSource = takeValuesFromMobile
17213        ? $regionSelectMobile
17214        : $regionSelect;
17215      var $municipalitiesSource = takeValuesFromMobile
17216        ? $municipalitiesSelectMobile
17217        : $municipalitiesSelect;
17218      var $regionDestination = takeValuesFromMobile
17219        ? $regionSelect
17220        : $regionSelectMobile;
17221
17222      var $selectedOption = $(":selected", $regionSource[0]);
17223      var val = $selectedOption.val();
17224      $regionDestination.val(val);
17225
17226      // Track filter change with debouncing
17227      trackFilterChange();
17228
17229      // Selected region name
17230      mf.setRegionName();
17231
17232      mf.setCountsFromSelectedOptions();
17233
17234      // Set new window.location.hash for deeplinking
17235      var $regionAndMunicipality = $regionSelect.add($municipalitiesSelect);
17236      mf.setFilterDeeplink($regionAndMunicipality);
17237
17238      // Reset borders if all filters is set to 'all'
17239      var resetBorders = true;
17240      $(".map-filter-select").each(function () {
17241        if ($(this).val() != "all") {
17242          resetBorders = false;
17243        }
17244      });
17245
17246      if (resetBorders) {
17247        mf.hideBorders();
17248        // Pan and zoom to Denmark
17249        mf.map.setCenter(mf.denmarkCenter);
17250        mf.map.fitBounds(mf.boundsDenmark);
17251      }
17252
17253      mf.setRegionBoundsDawa("regioner", val, function () {
17254        if (applyMunicipalityFilterAfterwards) {
17255          mf.applyMunicipalityFilter(
17256            takeValuesFromMobile,
17257            $regionSelect,
17258            $regionSelectMobile,
17259            $municipalitiesSelect,
17260            $municipalitiesSelectMobile
17261          );
17262        }
17263      });
17264    };
17265
17266    mf.applyMunicipalityFilter = function (
17267      takeValuesFromMobile,
17268      $regionSelect,
17269      $regionSelectMobile,
17270      $municipalitiesSelect,
17271      $municipalitiesSelectMobile
17272    ) {
17273      var $regionSource = takeValuesFromMobile
17274        ? $regionSelectMobile
17275        : $regionSelect;
17276      var $municipalitiesSource = takeValuesFromMobile
17277        ? $municipalitiesSelectMobile
17278        : $municipalitiesSelect;
17279      var $municipalitiesDestination = takeValuesFromMobile
17280        ? $municipalitiesSelect
17281        : $municipalitiesSelectMobile;
17282
17283      var $selectedOption = $(":selected", $municipalitiesSource[0]);
17284      var val = $selectedOption.val();
17285      $municipalitiesDestination.val(val);
17286
17287      // Track filter change with debouncing
17288      trackFilterChange();
17289
17290      mf.setCountsFromSelectedOptions();
17291
17292      if ($regionSource.find("option:selected").val() != "all") {
17293        $municipalitiesSource
17294          .find(
17295            'option[data-region!="' +
17296            $regionSelect.find("option:selected").val() +
17297            '"]'
17298          )
17299          .not(":first")
17300          .remove();
17301      }
17302
17303      // Set new window.location.hash for deeplinking
17304      var $regionAndMunicipality = $regionSelect.add($municipalitiesSelect);
17305      mf.setFilterDeeplink($regionAndMunicipality);
17306
17307      mf.setRegionBoundsDawa("kommuner", val);
17308
17309      // Make a fake event for $regionSelect change to reset borders to regions if kommuner is set to 'all'
17310      if (val == "all") {
17311        $regionSource.change();
17312      }
17313
17314      // Reset borders if all filters is set to 'all'
17315      var resetBorders = true;
17316      $(".map-filter-select").each(function () {
17317        if ($(this).val() != "all") {
17318          resetBorders = false;
17319        }
17320      });
17321
17322      if (resetBorders) {
17323        mf.hideBorders();
17324        // Pan and zoom to Denmark
17325        mf.map.setCenter(mf.denmarkCenter);
17326        mf.map.fitBounds(mf.boundsDenmark);
17327      }
17328    };
17329
17330    mf.applyFilters = function (takeValuesFromMobile) {
17331      var $elem = $(this);
17332      var $regionSelect = $elem.find(
17333        ".map-filter-regions:not([data-filter-on-submit])"
17334      ),
17335        $regionSelectMobile = $elem.find(
17336          ".map-filter-regions[data-filter-on-submit]"
17337        ),
17338        $municipalitiesSelect = $elem.find(
17339          ".map-filter-municipalities:not([data-filter-on-submit])"
17340        ),
17341        $municipalitiesSelectMobile = $elem.find(
17342          ".map-filter-municipalities[data-filter-on-submit]"
17343        );
17344
17345      mf.applyRegionFilter(
17346        takeValuesFromMobile,
17347        $regionSelect,
17348        $regionSelectMobile,
17349        $municipalitiesSelect,
17350        $municipalitiesSelectMobile,
17351        true
17352      );
17353    };
17354
17355    // Create region dropdown
17356    mf.loadRegions = function () {
17357      vt.mapCountsData.then(
17358        function () {
17359          var $regionSelect = $(elem).find(
17360            ".map-filter-regions:not([data-filter-on-submit])"
17361          ),
17362            $municipalitiesSelect = $(elem).find(
17363              ".map-filter-municipalities:not([data-filter-on-submit])"
17364            ),
17365            $regionSelectMobile = $(elem).find(
17366              ".map-filter-regions[data-filter-on-submit]"
17367            ),
17368            $municipalitiesSelectMobile = $(elem).find(
17369              ".map-filter-municipalities[data-filter-on-submit]"
17370            );
17371
17372          // Save regions and municipalities in object
17373          mf.regions = new Array();
17374
17375          $regionSelect.find("option").each(function () {
17376            var $option = $(this),
17377              optionText = $option.html(),
17378              optionData = $option.data(),
17379              id = $option.val(),
17380              municipalitiesData = vt.mapCounts.municipalities.filter(function (
17381                x
17382              ) {
17383                return x.regionId + "" === id;
17384              });
17385
17386            mf.regions.push({
17387              title: optionText,
17388              data: optionData,
17389              municipalities: municipalitiesData,
17390            });
17391          });
17392
17393          $regionSelectMobile.html($regionSelect.html());
17394          $municipalitiesSelectMobile.html($municipalitiesSelect.html());
17395
17396          // Reset search field on change
17397          $(".map-filter-select").on("change", function () {
17398            $(".c-map-search__input").val("");
17399          });
17400
17401          $regionSelectMobile.on("change", function () {
17402            mf.clearSearchValues();
17403            var newMunicipalities = mf.getUpdatedMunicipalities(
17404              $regionSelectMobile.val()
17405            );
17406            mf.updateMunicipalityList(
17407              newMunicipalities,
17408              $municipalitiesSelectMobile
17409            );
17410          });
17411
17412          $regionSelect.on("change", function () {
17413            //var regionSelect = e.target;
17414
17415            mf.clearSearchValues();
17416
17417            var newMunicipalities = mf.getUpdatedMunicipalities(
17418              $regionSelect.val()
17419            );
17420            mf.updateMunicipalityList(newMunicipalities, $municipalitiesSelect);
17421            /*
17422        var $selectedOption = $(":selected", this);
17423        var val = $selectedOption.val();
17424        $regionSelectMobile.val(val);
17425        */
17426
17427            mf.applyRegionFilter(
17428              false,
17429              $regionSelect,
17430              $regionSelectMobile,
17431              $municipalitiesSelect,
17432              $municipalitiesSelectMobile
17433            );
17434          });
17435
17436          $municipalitiesSelectMobile.on("change", function (e) {
17437            mf.clearSearchValues();
17438
17439            // Set the region value to match the municipality
17440            var regionId =
17441              e.target.options[e.target.selectedIndex].dataset.region;
17442
17443            if (typeof regionId !== "undefined") {
17444              $regionSelectMobile.val(regionId);
17445            }
17446          });
17447
17448          $municipalitiesSelect.on("change", function () {
17449            mf.clearSearchValues();
17450
17451            if (
17452              $municipalitiesSelect.val() !== $municipalitiesSelectMobile.val()
17453            ) {
17454              mf.applyMunicipalityFilter(
17455                false,
17456                $regionSelect,
17457                $regionSelectMobile,
17458                $municipalitiesSelect,
17459                $municipalitiesSelectMobile
17460              );
17461            }
17462          });
17463        }.bind(this)
17464      );
17465    };
17466
17467    // Set filter deeplink
17468    mf.setFilterDeeplink = function ($el) {
17469      var newLocationHash = "#filter/";
17470
17471      $el.each(function () {
17472        var filter = $(this).data("filter"),
17473          val = $(":selected", this).val();
17474
17475        newLocationHash = newLocationHash + filter + "/" + val + "/";
17476      });
17477
17478      var only247 = $(elem).find('input[name="only-247"]').is(":checked")
17479        ? "/only247"
17480        : "";
17481      window.location.hash = newLocationHash + only247;
17482    };
17483
17484    // Set filters from deeplinking
17485    mf.setFilters = function (filters) {
17486      $.each(filters, function () {
17487        $('.map-filter-select[data-filter="' + this.filter + '"]').val(
17488          this.val
17489        );
17490      });
17491
17492      // Trigger last select that is not set to show all
17493      var $selectedOption;
17494      $(".map-filter-select").each(function () {
17495        if ($(":selected", this).val() != "all") {
17496          $selectedOption = $(this);
17497        }
17498      });
17499
17500      if ($selectedOption) {
17501        // If any filter is selected trigger change
17502        $selectedOption.trigger("change");
17503      }
17504    };
17505
17506    // Reset filters
17507    mf.resetFilters = function () {
17508      $(".map-filter-select").each(function () {
17509        $(this).val("all");
17510      });
17511    };
17512
17513    mf.createPreview = function (data) {
17514      var title = data.address.organisation;
17515      var address =
17516        data.address.street.trim() +
17517        " " +
17518        data.address.number.trim() +
17519        (data.address.letter || "").trim() +
17520        (data.address.floor.trim() != ""
17521          ? ", " + data.address.floor.trim()
17522          : "") +
17523        "" +
17524        (data.address.door || "").trim() +
17525        ", " +
17526        (data.address.postalCode + "").trim();
17527
17528      var show247 = data.availability.availability === "Always";
17529      var showOpeningHours = data.availability.availability === "OpeningHours";
17530      var showBehindLock = data.availability.availability === "BehindLock";
17531      var unavailable = data.status.status === "Deactivated";
17532      var iconClass = "";
17533      var statusLabel = "";
17534      var availabilityHtml = "";
17535      if (show247 && !unavailable) {
17536        iconClass = "open--247";
17537        statusLabel = "Døgnåbent";
17538        availabilityHtml =
17539          '<p class="map-modal-open--247">Tilgængelig døgnet rundt</p>';
17540      } else if (showOpeningHours && !unavailable) {
17541        iconClass = "open--hours";
17542        statusLabel = "Se åbningstider";
17543        availabilityHtml =
17544          '<p class="map-modal-open--hours">Tilgængelig i åbningstiden</p>';
17545      } else if (showBehindLock && !unavailable) {
17546        iconClass = "open--locked";
17547        statusLabel = "Lukket";
17548        availabilityHtml = '<p class="map-modal-open--lock">LÃ¥st inde</p>';
17549      } else if (unavailable) {
17550        iconClass = "open--unavailable";
17551        statusLabel = "Ude af drift";
17552        availabilityHtml =
17553          '<p class="map-modal-open--unavailable">Pt utilgængelig</p>';
17554      }
17555      // PIN-CARD-MODAL
17556      var html = "";
17557      html += '<article class="c-pin-card">';
17558      html += '<div class="c-pin-card__close">';
17559      html +=
17560        '<svg class="c-icon c-icon-close" aria-hidden="true"><use xlink:href="/assets/img/svg-sprite/symbol-defs.svg#icon-close"></use></svg>';
17561      html += "</div>";
17562      html += '<div class="c-pin-card__content">';
17563      html += '<div class="c-pin-card__content-top">';
17564      if (iconClass === "open--247") {
17565        html +=
17566          '<svg class="c-pin-card__icon" width="40" height="41" viewBox="0 0 40 41" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="20" cy="20.3145" r="20" fill="#35A845"/><mask id="mask0_5227_4617" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="8" y="8" width="24" height="25"><rect x="8" y="8.31445" width="24" height="24" fill="white"/></mask><g mask="url(#mask0_5227_4617)"><path d="M17.5497 23.4645L26.0247 14.9895C26.2247 14.7895 26.4581 14.6895 26.7247 14.6895C26.9914 14.6895 27.2247 14.7895 27.4247 14.9895C27.6247 15.1895 27.7247 15.427 27.7247 15.702C27.7247 15.977 27.6247 16.2145 27.4247 16.4145L18.2497 25.6145C18.0497 25.8145 17.8164 25.9145 17.5497 25.9145C17.2831 25.9145 17.0497 25.8145 16.8497 25.6145L12.5497 21.3145C12.3497 21.1145 12.2539 20.877 12.2622 20.602C12.2706 20.327 12.3747 20.0895 12.5747 19.8895C12.7747 19.6895 13.0122 19.5895 13.2872 19.5895C13.5622 19.5895 13.7997 19.6895 13.9997 19.8895L17.5497 23.4645Z" fill="white"/></g></svg>';
17567      }
17568      if (iconClass === "open--hours") {
17569        html +=
17570          '<svg class="map-modal__icon" width="40" height="41" viewBox="0 0 40 41" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="20" cy="20.3145" r="20" fill="#FFCE00"/><mask id="mask0_5227_4625" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="8" y="8" width="24" height="25"><rect x="8" y="8.31445" width="24" height="24" fill="#D9D9D9"/></mask><g mask="url(#mask0_5227_4625)">' +
17571          '<path d="M21 19.9148L24 22.9148C24.1833 23.0982 24.275 23.3315 24.275 23.6148C24.275 23.8982 24.1833 24.1315 24 24.3148C23.8167 24.4982 23.5833 24.5898 23.3 24.5898C23.0167 24.5898 22.7833 24.4982 22.6 24.3148L19.3 21.0148C19.2 20.9148 19.125 20.8023 19.075 20.6773C19.025 20.5523 19 20.4232 19 20.2898V16.3148C19 16.0315 19.0958 15.794 19.2875 15.6023C19.4792 15.4107 19.7167 15.3148 20 ' +
17572          '15.3148C20.2833 15.3148 20.5208 15.4107 20.7125 15.6023C20.9042 
1757215.794 21 16.0315 21 16.3148V19.9148ZM23.25 13.3148C22.9 13.3148 22.6042 13.194 22.3625 12.9523C22.1208 12.7107 22 12.4148 22 12.0648C22 11.7148 22.1208 11.419 22.3625 11.1773C22.6042 10.9357 22.9 10.8148 23.25 10.8148C23.6 10.8148 23.8958 10.9357 24.1375 11.1773C24.3792 11.419 24.5 11.7148 24.5 12.0648C24.5 12.4148 24.3792 ' +
17573          '12.7107 24.1375 12.9523C23.8958 13.194 23.6 13.3148 23.25 13.3148ZM23.25 29.8148C22.9 29.8148 22.6042 29.694 22.3625 29.4523C22.1208 29.2107 22 28.9148 22 28.5648C22 28.2148 22.1208 27.919 22.3625 27.6773C22.6042 27.4357 22.9 27.3148 23.25 27.3148C23.6 27.3148 23.8958 27.4357 24.1375 27.6773C24.3792 27.919 24.5 28.2148 24.5 28.5648C24.5 28.9148 24.3792 29.2107 24.1375 29.4523C23.8958 29.694 23.6 29.8148 23.25 29.8148ZM27.25 16.8148C26.9 16.8148 26.6042 16.694 26.3625 16.4523C26.1208 16.2107 26 15.9148 26 15.5648C26 15.2148 26.1208 14.919 26.3625 14.6773C26.6042 14.4357 26.9 14.3148 27.25 14.3148C27.6 14.3148 27.8958 14.4357 28.1375 14.6773C28.3792 14.919 28.5 15.2148 28.5 15.5648C28.5 15.9148 28.3792 16.2107 28.1375 16.4523C27.8958 16.694 27.6 16.8148 27.25 16.8148ZM27.25 26.3148C26.9 26.3148 26.6042 26.194 26.3625 25.9523C26.1208 25.7107 26 25.4148 26 25.0648C26 24.7148 26.1208 24.419 26.3625 24.1773C26.6042 23.9357 26.9 23.8148 27.25 23.8148C27.6 23.8148 27.8958 23.9357 28.1375 24.1773C28.3792 24.419 28.5 24.7148 28.5 25.0648C28.5 25.4148 28.3792 25.7107 28.1375 25.9523C27.8958 26.194 27.6 26.3148 27.25 26.3148ZM28.75 21.5648C28.4 21.5648 28.1042 21.444 27.8625 21.2023C27.6208 20.9607 27.5 20.6648 27.5 20.3148C27.5 19.9648 27.6208 19.669 27.8625 19.4273C28.1042 19.1857 28.4 19.0648 28.75 19.0648C29.1 19.0648 29.3958 19.1857 29.6375 19.4273C29.8792 19.669 30 19.9648 30 20.3148C30 20.6648 29.8792 20.9607 29.6375 21.2023C29.3958 21.444 29.1 21.5648 28.75 21.5648ZM10 20.3148C10 17.6982 10.8708 15.4482 12.6125 13.5648C14.3542 11.6815 16.4917 10.6148 19.025 10.3648C19.2917 10.3315 19.5208 10.4107 19.7125 10.6023C19.9042 10.794 20 11.0315 20 11.3148C20 11.5815 19.9125 11.8148 19.7375 12.0148C19.5625 12.2148 19.3417 12.3315 19.075 12.3648C17.0583 12.5982 15.375 13.4648 14.025 14.9648C12.675 16.4648 12 18.2482 12 20.3148C12 22.3982 12.675 24.1857 14.025 25.6773C15.375 27.169 17.0583 28.0315 19.075 28.2648C19.3417 28.2982 19.5625 28.4148 19.7375 28.6148C19.9125 28.8148 20 29.0482 20 29.3148C20 29.5982 19.9042 29.8357 19.7125 30.0273C19.5208 30.219 19.2917 30.2982 19.025 30.2648C16.475 30.0148 14.3333 28.9482 12.6 27.0648C10.8667 25.1815 10 22.9315 10 20.3148Z" fill="white"/></g></svg>';
17574      }
17575      if (iconClass === "open--locked") {
17576        html +=
17577          '<svg class="map-modal__icon" width="40" height="41" viewBox="0 0 40 41" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="20" cy="20.3145" r="20" fill="#DE0000"/><mask id="mask0_5227_4633" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="8" y="8" width="24" height="25"><rect x="8" y="8.31445" width="24" height="24" fill="#D9D9D9"/></mask><g mask="url(#mask0_5227_4633)"><path d="M14 30.3145C13.45 30.3145 12.9792 30.1186 12.5875 29.727C12.1958 29.3353 12 28.8645 12 28.3145V18.3145C12 17.7645 12.1958 17.2936 12.5875 16.902C12.9792 16.5103 13.45 16.3145 14 16.3145H15V14.3145C15 12.9311 15.4875 11.752 16.4625 10.777C17.4375 9.80195 18.6167 9.31445 20 9.31445C21.3833 9.31445 22.5625 9.80195 23.5375 10.777C24.5125 11.752 25 12.9311 25 14.3145V16.3145H26C26.55 16.3145 27.0208 16.5103 27.4125 16.902C27.8042 17.2936 28 17.7645 28 18.3145V28.3145C28 28.8645 27.8042 29.3353 27.4125 29.727C27.0208 30.1186 26.55 30.3145 26 30.3145H14ZM14 28.3145H26V18.3145H14V28.3145ZM20 25.3145C20.55 25.3145 21.0208 25.1186 21.4125 24.727C21.8042 24.3353 22 23.8645 22 23.3145C22 22.7645 21.8042 22.2936 21.4125 21.902C21.0208 21.5103 20.55 21.3145 20 21.3145C19.45 21.3145 18.9792 21.5103 18.5875 21.902C18.1958 22.2936 18 22.7645 18 23.3145C18 23.8645 18.1958 24.3353 18.5875 24.727C18.9792 25.1186 19.45 25.3145 20 25.3145ZM17 16.3145H23V14.3145C23 13.4811 22.7083 12.7728 22.125 12.1895C21.5417 11.6061 20.8333 11.3145 20 11.3145C19.1667 11.3145 18.4583 11.6061 17.875 12.1895C17.2917 12.7728 17 13.4811 17 14.3145V16.3145Z" fill="white"/></g></svg>';
17578      }
17579      if (iconClass === "open--unavailable") {
17580        html +=
17581          '<svg class="map-modal__icon" width="40" height="41" viewBox="0 0 40 41" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="20" cy="20.3145" r="20" fill="#D1D1D1"/><g opacity="0.6"><mask id="mask0_5227_4641" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="8" y="8" width="24" height="25"><rect x="8" y="8.31445" width="24" height="24" fill="#D9D9D9"/></mask><g mask="url(#mask0_5227_4641)"><path d="M20 30.3145C18.6167 30.3145 17.3167 30.052 16.1 29.527C14.8833 29.002 13.825 28.2895 12.925 27.3895C12.025 26.4895 11.3125 25.4311 10.7875 24.2145C10.2625 22.9978 10 21.6978 10 20.3145C10 18.9311 10.2625 17.6311 10.7875 16.4145C11.3125 15.1978 12.025 14.1395 12.925 13.2395C13.825 12.3395 14.8833 11.627 16.
175811 11.102C17.3167 10.577 18.6167 10.3145 20 10.3145C21.3833 10.3145 22.6833 10.577 23.9 11.102C25.1167 11.627 26.175 12.3395 27.075 13.2395C27.975 14.1395 28.6875 15.1978 29.2125 16.4145C29.7375 17.6311 30 18.9311 30 20.3145C30 21.6978 29.7375 22.9978 29.2125 24.2145C28.6875 25.4311 27.975 26.4895 27.075 27.3895C26.175 28.2895 25.1167 29.002 23.9 29.527C22.6833 30.052 21.3833 30.3145 20 30.3145ZM20 28.3145C20.9 28.3145 21.7667 28.1686 22.6 27.877C23.4333 27.5853 24.2 27.1645 24.9 26.6145L13.7 15.4145C13.15 16.1145 12.7292 16.8811 12.4375 17.7145C12.1458 18.5478 12 19.4145 12 20.3145C12 22.5478 12.775 24.4395 14.325 25.9895C15.875 27.5395 17.7667 28.3145 20 28.3145ZM26.3 25.2145C26.85 24.5145 27.2708 23.7478 27.5625 22.9145C27.8542 22.0811 28 21.2145 28 20.3145C28 18.0811 27.225 16.1895 25.675 14.6395C24.125 13.0895 22.2333 12.3145 20 12.3145C19.1 12.3145 18.2333 12.4603 17.4 12.752C16.5667 13.0436 15.8 13.4645 15.1 14.0145L26.3 25.2145Z" fill="black"/></g></g></svg>';
17582      }
17583      html += '<div class="c-pin-card__address-wrap">';
17584      html += '<h3 class="c-pin-card__title">' + title + "</h3>";
17585      html += '<address class="c-pin-card__address">' + address + '</address>';
17586      html += '</div>';
17587      html += '</div>';
17588      if ($(".hunt").length > 0) {
17589        html += "<div class='map-modal__btn-wrap'><button class='btn colored btn--small js-pick-this' data-address='" + address + "'>Vælg denne hjertestarter</button></div>";
17590      } else {
17591        html += '' + availabilityHtml;
17592        html += '<div class="c-pin-card__open-status">';
17593        html += '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none"><mask id="mask0_1763_5777" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="20" height="20"><rect width="20" height="20" fill="#D9D9D9"/></mask><g mask="url(#mask0_1763_5777)"><path d="M10.5429 9.45641V5.54293C10.5429 5.38947 10.4914 5.26083 10.3884 5.15701C10.2854 5.05319 10.1577 5.00128 10.0054 5.00128C9.85308 5.00128 9.72405 5.05319 9.61828 5.15701C9.51252 5.26083 9.45964 5.38947 9.45964 5.54293V9.64872C9.45964 9.74682 9.4778 9.83491 9.51411 9.91299C9.55043 9.99107 9.60065 10.0622 9.66476 10.1263L12.5574 13.0189C12.6749 13.1364 12.8036 13.193 12.9436 13.1888C13.0836 13.1845 13.2139 13.122 13.3346 13.0013C13.4553 12.8806 13.5157 12.7516 13.5157 12.6143C13.5157 12.477 13.453 12.3462 13.3276 12.222L10.5429 9.45641ZM10.0027 17.5846C8.95808 17.5846 7.97619 17.3872 7.05701 16.9925C6.13783 16.5978 5.33232 16.0547 4.64047 15.3631C3.94861 14.6716 3.40523 13.8654 3.01032 12.9447C2.61542 12.0239 2.41797 11.04 2.41797 9.99314C2.41797 8.94622 2.61533 7.96187 3.01005 7.04009C3.40477 6.1183 3.9479 5.31495 4.63943 4.63005C5.33097 3.94514 6.13713 3.40523 7.05791 3.01032C7.97868 2.61542 8.96252 2.41797 10.0094 2.41797C11.0563 2.41797 12.0407 2.617 12.9626 3.01505C13.8845 3.41311 14.6864 3.95332 15.3683 4.63568C16.0502 5.31805 16.5901 6.12048 16.9879 7.04295C17.3857 7.96541 17.5846 8.95105 17.5846 9.99989C17.5846 11.0445 17.3872 12.0264 16.9925 12.9456C16.5978 13.8647 16.0581 14.6702 15.3736 15.3621C14.689 16.054 13.8851 16.5973 12.9619 16.9922C12.0387 17.3871 11.0523 17.5846 10.0027 17.5846ZM10.0112 16.5013C11.8095 16.5013 13.3408 15.8659 14.605 14.595C15.8692 13.3242 16.5013 11.7896 16.5013 9.99134C16.5013 8.19305 15.8692 6.6618 14.605 5.39759C13.3408 4.13339 11.8095 3.50128 10.0112 3.50128C8.21293 3.50128 6.67836 4.13339 5.40753 5.39759C4.1367 6.6618 3.50128 8.19305 3.50128 9.99134C3.50128 11.7896 4.1367 13.3242 5.40753 14.595C6.67836 15.8659 8.21293 16.5013 10.0112 16.5013Z" fill="#081D3F"/></g></svg>';
17594        html += '<div class="c-pin-card__open-status-label">';
17595        html += '' + statusLabel;
17596        html += "</div>";
17597        html += "</div>";
17598      }
17599      html += "</div>";
17600      html += "</article>";
17601      return html;
17602    };
17603
17604    // Appends preview card(s)
17605    mf.openPreview = function (data) {
17606      var previewElem = document.querySelector(".c-pin-previews");
17607      var previewRowElem = previewElem.querySelector(".c-pin-previews__row");
17608
17609      var previews = [];
17610
17611      $.each(data, function () {
17612        previews.push(mf.createPreview(this));
17613      });
17614
17615      previewRowElem.innerHTML = previews.join("");
17616      vt.pinPreview(data);
17617      previewElem.classList.add("c-pin-previews--active");
17618      if (data.length > 1) {
17619        previewElem.classList.add("c-pin-previews--multiple");
17620      }
17621
17622      $(".c-pin-card__close").on("click", function () {
17623        previewElem.classList.remove("c-pin-previews--active");
17624        previewElem.classList.remove("c-pin-previews--multiple");
17625      });
17626
17627      $(".js-pick-this").on("click", function () {
17628        $(".js-placement").val($(this).data("address")).trigger("change");
17629        vt.closePreview();
17630      })
17631    };
17632
17633    // Append modals and show modal
17634    mf.openModal = function (data) {
17635      var $modalContainer = $(elem).find(".map-modal-container");
17636
17637      // Clear modal container
17638      $modalContainer.html(
17639        '<div class="map-modal-inner"><div class="modal-top"><span class="modal-close">Luk</span></div><div class="modal-items"></div></div>'
17640      );
17641
17642      if (data.length === 1) {
17643        $modalContainer
17644          .find(".map-modal-inner .modal-items")
17645          .addClass("single-item");
17646      }
17647
17648      // Append each modal to modal container
17649      $.each(data, function (_aedIndex, aedData) {
17650        // Create modal from data
17651        var $modal = $(mf.createModal(aedData, data.length));
17652
17653        // Append modal to modal container
17654        $modalContainer.find(".map-modal-inner .modal-items").append($modal);
17655
17656        if (aedData.pictures.length === 1) {
17657          $modal.find(".map-modal-slides").addClass("single-image");
17658        }
17659        // Init slider
17660        $modal.find(".map-modal-slider").slick({
17661          dots: true,
17662          centerMode: aedData.pictures.length > 1,
17663          arrows: true,
17664          centerPadding: "32px",
17665        });
17666
17667        $modal.find(".map-modal-slider").on("afterChange", function () {
17668          registerEvent({
17669            event: "GTM_Event",
17670            eventAction: "Pin opened",
17671            eventCategory: "Google Maps",
17672            eventLabel:
17673              aedData.address.organisation + ", " + aedData.id + ", Slider",
17674          });
17675        });
17676        // Add handler to cta
17677        $(".js-pick-this").on("click", function () {
17678          $(".js-placement").val($(this).data("address")).trigger("change");
17679          vt.closePreview();
17680        })
17681
17682        // Toggle opening hours
17683        $modal.find(".modal-opening-hours-toggle").on("click", function () {
17684          registerEvent({
17685            event: "GTM_Event",
17686            eventAction: "Pin opened",
17687            eventCategory: "Google Maps",
17688            eventLabel:
17689              aedData.address.organisation +
17690              ", " +
17691              aedData.id +
17692              ", Opening hours",
17693          });
17694
17695          $(this).next().slideToggle();
17696        });
17697
17698        // Toggle closed items
17699        if ($modal.hasClass("closed")) {
17700          // Hack to prevent slick slider from setting strange width
17701          $modal.find(".map-modal__content-main").css({
17702            display: "none",
17703            height: "auto",
17704          });
17705
17706          $modal.find(".map-modal-address").on("click", function () {
17707            $(this)
17708              .toggleClass("active")
17709              .closest(".modal-item")
17710              .find(".map-modal-toggle")
17711              .toggleClass("active");
17712            $(this)
17713              .closest(".modal-item")
17714              .find(".map-modal__content-main")
17715              .slideToggle();
17716          });
17717        }
17718
17719        setTimeout(function () {
17720          // setTimeout hack to trigger css transition when .active is added
17721          $(".map-modal-container").addClass("active");
17722        }, 50);
17723      });
17724
17725      $modalContainer.find(".modal-close").on("click", function () {
17726        mf.closeModal();
17727      });
17728    };
17729
17730    // Create modal markup
17731    mf.createModal = function (data, modalCount) {
17732      // Create new array for the template markup
17733      var $modal = new Array();
17734      var show247 = data.availability.availability === "Always";
17735      var showOpeningHours = data.availability.availability === "OpeningHours";
17736      var showBehindLock = data.availability.availability === "BehindLock";
17737      var unavailable = data.status.status === "Deactivated";
17738      var iconClass = "";
17739      var statusLabel = "";
17740
17741      if (show247 && !unavailable) {
17742        iconClass = "open--247";
17743        statusLabel = "Døgnåbent";
17744        availabilityHtml =
17745          '<div class="map-modal-open--247">Tilgængelig døgnet rundt</div>';
17746      } else if (showOpeningHours && !unavailable) {
17747        iconClass = "open--hours";
17748        statusLabel = "Se åbningstider";
17749        availabilityHtml =
17750          '<div class="map-modal-open--hours">Tilgængelig i åbningstiden</div>';
17751      } else if (showBehindLock && !unavailable) {
17752        iconClass = "open--lock";
17753        statusLabel = "Lukket";
17754        availabilityHtml = '<div class="map-modal-open--lock">LÃ¥st inde</div>';
17755      } else if (unavailable) {
17756        iconClass = "open--unavailable";
17757        statusLabel = "Utilgængelig";
17758        availabilityHtml =
17759          '<div class="map-modal-open--unavailable">Pt utilgængelig</div>';
17760      }
17761
17762      /* Start modal wrapper */
17763      if (modalCount > 1) {
17764        // If more than one modal, add .closed class
17765        $modal.push('<div class="modal-item closed">');
17766      } else {
17767        $modal.push('<div class="modal-item">');
17768      }
17769
17770      /* Start address */
17771      $modal.push('<div class="map-modal__content-top">');
17772      if (iconClass === "open--247") {
17773        $modal.push(
17774          '<svg class="map-modal__icon" width="40" height="41" viewBox="0 0 40 41" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="20" cy="20.3145" r="20" fill="#35A845"/><mask id="mask0_5227_4617" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="8" y="8" width="24" height="25"><rect x="8" y="8.31445" width="24" height="24" fill="white"/></mask><g mask="url(#mask0_5227_4617)"><path d="M17.5497 23.4645L26.0247 14.9895C26.2247 14.7895 26.4581 14.6895 26.7247 14.6895C26.9914 14.6895 27.2247 14.7895 27.4247 14.9895C27.6247 15.1895 27.7247 15.427 27.7247 15.702C27.7247 15.977 27.6247 16.2145 27.4247 16.4145L18.2497 25.6145C18.0497 25.8145 17.8164 25.9145 17.5497 25.9145C17.2831 25.9145 17.0497 25.8145 16.8497 25.6145L12.5497 21.3145C12.3497 21.1145 12.2539 20.877 12.2622 20.602C12.2706 20.327 12.3747 20.0895 12.5747 19.8895C12.7747 19.6895 13.0122 19.5895 13.2872 19.5895C13.5622 19.5895 13.7997 19.6895 13.9997 19.8895L17.5497 23.4645Z" fill="white"/></g></svg>'
17775        );
17776      }
17777      if (iconClass === "open--hours") {
17778        $modal.push(
17779          '<svg class="map-modal__icon" width="40" height="41" viewBox="0 0 40 41" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="20" cy="20.3145" r="20" fill="#FFCE00"/><mask id="mask0_5227_4625" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="8" y="8" width="24" height="25"><rect x="8" y="8.31445" width="24" height="24" fill="#D9D9D9"/></mask><g mask="url(#mask0_5227_4625)">' +
17780          '<path d="M21 19.9148L24 22.9148C24.1833 23.0982 24.275 23.3315 24.275 23.6148C24.275 23.8982 24.1833 24.1315 24 24.3148C23.8167 24.4982 23.5833 24.5898 23.3 24.5898C23.0167 24.5898 22.7833 24.4982 22.6 24.3148L19.3 21.0148C19.2 20.9148 19.125 20.8023 19.075 20.6773C19.025 20.5523 19 20.4232 19 20.2898V16.3148C19 16.0315 19.0958 15.794 19.2875 15.6023C19.4792 15.4107 19.7167 15.3148 20 ' +
17781          '15.3148C20.2833 15.3148 20.5208 15.4107 20.7125 15.6023C20.9042 
1778115.794 21 16.0315 21 16.3148V19.9148ZM23.25 13.3148C22.9 13.3148 22.6042 13.194 22.3625 12.9523C22.1208 12.7107 22 12.4148 22 12.0648C22 11.7148 22.1208 11.419 22.3625 11.1773C22.6042 10.9357 22.9 10.8148 23.25 10.8148C23.6 10.8148 23.8958 10.9357 24.1375 11.1773C24.3792 11.419 24.5 11.7148 24.5 12.0648C24.5 12.4148 24.3792 ' +
17782          '12.7107 24.1375 12.9523C23.8958 13.194 23.6 13.3148 23.25 13.3148ZM23.25 29.8148C22.9 29.8148 22.6042 29.694 22.3625 29.4523C22.1208 29.2107 22 28.9148 22 28.5648C22 28.2148 22.1208 27.919 22.3625 27.6773C22.6042 27.4357 22.9 27.3148 23.25 27.3148C23.6 27.3148 23.8958 27.4357 24.1375 27.6773C24.3792 27.919 24.5 28.2148 24.5 28.5648C24.5 28.9148 24.3792 29.2107 24.1375 29.4523C23.8958 29.694 23.6 29.8148 23.25 29.8148ZM27.25 16.8148C26.9 16.8148 26.6042 16.694 26.3625 16.4523C26.1208 16.2107 26 15.9148 26 15.5648C26 15.2148 26.1208 14.919 26.3625 14.6773C26.6042 14.4357 26.9 14.3148 27.25 14.3148C27.6 14.3148 27.8958 14.4357 28.1375 14.6773C28.3792 14.919 28.5 15.2148 28.5 15.5648C28.5 15.9148 28.3792 16.2107 28.1375 16.4523C27.8958 16.694 27.6 16.8148 27.25 16.8148ZM27.25 26.3148C26.9 26.3148 26.6042 26.194 26.3625 25.9523C26.1208 25.7107 26 25.4148 26 25.0648C26 24.7148 26.1208 24.419 26.3625 24.1773C26.6042 23.9357 26.9 23.8148 27.25 23.8148C27.6 23.8148 27.8958 23.9357 28.1375 24.1773C28.3792 24.419 28.5 24.7148 28.5 25.0648C28.5 25.4148 28.3792 25.7107 28.1375 25.9523C27.8958 26.194 27.6 26.3148 27.25 26.3148ZM28.75 21.5648C28.4 21.5648 28.1042 21.444 27.8625 21.2023C27.6208 20.9607 27.5 20.6648 27.5 20.3148C27.5 19.9648 27.6208 19.669 27.8625 19.4273C28.1042 19.1857 28.4 19.0648 28.75 19.0648C29.1 19.0648 29.3958 19.1857 29.6375 19.4273C29.8792 19.669 30 19.9648 30 20.3148C30 20.6648 29.8792 20.9607 29.6375 21.2023C29.3958 21.444 29.1 21.5648 28.75 21.5648ZM10 20.3148C10 17.6982 10.8708 15.4482 12.6125 13.5648C14.3542 11.6815 16.4917 10.6148 19.025 10.3648C19.2917 10.3315 19.5208 10.4107 19.7125 10.6023C19.9042 10.794 20 11.0315 20 11.3148C20 11.5815 19.9125 11.8148 19.7375 12.0148C19.5625 12.2148 19.3417 12.3315 19.075 12.3648C17.0583 12.5982 15.375 13.4648 14.025 14.9648C12.675 16.4648 12 18.2482 12 20.3148C12 22.3982 12.675 24.1857 14.025 25.6773C15.375 27.169 17.0583 28.0315 19.075 28.2648C19.3417 28.2982 19.5625 28.4148 19.7375 28.6148C19.9125 28.8148 20 29.0482 20 29.3148C20 29.5982 19.9042 29.8357 19.7125 30.0273C19.5208 30.219 19.2917 30.2982 19.025 30.2648C16.475 30.0148 14.3333 28.9482 12.6 27.0648C10.8667 25.1815 10 22.9315 10 20.3148Z" fill="white"/></g></svg>'
17783        );
17784      }
17785      if (iconClass === "open--lock") {
17786        $modal.push(
17787          '<svg class="map-modal__icon" width="40" height="41" viewBox="0 0 40 41" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="20" cy="20.3145" r="20" fill="#DE0000"/><mask id="mask0_5227_4633" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="8" y="8" width="24" height="25"><rect x="8" y="8.31445" width="24" height="24" fill="#D9D9D9"/></mask><g mask="url(#mask0_5227_4633)"><path d="M14 30.3145C13.45 30.3145 12.9792 30.1186 12.5875 29.727C12.1958 29.3353 12 28.8645 12 28.3145V18.3145C12 17.7645 12.1958 17.2936 12.5875 16.902C12.9792 16.5103 13.45 16.3145 14 16.3145H15V14.3145C15 12.9311 15.4875 11.752 16.4625 10.777C17.4375 9.80195 18.6167 9.31445 20 9.31445C21.3833 9.31445 22.5625 9.80195 23.5375 10.777C24.5125 11.752 25 12.9311 25 14.3145V16.3145H26C26.55 16.3145 27.0208 16.5103 27.4125 16.902C27.8042 17.2936 28 17.7645 28 18.3145V28.3145C28 28.8645 27.8042 29.3353 27.4125 29.727C27.0208 30.1186 26.55 30.3145 26 30.3145H14ZM14 28.3145H26V18.3145H14V28.3145ZM20 25.3145C20.55 25.3145 21.0208 25.1186 21.4125 24.727C21.8042 24.3353 22 23.8645 22 23.3145C22 22.7645 21.8042 22.2936 21.4125 21.902C21.0208 21.5103 20.55 21.3145 20 21.3145C19.45 21.3145 18.9792 21.5103 18.5875 21.902C18.1958 22.2936 18 22.7645 18 23.3145C18 23.8645 18.1958 24.3353 18.5875 24.727C18.9792 25.1186 19.45 25.3145 20 25.3145ZM17 16.3145H23V14.3145C23 13.4811 22.7083 12.7728 22.125 12.1895C21.5417 11.6061 20.8333 11.3145 20 11.3145C19.1667 11.3145 18.4583 11.6061 17.875 12.1895C17.2917 12.7728 17 13.4811 17 14.3145V16.3145Z" fill="white"/></g></svg>'
17788        );
17789      }
17790      if (iconClass === "open--unavailable") {
17791        $modal.push(
17792          '<svg class="map-modal__icon" width="40" height="41" viewBox="0 0 40 41" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="20" cy="20.3145" r="20" fill="#D1D1D1"/><g opacity="0.6"><mask id="mask0_5227_4641" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="8" y="8" width="24" height="25"><rect x="8" y="8.31445" width="24" height="24" fill="#D9D9D9"/></mask><g mask="url(#mask0_5227_4641)"><path d="M20 30.3145C18.6167 30.3145 17.3167 30.052 16.1 29.527C14.8833 29.002 13.825 28.2895 12.925 27.3895C12.025 26.4895 11.3125 25.4311 10.7875 24.2145C10.2625 22.9978 10 21.6978 10 20.3145C10 18.9311 10.2625 17.6311 10.7875 16.4145C11.3125 15.1978 12.025 14.1395 12.925 13.2395C13.825 12.3395 14.8833 11.627 16.
177921 11.102C17.3167 10.577 18.6167 10.3145 20 10.3145C21.3833 10.3145 22.6833 10.577 23.9 11.102C25.1167 11.627 26.175 12.3395 27.075 13.2395C27.975 14.1395 28.6875 15.1978 29.2125 16.4145C29.7375 17.6311 30 18.9311 30 20.3145C30 21.6978 29.7375 22.9978 29.2125 24.2145C28.6875 25.4311 27.975 26.4895 27.075 27.3895C26.175 28.2895 25.1167 29.002 23.9 29.527C22.6833 30.052 21.3833 30.3145 20 30.3145ZM20 28.3145C20.9 28.3145 21.7667 28.1686 22.6 27.877C23.4333 27.5853 24.2 27.1645 24.9 26.6145L13.7 15.4145C13.15 16.1145 12.7292 16.8811 12.4375 17.7145C12.1458 18.5478 12 19.4145 12 20.3145C12 22.5478 12.775 24.4395 14.325 25.9895C15.875 27.5395 17.7667 28.3145 20 28.3145ZM26.3 25.2145C26.85 24.5145 27.2708 23.7478 27.5625 22.9145C27.8542 22.0811 28 21.2145 28 20.3145C28 18.0811 27.225 16.1895 25.675 14.6395C24.125 13.0895 22.2333 12.3145 20 12.3145C19.1 12.3145 18.2333 12.4603 17.4 12.752C16.5667 13.0436 15.8 13.4645 15.1 14.0145L26.3 25.2145Z" fill="black"/></g></g></svg>'
17793        );
17794      }
17795      $modal.push('<div class="map-modal__address-wrap">');
17796      $modal.push('<div class="map-modal-address">');
17797      $modal.push("<div><strong>" + data.address.organisation + "</strong>");
17798
17799      let address =
17800        data.address.street.trim() +
17801        " " +
17802        data.address.number.trim() +
17803        (data.address.letter || "").trim() +
17804        (data.address.floor.trim() != ""
17805          ? ", " + data.address.floor.trim()
17806          : "") +
17807        "" +
17808        (data.address.door || "").trim() +
17809        ", " +
17810        (data.address.postalCode + "").trim();
17811      $modal.push(address + "</div>");
17812      $modal.push("</div>");
17813      $modal.push("</div>");
17814      $modal.push('<span class="map-modal-toggle"></span>');
17815      $modal.push("</div>");
17816      /* End address */
17817      $modal.push('<div class="map-modal__content-main modal-content">');
17818      $modal.push("" + availabilityHtml);
17819      $modal.push('<div class="modal-opening-hours">');
17820      if (!showOpeningHours) {
17821        $modal.push('<div class="c-pin-card__open-status">');
17822        $modal.push(
17823          '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none"><mask id="mask0_1763_5777" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="20" height="20"><rect width="20" height="20" fill="#D9D9D9"/></mask><g mask="url(#mask0_1763_5777)"><path d="M10.5429 9.45641V5.54293C10.5429 5.38947 10.4914 5.26083 10.3884 5.15701C10.2854 5.05319 10.1577 5.00128 10.0054 5.00128C9.85308 5.00128 9.72405 5.05319 9.61828 5.15701C9.51252 5.26083 9.45964 5.38947 9.45964 5.54293V9.64872C9.45964 9.74682 9.4778 9.83491 9.51411 9.91299C9.55043 9.99107 9.60065 10.0622 9.66476 10.1263L12.5574 13.0189C12.6749 13.1364 12.8036 13.193 12.9436 13.1888C13.0836 13.1845 13.2139 13.122 13.3346 13.0013C13.4553 12.8806 13.5157 12.7516 13.5157 12.6143C13.5157 12.477 13.453 12.3462 13.3276 12.222L10.5429 9.45641ZM10.0027 17.5846C8.95808 17.5846 7.97619 17.3872 7.05701 16.9925C6.13783 16.5978 5.33232 16.0547 4.64047 15.3631C3.94861 14.6716 3.40523 13.8654 3.01032 12.9447C2.61542 12.0239 2.41797 11.04 2.41797 9.99314C2.41797 8.94622 2.61533 7.96187 3.01005 7.04009C3.40477 6.1183 3.9479 5.31495 4.63943 4.63005C5.33097 3.94514 6.13713 3.40523 7.05791 3.01032C7.97868 2.61542 8.96252 2.41797 10.0094 2.41797C11.0563 2.41797 12.0407 2.617 12.9626 3.01505C13.8845 3.41311 14.6864 3.95332 15.3683 4.63568C16.0502 5.31805 16.5901 6.12048 16.9879 7.04295C17.3857 7.96541 17.5846 8.95105 17.5846 9.99989C17.5846 11.0445 17.3872 12.0264 16.9925 12.9456C16.5978 13.8647 16.0581 14.6702 15.3736 15.3621C14.689 16.054 13.8851 16.5973 12.9619 16.9922C12.0387 17.3871 11.0523 17.5846 10.0027 17.5846ZM10.0112 16.5013C11.8095 16.5013 13.3408 15.8659 14.605 14.595C15.8692 13.3242 16.5013 11.7896 16.5013 9.99134C16.5013 8.19305 15.8692 6.6618 14.605 5.39759C13.3408 4.13339 11.8095 3.50128 10.0112 3.50128C8.21293 3.50128 6.67836 4.13339 5.40753 5.39759C4.1367 6.6618 3.50128 8.19305 3.50128 9.99134C3.50128 11.7896 4.1367 13.3242 5.40753 14.595C6.67836 15.8659 8.21293 16.5013 10.0112 16.5013Z" fill="#081D3F"/></g></svg>'
17824        );
17825        $modal.push('<div class="c-pin-card__open-status-label">');
17826        $modal.push("" + statusLabel);
17827        $modal.push("</div>");
17828        $modal.push("</div>");
17829      }
17830      /* Start opening hours accordion */
17831      if (showOpeningHours) {
17832        $modal.push(
17833          '<div class="c-pin-card__open-status modal-opening-hours-toggle">'
17834        );
17835        $modal.push(
17836          '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none"><mask id="mask0_1763_5777" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="20" height="20"><rect width="20" height="20" fill="#D9D9D9"/></mask><g mask="url(#mask0_1763_5777)"><path d="M10.5429 9.45641V5.54293C10.5429 5.38947 10.4914 5.26083 10.3884 5.15701C10.2854 5.05319 10.1577 5.00128 10.0054 5.00128C9.85308 5.00128 9.72405 5.05319 9.61828 5.
1783615701C9.51252 5.26083 9.45964 5.38947 9.45964 5.54293V9.64872C9.45964 9.74682 9.4778 9.83491 9.51411 9.91299C9.55043 9.99107 9.60065 10.0622 9.66476 10.1263L12.5574 13.0189C12.6749 13.1364 12.8036 13.193 12.9436 13.1888C13.0836 13.1845 13.2139 13.122 13.3346 13.0013C13.4553 12.8806 13.5157 12.7516 13.5157 12.6143C13.5157 12.477 13.453 12.3462 13.3276 12.222L10.5429 9.45641ZM10.0027 17.5846C8.95808 17.5846 7.97619 17.3872 7.05701 16.9925C6.13783 16.5978 5.33232 16.0547 4.64047 15.3631C3.94861 14.6716 3.40523 13.8654 3.01032 12.9447C2.61542 12.0239 2.41797 11.04 2.41797 9.99314C2.41797 8.94622 2.61533 7.96187 3.01005 7.04009C3.40477 6.1183 3.9479 5.31495 4.63943 4.63005C5.33097 3.94514 6.13713 3.40523 7.05791 3.01032C7.97868 2.61542 8.96252 2.41797 10.0094 2.41797C11.0563 2.41797 12.0407 2.617 12.9626 3.01505C13.8845 3.41311 14.6864 3.95332 15.3683 4.63568C16.0502 5.31805 16.5901 6.12048 16.9879 7.04295C17.3857 7.96541 17.5846 8.95105 17.5846 9.99989C17.5846 11.0445 17.3872 12.0264 16.9925 12.9456C16.5978 13.8647 16.0581 14.6702 15.3736 15.3621C14.689 16.054 13.8851 16.5973 12.9619 16.9922C12.0387 17.3871 11.0523 17.5846 10.0027 17.5846ZM10.0112 16.5013C11.8095 16.5013 13.3408 15.8659 14.605 14.595C15.8692 13.3242 16.5013 11.7896 16.5013 9.99134C16.5013 8.19305 15.8692 6.6618 14.605 5.39759C13.3408 4.13339 11.8095 3.50128 10.0112 3.50128C8.21293 3.50128 6.67836 4.13339 5.40753 5.39759C4.1367 6.6618 3.50128 8.19305 3.50128 9.99134C3.50128 11.7896 4.1367 13.3242 5.40753 14.595C6.67836 15.8659 8.21293 16.5013 10.0112 16.5013Z" fill="#081D3F"/></g></svg>'
17837        );
17838        $modal.push('<div class="c-pin-card__open-status-label">');
17839        $modal.push("" + statusLabel);
17840        $modal.push("</div>");
17841        $modal.push("</div>");
17842        $modal.push('<div class="modal-opening-hours-accordion">');
17843
17844        /* Start access exceptions */
17845        $modal.push('<div class="map-modal-access-exceptions">');
17846
17847        var showSpecialOpeningHeadline = true;
17848        var periods = vt.createModalHelpers.flattenAvailability(
17849          data.availability
17850        );
17851        $.each(periods, function (i, item) {
17852          // Container div with indexed classname
17853          $modal.push('<div class="map-modal-opening-period period' + i + '">');
17854
17855          //if startdate is set then make a period headline
17856          if (item.begin && item.end) {
17857            var startDate = vt.createModalHelpers.returnDate(item.begin);
17858            var endDate = vt.createModalHelpers.returnDate(item.end);
17859            var specialOpeningHeadline = showSpecialOpeningHeadline
17860              ? "<p><strong>SÆRLIGE ÅBNINGSTIDER</strong></p>"
17861              : "";
17862            $modal.push(
17863              specialOpeningHeadline +
17864              '<div class="map-modal-period-headline">' +
17865              vt.createModalHelpers.formatDate(startDate) +
17866              " - " +
17867              vt.createModalHelpers.formatDate(endDate) +
17868              "</div>"
17869            );
17870            showSpecialOpeningHeadline = false;
17871          }
17872
17873          // If it's even
17874          if (item.OnlyEvenWeeks) {
17875            $modal.push("<p>(kun lige uger)</p>");
17876          }
17877          // If it's odd
17878          if (item.OnlyOddWeeks) {
17879            $modal.push("<p>(kun ulige uger)</p>");
17880          }
17881
17882          //go through all days and show the information about the time it's opened
17883          $.each(item.weekDays, function (_weekdayIndex, weekday) {
17884            $modal.push('<p class="map-modal-day-container">');
17885
17886            $modal.push(
17887              '<span class="map-modal-dayName">' + weekday.name + ": </span>"
17888            );
17889
17890            if (weekday.timeSlots == null || weekday.timeSlots.length === 0) {
17891              $modal.push('<span class="map-modal-timeslot">Lukket</span>');
17892            } else {
17893              $.each(weekday.timeSlots, function (_timeslotIndex, timeslot) {
17894                var startTime = vt.createModalHelpers.returnTime(
17895                  timeslot.begin
17896                );
17897                var endTime = vt.createModalHelpers.returnTime(timeslot.end);
17898
17899                if (startTime == "" && endTime == "") {
17900                  $modal.push('<span class="map-modal-timeslot">Lukket</span>');
17901                } else {
17902                  $modal.push(
17903                    '<span class="map-modal-timeslot">' +
17904                    startTime +
17905                    " - " +
17906                    endTime +
17907                    "</span>"
17908                  );
17909                }
17910              });
17911            }
17912
17913            $modal.push("</p>");
17914          });
17915
17916          // Container div with indexed classname end
17917          $modal.push("</div>");
17918        });
17919
17920        if (data.availability.closed) {
17921          var showExceptionPeriod = false;
17922          $.each(data.availability.closed, function (i, item) {
17923            var startDate = vt.createModalHelpers.toDate(item.begin);
17924            var endDate = vt.createModalHelpers.toDate(item.end);
17925            if (!showExceptionPeriod) {
17926              $modal.push(
17927                '<p class="map-modalperiod-headline"><strong>FERIE OG LUKKEDAGE</strong></p>'
17928              );
17929              showExceptionPeriod = true;
17930            }
17931            if (item.begin == item.end) {
17932              $modal.push(
17933                '<div class="map-modal-access-exception period' +
17934                i +
17935                '"><strong>' +
17936                vt.createModalHelpers.formatDate(startDate) +
17937                "</strong><br/>" +
17938                (item.note || "Lukket") +
17939                "</div>"
17940              );
17941            } else {
17942              $modal.push(
17943                '<div class="map-modal-access-exception period' +
17944                i +
17945                '"><strong>' +
17946                vt.createModalHelpers.formatDate(startDate) +
17947                " - " +
17948                vt.createModalHelpers.formatDate(endDate) +
17949                "</strong><br/>" +
17950                (item.note || "Lukket") +
17951                "</div>"
17952              );
17953            }
17954          });
17955        }
17956
17957        /* End access exceptions */
17958        $modal.push("</div>");
17959
17960        /* Start public holidays */
17961        if (!show247) {
17962          $modal.push('<div class="map-modal-public-holidays">');
17963          $.each(data.availability.holidays, function (key, itm) {
17964            if (itm.status === "Closed") {
17965              $modal.push(
17966                '<p class="map-modal-holiday-timeslot"><span>' +
17967                vt.createModalHelpers.publicHolidays[key] +
17968                "</span><span>Lukket</span></p>"
17969              );
17970            } else {
17971              var value = itm.timespans;
17972              $.each(value.reverse(), function (i, timeslot) {
17973                var startTime = vt.createModalHelpers.returnTime(
17974                  timeslot.begin
17975                ),
17976                  endTime = vt.createModalHelpers.returnTime(timeslot.end);
17977                $modal.push(
17978                  '<p class="map-modal-holiday-timeslot"><span>' +
17979                  vt.createModalHelpers.publicHolidays[key] +
17980                  "</span><span>" +
17981                  startTime +
17982                  " - " +
17983                  endTime +
17984                  "</span></p>"
17985                );
17986              });
17987            }
17988          });
17989
17990          $modal.push("</div>");
17991        }
17992        /* End public holidays */
17993
17994        /* End opening hours accordion */
17995        $modal.push("</div>");
17996
17997        /* End show openhours condition */
17998      }
17999
18000      /* End openhours */
18001      $modal.push("</div>");
18002
18003      /* Start placement */
18004      $modal.push('<div class="map-modal-placement">');
18005      $modal.push('<div class="map-modal-placement__title">Placering</div>');
18006      $modal.push(
18007        '<div class="map-modal-placement__description">' +
18008        data.placement.description +
18009        "</div>"
18010      );
18011
18012      /* Start slides */
18013      if (data.pictures.length == 0) {
18014        //$modal.push('<p>Ingen billeder</p>');
18015      } else {
18016        $modal.push('<div class="map-modal-slides">');
18017        // Start slider container
18018        $modal.push('<ul class="map-modal-slider">');
18019
18020        $.each(data.pictures, function (i, imageElement) {
18021          $modal.push(
18022            '<li><img src="' +
18023            ENV +
18024            imageElement.imageUrl +
18025            '"/><div class="map-modal-slide-caption">' +
18026            imageElement.name +
18027            "<br/>" +
18028            imageElement.text +
18029            "</div></li>"
18030          );
18031        });
18032
18033        // End slider container
18034        $modal.push("</ul>");
18035
18036        $modal.push("</div>");
18037      }
18038      /* End slides */
18039
18040      /* End placement */
18041      $modal.push("</div>");
18042
18043      //If multiple items, we put the button inside the toggleable item
18044      if (modalCount > 1) {
18045        if ($(".hunt").length > 0) {
18046          $modal.push(
18047            "<div class='map-modal__btn-wrap'><button class='btn colored btn--small js-pick-this' data-address='" +
18048            address +
18049            "'>Vælg denne hjertestarter</button></div>"
18050          );
18051        } else {
18052          $modal.push(
18053            "<div class='map-modal__btn-wrap'><div class='map-modal__cta-text'>Er der fejl på hjertestarteren?</div><a href='" +
18054            MAP_HUNT_URL +
18055            data.huntProperties +
18056            "' class='btn colored btn--small'>Tip os</a></div>"
18057          );
18058        }
18059      }
18060
18061      /* End content main*/
18062      $modal.push("</div>");
18063
18064      //If onle one item, we but the button outside main, so it sticks to the bottom
18065      if (modalCount === 1) {
18066        if ($(".hunt").length > 0) {
18067          $modal.push(
18068            "<div class='map-modal__btn-wrap'><button class='btn colored btn--small js-pick-this' data-address='" +
18069            address +
18070            "'>Vælg denne hjertestarter</button></div>"
18071          );
18072        } else {
18073          $modal.push(
18074            "<div class='map-modal__btn-wrap'><div class='map-modal__cta-text'>Er der fejl på hjertestarteren?</div><a href='" +
18075            MAP_HUNT_URL +
18076            data.huntProperties +
18077            "' class='btn colored btn--small'>Tip os</a></div>"
18078          );
18079        }
18080      }
18081
18082      // End modal content
18083      $modal.push("</div>");
18084
18085      /* End modal wrapper */
18086      $modal.push("</div>");
18087
18088      // Return joined markup
18089      return $modal.join("");
18090    };
18091
18092    // Close modal
18093    mf.closeModal = function () {
18094      // Reset current marker id
18095      mf.settings.currentMarker = 0;
18096
18097      // Reset hash for marker id's only
18098      if (window.location.hash.indexOf("id/") != -1) {
18099        window.location.hash = "";
18100      }
18101
18102      // Remove active class from active marker and modal
18103      $(".active-marker").removeClass("active-marker");
18104      $(elem).find(".map-modal-container").removeClass("active");
18105    };
18106
18107    // Open marker
18108    mf.openMarker = function (markerIds, setZoom) {
18109      if (markerIds + "" === "0") {
18110        return;
18111      }
18112
18113      var defers = (markerIds + "")
18114        .split(",")
18115        .map((id) => $.get(ENV + "/api/v4/aedspublic/" + id));
18116      $.when
18117        .apply($, defers)
18118        .done(function () {
18119          var data =
18120            defers.length === 1
18121              ? [arguments[0]]
18122              : Array.prototype.slice.call(arguments).map(function (a) {
18123                return a[0];
18124              });
18125
18126          // Update map_location_click event with actual location name
18127          var activeFilters = [];
18128          if ($("#filter-247").length > 0 && $("#filter-247")[0].checked) {
18129            activeFilters.push("24/7");
18130          }
18131          if ($("#filter-opening-hours").length > 0 && $("#filter-opening-hours")[0].checked) {
18132            activeFilters.push("Åbningstider");
18133          }
18134          if ($("#filter-behind-lock").length > 0 && $("#filter-behind-lock")[0].checked) {
18135            activeFilters.push("LÃ¥st");
18136          }
18137          if ($("#filter-unavailable").length > 0 && $("#filter-unavailable")[0].checked) {
18138            activeFilters.push("Utilgængelig");
18139          }
18140
18141          // Determine location type based on first AED in the list
18142          var locationType = "";
18143          if (data[0].availability.availability === "Always" && data[0].status.status !== "Deactivated") {
18144            locationType = "24/7";
18145          } else if (data[0].availability.availability === "OpeningHours" && data[0].status.status !== "Deactivated") {
18146            locationType = "Åbningstider";
18147          } else if (data[0].availability.availability === "BehindLock" && data[0].status.status !== "Deactivated") {
18148            locationType = "LÃ¥st";
18149          } else if (data[0].status.status === "Deactivated") {
18150            locationType = "Utilgængelig";
18151          }
18152
18153          vt.functions.pushToDataLayer({
18154            event: "map_location_click",
18155            location_name: data[0].address.organisation || "AED Location",
18156            active_map_filters: activeFilters.join(", ") || "",
18157            map_location_type: locationType
18158          });
18159
18160          // Pan to marker position
18161          var pos = {
18162            lat: parseFloat(data[0].placement.latitude),
18163            lng: parseFloat(data[0].placement.longitude),
18164          };
18165
18166          // Center map to current marker
18167          mf.map.panTo(pos);
18168
18169          if (setZoom) {
18170            mf.map.setZoom(parseInt(setZoom));
18171          }
18172
18173          // Update currentMarker from mf.settings
18174          mf.settings.currentMarker = markerIds;
18175
18176          // Activate selected marker when map is idle
18177          google.maps.event.addListenerOnce(mf.map, "idle", mapIsIdle);
18178
18179          // Open marker when map is idle
18180          function mapIsIdle() {
18181            if ($('div[data-ids="' + markerIds + '"]').length) {
18182              // Open marker modal
18183              if (window.innerWidth < DesktopView) {
18184                mf.openPreview(data);
18185              } else {
18186                mf.openModal(data);
18187              }
18188            } else {
18189              // Hack: there is no callback when markers from FastMarkerOverlay.js is done appending markers
18190              setTimeout(function () {
18191                mapIsIdle();
18192              }, 200);
18193            }
18194          }
18195        })
18196        .fail(function (e) {
18197          console.error("error", e);
18198        });
18199    };
18200
18201    mf.clearSearchValues = function () {
18202      mf.settings.searchLocation = null;
18203      mf.settings.searchLatLng = null;
18204    };
18205
18206    mf.setSearchValues = function (latlng, val) {
18207      mf.settings.searchLocation = val;
18208      mf.settings.searchLatLng = latlng;
18209    };
18210
18211    mf.panToLatLng = function (latlng, val) {
18212      mf.resetFilters();
18213
18214      mf.setCountsFromSelectedOptions();
18215      // Selected region name
18216      mf.setRegionName();
18217
18218      mf.setSearchValues(latlng, val);
18219      // Pan and zoom to location
18220      mf.map.panTo(latlng);
18221      mf.map.setZoom(18);
18222    };
18223
18224    // Search
18225    mf.search = function (val, lat, lng) {
18226      // Track map search
18227      vt.functions.pushToDataLayer({
18228        event: "map_search",
18229        search_term: val || ""
18230      });
18231
18232      var latlng = null;
18233
18234      // If we have the lat/long then bypass the geocoder
18235      if (lat && lng) {
18236        latlng = { lat: lat, lng: lng };
18237        mf.panToLatLng(latlng, val);
18238      } else {
18239        mf.geocoder.geocode({ address: val }, function (results, status) {
18240          if (status == google.maps.GeocoderStatus.OK) {
18241            latlng = results[0].geometry.location;
18242            mf.panToLatLng(latlng, val);
18243          }
18244        });
18245      }
18246    };
18247
18248    // Close autocomplete list
18249    mf.closeAutocompleteList = function () {
18250      $(elem).find(".c-map-search__list").html("");
18251      $(".map-filter-search-autocomplete").removeClass("active");
18252    };
18253
18254    // Tab through autocomplete list
18255    mf.tabAutocomplete = function (direction) {
18256      var $autocompleteList = $(elem).find(".c-map-search__list"),
18257        $activeListItem = $autocompleteList.find("li.autocomplete-list-active"),
18258        activeClassName = "autocomplete-list-active";
18259
18260      // Tab direction in list
18261      if (direction) {
18262        if (direction == "up") {
18263          if (!$activeListItem.is(":first-child")) {
18264            $activeListItem
18265              .removeClass(activeClassName)
18266              .prev()
18267              .addClass(activeClassName);
18268            $activeListItem.prev()[0].scrollIntoView({ block: "nearest" });
18269          }
18270        } else if (direction == "down") {
18271          if (!$activeListItem.is(":last-child")) {
18272            $activeListItem
18273              .removeClass(activeClassName)
18274              .next()
18275              .addClass(activeClassName);
18276            $activeListItem.next()[0].scrollIntoView({ block: "nearest" });
18277          }
18278        }
18279      } else {
18280        // Select first item in list if no direction is passed
18281        $autocompleteList
18282          .find("li:first-child")
18283          .addClass("autocomplete-list-active");
18284      }
18285    };
18286
18287    // Search autocomplete
18288    mf.autocomplete = function (val) {
18289      if (val != "") {
18290        var autocompleteService =
18291          "https://adresser.trygfonden.dk/api/adresser/autocomplete?q=" +
18292          val.split(" ").join("+"),
18293          $autocompleteList = $(elem).find(".c-map-search__list");
18294
18295        // Clear autocomple list
18296        if ($autocompleteList.find("li, p").length > 0) {
18297          $autocompleteList.html("");
18298        }
18299
18300        $.get(autocompleteService, function (data) {
18301          if (data.length > 0) {
18302            // Add active class to autocomplete container
18303            $(".map-filter-search-autocomplete").addClass("active");
18304
18305            $.each(data, function () {
18306              $autocompleteList.append(
18307                "<li data-latitude='" +
18308                this.adresse.y +
18309                "' data-longitude='" +
18310                this.adresse.x +
18311                "'>" +
18312                this.tekst +
18313                "</li>"
18314              );
18315            });
18316          } else {
18317            // Add active class to autocomplete container
18318            $(".map-filter-search-autocomplete").addClass("active");
18319
18320            $autocompleteList.append(
18321              "<p>Der kan ikke findes nogen adresse ud fra de oplysninger, der er indtastet. Prøv igen.</p>"
18322            );
18323          }
18324        });
18325      }
18326    };
18327
18328    // Init search
18329    mf.initSearch = function () {
18330      // Search Address
18331      $(".c-map-search__input").on(
18332        "keyup",
18333        debounce(function (e) {
18334          var searchValue = $(this).val();
18335
18336          // Autocomplete on letter, delete and backspace keypress
18337          if (
18338            (e.which <= 90 && e.which >= 48) ||
18339            e.which === 8 ||
18340            e.which === 46 ||
18341            e.which === 229 // Android is weird
18342          ) {
18343            mf.autocomplete(searchValue);
18344          }
18345        }, 500)
18346      );
18347
18348      $(".c-map-search__input").on("focus", function () {
18349        mf.autocomplete($(this).val());
18350      });
18351
18352      $(".c-map-search__input").on("keyup", function (e) {
18353        var $input = $(this);
18354        var searchValue = $input.val();
18355        var lat = $input.data("latitude");
18356        var lng = $input.data("longitude");
18357        // Tab to first item in autocomplete list on arrow down
18358        if (e.which == 40) {
18359          mf.tabAutocomplete();
18360
18361          // Blur input field
18362          $input.blur();
18363        }
18364
18365        // Search on enter
18366        if (e.which == 13) {
18367          mf.search(searchValue, lat, lng);
18368          mf.closeAutocompleteList();
18369          $input.blur();
18370        }
18371      });
18372
18373      $(document).on("keydown", function (e) {
18374        if ($(".autocomplete-list-active").length > 0) {
18375          // Disable scrolling on arrow up/down or enter if autocomplete list is active
18376          if (e.which == 40 || e.which == 38 || e.which == 13) {
18377            e.preventDefault();
18378          } else {
18379            mf.closeAutocompleteList();
18380          }
18381
18382          // Send direction to mf.tabAutocomplete()
18383          if (e.which == 40) {
18384            // Tab down
18385            mf.tabAutocomplete("down");
18386          } else if (e.which == 38) {
18387            // Tab up
18388            mf.tabAutocomplete("up");
18389          }
18390
18391          // Search on enter
18392          if (e.which == 13) {
18393            var $listitem = $(".autocomplete-list-active");
18394            var selectedValue = $listitem.html();
18395            var lat = $listitem.data("latitude");
18396            var lng = $listitem.data("longitude");
18397
18398            // Insert value into searchfield
18399            $(".c-map-search__input").val(selectedValue);
18400
18401            // Search selected value
18402            mf.search(selectedValue, lat, lng);
18403
18404            // Close list
18405            mf.closeAutocompleteList();
18406          }
18407        }
18408      });
18409
18410      $("body").on("mouseup", function () {
18411        // Close autocomplete list on any mouseup events
18412        mf.closeAutocompleteList();
18413      });
18414
18415      $("body").on("mousedown", ".c-map-search__list li", function () {
18416        var $listitem = $(this);
18417        var selectedValue = $listitem.html();
18418        var lat = $listitem.data("latitude");
18419        var lng = $listitem.data("longitude");
18420
18421        // Insert value into searchfield
18422        $(".c-map-search__input").val(selectedValue);
18423
18424        // Search selected value
18425        mf.search(selectedValue, lat, lng);
18426      });
18427
18428      $(".c-map-search__list").on("mouseover", "li", function (e) {
18429        $(e.delegateTarget)
18430          .find(".autocomplete-list-active")
18431          .removeClass("autocomplete-list-active");
18432        $(this).addClass("autocomplete-list-active");
18433      });
18434
18435      if ($(".js-placement").val()) {
18436        mf.search($(".js-placement").val());
18437      }
18438    };
18439
18440    // Init who you gonna call
18441    mf.whoYouGonnaCall = function () {
18442      var $body = $("body");
18443      $body.addClass("banner-112");
18444
18445      // Hide $el on click
18446      $body.find(".c-call-emergency__close").on("click", function () {
18447        $body.removeClass("banner-112");
18448      });
18449    };
18450
18451    // Tablet menu toggle
18452    mf.tabletMenu = function () {
18453      var $nav = $(elem).find(".map-filter"),
18454        $toggle = $(elem).find(".map-tablet-toggle");
18455
18456      $toggle.on("click", function () {
18457        $nav.toggleClass("active");
18458        $toggle.toggleClass("open");
18459      });
18460    };
18461
18462    // Custom map styles
18463    mf.mapStyles = [
18464      {
18465        featureType: "all",
18466        elementType: "labels.text.fill",
18467        stylers: [
18468          {
18469            saturation: 36,
18470          },
18471          {
18472            color: "#333333",
18473          },
18474          {
18475            lightness: 40,
18476          },
18477        ],
18478      },
18479      {
18480        featureType: "all",
18481        elementType: "labels.text.stroke",
18482        stylers: [
18483          {
18484            visibility: "on",
18485          },
18486          {
18487            color: "#ffffff",
18488          },
18489          {
18490            lightness: 16,
18491          },
18492        ],
18493      },
18494      {
18495        featureType: "all",
18496        elementType: "labels.icon",
18497        stylers: [
18498          {
18499            visibility: "off",
18500          },
18501        ],
18502      },
18503      {
18504        featureType: "administrative",
18505        elementType: "geometry.fill",
18506        stylers: [
18507          {
18508            color: "#fefefe",
18509          },
18510          {
18511            lightness: 20,
18512          },
18513        ],
18514      },
18515      {
18516        featureType: "administrative",
18517        elementType: "geometry.stroke",
18518        stylers: [
18519          {
18520            color: "#fefefe",
18521          },
18522          {
18523            lightness: 17,
18524          },
18525          {
18526            weight: 1.2,
18527          },
18528        ],
18529      },
18530      {
18531        featureType: "administrative",
18532        elementType: "labels",
18533        stylers: [
18534          {
18535            visibility: "on",
18536          },
18537        ],
18538      },
18539      {
18540        featureType: "administrative.neighborhood",
18541        elementType: "labels",
18542        stylers: [
18543          {
18544            visibility: "on",
18545          },
18546        ],
18547      },
18548      {
18549        featureType: "administrative.neighborhood",
18550        elementType: "labels.text",
18551        stylers: [
18552          {
18553            visibility: "on",
18554          },
18555        ],
18556      },
18557      {
18558        featureType: "landscape",
18559        elementType: "geometry",
18560        stylers: [
18561          {
18562            color: "#f5f5f5",
18563          },
18564          {
18565            lightness: 20,
18566          },
18567        ],
18568      },
18569      {
18570        featureType: "landscape",
18571        elementType: "geometry.fill",
18572        stylers: [
18573          {
18574            lightness: "100",
18575          },
18576          {
18577            saturation: "0",
18578          },
18579          {
18580            gamma: "1.82",
18581          },
18582          {
18583            weight: "2.35",
18584          },
18585          {
18586            color: "#e1e1e1",
18587          },
18588        ],
18589      },
18590      {
18591        featureType: "landscape.natural",
18592        elementType: "labels",
18593        stylers: [
18594          {
18595            visibility: "simplified",
18596          },
18597        ],
18598      },
18599      {
18600        featureType: "landscape.natural.landcover",
18601        elementType: "geometry.fill",
18602        stylers: [
18603          {
18604            lightness: "0",
18605          },
18606          {
18607            color: "#e912ce",
18608          },
18609          {
18610            visibility: "off",
18611          },
18612        ],
18613      },
18614      {
18615        featureType: "landscape.natural.landcover",
18616        elementType: "labels.text.fill",
18617        stylers: [
18618          {
18619            color: "#00ff90",
18620          },
18621        ],
18622      },
18623      {
18624        featureType: "landscape.natural.terrain",
18625        elementType: "geometry.fill",
18626        stylers: [
18627          {
18628            visibility: "off",
18629          },
18630        ],
18631      },
18632      {
18633        featureType: "poi",
18634        elementType: "geometry",
18635        stylers: [
18636          {
18637            color: "#f5f5f5",
18638          },
18639          {
18640            lightness: 21,
18641          },
18642        ],
18643      },
18644      {
18645        featureType: "poi",
18646        elementType: "labels",
18647        stylers: [
18648          {
18649            visibility: "simplified",
18650          },
18651        ],
18652      },
18653      {
18654        featureType: "poi.park",
18655        elementType: "geometry",
18656        stylers: [
18657          {
18658            color: "#dedede",
18659          },
18660          {
18661            lightness: 21,
18662          },
18663        ],
18664      },
18665      {
18666        featureType: "road",
18667        elementType: "all",
18668        stylers: [
18669          {
18670            visibility: "on",
18671          },
18672        ],
18673      },
18674      {
18675        featureType: "road",
18676        elementType: "labels",
18677        stylers: [
18678          {
18679            visibility: "on",
18680          },
18681        ],
18682      },
18683      {
18684        featureType: "road",
18685        elementType: "labels.text",
18686        stylers: [
18687          {
18688            visibility: "on",
18689          },
18690        ],
18691      },
18692      {
18693        featureType: "road.highway",
18694        elementType: "geometry.fill",
18695        stylers: [
18696          {
18697            color: "#ffffff",
18698          },
18699          {
18700            lightness: 17,
18701          },
18702        ],
18703      },
18704      {
18705        featureType: "road.highway",
18706        elementType: "geometry.stroke",
18707        stylers: [
18708          {
18709            color: "#ffffff",
18710          },
18711          {
18712            lightness: 29,
18713          },
18714          {
18715            weight: 0.2,
18716          },
18717        ],
18718      },
18719      {
18720        featureType: "road.highway",
18721        elementType: "labels",
18722        stylers: [
18723          {
18724            visibility: "off",
18725          },
18726        ],
18727      },
18728      {
18729        featureType: "road.arterial",
18730        elementType: "geometry",
18731        stylers: [
18732          {
18733            color: "#ffffff",
18734          },
18735          {
18736            lightness: 18,
18737          },
18738        ],
18739      },
18740      {
18741        featureType: "road.arterial",
18742        elementType: "labels",
18743        stylers: [
18744          {
18745            visibility: "simplified",
18746          },
18747        ],
18748      },
18749      {
18750        featureType: "road.local",
18751        elementType: "geometry",
18752        stylers: [
18753          {
18754            color: "#ffffff",
18755          },
18756          {
18757            lightness: 16,
18758          },
18759        ],
18760      },
18761      {
18762        featureType: "road.local",
18763        elementType: "labels",
18764        stylers: [
18765          {
18766            visibility: "on",
18767          },
18768        ],
18769      },
18770      {
18771        featureType: "transit",
18772        elementType: "geometry",
18773        stylers: [
18774          {
18775            color: "#F4F3F2",
18776          },
18777          {
18778            lightness: 19,
18779          },
18780        ],
18781      },
18782      {
18783        featureType: "transit",
18784        elementType: "labels",
18785        stylers: [
18786          {
18787            visibility: "simplified",
18788          },
18789        ],
18790      },
18791      {
18792        featureType: "water",
18793        elementType: "all",
18794        stylers: [
18795          {
18796            visibility: "on",
18797          },
18798        ],
18799      },
18800      {
18801        featureType: "water",
18802        elementType: "geometry",
18803        stylers: [
18804          {
18805            color: "#e9e9e9",
18806          },
18807          {
18808            lightness: 17,
18809          },
18810        ],
18811      },
18812      {
18813        featureType: "water",
18814        elementType: "geometry.fill",
18815        stylers: [
18816          {
18817            lightness: "-3",
18818          },
18819          {
18820            color: "#e6f9ff",
18821          },
18822        ],
18823      },
18824    ];
18825
18826    mf.getCurrentTileColors = function () {
18827      mf.color247 =
18828        $("#filter-247").length > 0 ? $("#filter-247")[0].checked + "" : "true";
18829      mf.colorOpeningHours =
18830        $("#filter-opening-hours").length > 0
18831          ? $("#filter-opening-hours")[0].checked + ""
18832          : "true";
18833      mf.colorBehindLock =
18834        $("#filter-behind-lock").length > 0
18835          ? $("#filter-behind-lock")[0].checked + ""
18836          : "true";
18837      mf.colorUnavailable =
18838        $("#filter-unavailable").length > 0
18839          ? $("#filter-unavailable")[0].checked + ""
18840          : "true";
18841      //If all filters are off, then we show all colors
18842      if (
18843        mf.color247 === "false" &&
18844        mf.colorOpeningHours === "false" &&
18845        mf.colorBehindLock === "false" &&
18846        mf.colorUnavailable === "false"
18847      ) {
18848        mf.color247 = "true";
18849        mf.colorOpeningHours = "true";
18850        mf.colorBehindLock = "true";
18851        mf.colorUnavailable = "true";
18852      }
18853    };
18854
18855    mf.filterChange = function () {
18856      mf.getCurrentTileColors();
18857      mf.closeModal();
18858      mf.mapChanged("filterChange");
18859    };
18860
18861    mf.setRegionName = function () {
18862      $(".map-filter-region").html(
18863        $(".map-filter-regions option:selected").data("name")
18864      );
18865    };
18866    mf.isMobile = function () {
18867      return (isMobile = /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
18868        navigator.userAgent
18869      ));
18870    };
18871    mf.isTablet = function () {
18872      return (isTablet = /iPad|tablet/i.test(navigator.userAgent));
18873    };
18874
18875    // Init each map
18876    mf.initMap = function () {
18877      var $regionSelect = $(elem).find(".map-filter-regions"),
18878        $municipalitiesSelect = $(elem).find(".map-filter-municipalities"),
18879        $zoom = mf.isMobile() ? 6.2 : mf.isTablet() ? 7.1 : 7.5;
18880
18881      // if region and municipality is empty then go to danmark
18882      var goToLocation = "Denmark";
18883      if (
18884        $regionSelect.find(":selected").val() != "all" ||
18885        $municipalitiesSelect.find(":selected").val() != "all"
18886      ) {
18887        if ($regionSelect.find(":selected").val() != "all") {
18888          goToLocation = $regionSelect.find(":selected").html();
18889        }
18890
18891        if ($municipalitiesSelect.find(":selected").val() != "all") {
18892          goToLocation = $municipalitiesSelect.find(":selected").html();
18893        }
18894      }
18895
18896      // Pan and zoom to goToLocation
18897      mf.geocoder = new google.maps.Geocoder();
18898      mf.geocoder.geocode({ address: goToLocation }, function (
18899        results,
18900        status
18901      ) {
18902        if (status == google.maps.GeocoderStatus.OK) {
18903          // Trigger filter to make sure google maps sorts the layer correct (safari bug/hack)
18904
18905          mf.map.setCenter(results[0].geometry.location);
18906          mf.map.fitBounds(results[0].geometry.viewport);
18907        }
18908      });
18909
18910      mf.getCurrentTileColors();
18911
18912      $('[data-js-initialize="vt.hjertestarterMap"]').each(function () {
18913        mf.markers = new Array();
18914        mf.overlays = new Array();
18915        mf.lastViewType = "dots";
18916        mf.viewType = "dots";
18917
18918        // Count up aed's in denmark on load
18919
18920        // Selected region name
18921        mf.setRegionName();
18922
18923        var $mapContainer = $(this).find(MAP_CONTAINER),
18924          $filterContainer = $(this).find(".map-filter");
18925
18926        $filterContainer.click(function (e) {
18927          if (Modernizr.touchevents) {
18928            e.stopPropagation();
18929          }
18930        });
18931
18932        function addMunicipalityOption(itm, id, label, name) {
18933          var mLabel = label || itm.title;
18934          var mId = id || itm.id;
18935          var mName = name || mLabel;
18936          var option = new Option(mLabel, mId);
18937          option.setAttribute("data-aed", itm.countTotal);
18938          option.setAttribute("data-247-aed", itm.countAlways);
18939          option.setAttribute("data-opening-hours-aed", itm.countOpeningHours);
18940          option.setAttribute("data-behind-lock-aed", itm.countBehindLock);
18941          option.setAttribute("data-unavailable-aed", itm.countGreyAed);
18942          if (itm.regionId) {
18943            option.setAttribute("data-region", itm.regionId);
18944          }
18945          option.setAttribute("data-name", mName);
18946          $municipalitiesSelect.append(option);
18947        }
18948
18949        vt.mapCountsData.then(function () {
18950          // Load regions
18951          mf.loadRegions();
18952
18953          addMunicipalityOption(
18954            vt.mapCounts.regions.all,
18955            "all",
18956            "Alle kommuner",
18957            "Danmark"
18958          );
18959          vt.mapCounts.municipalities.map(function (itm) {
18960            addMunicipalityOption(itm);
18961          });
18962
18963          // Init deeplinking
18964          mf.deepLink();
18965
18966          // Filters
18967          mf.$checkboxes = $('input[name="map-filter"]').on(
18968            "change",
18969            function () {
18970              mf.filterChange();
18971
18972              // Get active filters for tracking
18973              // Track filter change with debouncing
18974              trackFilterChange();
18975            }
18976          );          // Set counts
18977          mf.setCountsFromSelectedOptions();
18978        });
18979
18980        // Init map with performance optimizations
18981        mf.map = new google.maps.Map($mapContainer.get(0), {
18982          center: { lat: 55.9528884, lng: 11.5385978 },
18983          zoom: $zoom,
18984          fullscreenControl: false,
18985          scrollwheel: true,
18986          navigationControl: true,
18987          mapTypeControl: false,
18988          draggable: true,
18989          mapTypeId: "roadmap",
18990          scaleControl: true,
18991          backgroundColor: "#ffffff",
18992          streetViewControl: false,
18993          draggableCursor: "default",
18994          minZoom: 3,
18995          maxZoom: 20,
18996          tilt: 45,
18997          rotateControl: true,
18998          rotateControlOptions: {
18999            position: google.maps.ControlPosition.RIGHT_CENTER,
19000          },
19001          panControl: true,
19002          panControlOptions: {
19003            position: google.maps.ControlPosition.LEFT_TOP,
19004          },
19005          zoomControl: true,
19006          zoomControlOptions: {
19007            style: google.maps.ZoomControlStyle.LARGE,
19008            position: google.maps.ControlPosition.RIGHT_BOTTOM,
19009          },
19010          // Performance optimizations
19011          clickableIcons: false,
19012          gestureHandling: 'greedy',
19013          disableDoubleClickZoom: false
19014        });
19015
19016        mf.map.setTilt(45);
19017
19018        mf.map.setOptions({ styles: mf.mapStyles });
19019
19020        // Generate static png tiles
19021        mf.generateTiles();
19022
19023        // Close modal when dragging the map
19024        /*
19025        mf.map.addListener('dragstart', function () {
19026            // Close modal
19027            mf.closeModal();
19028        });
19029
19030        // Set new markers on dragend
19031        mf.map.addListener('dragend', function () {
19032            //mf.mapChanged();
19033        });*/
19034
19035        // add marker click event
19036        if ($(".hunt").length > 0) {
19037          mf.map.addListener("click", function (e) {
19038            mf.search("", e.latLng.lat(), e.latLng.lng());
19039            let searchInput = $(".js-placement");
19040            searchInput
19041              .val(e.latLng.lat() + " " + e.latLng.lng())
19042              .trigger("change");
19043            searchInput.val(searchInput.data("coordinates-placeholder"));
19044            searchInput.attr(
19045              "data-coordinates",
19046              e.latLng.lat() + " " + e.latLng.lng()
19047            );
19048            $(".c-map-search__clear").show();
19049          });
19050        }
19051        // Initialize filter cache after DOM is ready
19052        initFilterCache();
19053
19054        // Throttled map idle handler to prevent excessive updates (500ms)
19055        var throttledMapChanged = debounce(function () {
19056          mf.mapChanged("map idle event");
19057        }, 500);
19058
19059        // Set new markers on idle
19060        mf.map.addListener("idle", function () {
19061          // Only trigger a map changed event if the view changes
19062          // var zoomLevelChange = mf.viewType !== mf.lastViewType;
19063          // if (zoomLevelChange || mf.map.getZoom() > mf.settings.switchToRealMarkers) {
19064
19065          throttledMapChanged();
19066          // }
19067        });
19068
19069        $(".map__container").click(function () {
19070          vt.deselectMarker();
19071        });
19072
19073        // Close modal if map is switching to tiles instead of markers
19074        mf.map.addListener("zoom_changed", function () {
19075          if (mf.map.getZoom() < mf.settings.switchToRealMarkers) {
19076            mf.lastViewType = mf.viewType;
19077            mf.viewType = "dots";
19078            mf.closeModal();
19079          } else {
19080            mf.lastViewType = mf.viewType;
19081            mf.viewType = "markers";
19082          }
19083        });
19084
19085        // Set only-247 checkbox checked if deeplinked
19086        if (window.location.hash.indexOf("only247") > 0) {
19087          $(elem).find("#filter-247").attr("checked", true);
19088          // Trigger mapChanged
19089          mf.mapChanged("only 247 init");
19090        }
19091
19092        // Trigger map changed on is247 checkbox change
19093        $(elem)
19094          .find('input[name="only-247"]')
19095          .on("change", function () {
19096            // 24/7 filter tracking is handled by map_filters_click event in mf.$checkboxes change handler
19097          });
19098
19099        // Create custom button to find users location
19100        var centerControlDiv = document.createElement("div");
19101        centerControlDiv.style.zIndex = "10";
19102
19103        var modalOnClick = function (mf) {
19104          // Show marker modal on click
19105          $(elem).on("click", ".marker div[data-ids]", function () {
19106            var $this = $(this),
19107              data = $this.data();
19108
19109            // Check if this marker is already open
19110            if (!$(this).parent().hasClass("active-marker")) {
19111              if (data.count > 1 && mf.map.getZoom() <= 19) {
19112                // Zoom closer to cluster if more than 8 clustered in marker
19113                // Set new zoom level
19114                var newZoom = mf.map.getZoom() + 1;
19115                mf.map.setZoom(newZoom);
19116
19117                // Get lat lng
19118                var pos = {
19119                  lat: parseFloat(data.lat),
19120                  lng: parseFloat(data.lng),
19121                };
19122
19123                // Center map to lat lng
19124                mf.map.panTo(pos);
19125              } else {
19126                // Set new hash for deeplinking
19127                var only247 = $(elem)
19128                  .find('input[name="only-247"]')
19129                  .is(":checked")
19130                  ? "/only247"
19131                  : "";
19132                window.location.hash =
19133                  "id/" + data.ids + "/" + mf.map.getZoom() + only247;
19134
19135                // Get marker(s) and show modal
19136                mf.openMarker(data.ids, mf.map.getZoom(), data);
19137              }
19138            }
19139          });
19140        };
19141
19142        /*
19143        var centerControl = new mf.myLocationControl(
19144          centerControlDiv,
19145          mf.map,
19146          modalOnClick,
19147        );
19148        mf.map.controls[google.maps.ControlPosition.LEFT_CENTER].push(
19149          centerControlDiv,
19150        );
19151        */
19152
19153        var geoControl = document.createElement("div");
19154        mf.geoControl(geoControl, mf.map);
19155        geoControl.index = 1;
19156        mf.map.controls[google.maps.ControlPosition.RIGHT_BOTTOM].push(
19157          geoControl
19158        );
19159
19160        var typeControl = document.createElement("div");
19161        mf.typeControl(typeControl, mf.map);
19162        typeControl.index = 1;
19163        mf.map.controls[google.maps.ControlPosition.LEFT_BOTTOM].push(
19164          typeControl
19165        );
19166
19167        modalOnClick(mf);
19168
19169        // Init search and autocomplete
19170        mf.initSearch();
19171
19172        // Init call 112 bar
19173        mf.whoYouGonnaCall();
19174
19175        // Init tablet menu
19176        mf.tabletMenu();
19177
19178        // Toggle mobile filters
19179        $(elem)
19180          .find(".map-toggle-mobile-filter")
19181          .on("touchend", function () {
19182            $(elem)
19183              .find(".map-toggle-mobile-filter-target")
19184              .toggleClass("active");
19185          });
19186
19187        $("body").addClass("map-initialized");
19188        $("html, body").scrollTop(0);
19189
19190        // Toggle filter for mobile
19191        /*
19192        $(elem)
19193          .find(".c-map-search__input")
19194          .on("focus", function () {
19195            $(elem).find(".map-filter").addClass("active-mobile");
19196          });
19197
19198        $(elem)
19199          .find(".map-mobile-only")
19200          .on("click", function () {
19201            $(elem).find(".map-filter").removeClass("active-mobile");
19202          });
19203          */
19204
19205        google.maps.event.addListenerOnce(
19206          mf.map,
19207          "tilesloaded",
19208          function fixMyPageOnce() {
19209            setTimeout(function () {
19210              $(".gm-bundled-control-on-bottom .gm-control-active").on(
19211                "click",
19212                function () {
19213                  registerEvent({
19214                    event: "GTM_Event",
19215                    eventAction: "Zoom clicked",
19216                    eventCategory: "Google Maps",
19217                    eventLabel: $(this).attr("title"),
19218                  });
19219                }
19220              );
19221            }, 500);
19222          }
19223        );
19224      });
19225
19226      if ($(".hunt").length > 0) {
19227        mf.positionSearch();
19228        window.addEventListener('resize', function (event) {
19229          mf.positionSearch();
19230        });
19231      }
19232    };
19233
19234    mf.positionSearch = function () {
19235      if (mf.isMobile()) {
19236        if ($(".hunt__map-search-mobile").html() === "") {
19237          $(".hunt__map-search").appendTo(".hunt__map-search-mobile")
19238        }
19239      } else {
19240        $(".hunt__map-search").appendTo(".hunt__map-search-desktop")
19241      }
19242    }
19243
19244    mf.closeNavigation = function () {
19245      $(elem)
19246        .find(".map-toggle-mobile-filter-target, .map-filter")
19247        .removeClass("active");
19248    };
19249
19250    mf.initMap();
19251
19252    vt.hjertestarterMapMethods = {
19253      applyFilters: mf.applyFilters.bind(elem),
19254      search: mf.search,
19255    };
19256  };
19257})(jQuery);;
19258; (function ($) {
19259
19260	window.vt = window.vt || {};
19261
19262	vt.initHugeSearch = function () {
19263
19264		var $searchText = $('.huge-search-text'),
19265			$searchButton = $('#huge-search-button'),
19266			searchConfig = {
19267				"searchUrl": $('body').data('search-url') + "?q="
19268			},
19269			query = '';
19270
19271		var performSearch = function () {
19272			query = $searchText.val() || '';
19273			query = query.trim();
19274			window.location.href = searchConfig.searchUrl + encodeURIComponent(query);
19275		};
19276
19277		var handleSearchButtonClick = function (e) {
19278			if ($searchText.val() !== '') {
19279				performSearch();
19280			}
19281			e.preventDefault();
19282		};
19283
19284		var handleKeyboardInput = function (e) {
19285			if (e.keyCode === 13 && $searchText.val()) {
19286				performSearch();
19287			}
19288		};
19289
19290		$searchButton.on('click', handleSearchButtonClick);
19291		$searchText.on('keyup', handleKeyboardInput);
19292
19293	};
19294
19295})(jQuery);
19296;
19297; (function ($) {
19298
19299	window.vt = window.vt || {};
19300
19301	vt.initHunt = function () {
19302        const urlParams = new URLSearchParams(window.location.search);
19303        const paramUrl = urlParams.get('redirect-url');
19304        const skipIntro = urlParams.get('skip-intro');
19305        let currentStep = 0;
19306        let hasSubmitted = false;
19307
19308        //Check if we have initial placement value
19309        if($(".js-placement").val() != ""){
19310            $(".js-placement").closest(".js-step").find(".js-next-step").attr("disabled", false);
19311            $(".c-map-search__clear").show();
19312        }
19313
19314        //Add click events
19315        $(".js-next-step").on("click", function() {
19316            currentStep++;
19317            goToStep(currentStep);
19318        });
19319
19320        $(".js-prev-step").on("click", function() {
19321            currentStep--;
19322            goToStep(currentStep);
19323        });
19324
19325        if(skipIntro === "true") {
19326            currentStep++;
19327            goToStep(currentStep);
19328        }
19329        //Show intro after above check, so it doesnt show initially, but the user is still able to go back.
19330        $(".hunt__intro .hunt__page-inner-wrap").show();
19331
19332        // Submit
19333        $(".js-submit").on("click", function(){
19334            $(this).prop('disabled', true);
19335            var opts = {
19336                contentType: "application/json;charset=utf-8",
19337                data: JSON.stringify(collectData()),
19338                type: $(".hunt").attr('data-request-method')
19339            };
19340    
19341            var url = $(".hunt").attr('data-api');
19342    
19343            var call = window.vt.ajax.call(url, opts);
19344            call.success(function (data) {
19345                goToStep(8);
19346                setTimeout(function () {
19347                    let redirectUrl = $(".hunt").attr('data-redirect-url');
19348                    if(paramUrl) {
19349                        redirectUrl = paramUrl;
19350                    }
19351                    hasSubmitted = true;
19352                    window.location.href = redirectUrl; 
19353                }, $(".hunt").attr('data-delay'));
19354            });
19355            call.error(function (data) {
19356                goToStep(9);
19357            });
19358        });
19359
19360        // Validate placement step
19361        $(".js-placement").on("change blur", function(){
19362            let nextButton = $(this).closest(".js-step").find(".js-next-step");
19363            if($(this).val() != "") {
19364                nextButton.attr("disabled", false);
19365            }else {
19366                nextButton.attr("disabled", true);
19367            }
19368        });
19369
19370        // validate reason step
19371        $(".js-reason, .js-reason-checkbox").on("change keyup", function(){
19372            let nextButton = $(this).closest(".js-step").find(".js-next-step");
19373            if($(".js-reason").val() != "" || $(".js-reason-checkbox:checked").length > 0) {
19374                nextButton.attr("disabled", false);
19375            }else {
19376                nextButton.attr("disabled", true);
19377            }
19378        });
19379
19380        function goToStep(step){
19381            let selector = ".js-step-" + step;
19382            $(".js-step").hide();
19383            $(selector).show();
19384            $(".hunt__progress-item").removeClass("hunt__progress-item--active hunt__progress-item--completed");
19385            $(".js-hunt-progress .hunt__progress-item").each(function(index){
19386                if(step > index){
19387                    $(this).addClass("hunt__progress-item--completed");
19388                }
19389            });
19390            if(step > 0 && step < 4) {
19391                $(".js-hunt-progress").show();
19392                let active = $(".js-hunt-progress .hunt__progress-item")[step - 1];
19393                $(active).addClass("hunt__progress-item--active");
19394            }else {
19395                $(".js-hunt-progress").hide();
19396            }
19397            let percentage = (step / $(".js-hunt-progress .hunt__progress-item").length) * 100;
19398            $(".js-hunt-progress .hunt__progress-status").width(percentage + "%");
19399        };
19400
19401        function collectData(){
19402            let placement = $(".js-placement").val();
19403            if(placement === $(".js-placement").data("coordinates-placeholder")) {
19404                placement = $(".js-placement").data("coordinates");
19405            }
19406            let data = {
19407                address: placement,
19408                accessConditions: $(".js-access").val(),
19409                organizationCompany: $(".js-company").val(),
19410                reasons: [],
19411                attachments: []
19412            };
19413
19414            $(".js-reason-checkbox:checked").each(function(){
19415                data.reasons.push($(this).val());
19416            });
19417            if($(".js-reason").val() != ""){
19418                data.reasons.push($(".js-reason").val());
19419            }
19420            $(".js-image-file").each(function() {
19421                data.attachments.push($(this).data("image"));
19422            });
19423
19424            return data;
19425        }
19426
19427        //Warn user if the try to navigate away
19428        window.addEventListener('beforeunload', function (event) {
19429            if(!hasSubmitted) {
19430                event.preventDefault();
19431                return (event.returnValue = "");
19432            }
19433        });
19434
19435	};
19436
19437})(jQuery);;
19438; (function ($) {
19439
19440    window.vt = window.vt || {};
19441
19442    var options = {
19443        aspectRatio: 750 / 480,
19444        viewMode: 3,
19445        dragMode: 'move',
19446        responsive: false,
19447        modal: false,
19448        guides: false,
19449        center: false,
19450        highlight: false,
19451        background: true,
19452        cropBoxMovable: false,
19453        cropBoxResizable: false,
19454        toggleDragModeOnDblclick: false,
19455        minCropBoxWidth: 1000,// This number is high to ensure that the crop box is full size of the canvas
19456        crop: function (e) {
19457        }
19458    };
19459
19460    vt.initImageCropper = function (element) {
19461        var imageCroppers = $('.image-cropper');
19462
19463        var settings = {};
19464        var lastUsedId;
19465        settings.viewport = {};
19466        settings.image = { minWidth: 800, minHeight: 500 };
19467
19468        if (imageCroppers.length) {
19469
19470            $.each(imageCroppers, function (i, val) {
19471                var $scope = $(imageCroppers[i]),
19472                    $placeholder = $scope.parents('.aed-images');
19473
19474                if ($scope.hasClass('initiated')) {
19475                    return;
19476                }
19477
19478                // disable save button
19479                $scope.find('.image-cropper__result').prop('disabled', true)
19480
19481                // if 6 images then hide add new image button
19482                if ($placeholder.attr('data-images') >= '6') {
19483                    $placeholder.find('.aed-images__item-link').hide();
19484                }
19485
19486                // AJAX url request
19487                var url = $scope.attr('data-api');
19488
19489                $scope.addClass('initiated');
19490
19491                function saveImage(response, $elem) {
19492
19493                    // Remove encoding info from image
19494                    var image = response.split(';base64,')[1];
19495
19496                    // if find data-validate then validate
19497                    if ($scope.find('[data-validate]').length > 0) {
19498                        $.each($scope.find('[data-validate]'), function(index, validateContainer) {
19499                            var items = $(validateContainer).find('.js-validate');
19500
19501                            // on change of inputs
19502                            $(items).on('keydown', function(e) {
19503
19504                                // remove error message for input
19505                                $(e.target).next('.ui-state-error').remove();
19506                                $(e.target).removeClass('invalid');
19507                            });
19508
19509                            // each field with js-validate
19510                            $.each(items, function(index, item) {
19511
19512                                // if field is empty
19513                                if ($(item).val() === '') {
19514
19515                                    // clear up
19516                                    if ($(item).next().hasClass('ui-state-error')) {
19517                                        $(item).next().remove();
19518                                        $(item).removeClass('invalid');
19519                                    }
19520
19521                                    // insert error message
19522                                    var errorMessageText = $(item).attr('data-error');
19523                                    var errorMessage = '<p class="ui-state-error message">' + errorMessageText + '</p>';
19524                                    $(item).addClass('invalid');
19525                                    $(errorMessage).insertAfter($(item));
19526                                }
19527                            });
19528                        });
19529                    }
19530
19531                    if ($scope.find('.ui-state-error').length > 0) return;
19532
19533                    //Only append files to DOM if we are in the hunt flow
19534                    if($scope.attr('data-is-hunt') === "True"){
19535                        appendFileItem(response);
19536                        vt.modal.closeModal($scope.closest('.modal'), vt.imageCropper.onClose);
19537                        return
19538                    }
19539
19540                    // The AJAX object
19541                    var content = {
19542                        DefibrillatorId: $('body').find('[name="Defibr
19542illatorId"]').attr('value'),
19543                        SortOrder: $scope.attr('data-sorting-id'),
19544                        ImageId: $scope.attr('data-id'),
19545                        ImageBase64: image
19546                    };
19547
19548                    // get texts and put it into content
19549                    $.each($scope.find('[data-validate] .js-validate'), function(index, input) {
19550                        var name = $(input).attr('name');
19551                        var value = $(input).val();
19552
19553                        content[name] = value;
19554                    });
19555
19556                    var opts = {
19557                        contentType: "application/json;charset=utf-8",
19558                        data: JSON.stringify(content),
19559                        type: $placeholder.find('.image-cropper').attr('data-request-method'),
19560                        mimeType: 'text/html'
19561                    };
19562
19563                    vt.animations.feedbackEvaluate($elem);
19564
19565                    var call = window.vt.ajax.call(url, opts);
19566                    call.success(function (data) {
19567                        data = JSON.parse(data);
19568                        //var newImage = data.Url;
19569
19570                        vt.animations.feedbackApproved($elem);
19571
19572                        // Get current picture
19573                        var $currentImage = $('.js-current-profile-picture');
19574
19575                        // Apply the new image to the profile (Desktop and mobile)
19576                        // $currentImage[0].src = newImage;
19577                        // $('.dashboard__dog-profil-link').css('background-image', 'url("' + newImage + '")');
19578
19579                        // check if this is a new image been uploaded
19580                        if ($placeholder.find('.image-cropper').attr('data-request-method') === 'POST') {
19581
19582                            content.ImageId = data;
19583
19584                            // make html element from template
19585                            var htmlOutput = vt.templates.renderTemplate('cropperImage', content);
19586
19587                            // append new item to list
19588                            $(htmlOutput).insertBefore($placeholder.find('.aed-images__item-link'));
19589
19590                            // add event listener to new buttons on new element
19591                            $placeholder.find('.aed-images__edit-button').on('click', function(e) {
19592                                methodButton(e);
19593                            });
19594
19595                            // update number of images
19596                            $placeholder.attr('data-images', Number($placeholder.attr('data-images')) + 1);
19597
19598                            // if 6 images then hide add new image button
19599                            if ($placeholder.attr('data-images') >= '6') {
19600                                $placeholder.find('.aed-images__item-link').hide();
19601                            }
19602
19603                            // update modal sorting number to +1 of the sorting number of the last image
19604                            var $allImageItems = $placeholder.find('.aed-images__item');
19605                            $placeholder.find('.image-cropper').attr('data-sorting-id', Number($allImageItems.last().attr('data-sorting-id')) + 1);
19606
19607                        } else if ($placeholder.find('.image-cropper').attr('data-request-method') === 'PATCH') {
19608                            var currentElm = $('[data-id="' + content.ImageId + '"]');
19609
19610                           // inset updated text/image to editeded element
19611                           $(currentElm).find('.js-current-profile-picture').attr('src', 'data:image/png;base64, ' + content.ImageBase64);
19612                           $(currentElm).find('.form-element--heading').text(content.Name);
19613                           $(currentElm).find('.form-element--description').text(content.Text);
19614                       }
19615
19616                        // Wait for feedbackApproved animation
19617                        setTimeout(function () {
19618
19619                            // Reset button
19620                            vt.animations.feedbackReset($elem);
19621
19622                            // disable button again
19623                            $scope.find('.image-cropper__result').prop('disabled', true);
19624
19625                            vt.modal.closeModal($scope.closest('.modal'), vt.imageCropper.onClose);
19626                        }, 1000);
19627                    });
19628
19629                    call.error(function (data) {
19630                        vt.animations.feedbackError($elem);
19631                        $scope.find('.image-cropper__error.js-hidden').removeClass('js-hidden');
19632                    });
19633                }
19634
19635                function appendFileItem(image) {
19636                    $(".js-file-list").show().append("<div class='hunt__file-item js-image-file' data-image='" + image + "' >" + $('.modal .js-validate[name="Name"]').val() + "<div class='hunt__file-item-close'></div></div>")
19637                    $(".hunt__file-item-close").on("click", function(){
19638                        $(this).parent().remove();
19639                        updateFileCount();
19640                        if($(".hunt__file-item").length > 0) {
19641                            $(".js-file-list").show();
19642                        }else{
19643                            $(".js-file-list").hide();
19644                        }
19645                    });
19646                    updateFileCount();
19647                }
19648
19649                function updateFileCount() {
19650                    $(".hunt__file-count").text($(".hunt__file-item").length + " filer");
19651                }
19652
19653                // delete image
19654                function deleteImage(id) {
19655
19656                    // The AJAX object
19657                    var content = {
19658                        ImageId: id,
19659                        DefibrillatorId: $('body').find('[name="DefibrillatorId"]').attr('value')
19660                    };
19661
19662                    var opts = {
19663                        contentType: "application/json;charset=utf-8",
19664                        data: JSON.stringify(content),
19665                        type: 'DELETE',
19666                        mimeType: 'text/html'
19667                    };
19668
19669                    var call = window.vt.ajax.call(url, opts);
19670                    call.success(function (data) {
19671
19672                        // Get reference to image to delete
19673                        var $imageToDelete = $placeholder.find('[data-id="' + id + '"]');
19674
19675                        // Remove the image
19676                        $imageToDelete.remove();
19677
19678                        // Get reference to all image items
19679                        var $allImageItems = $placeholder.find('.aed-images__item');
19680
19681                        // Set next sorting id to one higher than last item
19682                        var highestSortOrder = $allImageItems.length ? parseInt($allImageItems.last().attr('data-sorting-id'), 10) : 0;
19683                        $placeholder.find('.image-cropper').attr('data-sorting-id', highestSortOrder + 1);
19684                        // update number of images
19685                        $placeholder.attr('data-images', Number($placeholder.attr('data-images')) - 1);
19686                        $placeholder.find('.aed-images__item-link').show();
19687                    });
19688                    call.error(function (data) {
19689
19690                        // show errorpopup
19691                        vt.initFormTools.serverError(data);
19692                    });
19693                }
19694
19695                var URL = window.URL || window.webkitURL;
19696                var $image = $scope.find('.image-cropper__cropping');
19697
19698                var originalImageURL = $image.attr('src');
19699                var uploadedImageType = 'image/jpeg';
19700                var uploadedImageURL;
19701
19702                // Add the default image to the cropper
19703                var currentImageData = '/assets/img/image-placeholder.png';
19704
19705                // Get the original size image
19706                $image.attr('src', currentImageData);
19707
19708                // Cropper
19709                $image.on({
19710                    //ready: function (e) {
19711                    //    console.log(e.type);
19712                    //},
19713                    //cropstart: function (e) {
19714                    //    console.log(e.type, e.action);
19715                    //},
19716                    //cropmove: function (e) {
19717                    //    console.log(e.type, e.action);
19718                    //},
19719                    //cropend: function (e) {
19720                    //    console.log(e.type, e.action);
19721                    //},
19722                    //crop: function (e) {
19723                    //    console.log(e.type, e.x, e.y, e.width, e.height, e.rotate, e.scaleX, e.scaleY);
19724                    //},
19725                    //zoom: function (e) {
19726                    //    console.log(e.type, e.ratio);
19727                    //}
19728                }).cropper(options);
19729
19730                // Buttons
19731                if (!$.isFunction(document.createElement('canvas').getContext)) {
19732                    $scope.find('button[data-method="getCroppedCanvas"]').prop('disabled', true);
19733                }
19734
19735                if (typeof document.createElement('cropper').style.transition === 'undefined') {
19736                    $scope.find('button[data-method="rotate"]').prop('disabled', true);
19737                    $scope.find('button[data-method="scale"]').prop('disabled', true);
19738                }
19739
19740                // change image
19741                function changeImage(id, sortingId, $elm) {
19742
19743                    // on change enable save button
19744                    $scope.find('.image-cropper__result').prop('disabled', false);
19745
19746                    // change request-method to UPDATE (PATCH)
19747                    $placeholder.find('.image-cropper').attr('data-request-method', 'PATCH');
19748
19749                    // change modal attr to editing image values
19750                    $placeholder.find('.image-cropper').attr('data-id', id);
19751                    $placeholder.find('.image-cropper').attr('data-sorting-id', sortingId);
19752
19753                    // get title and discription and inset into input fields
19754                    var title = $elm.find('.form-element--heading').text();
19755                    var description = $elm.find('.form-element--description').text();
19756
19757                    $placeholder.find('.modal .js-validate[name="Name"]').val(title);
19758                    $placeholder.find('.modal .js-validate[name="Text"]').val(description);
19759
19760                    // set current image to canvas
19761                    var currentImage = $elm.find('.js-current-profile-picture').attr('src');
19762                    $image.cropper('destroy').attr('src', currentImage).cropper(options);
19763
19764                    // be sure that modal is diplayed
19765                    $placeholder.find('.aed-images__item-link').show();
19766
19767                    // open modal
19768                    $placeholder.find('.modal').addClass('js-active');
19769                }
19770
19771                // edit/delete buttons fuction
19772                function methodButton(e) {
19773                    var method = $(e.target).attr('data-method');
19774                    var id = $(e.target).parents('[data-id]').attr('data-id');
19775                    var sortingId = $(e.target).parents('[data-sorting-id]').attr('data-sorting-id');
19776
19777                    e.preventDefault();
19778
19779                    // check with method and call that function
19780                    if (method === 'change') {
19781                        changeImage(id, sortingId,  $(e.target).parents('.aed-images__item'))
19782                    } else if (method === 'delete') {
19783                        //run only if the image hasn't been deleted yet
19784                        if (lastUsedId != id) {
19785                            deleteImage(id);
19786                            lastUsedId = id;
19787                        }
19788                        
19789                    }
19790                }
19791
19792                // Download
19793                // if (typeof $download[0].download === 'undefined') {
19794                //    $download.addClass('disabled');
19795                // }
19796
19797                // Options
19798                $('.docs-toggles').on('change', 'input', function () {
19799                    var $this = $(this);
19800                    var name = $this.attr('name');
19801                    var type = $this.prop('type');
19802                    var cropBoxData;
19803                    var canvasData;
19804
19805                    if (!$image.data('cropper')) {
19806                        return;
19807                    }
19808
19809                    if (type === 'checkbox') {
19810                        options[name] = $this.prop('checked');
19811                        cropBoxData = $image.cropper('getCropBoxData');
19812                        canvasData = $image.cropper('getCanvasData');
19813
19814                        options.ready = function () {
19815                            $image.cropper('setCropBoxData', cropBoxData);
19816                            $image.cropper('setCanvasData', canvasData);
19817                        };
19818                    } else if (type === 'radio') {
19819                        options[name] = $this.val();
19820                    }
19821
19822                    $image.cropper('destroy').cropper(options);
19823                });
19824
19825                $scope.on('input change', '.image-cropper__zoom-range', function () { // oninput does not work in IE, so we also added onchange
19826                    var method = $(this).attr('data-method');
19827                    var value = $(this).val();
19828                    $image.cropper(method, value);
19829                });
19830
19831                $image.on('zoom', function (e) {
19832                    $scope.find('.image-cropper__zoom-range').val(e.originalEvent.detail.ratio);
19833                });
19834
19835                $scope.on('ready', function (e) {
19836                    var newImage = {
19837                        height: e.target.naturalHeight,
19838                        width: e.target.naturalWidth
19839                    }
19840
19841                    var canvas = {
19842                        height: $('.cropper-container').height(),
19843                        width: $('.cropper-container').width()
19844                    }
19845
19846                    var ratio = {
19847                        height: canvas.height / newImage.height,
19848                        width: canvas.width / newImage.width
19849                    }
19850
19851                    // Get the biggest of the ratios, truncate it at 1 decimal, make it a number
19852                    ratio.biggest = Number(Math.max.apply(Math, [ratio.height, ratio.width]).toFixed(1));
19853
19854                    var uploadedImage = $image.attr('src') || '';
19855
19856                    var substring = 'blob:';
19857
19858                    // If the imageURL has 'blob', meaning it is an uploaded image
19859                    if (uploadedImage.indexOf(substring) !== -1) {
19860
19861                        // If the uploaded image is too small
19862                        if (newImage.height < settings.image.minHeight || newImage.width < settings.image.minWidth) {
19863
19864                            // Alert about the image being too small
19865                            alert('Billedet er for småt, brug venligst et billede større end 800px x 500px.');
19866
19867                            // Set image back to the current one
19868                            $image.cropper('destroy').attr('src', currentImageData).cropper(options);
19869                        }
19870                    }
19871
19872                    // Set zoom range minimum
19873                    $scope.find('.image-cropper__zoom-range').attr('min', ratio.biggest);
19874
19875                    // Set initial value of zoom range
19876                    $scope.find('.image-cropper__zoom-range').val(ratio.biggest);
19877                });
19878
19879                // Methods
19880                $scope.on('click', '[data-method]', function (e) {
19881                    e.preventDefault();
19882                    var $this = $(this);
19883                    var data = $this.data();
19884                    var cropper = $image.data('cropper');
19885                    var cropped;
19886                    var $target;
19887                    var result;
19888
19889                    if ($this.prop('disabled') || $this.hasClass('disabled')) {
19890                        return;
19891                    }
19892
19893                    if (cropper && data.method) {
19894                        data = $.extend({}, data); // Clone a new one
19895
19896                        if (typeof data.target !== 'undefined') {
19897                            $target = $(data.target);
19898
19899                            if (typeof data.option === 'undefined') {
19900                                try {
19901                                    data.option = JSON.parse($target.val());
19902                                } catch (e) {
19903                                    console.log(e.message);
19904                                }
19905                            }
19906                        }
19907
19908                        cropped = cropper.cropped;
19909
19910                        switch (data.method) {
19911                            case 'rotate':
19912                                if (cropped && options.viewMode > 0) {
19913                                    $image.cropper('clear');
19914                                }
19915
19916                                break;
19917
19918                            case 'getCroppedCanvas':
19919                                if (uploadedImageType === 'image/jpeg') {
19920                                    if (!data.option) {
19921                                        data.option = {};
19922                                    }
19923
19924                                    data.option.fillColor = '#fff';
19925                                }
19926
19927                                break;
19928                        }
19929
19930                        result = $image.cropper(data.method, data.option, data.secondOption);
19931
19932                        switch (data.method) {
19933                            case 'rotate':
19934                                if (cropped && options.viewMode > 0) {
19935                                    $image.cropper('crop');
19936                                }
19937
19938                                break;
19939
19940                            case 'scaleX':
19941                            case 'scaleY':
19942                                $(this).data('option', -data.option);
19943                                break;
19944
19945                            case 'getCroppedCanvas':
19946                                if (result) {
19947                                    saveImage(result.toDataURL(uploadedImageType), $this);
19948                                }
19949
19950                                break;
19951
19952                            case 'destroy':
19953                                if (uploadedImageURL) {
19954                                    URL.revokeObjectURL(uploadedImageURL);
19955                                    uploadedImageURL = '';
19956                                    $image.attr('src', originalImageURL);
19957                                }
19958
19959                                break;
19960                        }
19961
19962                        if ($.isPlainObject(result) && $target) {
19963                            try {
19964                                $target.val(JSON.stringify(result));
19965                            } catch (e) {
19966                                console.log(e.message);
19967                            }
19968                        }
19969
19970                    }
19971                });
19972
19973                // current image methods
19974                $placeholder.find('.aed-images__edit-button').on('click', function(e) {
19975                    methodButton(e);
19976                });
19977
19978                // Import image
19979                var $inputImage = $scope.find('.image-cropper__upload');
19980
19981                if (URL) {
19982                    $inputImage.change(function () {
19983                        var files = this.files;
19984                        var file;
19985
19986                        // enable save button again
19987                        $scope.find('.image-cropper__result').prop('disabled', false);
19988
19989                        if (!$image.data('cropper')) {
19990                            return;
19991                        }
19992
19993                        if (files && files.length) {
19994                            file = files[0];
19995
19996                            if (/^image\/\w+$/.test(file.type)) {
19997                                uploadedImageType = file.type;
19998
19999                                if (uploadedImageURL) {
20000                                    URL.revokeObjectURL(uploadedImageURL);
20001                                }
20002
20003                                uploadedImageURL = URL.createObjectURL(file);
20004                                $image.cropper('destroy').attr('src', uploadedImageURL).cropper(options);
20005                                $inputImage.val('');
20006                            } else {
20007                                window.alert('Vælg venligst et billede.');
20008                            }
20009                        }
20010                    });
20011                } else {
20012                    $inputImage.prop('disabled', true).parent().addClass('disabled');
20013                }
20014            });
20015        }
20016    };
20017
20018    // Wrapping methods in a module
20019    vt.imageCropper = (function (e) {
20020
20021        var onOpen = function ($element) {
20022
20023        }
20024
20025        var onClose = function ($element) {
20026
20027            // change request-method back to POST
20028            $element.find('.image-cropper').attr('data-request-method', 'POST');
20029
20030            // reset title and description
20031            $element.find('.js-validate[name="Name"]').val('');
20032            $element.find('.js-validate[name="Text"]').val('');
20033
20034            // Reset file input
20035            var $fileInput = $element.find('.image-cropper__upload');
20036            $fileInput.val('');
20037
20038            // reset image to default
20039            var $image = $element.find('.image-cropper__cropping');
20040            var currentImageData = '/assets/img/image-placeholder.png';
20041            $image.cropper('destroy').attr('src', currentImageData).cropper(options);
20042
20043            // Hide error message
20044            $element.find('.image-cropper__error').addClass('js-hidden');
20045
20046            // if 6 images then hide add new image button
20047            if ($element.parents('.aed-images').attr('data-images') >= '6') {
20048                $element.parents('.aed-images').find('.aed-images__item-link').hide();
20049            }
20050        }
20051
20052        return {
20053            onOpen: onOpen,
20054            onClose: onClose
20055        }
20056
20057    })();
20058
20059})(jQuery);
20060;
20061; (function ($) {
20062
20063    window.vt = window.vt || {};
20064
20065    if ($('.pageedit').length > 0) {
20066        return;
20067    }
20068
20069    vt.inView = function () {
20070        if ($(".info-cards.news").length) {
20071
20072            $("").addClass("inview");
20073        }
20074
20075        $('.card-wrap').bind('inview', function (event, isInView, visiblePartX, visiblePartY) {
20076            if (isInView) {
20077                $(this).addClass('inview');
20078            }
20079        });
20080
20081        $('.social-spot, .wysiwyg p > img, .wysiwyg .quote, .wysiwyg blockquote, .cardboard-figure, #main > .video-section, .step-list .video-wrapper, .step-list .container > img').bind('inview', function (event, isInView) {
20082            if (isInView) {
20083                $(this).addClass('inview');
20084            }
20085        });
20086    };
20087
20088})(jQuery);;
20089(function initMenu($) {
20090  var $html = $("html"),
20091    $mobileMenuButton = $(".c-mobile-nav-button--menu"),
20092    $mobileSearchButton = $(".c-mobile-nav-button--search"),
20093    $searchGlobal = $("#search-global"),
20094    $mainmenu = $(".main-menu"),
20095    $submenus = $(".main-menu > li"),
20096    $submenus = $submenus.filter(function (i, elem) {
20097      return $(elem).children().first().is(".main-menu-dropdown");
20098    });
20099  $submenuitems = $(".main-menu li li");
20100
20101  var toggleSearch = function () {
20102    if ($html.hasClass("main-menu--open")) {
20103      toggleMenu();
20104    }
20105    $html.toggleClass("c-site-search--open");
20106    if ($mobileSearchButton.attr("aria-expanded") === "true") {
20107      $mobileSearchButton.attr("aria-expanded", "false");
20108    } else {
20109      $mobileSearchButton.attr("aria-expanded", "true");
20110      setTimeout(function () {
20111        console.log($searchGlobal);
20112        $searchGlobal.focus();
20113      }, 100);
20114    }
20115  };
20116
20117  var toggleMenu = function () {
20118    if ($html.hasClass("c-site-search--open")) {
20119      toggleSearch();
20120    }
20121    $html.toggleClass("main-menu--open");
20122    if ($mobileMenuButton.attr("aria-expanded") === "true") {
20123      $mobileMenuButton.attr("aria-expanded", "false");
20124    } else {
20125      $mobileMenuButton.attr("aria-expanded", "true");
20126    }
20127    $mobileMenuButton.attr("");
20128    $mainmenu.toggleClass("open");
20129    $submenus.removeClass("open");
20130  };
20131
20132  var toggleSubMenu = function (e) {
20133    var $sub = $(e.target).closest("li");
20134    if (
20135      window.innerWidth < 800 &&
20136      !$sub.hasClass("open") &&
20137      $sub.children("ul").length
20138    ) {
20139      $submenus.removeClass("open");
20140      $sub.addClass("open");
20141      e.preventDefault();
20142    }
20143  };
20144
20145  var followSub = function (e) {
20146    e.stopPropagation();
20147  };
20148
20149  $mobileSearchButton.on("click", toggleSearch);
20150  $mobileMenuButton.on("click", toggleMenu);
20151  $submenus.on("click", toggleSubMenu);
20152  $submenuitems.on("click", followSub);
20153})(jQuery);
20154;
20155; (function ($) {
20156
20157    // Declaring namespace if not already declared
20158    window.vt = window.vt || {};
20159
20160    // Wrapping methods in a module
20161    vt.initLoginPanel = function (element) {
20162
20163        if ($('body').is('.pageedit'))
20164            return;
20165
20166        // Variables
20167        var oUser = {},
20168            sMypageUrl = $('body').data('mypage-url'),
20169            oBtn = $(".login-action");
20170
20171        // init progress btn plugin ToDo: move to other file???
20172        $('.progress-button').progressInitialize();
20173
20174        // Toggle login panel
20175        var togglePanel = function (e) {
20176            // Toggle Nav on Click
20177
20178            if (e) {
20179                e.preventDefault();
20180            }
20181
20182            if ($('body').hasClass('show-panel')) {
20183
20184                $('body').toggleClass('show-panel');
20185                $('body').toggleClass('show-panel-reset');
20186
20187
20188                setTimeout(function () {
20189                    $('body').toggleClass('show-panel-reset');
20190
20191                    $('body').removeClass('show-panel').one("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function () {
20192                        $("#login-panel").addClass("invisible");
20193                        clearLoginForm();
20194                        if ($(".login-message").hasClass("error")) {
20195                            $(".login-message").hide();
20196                        }
20197                    });
20198
20199
20200
20201                }, 10);
20202
20203
20204
20205            } else {
20206                $("html, body").animate({
20207                    scrollTop: 0
20208                }, 300)
20209                $("#login-panel").removeClass("invisible");
20210                $('body').toggleClass('show-panel-reset');
20211                setTimeout(function () {
20212                    $('body').toggleClass('show-panel-reset');
20213                    $('body').toggleClass('show-panel');
20214                }, 10);
20215            }
20216
20217            return false;
20218        };
20219
20220        // Login
20221        var loginAction = function (e) {
20222
20223            e.preventDefault();
20224
20225            oUser.Username = $('#user-email').val();
20226            oUser.Password = $('#user-password').val();
20227            //oBtn = $(this);
20228
20229            oBtn.progressStart();
20230
20231            //getData("/api/sag/login", oUser)
20232            //    .done(function (msg) {
20233            //        console.log("Login done");
20234            //        oBtn.progressFinish().one("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function () {
20235            //            console.log("Login 1");
20236            //            //$('.toggle-login').toggleClass("fade-out").one("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function () {
20237            //                console.log("Login 2");
20238            //                $("#username").text(msg);
20239            //                $(".user-menu").toggleClass("fade-out");
20240            //                $('body').removeClass('show-panel').one("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function () {
20241            //                    //window.location.href = sMypageUrl;
20242            //                });
20243            //                console.log("Login 2.2");
20244            //            //});
20245            //            console.log("Login 1.2");
20246            //        });
20247            //        console.log("Login end");
20248
20249            //      //  setTimeout(function () {
20250
20251
20252            //        //}, 500);
20253
20254            //    })
20255            //    .fail(function (msg) {
20256            //        // Msg to login panel
20257            //    })
20258            //    .always(function (msg) {
20259
20260            //    });
20261
20262            function ajaxLogin() {
20263                return $.ajax({
20264                    type: 'POST',
20265                    url: "/api/sag/login",
20266                    data: JSON.stringify(oUser),
20267                    contentType: "application/json;charset=utf-8",
20268                    dataType: "json",
20269                    statusCode: {
20270                        200: function (msg) {
20271
20272                            oBtn.progressFinish();
20273
20274                            // Track login event
20275                            if (window.vt && window.vt.functions && window.vt.functions.pushToDataLayer) {
20276                                vt.functions.pushToDataLayer({
20277                                    event: "login",
20278                                    user_id: window.vt.userId || ""
20279                                });
20280                            }
20281
20282                            setTimeout(function () {
20283                                $(".toggle-login").addClass("hidden");
20284                                $("#username").text(msg);
20285                                $(".user-menu").toggleClass("hidden");
20286                                $('body').removeClass('show-panel').one("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function () {
20287                                    $("#login-panel").addClass("invisible");
20288                                    window.location.href = sMypageUrl;
20289                                });
20290                            }, 800);
20291                        },
20292                        401: function () {
20293                            oBtn.progressFinish();
20294                            clearLoginForm();
20295                            $(".login-message").show(); // needs to be generic msg...
20296                        }
20297                    }
20298                });
20299
20300            };
20301            ajaxLogin();
20302        };
20303
20304        // Trigger login
20305        var simClickLoginAction = function () {
20306            $(".login-action").click();
20307        };
20308
20309        // Clear form
20310        var clearLoginForm = function () {
20311            $("#user-password").val("");
20312            oUser.Username = "";
20313            oUser.Password = "";
20314            if (oBtn.hasClass("finished")) {
20315                oBtn.removeClass("finished");
20316            }
20317        };
20318
20319        // Logout
20320        var logoutAction = function (e) {
20321
20322            e.preventDefault();
20323
20324            //getData("/api/sag/logout")
20325
20326            //    // why is .done not working...
20327            //    .done(function (msg) {
20328            //        console.log("Logout done1.1");
20329            //        //$('.user-menu').toggleClass("fade-out").one("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function () {
20330            //            console.log("Logout done2.1");
20331            //            $("#username").text("");
20332            //            $(".toggle-login").toggleClass("fade-out").promise().done(function () {
20333            //                //window.location.href = "/";
20334            //            });
20335            //            console.log("Logout done2.2");
20336            //        //});
20337            //        console.log("Logout done1.2");
20338            //    })
20339            //    .fail(function (msg) {
20340            //        // Msg to login panel
20341            //    })
20342            //    .always(function (msg) {
20343            //        // why...?
20344            //        //console.log("Logout always1.1");
20345            //        ////$('.user-menu').toggleClass("fade-out").one("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function () {
20346            //        //    console.log("Logout always2.1");
20347            //        //    $("#username").text("");
20348            //        //    $(".toggle-login").toggleClass("fade-out").promise().done(function () {
20349            //        //        //window.location.href = "/";
20350            //        //    });
20351            //        //    console.log("Logout always2.2");
20352            //        ////});
20353            //        //console.log("Logout always1.2");
20354            //    });
20355            function ajaxLogout() {
20356                return $.ajax({
20357                    type: 'POST',
20358                    url: "/api/sag/logout",
20359                    //data: JSON.stringify(oUser),
20360                    contentType: "application/json;charset=utf-8",
20361                    dataType: "json",
20362                    statusCode: {
20363                        200: function (msg) {
20364                            // Track logout event
20365                            if (window.vt && window.vt.functions && window.vt.functions.pushToDataLayer) {
20366                                vt.functions.pushToDataLayer({
20367                                    event: "logout",
20368                                    user_id: window.vt.userId || ""
20369                                });
20370                            }
20371
20372                            $('.user-menu').toggleClass("fade-out").one("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function () {
20373                                $("#username").text("");
20374                                $(".user-menu").addClass("hidden");
20375                                $(".toggle-login").toggleClass("fade-out");
20376                            });
20377                            window.location.href = "/";
20378                        },
20379                        401: function (msg) {
20380                            $(".login-message").show(); // needs to be generic msg...
20381                        }
20382                    }
20383                });
20384
20385            };
20386            ajaxLogout();
20387
20388        };
20389
20390        // Forgot password
20391        var forgotPwd = function (e) {
20392
20393            e.preventDefault();
20394
20395            $('.login-form, .forget-pw-form').toggleClass("hidden");
20396
20397            return false;
20398
20399        };
20400
20401        // Forgot password
20402        $('#forgot-pw-btn').click(function (e) {
20403
20404        });
20405
20406        // Get data with Ajax
20407        //var getData = function (url, params) {
20408        //    return $.ajax({
20409        //        type: 'POST',
20410        //        url: url,
20411        //        data: JSON.stringify(params),
20412        //        contentType: "application/json;charset=utf-8",
20413        //        dataType: "json",
20414        //        statusCode: {
20415        //            401: function () {
20416        //                $(".login-message").show(); // needs to be generic msg...
20417        //            }
20418        //        }
20419        //    });
20420
20421        //};
20422
20423        // check for Safari
20424        if (navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1) {
20425            $("html").addClass("safari");
20426        }
20427
20428        // Is IE 10 or 11
20429        // IE 10 only CSS properties
20430        var ie10Styles = [
20431            'msTouchAction'];
20432
20433        var ie11Styles = [
20434            'msTextCombineHorizontal'];
20435
20436        /*
20437         * Test all IE only CSS properties
20438         */
20439        var ieVersion = null;
20440        var property;
20441
20442        // Test IE10 properties
20443        for (var i = 0; i < ie10Styles.length; i++) {
20444            property = ie10Styles[i];
20445
20446            if (document.body.style[property] != undefined) {
20447                ieVersion = "ie10";
20448            }
20449        }
20450
20451        // Test IE11 properties
20452        for (var i = 0; i < ie11Styles.length; i++) {
20453            property = ie11Styles[i];
20454
20455            if (document.body.style[property] != undefined) {
20456                ieVersion = "ie11";
20457            }
20458        }
20459
20460        if (ieVersion) {
20461            $("html").addClass(ieVersion);
20462        }
20463        // Is IE 10 or 11
20464
20465
20466        // Event listensers
20467        // Mouse click
20468        $('.toggle-login, .close-login').on('click', togglePanel);
20469        $('.login-action').on('click', loginAction);
20470        $('.logout-action').on('click', logoutAction);
20471        $('.forgot-pwd').on('click', forgotPwd);
20472
20473        // Keyboard
20474        $("#user-password").keydown(function (e) {
20475            if (e.which === 13) { // Enter key
20476                simClickLoginAction();
20477            }
20478        });
20479
20480
20481        // Making methods public
20482        return {
20483            //togglePanel: togglePanel,
20484            //loginAction: loginAction,
20485            //loginActionKeyEnter: loginActionKeyEnter,
20486            //logoutAction: logoutAction,
20487            //forgotPwd: forgotPwd
20488        };
20489    };
20490
20491    // Naming the module
20492    //vt.initLoginPanel = initLoginPanel;
20493
20494})(jQuery);;
20495; (function ($) {
20496
20497    window.vt = window.vt || {};
20498
20499    vt.initLogin = function () {
20500
20501        vt.showCookieMessage('loginPageMsg', true); //Any cookie messages to show? If any show them and delete them afterwards
20502
20503        $('#email').val(Cookies.get('loginUserName'));
20504    }
20505
20506})(jQuery);;
20507; (function ($) {
20508    'use strict';
20509
20510    window.vt = window.vt || {};
20511
20512    vt.initMastHead = function (element) {
20513        var $el = $(element);
20514        var url = $el.data('bg-image');
20515
20516        if (!url) {
20517            return;
20518        }
20519
20520        var encodedUrl = encodeURI(url);
20521        var currentBg = $el.css('background-image');
20522
20523        // Only set background if not already set or different
20524        if (!currentBg || currentBg === 'none' || currentBg.indexOf(encodedUrl) === -1) {
20525            $el.css('background-image', 'url(' + encodedUrl + ')');
20526
20527            setTimeout(function () {
20528                $el.addClass('bg-loaded');
20529            }, 500);
20530        }
20531    };
20532
20533})(jQuery);
20534;
20535; (function ($) {
20536
20537    window.vt = window.vt || {};
20538
20539    vt.initMobileTools = function (elem) {
20540
20541        var title = $(elem).data('nav-title');
20542
20543        $('<a href="#subnav" class="nav-jump">' + title + '  <i data-icon="a" class="icon"></i></a>')
20544            .insertBefore('.page-left')
20545            .on('click', function () {
20546                $('html, body').animate({
20547                    scrollTop: $('#subnav').offset().top
20548                }, 300);
20549            });
20550
20551    };
20552
20553})(jQuery);;
20554; (function ($) {
20555
20556    window.vt = window.vt || {};
20557
20558    vt.initModal = function (element) {
20559
20560        var $scope = $(element);
20561
20562        var onOpen = $scope.attr('data-modal-callback-open'),
20563            fOnOpen = vt.functions.evaluator(onOpen),
20564
20565            onClose = $scope.attr('data-modal-callback-close'),
20566            fOnClose = vt.functions.evaluator(onClose);
20567
20568        // Event listeners
20569        $scope.find('.modal__open').on('click', function (e) {
20570            e.preventDefault();
20571            vt.modal.openModal($scope, fOnOpen);
20572        });
20573
20574        $scope.find('.modal__overlay').on('click', function (e) {
20575            vt.modal.closeModal($scope, fOnClose);
20576        });
20577
20578        $scope.find('.modal__close').on('click', function () {
20579            vt.modal.closeModal($scope, fOnClose);
20580        });
20581
20582        $(document).keyup(function (e) {
20583            if (e.keyCode == 27) { // escape key maps to keycode `27`
20584                vt.modal.closeModal($scope, fOnClose);
20585            }
20586        });
20587
20588        $scope.find('.modal__content').on('click', function (e) {
20589            e.stopPropagation();
20590        });
20591
20592        $scope.find('.modal__open').hover(function () {
20593            $scope.addClass('prepare-animation');
20594        }, function () {
20595            $scope.removeClass('prepare-animation');
20596        });
20597    };
20598
20599    vt.modal = (function () {
20600
20601        var openModal = function ($element, callback) {
20602
20603            // make html unscrollable
20604            $('html, body').addClass('no-scroll'); // TODO: This repaints the whole page, because scroll bar is removed. This should be fixed if possible. For now I will make a timeout on the animation so that it animates better.
20605
20606            // Prepare browser to run animation
20607            $element.addClass('prepare-animation');
20608
20609            // Reset scroll position in modal
20610            $element.find('.modal__content').scrollTop(0);
20611
20612            // Wait for repaint because scrollbar was removed, to prevent jank in animation
20613            setTimeout(function () {
20614
20615                // Open modal
20616                $element.addClass('js-active');
20617
20618                // Wait until animations has finished
20619                setTimeout(function () {
20620
20621                    // Remove animation preparation class
20622                    $element.removeClass('prepare-animation');
20623
20624                    if (callback) {
20625
20626                        // Call back
20627                        callback($element);
20628                    }
20629                }, 800);
20630            }, 200);
20631        }
20632
20633        var closeModal = function ($element, callback) {
20634
20635            // Close modal
20636            $element.removeClass('js-active');
20637
20638            // Wait until animations has finished
20639            setTimeout(function () {
20640
20641                // Make html scrollable again
20642                $('html, body').removeClass('no-scroll');
20643
20644                if (callback) {
20645
20646                    // Call back
20647                    callback($element);
20648                }
20649            }, 800);
20650        }
20651
20652        return {
20653            openModal: openModal,
20654            closeModal: closeModal
20655        }
20656    })();
20657
20658})(jQuery);
20659;
20660; (function ($) {
20661
20662    window.vt = window.vt || {};
20663
20664    vt.initMyPage = function (element) {
20665
20666        // Move to general utility module
20667        function whichTransitionEvent() {
20668            var t,
20669                el = document.createElement("fakeelement");
20670
20671            var transitions = {
20672                "transition": "transitionend",
20673                "OTransition": "oTransitionEnd",
20674                "MozTransition": "transitionend",
20675                "WebkitTransition": "webkitTransitionEnd"
20676            }
20677
20678            for (t in transitions) {
20679                if (el.style[t] !== undefined) {
20680                    return transitions[t];
20681                }
20682            }
20683        }
20684
20685        function whichAnimationEvent() {
20686            var t,
20687                el = document.createElement("fakeelement");
20688
20689            var animations = {
20690                "animation": "animationend",
20691                "OAnimation": "oAnimationEnd",
20692                "MozAnimation": "animationend",
20693                "WebkitAnimation": "webkitAnimationEnd"
20694            }
20695
20696            for (t in animations) {
20697                if (el.style[t] !== undefined) {
20698                    return animations[t];
20699                }
20700            }
20701        }
20702
20703        var transitionEvent = whichTransitionEvent();
20704        var animationEvent = whichAnimationEvent();
20705
20706        var applicationID;
20707
20708        vt.showCookieMessage('myPageMsg', true); //Any cookie messages to show? If any show them and delete them afterwards
20709
20710        // handle login
20711        $('.delete').click(function (e) {
20712
20713            e.preventDefault();
20714
20715            applicationID = $(this).data("applicationid");
20716
20717            showDeleteConfirmMessage();
20718        });
20719
20720        $(document).on('click', '.btnConfirmDeleteApp', function (event) {
20721
20722            var oBtn = $(this),
20723                oLi = $("li").filter(function () { return $(this).data().applicationid == a
20723pplicationID; });
20724            
20725            oBtn.progressInitialize().progressStart();
20726
20727            // add event listener for animation end
20728            oLi.one(animationEvent, function (event) {
20729                oLi.slideToggle(function () {
20730                    $(this).remove();
20731                });
20732            });
20733
20734            $.ajax({
20735                type: 'DELETE',
20736                url: "/api/sag/application/" + applicationID
20737            })
20738            .done(function (msg) { 
20739                oBtn.progressFinish().promise().done(function () {
20740                    vt.userMessageHide();
20741
20742                    setTimeout(function () {
20743                        oLi.addClass('yellow-fade');
20744
20745                        if ($('html').is('.ie8, .ie9')) {
20746                            oLi.slideToggle();
20747                        }
20748
20749                    }, 500);
20750                    
20751                });                    
20752            });
20753
20754            event.preventDefault();
20755        });
20756
20757        $(document).on('click', '.btnCancelDeleteApp', function (event) {
20758            vt.userMessageHide();
20759            event.preventDefault();
20760        });
20761
20762        function showDeleteConfirmMessage() {
20763            var msgConfig = {
20764                container: $('body'),
20765                content: $('#confirm-dialog').html(),
20766                addOverlay: true
20767            };
20768            vt.userMessage(msgConfig);
20769        };
20770
20771    };
20772
20773})(jQuery);;
20774; (function ($) {
20775
20776    window.vt = window.vt || {};
20777    vt.initParallax = function (element) {
20778        var runParallax = function () {
20779            var image = $(".parallax-container").attr("data-image-src");
20780            $('.parallax-container').parallax({ imageSrc: image });
20781        }
20782
20783        runParallax();
20784
20785        var TO = false;
20786        $(window).resize(function () {
20787            if (TO !== false) {
20788                clearTimeout(TO);
20789            }
20790            TO = setTimeout(function () {
20791                $(window).trigger('resize.px.parallax')
20792            }, 1000); //1000 is time in miliseconds
20793        });
20794    };
20795
20796})(jQuery);;
20797; (function ($) {
20798
20799    window.vt = window.vt || {};
20800
20801    vt.initProfileSlider = function () {
20802        var $selector = $(".random-candidates");
20803        var p_standard = $("ol.candidates-paging-standard");
20804        var p_fallback = $("div.candidates-paging-fallback");
20805
20806        //  Paging update:
20807        var _updtPD = function (currentPage, pageCount) {
20808            if (p_standard.find("li").length != pageCount) {
20809                p_standard.empty();
20810                for (var i = 0; i < pageCount; i++) {
20811                    p_standard.append("<li>&nbsp;</li>");
20812                }
20813                p_standard.find("li").on("click", function () {
20814                    $selector.yass("scrollToPage", $(this).index());
20815                });
20816            }
20817            else p_standard.find("li").removeClass("current");
20818            p_standard.find("li:nth-child(" + currentPage + ")").addClass("current");
20819            p_fallback.html(currentPage + " / " + pageCount);
20820
20821            if (pageCount > 10) {
20822                p_standard.hide();
20823                p_fallback.show();
20824            }
20825            else {
20826                p_standard.show();
20827                p_fallback.hide();
20828            }
20829        };
20830
20831        //  Yass init:
20832        $selector.yass(
20833            {
20834                selectors:
20835                  {
20836                      content: ".random-candidates-list",
20837                      viewport: ".random-candidates-viewport",
20838                      prev: ".candidates-nav-prev",
20839                      next: ".candidates-nav-next"
20840                  },
20841                pageChange: function(yass){
20842                    _updtPD(
20843                        Math.ceil(yass.currentPage()) + 1,
20844                        Math.ceil(yass.pageCount())
20845                    );
20846                }
20847            });
20848
20849        //  Resize handler:
20850        $(window).on('resize', function () {
20851            $selector.yass("refresh");
20852            var pCount = Math.ceil($selector.yass("pageCount"));
20853            var cPage = Math.ceil($selector.yass("currentPage")) + 1;
20854            _updtPD(cPage, pCount);
20855        });
20856        init = true;
20857
20858    };
20859
20860})(jQuery);;
20861(function ($) {
20862  window.vt = window.vt || {};
20863  vt.quizzes = [];
20864
20865  var QUESTION_BASE = "quiz__question";
20866  var QUESTION_ACTIVE = QUESTION_BASE + "--active";
20867  var QUESTION_INTRO = QUESTION_BASE + "--intro";
20868  var QUESTION_RESULT = QUESTION_BASE + "--result";
20869
20870  var PAGINATION_BASE = "quiz__pagination";
20871  var PAGINATION_ITEM = PAGINATION_BASE + "-item";
20872  var PAGINATION_ACTIVE = PAGINATION_ITEM + "--active";
20873
20874  var RESPONSE_BASE = "quiz__response";
20875  var RESPONSE_CORRECT = RESPONSE_BASE + "--correct";
20876  var RESPONSE_INCORRECT = RESPONSE_BASE + "--incorrect";
20877
20878  /**
20879   * @class
20880   * @name QuizNewsletter
20881   * @param {HTMLElement} quizContainer
20882   */
20883  function QuizNewsletter(quizContainer) {
20884    this.form = $("#mc-embedded-subscribe-form");
20885    this.form.on("submit", this.submit.bind(this));
20886    this.container = $(quizContainer).find(".quiz__newsletter");
20887    this.inputs = this.form.find("input");
20888    this.jsonObj = {};
20889    this.jsonString = null;
20890  }
20891
20892  /**
20893   * @method
20894   * @name QuizNewsletter#ajaxCall
20895   */
20896  QuizNewsletter.prototype.ajaxCall = function () {
20897    var quizNewsletter = this;
20898    $.ajax({
20899      url: quizNewsletter.form.attr("data-api"),
20900      type: "POST",
20901      contentType: "application/json;charset=utf-8",
20902      data: quizNewsletter.jsonString,
20903    })
20904      .done(function (data) {
20905        // Thank you message should display regardless of whether checkbox is checked or not
20906        quizNewsletter.container.find(".quiz__newsletter--confirm").show();
20907        quizNewsletter.container.find("form").hide();
20908      })
20909      .error(function (data) {
20910        if (!data.responseJSON.UserMessage) {
20911          data.responseJSON = {
20912            UserMessage: "Noget gik galt, prøv igen senere",
20913          };
20914        }
20915        // clear up
20916        var FORM_ERROR_SELECTOR = ".form-element-popup-error";
20917        var $oldFormErrorElem = $(FORM_ERROR_SELECTOR);
20918        $oldFormErrorElem.remove();
20919
20920        $("body").append(
20921          '<div class="cookie-disclaimer form-element-popup-error">' +
20922          data.responseJSON.UserMessage +
20923          "</div>"
20924        );
20925        var $newFormErrorElem = $(FORM_ERROR_SELECTOR).css("bottom", "-50px");
20926        $newFormErrorElem.css("display", "block");
20927        setTimeout(function () {
20928          $newFormErrorElem.css("bottom", "0px");
20929        }, 500);
20930        setTimeout(function () {
20931          $newFormErrorElem.css("bottom", "-50px");
20932        }, 5000);
20933      });
20934  };
20935
20936  /**
20937   * @method
20938   * @name QuizNewsletter#constructJSON
20939   */
20940  QuizNewsletter.prototype.constructJSON = function () {
20941    var form = this.form;
20942    var inputs = this.inputs;
20943    var jsonObj = this.jsonObj;
20944    $.each(this.inputs, function (index, input) {
20945      var $input = $(input);
20946      // if checkbox dont send it
20947      if ($input.attr("type") === "checkbox") return;
20948
20949      // if last input field
20950      if (index + 1 === inputs.length) return;
20951
20952      // set values into jsonObj
20953      if ($input.attr("name") !== "") {
20954        jsonObj[$input.attr("name")] = $input.val();
20955      }
20956    });
20957    this.jsonString = JSON.stringify(jsonObj);
20958  };
20959
20960  /**
20961   * @method
20962   * @name QuizNewsletter#signup
20963   */
20964  QuizNewsletter.prototype.signup = function (e) {
20965    this.constructJSON();
20966    this.ajaxCall();
20967  };
20968
20969  /**
20970   * @method
20971   * @name QuizNewsletter#submit
20972   */
20973  QuizNewsletter.prototype.submit = function (e) {
20974    if (!this.form.find('input[type="checkbox"]').is(":checked")) {
20975      e.preventDefault();
20976    }
20977    this.signup();
20978  };
20979
20980  function QuizPagination(items) {
20981    this.items = items;
20982  }
20983
20984  function QuizQuestions(container, quiz) {
20985    this.quiz = quiz;
20986    this.questions = [];
20987    var questions = container.querySelectorAll("." + QUESTION_BASE);
20988    for (var i = 0; i < questions.length; i += 1) {
20989      this.questions.push(
20990        new QuizQuestion(questions[i], i, quiz, questions.length)
20991      );
20992    }
20993  }
20994
20995  function QuizQuestion(container, index, quiz, numQuestions) {
20996    this.correct = null;
20997    this.quiz = quiz;
20998    this.index = index;
20999    this.nextIndex = index + 1;
21000    this.isIntro = index === 0;
21001    this.isFirstQuestion = index === 1;
21002    this.isLastQuestion = index === numQuestions - 1;
21003    this.container = container;
21004    this.nextButtons = this.container.querySelectorAll(
21005      "button.btn:not(#signup-button)"
21006    );
21007    this.radios = this.container.querySelectorAll(
21008      '.quiz__options input[type="radio"]'
21009    );
21010    this.rightAnswerRadios = [].slice
21011      .call(this.radios)
21012      .filter(function (radio) {
21013        return radio.value === "1";
21014      });
21015    this.timeSpentOnQuestion = 0;
21016    this.timeInterval = null;
21017    this.attachNextButtonClick();
21018    this.attachRadioButtonChange();
21019  }
21020
21021  QuizQuestion.prototype.checkAnswer = function (e) {
21022    this.stopVideo();
21023
21024    var checkedRadio = e.target;
21025    var container = this.container;
21026    this.correct = this.rightAnswerRadios.includes(checkedRadio) ? true : false;
21027
21028    var correctClass = this.correct
21029      ? QUESTION_BASE + "--correct"
21030      : QUESTION_BASE + "--incorrect";
21031    var responseClass = this.correct ? RESPONSE_CORRECT : RESPONSE_INCORRECT;
21032
21033    this.container.classList.add(correctClass);
21034    if (this.correct) {
21035      this.quiz.correctResponses.push(this);
21036    } else {
21037      this.quiz.incorrectResponses.push(this);
21038    }
21039    setTimeout(function () {
21040      container
21041        .querySelector("." + responseClass)
21042        .classList.remove("noopacity");
21043    }, 100);
21044    for (var i = 0; i < this.radios.length; i++) {
21045      if (
21046        this.rightAnswerRadios.includes(this.radios[i]) &&
21047        this.radios[i] === checkedRadio
21048      ) {
21049        this.radios[i].classList.add("correct");
21050      } else if (this.radios[i] === checkedRadio) {
21051        this.radios[i].classList.add("incorrect");
21052      }
21053    }
21054    [].forEach.call(this.radios, function (element) {
21055      element.disabled = true;
21056    });
21057    [].forEach.call(this.nextButtons, function (nextButton) {
21058      nextButton.removeAttribute("disabled");
21059    });
21060  };
21061
21062  QuizQuestion.prototype.showResponse = function () {
21063    this.quiz.updateIntroText();
21064    var teaser = this.quiz.element.querySelector(".quiz__teaser-inner");
21065    var activeTextClass = "quiz__teaser-text--active";
21066
21067    if (this.isIntro) {
21068      teaser.classList.add("quiz__teaser-inner--intro");
21069    } else if (this.isLastQuestion) {
21070      teaser.classList.remove("quiz__teaser-inner--quiz");
21071      teaser.classList.add("quiz__teaser-inner--result");
21072    } else {
21073      teaser.classList.remove("quiz__teaser-inner--intro");
21074      teaser.classList.add("quiz__teaser-inner--quiz");
21075    }
21076  };
21077
21078  QuizQuestion.prototype.addTracking = function () {
21079    var headlineEl = this.quiz.element.querySelector(".quiz__headline");
21080    var quizTitle = headlineEl ? headlineEl.innerHTML : "";
21081    var quizLength = this.quiz.questions.questions.length - 2;
21082    var activeQuestion = this.quiz.questions.questions[this.index];
21083    var nextQuestion = activeQuestion && activeQuestion.nextIndex !== undefined
21084      ? this.quiz.questions.questions[activeQuestion.nextIndex]
21085      : null;
21086
21087    if (this.index === 0) {
21088      vt.functions.pushToDataLayer({
21089        event: "quiz_start",
21090        quiz_title: quizTitle,
21091        quiz_total_questions: quizLength,
21092      });
21093    } else if (nextQuestion && nextQuestion.isLastQuestion) {
21094      var correct = this.quiz.correctResponses.length;
21095      var total = this.quiz.correctResponses.length + this.quiz.incorrectResponses.length;
21096      vt.functions.pushToDataLayer({
21097        event: "quiz_complete",
21098        quiz_title: quizTitle,
21099        quiz_total_questions: quizLength,
21100        quiz_result: correct + "/" + total,
21101      });
21102    } else {
21103      var currentQuestionNumber = this.index;
21104      var currentQuestion = this.quiz.questions.questions[this.index];
21105      var questionHeadingEl = currentQuestion && currentQuestion.container
21106        ? currentQuestion.container.querySelector(".quiz__question-heading")
21107        : null;
21108      vt.functions.pushToDataLayer({
21109        event: "quiz_progression",
21110        quiz_title: quizTitle,
21111        quiz_total_questions: quizLength,
21112        quiz_question_number: currentQuestionNumber + "/" + quizLength,
21113        quiz_question: questionHeadingEl ? questionHeadingEl.innerHTML : "",
21114      });
21115    }
21116  };
21117  QuizQuestion.prototype.next = function (e) {
21118    e.preventDefault();
21119    this.stopVideo();
21120    this.stopTimer();
21121    this.addTracking();
21122    this.quiz.next();
21123    // changeQuestion(container, changeButton.getAttribute('data-change-to'), false);
21124  };
21125
21126  QuizQuestion.prototype.tick = function () {
21127    this.timeSpentOnQuestion += 1;
21128  };
21129
21130  QuizQuestion.prototype.startTimer = function () {
21131    this.timeInterval = setInterval(this.tick.bind(this), 1000);
21132  };
21133
21134  QuizQuestion.prototype.stopTimer = function () {
21135    clearInterval(this.timeInterval);
21136    this.timeInterval = 0;
21137  };
21138
21139  QuizQuestion.prototype.stopVideo = function () {
21140    $(this.container)
21141      .find(".player")
21142      .each(function () {
21143        var playerElem = $(this);
21144        var playerObj = playerElem.data("player");
21145        playerObj.reset();
21146      });
21147  };
21148
21149  QuizQuestion.prototype.attachNextButtonClick = function () {
21150    var quizQuestion = this;
21151    if (this.nextButtons) {
21152      [].forEach.call(this.nextButtons, function (nextButton) {
21153        nextButton.setAttribute("data-change-to", quizQuestion.nextIndex);
21154        nextButton.addEventListener(
21155          "click",
21156          quizQuestion.next.bind(quizQuestion)
21157        );
21158      });
21159    }
21160  };
21161
21162  QuizQuestion.prototype.attachRadioButtonChange = function () {
21163    var thisQuestion = this;
21164    [].forEach.call(this.radios, function (radio) {
21165      radio.addEventListener(
21166        "change",
21167        thisQuestion.checkAnswer.bind(thisQuestion)
21168      );
21169    });
21170  };
21171
21172  function Quiz(element) {
21173    this.element = element;
21174    this.container = element.querySelector(".quiz__questions");
21175
21176    this.questions = new QuizQuestions(this.container, this);
21177    this.correctResponses = [];
21178    this.incorrectResponses = [];
21179    this.pagination = new QuizPagination(
21180      element.querySelectorAll("." + PAGINATION_ITEM)
21181    );
21182    this.intros = this.element.querySelectorAll(".quiz__teaser-text");
21183    this.newsletter = new QuizNewsletter(this.container);
21184    this.resultsCorrectElem = this.element.querySelectorAll(
21185      ".quiz__result-num"
21186    );
21187    this.resultsTotalElem = this.element.querySelectorAll(
21188      ".quiz__result-total"
21189    );
21190    this.activeIndex = 0;
21191
21192    window.addEventListener("resize", this.resize.bind(this));
21193  }
21194
21195  Quiz.prototype.updateIntroText = function () {
21196    var activeQuestion = this.questions.questions[this.activeIndex];
21197    if (activeQuestion.isIntro) {
21198      this.intros[0].classList.add("quiz__teaser-text--active");
21199      this.intros[1].classList.remove("quiz__teaser-text--active");
21200    } else if (activeQuestion.isFirstQuestion) {
21201      this.intros[0].classList.remove("quiz__teaser-text--active");
21202      this.intros[1].classList.add("quiz__teaser-text--active");
21203    } else if (activeQuestion.isLastQuestion) {
21204      this.intros[1].classList.remove("quiz__teaser-text--active");
21205    }
21206  };
21207
21208  Quiz.prototype.jumpToQuestion = function (i) {
21209    // Check if valid first
21210    this.activeIndex = i;
21211    // Move the slide
21212    this.updateActiveSlide();
21213    this.reposition(true);
21214  };
21215
21216  Quiz.prototype.reposition = function (scrollToTop) {
21217    var rect = this.container.getBoundingClientRect();
21218    var targetOffset = rect.width * this.activeIndex;
21219    this.container.style.transform = "translateX(-" + targetOffset + "px)";
21220
21221    if (typeof scrollToTop !== "undefined" && scrollToTop) {
21222      var quizRect = this.element.getBoundingClientRect();
21223      if (quizRect.y < 0) {
21224        document.documentElement.scrollTop =
21225          document.documentElement.scrollTop + quizRect.y;
21226      }
21227    }
21228  };
21229
21230  Quiz.prototype.hideResponseColumn = function () {
21231    var DELAY = 300;
21232    var responseColumn = this.container.querySelector(
21233      "." + QUESTION_ACTIVE + " .quiz__question-column:nth-child(2)"
21234    );
21235    if (responseColumn) {
21236      setTimeout(function () {
21237        responseColumn.classList.add("hidden");
21238      }, DELAY);
21239    }
21240  };
21241
21242  Quiz.prototype.removeOldActiveStates = function () {
21243    var pagination = this.pagination;
21244    [].forEach.call(this.questions.questions, function (question, index) {
21245      question.container.classList.remove(QUESTION_ACTIVE);
21246      pagination.items[index].classList.remove(PAGINATION_ACTIVE);
21247    });
21248  };
21249
21250  Quiz.prototype.setNewActiveStates = function () {
21251    var activeQuestion = this.questions.questions[this.activeIndex];
21252    var activeQuestionContainer = activeQuestion.container;
21253    activeQuestionContainer.classList.add(QUESTION_ACTIVE);
21254    activeQuestion.showResponse();
21255    this.pagination.items[this.activeIndex].classList.add(PAGINATION_ACTIVE);
21256  };
21257
21258  Quiz.prototype.updateActiveSlide = function () {
21259    this.hideResponseColumn();
21260    this.removeOldActiveStates();
21261    this.setNewActiveStates();
21262    var activeQuestion = this.questions.questions[this.activeIndex];
21263    if (activeQuestion.isLastQuestion) {
21264      this.showResults();
21265    }
21266  };
21267
21268  Quiz.prototype.showResults = function () {
21269    this.element.classList.add("quiz--complete");
21270    var correct = this.correctResponses.length;
21271    var total = this.correctResponses.length + this.incorrectResponses.length;
21272    this.percentageScore = (correct / total) * 100;
21273    this.highScore = this.percentageScore === 100;
21274    var highScoreFlag = this.highScore ? "--high" : "--low";
21275    var scoreElem = this.container.querySelector(
21276      "." + RESPONSE_BASE + highScoreFlag
21277    );
21278    scoreElem.classList.remove("hidden");
21279    scoreElem.classList.remove("noopacity");
21280
21281    var scoreButton = scoreElem.querySelector(".btn");
21282    if (scoreButton !== null) {
21283      scoreButton.classList.remove("noopacity");
21284    }
21285    $(this.resultsCorrectElem).html(correct);
21286    $(this.resultsTotalElem).html(total);
21287    $(this.container).addClass("quiz-results-show-form");
21288  };
21289
21290  Quiz.prototype.next = function () {
21291    this.jumpToQuestion(this.activeIndex + 1);
21292    if (!this.questions.questions[this.activeIndex].isLastQuestion) {
21293      this.questions.questions[this.activeIndex].startTimer();
21294    }
21295  };
21296
21297  Quiz.prototype.resize = function () {
21298    this.reposition();
21299  };
21300
21301  vt.initQuizSpot = function (element) {
21302    var quiz = new Quiz(element);
21303    vt.quizzes.push(quiz);
21304  };
21305})(jQuery);
21306;
21307; (function ($) {
21308
21309    // Declaring namespace if not already declared
21310    window.vt = window.vt || {};
21311
21312    // Wrapping methods in a module
21313    vt.initRequestPasswordResetForm = function (elem) {
21314
21315        vt.showCookieMessage('requestPasswordResetPageMsg', true); //Any cookie messages to show? If any show them and delete them afterwards
21316
21317        //Initiate form validation
21318        var oForm = $('form', elem);
21319        oForm.h5Validate();
21320
21321        var errorMsg = $('#errorMsg').text();
21322        
21323
21324        // Validate username
21325        var ajaxValidate = function (url, $element) {
21326
21327            //Fake progressbutton
21328            $('.btn-action').progressStart();
21329
21330            return $.ajax({
21331                type: 'POST',
21332                url: url,
21333                data: JSON.stringify({ Value: $element.val() }),
21334                contentType: "application/json;charset=utf-8",
21335                dataType: "json",
21336                statusCode: {
21337                    200: function (msg) {
21338                        $element.removeClass('ui-state-error');
21339                        $element.addClass("ui-state-valid").next().hide();
21340                        $.growl.notice({ title: $('#successGrowl strong').text(), message: $('#successGrowl p').text(), fixed: true });
21341                        $('.btn-action').progressFinish();
21342                    }
21343                }
21344            })
21345            .fail(function (msg) {
21346                $element.addClass('ui-state-error');
21347                $element.removeClass("ui-state-valid").next().html(errorMsg).show();
21348                $('.btn-action').progressFinish();
21349
21350                if ($('.btn-action').hasClass("finished")) {
21351                    $('.btn-action').removeClass("finished");
21352                }
21353                return false;
21354            })
21355            .always(function (msg) {
21356                console.log(msg);
21357            });
21358        };
21359
21360        // Validate form fields
21361        var validate = function () {
21362            if (oForm.h5Validate('allValid')){
21363                ajaxValidate('/api/sag/requestpasswordresetlink', $('.username', elem));
21364            }
21365            return false;
21366        }
21367
21368        // Trigger form-action
21369        var simClickBtnAction = function () {
21370            $(".btn-action").click();
21371        };
21372
21373        // Mouse click
21374        $('.btn-action').click(function (e) {
21375            validate();
21376            e.preventDefault();
21377        });
21378
21379        // Keyboard
21380        $("input").keydown(function (e) {
21381            if (e.which === 13) { // Enter key
21382                simClickBtnAction();
21383                e.preventDefault();
21384            }
21385        });
21386
21387        // Making methods public
21388        return {
21389
21390        };
21391    };
21392
21393})(jQuery);
21394;
21395; (function ($) {
21396
21397    // Declaring namespace if not already declared
21398    window.vt = window.vt || {};
21399
21400    // Wrapping methods in a module
21401    vt.initResetPasswordForm = function (elem) {
21402
21403        // Variables
21404
21405        $('.btn-action').progressInitialize();
21406
21407        // Initiate validation
21408        var validateInitiate = function ($element) {
21409            $element.addClass('validationInitiated');
21410        };
21411
21412        // Stop validation
21413        var validateStop = function ($element) {
21414            $element.removeClass('validationInitiated');
21415            validateEmpty($element);
21416        };
21417
21418        // Form has error
21419        var validateError = function ($element) {
21420            $element.addClass('ui-state-error');
21421            $element.removeClass("ui-state-valid");
21422            $element.siblings(".message").css("display", "block");
21423        };
21424
21425        // Form is valid
21426        var validateAccept = function ($element) {
21427            $element.removeClass('ui-state-error');
21428            $element.addClass("ui-state-valid");
21429            $element.siblings(".message").css("display", "none");
21430        };
21431
21432        // Form is empty
21433        var validateEmpty = function ($element) {
21434            $element.removeClass('ui-state-error');
21435            $element.removeClass("ui-state-valid");
21436            $element.siblings(".message").css("display", "none");
21437        };
21438
21439        // Ajax request done
21440        var ajaxDone = function ($element, msg) {
21441            if ($element.hasClass("validationInitiated")) {
21442                if (!msg.IsValid) {
21443                    validateError($element);
21444                } else {
21445                    validateAccept($element);
21446                    return "success!";
21447                }
21448            }
21449        };
21450
21451        // Validate username or password
21452        var ajaxValidate = function (url, $element) {
21453            return $.ajax({
21454                type: 'GET',
21455                url: url + $element.val()
21456            })
21457            .done(function (msg) { ajaxDone($element, msg) })
21458            .fail(function (msg) { validateError($element) })
21459            .always(function (msg) { console.log(msg) });
21460        };
21461
21462        var fieldPassword = function () {
21463            var $element = $(this);
21464
21465            $element.blur(function () {
21466                if ($element.val().length === 0) {
21467                    validateStop($element);
21468                } else {
21469                    validateInitiate($element);
21470                    ajaxValidate("/api/sag/isvalidpassword/", $element);
21471                }
21472            });
21473
21474            $element.on('keyup', function () {
21475                $(".passwordrepeated").keyup();
21476                if ($element.hasClass("validationInitiated") && $element.val().length > 0) {
21477                    ajaxValidate("/api/sag/isvalidpassword/", $element);
21478                } else if ($element.val().length === 0) {
21479                    validateStop($element);
21480                }
21481            });
21482        };
21483
21484        var fieldPasswordRepeated = function () {
21485            var $element = $(this);
21486
21487            $element.blur(function () {
21488                validateInitiate($element);
21489
21490                if ($element.val() != $('.password', elem).val() && $element.val().length > 0) {
21491                    validateError($element);
21492                } else if ($element.val().length === 0) {
21493                    validateStop($element);
21494                } else {
21495                    validateAccept($element);
21496                }
21497            });
21498
21499            $element.on('keyup', function () {
21500                if ($element.hasClass("validationInitiated") && $element.val().length > 0) {
21501
21502                    if ($element.val() != $('.password', elem).val()) {
21503                        validateError($element);
21504                    } else {
21505                        validateAccept($element);
21506                    }
21507                } else if ($element.val().length === 0) {
21508                    validateStop($element);
21509                }
21510            });
21511        };
21512
21513        // Validate all fields
21514        var validate = function () {
21515            if ($("input:not(.ui-state-valid):required").length) {
21516                var $invalid = $("input:not(.ui-state-valid):required");
21517                $invalid.blur();
21518
21519                $invalid = $("input:not(.ui-state-valid):required"); // needs to wait for server validation before running
21520                validateError($invalid);
21521                return false;
21522            } else {
21523                createAction();
21524            }
21525        }
21526
21527        // Trigger btn-action
21528        var simClickBtnAction = function () {
21529            $(".btn-action").click();
21530        };
21531
21532        // Submit form
21533        var createAction = function () {
21534            //Fake progressbutton
21535            $('.btn-action').progressStart();
21536            //add cookieMessage
21537            var cookieConfig = {
21538                title: $('#userMessage strong').html(),
21539                message: $('#userMessage p').html(),
21540                button: 'OK',
21541                addOverlay: true
21542            },
21543                redirectUrl = $('body').data('mypage-url');
21544
21545            var payload = {};
21546            payload.Token = $('#token').val();
21547            payload.NewPassword = $('#password').val();
21548
21549            $.ajax({
21550                type: 'POST',
21551                url: '/api/sag/resetpassword/',
21552                data: JSON.stringify(payload),
21553                contentType: "application/json;charset=utf-8",
21554            })
21555            .done(function (msg) {
21556                //Show cookie message on MyPage
21557                vt.setCookieMessage('myPageMsg', cookieConfig);
21558                window.location.href = redirectUrl;
21559            })
21560            .fail(function (msg) { console.log(msg); })
21561            .always(function (msg) {
21562                console.log(msg);
21563                $('.btn-action').progressFinish();
21564            });
21565        };
21566        
21567        // Event listensers
21568        $('.password', elem).each(fieldPassword);
21569        $('.passwordrepeated', elem).each(fieldPasswordRepeated);
21570
21571        // Mouse click
21572        $('.btn-action').click(function (e) {
21573            e.preventDefault();
21574            validate();
21575        });
21576
21577        // Keyboard
21578        $("input").keydown(function (e) {
21579            if (e.which === 13) { // Enter key
21580                simClickBtnAction();
21581            }
21582        });
21583    };
21584
21585})(jQuery);
21586;
21587; (function ($) {
21588
21589    window.vt = window.vt || {};
21590
21591    vt.initScrollTo = function (elem) {
21592
21593        // easing functions http://goo.gl/5HLl8
21594        Math.easeInOutQuad = function (t, b, c, d) {
21595            t /= d / 2;
21596            if (t < 1) {
21597                return c / 2 * t * t + b
21598            }
21599            t--;
21600            return -c / 2 * (t * (t - 2) - 1) + b;
21601        };
21602
21603        Math.easeInCubic = function (t, b, c, d) {
21604            var tc = (t /= d) * t * t;
21605            return b + c * (tc);
21606        };
21607
21608        Math.inOutQuintic = function (t, b, c, d) {
21609            var ts = (t /= d) * t,
21610            tc = ts * t;
21611            return b + c * (6 * tc * ts + -15 * ts * ts + 10 * tc);
21612        };
21613
21614        // requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
21615        var requestAnimFrame = (function () {
21616            return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) { window.setTimeout(callback, 1000 / 60); };
21617        })();
21618
21619        function scrollTo(to, callback, duration) {
21620            // because it's so fucking difficult to detect the scrolling element, just move them all
21621            function move(amount) {
21622                document.documentElement.scrollTop = amount;
21623                document.body.parentNode.scrollTop = amount;
21624                document.body.scrollTop = amount;
21625            }
21626            function position() {
21627                return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop;
21628            }
21629            var start = position(),
21630                change = to - start,
21631                currentTime = 0,
21632                increment = 20;
21633
21634            duration = (typeof (duration) === 'undefined') ? 500 : duration;
21635
21636            var animateScroll = function () {
21637                // increment the time
21638                currentTime += increment;
21639                // find the value with the quadratic in-out easing function
21640                var val = Math.easeInOutQuad(currentTime, start, change, duration);
21641                // move the document.body
21642                move(val);
21643                // do the animation unless its over
21644                if (currentTime < duration) {
21645                    requestAnimFrame(animateScroll);
21646                } else {
21647                    if (callback && typeof (callback) === 'function') {
21648                        // the animation is done so lets callback
21649                        callback();
21650                    }
21651                }
21652            };
21653            animateScroll();
21654        }
21655
21656        $(elem).on("click", function (e) {
21657            e.preventDefault();
21658            scrollTo(0, 300);
21659        });
21660
21661    }
21662
21663})(jQuery);;
21664; (function ($) {
21665
21666    window.vt = window.vt || {};
21667
21668    vt.initSearchBar = function () {
21669
21670        var $searchBox = $('.c-site-search'),
21671            $searchText = $('.c-site-search__input'),
21672            $searchButton = $('.c-site-search__button'),
21673            searchConfig = {
21674                "searchUrl": $('body').data('search-url'),
21675            },
21676            query = '';
21677
21678        var performSearch = function () {
21679            query = $searchText.val() || '';
21680            query = query.trim();
21681            $('#project-search').val(query);
21682            window.location.href = searchConfig.searchUrl + "?q=" + encodeURIComponent(query);
21683        };
21684
21685        var toggleSearchBar = function (e) {
21686            if ($searchBox.hasClass('open')) {
21687                if ($searchText.val() !== '') {
21688                    performSearch();
21689                }
21690                else {
21691                    $searchBox.toggleClass('open');
21692                }
21693            }
21694            else {
21695                $searchText.focus();
21696                $searchBox.toggleClass('open');
21697            }
21698            e.preventDefault();
21699        };
21700
21701        var handleKeyboardInput = function (e) {
21702            if (e.keyCode === 13 && $searchText.val()) {
21703	            performSearch();
21704            } else {
21705	            ensureExpandedSearch();
21706            }
21707        };
21708
21709        var ensureExpandedSearch = function () {
21710        	if (!$searchBox.hasClass('open')) {
21711        		$searchBox.addClass('open');
21712        	}
21713        };
21714
21715        var collapseSearch = function () {
21716        	if ($searchBox.hasClass('open')) {
21717        		$searchBox.removeClass('open');
21718        	}
21719        };
21720
21721        var focusOut = function () {
21722            setTimeout(collapseSearch, 200);
21723        };
21724
21725        $searchButton.on('click', toggleSearchBar);
21726        $searchText.on('click', ensureExpandedSearch).on('keyup', handleKeyboardInput);
21727        $searchText.on('blur', focusOut);
21728
21729    };
21730
21731})(jQuery);
21732;
21733; (function ($) {
21734
21735    window.vt = window.vt || {};
21736
21737    vt.initSearch = function (elem) {
21738        // Init Application
21739        CX.initSpa({
21740            eP: "npt",
21741            mDialog: document.querySelector(".modal-dialog"),
21742            nO: 500,
21743            fW  : ["q"],
21744            initSPA: "#!npt=donation&Sort=-year",
21745            ioURI: "/api/",
21746            clnFilter: true
21747        });
21748
21749        CX.setFilterWrapper(document.getElementById("wrpFilters"));
21750        CX.setFilterWrapper(document.getElementById("wrpSort"));
21751
21752        var sResult = CX.getUriKey("q");
21753        if (sResult) document.getElementById("project-search").value = decodeURIComponent(sResult);
21754
21755        $(".toggle-filters, .close-filters").click(function () {
21756            // all dropdowns
21757            $('body').toggleClass('show-filters');
21758        });
21759        $("#search-btn").click(function () {
21760            CX.searchForm();
21761        });
21762        $(".search-field").on("keyup", "input", function (event) {
21763            // If enter is pressed then hide keyboard.
21764            if (event.keyCode == 13) {
21765                $("input").blur();
21766            }
21767        });
21768
21769        $(document).click(function (event) {
21770            if ( (!$(event.target).hasClass('vt-dropdown')) && (!$(event.target).hasClass('vt-label')) ) {
21771                $('.vt-dropdown').removeClass('active');
21772            }
21773        });
21774
21775    };
21776
21777})(jQuery);;
21778; (function ($) {
21779
21780  window.vt = window.vt || {};
21781
21782  vt.initSocialButtons = function (elem) {
21783
21784    $(elem).find('a').prettySocial();
21785
21786  };
21787
21788})(jQuery);;
21789; (function ($) {
21790
21791    window.vt = window.vt || {};
21792
21793    vt.initSocialSpots = function () {
21794
21795        //if ($('.pageedit').length > 0) {
21796        //    return;
21797        //}
21798 
21799        $list = $('.social-spots .spot-list');
21800        
21801        // initialize Masonry first...
21802        $list.masonry({
21803            itemSelector: '.social-spot'
21804        });
21805
21806        // then trigger layout after images have loaded
21807        $list.imagesLoaded(function () {
21808            $list.masonry();
21809        });
21810
21811    };
21812
21813})(jQuery);;
21814; (function ($) {
21815    window.vt = window.vt || {};
21816
21817    // All templates stored in an array
21818    window.vt.templates = {
21819        templates: [],
21820        addTemplate: function (templateName, templateString, renderFunction) {
21821            var templateObj = {
21822                name: templateName,
21823                renderFunction: renderFunction,
21824                template: templateString
21825            };
21826
21827            this.templates.push(templateObj);
21828        },
21829
21830        getTemplateByName: function (name) {
21831            for (var i = 0; i < this.templates.length; i++) {
21832                if (this.templates[i].name === name) {
21833                    return this.templates[i];
21834                }
21835            }
21836            return null;
21837        },
21838
21839        defaultRender: function (templateString, data) {
21840            var templateOutput = templateString;
21841            for (var key in data) {
21842                if (data.hasOwnProperty(key)) {
21843                    templateOutput = templateOutput.replace(new RegExp('{{' + key + '}}', 'g'), data[key]);
21844                }
21845            }
21846            return templateOutput;
21847        },
21848
21849        renderTemplate: function (name, data) {
21850            var template = this.getTemplateByName(name);
21851            var templateOutput = template.template;
21852            if (template.renderFunction !== null) {
21853                templateOutput = template.renderFunction(template, data);
21854            } else {
21855                templateOutput = vt.templates.defaultRender(templateOutput, data);
21856            }
21857            return templateOutput;
21858        }
21859    };
21860
21861    var getValueOrDefault = function (value) {
21862
21863        // If value is falsy & not 0
21864        if (!value && value !== 0) {
21865            return '-';
21866        }
21867
21868        return value;
21869    }
21870
21871    // Matching care center list
21872    var cropperImage = ''
21873        + '<li class="aed-images__item" data-id="{{id}}" data-sorting-id="{{sorting_id}}">'
21874        + '    <div class="aed-images__picture">'
21875        + '        <img class="js-current-profile-picture" src="data:image/png;base64, {{img_url}}" alt="{{title}}">'
21876        + '        <div class="aed-images__edit">'
21877        + '            <div class="aed-images__edit-button aed-images__edit-button--edit " data-method="change">'
21878        + '                <i class="circle icon icon-pencil icon__styled-circle icon__styled-circle--filled"></i>'
21879        + '            </div>'
21880        + '            <div class="aed-images__edit-button aed-images__edit-delete" data-method="delete">'
21881        + '                <i class="circle icon icon-delete icon__styled-circle icon__styled-circle--filled"></i>'
21882        + '            </div>'
21883        + '        </div>'
21884        + '    </div>'
21885        + '    <p class="form-element--heading">{{title}}</p>'
21886        + '    <p class="form-element--description">{{description}}</p>'
21887        + '</li>';
21888
21889    var renderCropperImage = function (template, data) {
21890        var html = '';
21891
21892        var templateOutput = template.template;
21893        var mappedData = {};
21894
21895        mappedData.id = data.ImageId;
21896        mappedData.sorting_id = data.SortOrder;
21897
21898        mappedData.img_url = getValueOrDefault(data.ImageBase64);
21899
21900        mappedData.title = getValueOrDefault(data.Name);
21901        mappedData.description = getValueOrDefault(data.Text);
21902
21903        templateOutput = vt.templates.defaultRender(templateOutput, mappedData);
21904        html += templateOutput;
21905
21906        return html;
21907    };
21908
21909    var warningModal = ''
21910        + '<div class="modal modal--confirm js-active" data-js-initialize="vt.initModal">'
21911        + '    <div class="modal__overlay">'
21912        + '        <div class="modal__content">'
21913        + '            <p>{{message}}</p>'
21914        + '            <div class="modal__buttons-wrap">'    
21915        + '              <button class="btn default">Annuller</button>'
21916        + '              <button class="btn colored" data-method="delete">Slet</button>'
21917        + '            </div>'
21918        + '        </div>'
21919        + '    </div>'
21920        + '</div>';
21921
21922    var renderWarningModal = function (template, data) {
21923        var html = '';
21924
21925        var templateOutput = template.template;
21926        var mappedData = {};
21927
21928        mappedData.message = data;
21929
21930        templateOutput = vt.templates.defaultRender(templateOutput, mappedData);
21931        html += templateOutput;
21932
21933        return html;
21934    };
21935
21936    var activateModal = ''
21937        + '<div class="modal modal--confirm js-active" data-js-initialize="vt.initModal">'
21938        + '    <div class="modal__overlay">'
21939        + '        <div class="modal__content">'
21940        + '            <div class="form-element--heading">{{title}}</div>'
21941        + '            <p>{{message}}</p>'
21942        + '            <div class="modal__buttons-wrap">'
21943        + '              <button class="btn default">Annuller</button>'
21944        + '              <button class="btn colored" data-method="post">{{buttonTxt}}</button>'
21945        + '            </div>'
21946        + '        </div>'
21947        + '    </div>'
21948        + '</div>';
21949
21950    var renderActivateModal = function (template, data) {
21951        var html = '';
21952
21953        var templateOutput = template.template;
21954        var mappedData = {};
21955
21956        mappedData.message = data.message;
21957        mappedData.title = data.title;
21958        mappedData.buttonTxt = data.buttonTxt;
21959
21960        templateOutput = vt.templates.defaultRender(templateOutput, mappedData);
21961        html += templateOutput;
21962
21963        return html;
21964    };
21965
21966    window.vt.templates.addTemplate('cropperImage', cropperImage, renderCropperImage);
21967    window.vt.templates.addTemplate('warningModal', warningModal, renderWarningModal);
21968    window.vt.templates.addTemplate('activateModal', activateModal, renderActivateModal);
21969})(jQuery);
21970;
21971(function ($) {
21972  window.vt = window.vt || {};
21973
21974  vt.initTracking = function (elem) {
21975    let trackingTriggers = [
21976      ".pdf",
21977      "e-pages.dk",
21978      "genoplivning.dk",
21979      "reader.livedition.dk",
21980    ];
21981    $("a").on("click", function () {
21982      let url = $(this).attr("href");
21983      if (url) {
21984        trackingTriggers.forEach(function (item) {
21985          if (url.includes(item)) {
21986            vt.functions.pushToDataLayer({
21987              event: "PDF download",
21988              url: url
21989            });
21990          }
21991        });
21992      }
21993    });
21994
21995    $("#login-button").on("click", function () {
21996      vt.functions.pushToDataLayer({
21997        event: "login",
21998        user_id: window.vt.userId || ""
21999      });
22000    });
22001
22002    $("#huge-search-button").on("click", function () {
22003      const searchParam = new URLSearchParams(window.location.search);
22004      const numberOfResults = $("#search-result").data("data-js-tracking");
22005      vt.functions.pushToDataLayer({
22006        event: "search",
22007        search_term: searchParam.get("q") || "",
22008        search_result_found: numberOfResults ? "true" : "false",
22009        amount_of_results: numberOfResults ? numberOfResults : 0,
22010      });
22011    });
22012  };
22013})(jQuery);
22014;
22015; (function ($) {
22016
22017    window.vt = window.vt || {};
22018
22019    vt.userSession = {
22020
22021        domain: 'sag', //The required domain for a user
22022        timer: 1000 * 60 * 20, //Loop interval milliseconds * seconds * minutes
22023        testUrl: '/api/sag', //Webservice to test the current user
22024        pageUrl: window.location.pathname + window.location.search, //Current page url
22025        loginPage: '/Login?returnTo=', //Login page url
22026        userName: $('#username').text(), //The current users username/email
22027
22028        init: function () {
22029            vt.userSession.testSession(); //Test the session
22030
22031            Cookies.set('loginUserName', vt.userSession.userName); //Save username in cookie, used on login page.
22032        },
22033
22034        testSession: function () {
22035
22036            $.get(vt.userSession.testUrl, function (data) {
22037
22038                if (data.indexOf(vt.userSession.domain) != 0) { //If 'sag' is no longer the domain for the user, the user has lost the session
22039
22040                    //Add cookie message
22041                    var cookieConfig = {
22042                        title: 'Du er blevet logget ud',
22043                        message: 'Du har været inaktiv i 20 minutter og er derfor af sikkerhedsmæssige grunde blevet logget ud af systemet.',
22044                        button: 'OK',
22045                        addOverlay: true
22046                    };
22047
22048                    //clear username so user will receive cookieMessage after being logged out
22049                    $('#username').text("");
22050                    //Show cookie message on login page
22051                    vt.setCookieMessage('loginPageMsg', cookieConfig);
22052
22053                    //Redirect to login page
22054                    window.location.href = vt.userSession.loginPage + vt.userSession.pageUrl;
22055                };
22056            });
22057
22058            //Start testing the users session state in an interval.
22059            window.setTimeout(function () {
22060                vt.userSession.testSession();
22061            }, vt.userSession.timer); 
22062        }
22063    };
22064
22065    //Does the current page demand an authentication
22066    var authenticatonNeeded = $('body').data('authenticated');
22067    if (typeof (authenticatonNeeded) != 'undefined' && authenticatonNeeded.toLowerCase() == 'true') {
22068        vt.userSession.init(); //Test the user session
22069    }
22070
22071})(jQuery);
22072;
22073; (function ($) {
22074
22075    window.vt = window.vt || {};
22076
22077    vt.userMessage = function (messageArgs) {
22078        //messageArgs shold be as follows: { container: [where to append userMessage], content: [userMessage content], addOverlay [add overlay to content area]
22079        //console.log('userMessage...', messageArgs);
22080        if ($('body').is('.pageedit'))
22081            return;
22082        
22083        //default configuration...
22084        var messageConfig = {
22085            container: $('body'),
22086            content: '',
22087            addOverlay: false
22088        }
22089        
22090        //merge with message arguments...
22091        $.extend(messageConfig, messageArgs);
22092
22093        if ($('#userMessage').length == 0) {            
22094            messageConfig.container.append('<div id="userMessageOverlay"></div><div id="userMessage"><div class="container"></div></div>');
22095        }        
22096
22097        if (messageConfig.addOverlay) {
22098            $('#userMessageOverlay').addClass('fade-in');
22099        }
22100        else {
22101            $('#userMessageOverlay').removeClass('fade-in');
22102        }
22103
22104        var $userMsg = $('#userMessage');
22105
22106        if ($userMsg.hasClass('visible')) {
22107            //console.log('fadeOut current message and show new...');
22108            $('.container', $userMsg).addClass('change-mode')
22109            showMessage();
22110        }
22111        else {
22112            window.setTimeout(function () {
22113                $userMsg.addClass('visible');
22114                showMessage();
22115            }, 200);
22116        }
22117
22118        function showMessage() {
22119            var $msg = $('#userMessage');
22120
22121            window.setTimeout(function () {
22122                $('.container', $msg).html(messageConfig.content).removeClass('change-mode');
22123            }, 400)                
22124        }
22125        
22126        $(document).on('click', '.btnClose', function (event) { //Click on 'Luk' button, hide user message
22127            vt.userMessageHide();
22128            event.preventDefault();
22129        });
22130    }
22131
22132    vt.userMessageHide = function () {
22133        var $userMsg = $('#userMessage.visible');
22134
22135        if ($userMsg.length > 0) {
22136            $userMsg.removeClass('visible');
22137        };
22138
22139        $('#userMessageOverlay').removeClass('fade-in');
22140    }
22141
22142    vt.setCookieMessage = function (cookieName, messageConfig) {
22143        Cookies.set(getFullyQualifiedCookieName(cookieName), messageConfig);
22144    }
22145
22146    vt.showCookieMessage = function (cookieName, deleteThis) {
22147        var cookieMsg = Cookies.getJSON(getFullyQualifiedCookieName(cookieName));
22148        if (cookieMsg) {
22149            var txt = '';
22150            var buttonTxt = (typeof (cookieMsg.button) != 'undefined') ? cookieMsg.button : 'Luk';
22151
22152            txt += (typeof(cookieMsg.title) != 'undefined') ? '<h1>' + cookieMsg.title + '</h1>' : '';
22153            txt += (typeof(cookieMsg.message) != 'undefined') ? '<p>' + cookieMsg.message + '</p>' : '';
22154
22155            var messageConfig = {
22156                container: $('body'),
22157                content: txt + '<p class="text-align-right"><button class="btn colored black btnClose">' + buttonTxt + '</button></p>',
22158                addOverlay: cookieMsg.addOverlay
22159            }
22160            vt.userMessage(messageConfig);
22161        }
22162
22163        if (deleteThis) {
22164            vt.deleteCookieMessage(cookieName);
22165        }
22166
22167        return null;
22168    }
22169
22170    vt.deleteCookieMessage = function (cookieName) {
22171        Cookies.remove(getFullyQualifiedCookieName(cookieName));
22172    }
22173
22174    
22175    var getFullyQualifiedCookieName = function (cookieName) {
22176        var cookieNamePreFix = $('#username').text().replace('@', '~at~');
22177        var result = cookieNamePreFix + "_" + cookieName;
22178        return result;
22179    }
22180})(jQuery);;
22181(function ($) {
22182  window.vt = window.vt || {};
22183
22184  vt.initVideoPlayer = function (elem) {
22185    if ($(".pageedit").length > 0) {
22186      return;
22187    }
22188
22189    // Constants
22190    var CONSTANTS = {
22191      PROVIDERS: {
22192        YOUTUBE: 'youtube',
22193        VIMEO: 'vimeo',
22194        KONTAINER: 'kontainer'
22195      },
22196      PLAY_MODES: {
22197        INLINE: 'inline',
22198        OVERLAY: 'overlay'
22199      },
22200      KEY_CODES: {
22201        ENTER: 13,
22202        SPACE: 32,
22203        ESC: 27
22204      },
22205      TIMEOUTS: {
22206        OVERLAY_TRANSITION: 50,
22207        FRAME_LOAD: 500,
22208        OVERLAY_CLOSE: 500
22209      }
22210    };
22211
22212    // settings
22213    var settings = {
22214      mq: function () {
22215        return Modernizr.mq("(max-width: 1024px)");
22216      },
22217    };
22218
22219    // Subtitle URLs for Kontainer videos
22220    var subtitleUrls = {
22221      url1: null,
22222      url2: null
22223    };
22224
22225    // Tracking state
22226    var trackingState = {
22227      firedMilestones: [],
22228      hasVideoStartFired: false,
22229      hasVideoEnded: false,
22230      videoDuration: 0,
22231      videoTitle: null
22232    };
22233
22234    // Player instance storage
22235    var playerInstances = {
22236      ytPlayer: null,
22237      vimeoPlayer: null,
22238      kontainerPlayer: null,
22239      kontainerHls: null
22240    };
22241
22242    // Event handler references for cleanup
22243    var eventHandlers = {
22244      escHandler: null,
22245      consentHandler: null
22246    };
22247
22248    // Cleanup function to prevent memory leaks
22249    var cleanup = function () {
22250      // Stop tracking intervals
22251      stopProgressTracking();
22252      if (typeof stopProgressTrackingVimeo !== 'undefined') {
22253        stopProgressTrackingVimeo();
22254      }
22255      if (typeof stopProgressTrackingHtml5 !== 'undefined') {
22256        stopProgressTrackingHtml5();
22257      }
22258
22259      // Destroy player instances
22260      if (playerInstances.ytPlayer) {
22261        try {
22262          playerInstances.ytPlayer.destroy();
22263        } catch (e) {
22264          console.warn('Error destroying YouTube player:', e);
22265        }
22266        playerInstances.ytPlayer = null;
22267      }
22268
22269      if (playerInstances.vimeoPlayer) {
22270        try {
22271          playerInstances.vimeoPlayer.destroy();
22272        } catch (e) {
22273          console.warn('Error destroying Vimeo player:', e);
22274        }
22275        playerInstances.vimeoPlayer = null;
22276      }
22277
22278      if (playerInstances.kontainerHls) {
22279        try {
22280          playerInstances.kontainerHls.destroy();
22281        } catch (e) {
22282          console.warn('Error destroying HLS instance:', e);
22283        }
22284        playerInstances.kontainerHls = null;
22285      }
22286
22287      if (playerInstances.kontainerPlayer) {
22288        try {
22289          playerInstances.kontainerPlayer.pause();
22290          playerInstances.kontainerPlayer.src = '';
22291        } catch (e) {
22292          console.warn('Error destroying Kontainer player:', e);
22293        }
22294        playerInstances.kontainerPlayer = null;
22295      }
22296
22297      // Remove event listeners
22298      if (eventHandlers.escHandler) {
22299        $(document).off('keyup.videoPlayer', eventHandlers.escHandler);
22300        eventHandlers.escHandler = null;
22301      }
22302
22303      if (eventHandlers.consentHandler) {
22304        window.removeEventListener('CookieInformationConsentGiven', eventHandlers.consentHandler);
22305        eventHandlers.consentHandler = null;
22306      }
22307
22308      // Reset tracking state
22309      trackingState.firedMilestones = [];
22310      trackingState.hasVideoStartFired = false;
22311      trackingState.hasVideoEnded = false;
22312      trackingState.videoDuration = 0;
22313      trackingState.videoTitle = null;
22314
22315      // Reset subtitle URLs
22316      subtitleUrls.url1 = null;
22317      subtitleUrls.url2 = null;
22318    };
22319
22320    // Store cleanup function on element for external access
22321    $(elem).data('videoPlayerCleanup', cleanup);
22322
22323    // Pause playback without destroying the player, so other components
22324    // (e.g. sliders) can stop a video that scrolled out of view while still
22325    // allowing it to be resumed via its native controls.
22326    var pausePlayback = function () {
22327      if (playerInstances.ytPlayer && typeof playerInstances.ytPlayer.pauseVideo === 'function') {
22328        try {
22329          playerInstances.ytPlayer.pauseVideo();
22330        } catch (e) {
22331          console.warn('Error pausing YouTube player:', e);
22332        }
22333      }
22334
22335      if (playerInstances.vimeoPlayer && typeof playerInstances.vimeoPlayer.pause === 'function') {
22336        try {
22337          playerInstances.vimeoPlayer.pause();
22338        } catch (e) {
22339          console.warn('Error pausing Vimeo player:', e);
22340        }
22341      }
22342
22343      if (playerInstances.kontainerPlayer && typeof playerInstances.kontainerPlayer.pause === 'function') {
22344        try {
22345          playerInstances.kontainerPlayer.pause();
22346        } catch (e) {
22347          console.warn('Error pausing Kontainer player:', e);
22348        }
22349      }
22350    };
22351
22352    // Store pause function on element for external access
22353    $(elem).data('videoPlayerPause', pausePlayback);
22354
22355    // get data from video
22356    var getVideoData = function (elm, mobile) {
22357      var videoData = $(elm).closest(".js-video").data();
22358      // if videoUrl isn't there return
22359      if (!videoData.videoUrl) {
22360        return;
22361      }
22362      // Return if no user consent
22363      if (!consentGiven()) {
22364        //return;
22365      }
22366
22367      /*
22368      // if mobile view then always show video 'inline'
22369      if (settings.mq()) {
22370        videoData.playMode = "inline";
22371      }*/
22372
22373      var videoUrl = videoData.videoUrl;
22374      var isYoutube = videoUrl.indexOf(CONSTANTS.PROVIDERS.YOUTUBE);
22375      var isVimeo = videoUrl.indexOf(CONSTANTS.PROVIDERS.VIMEO);
22376      var isKontainer = videoUrl.indexOf('kontainer.com');
22377      var playMode = videoData.playMode;
22378      var videoId;
22379      var match;
22380
22381      // get video id
22382      if (isYoutube >= 0) {
22383        match = videoUrl.match(
22384          /(?:https?:\/{2})?(?:w{3}\.)?youtu(?:be)?\.(?:com|be)(?:\/watch\?v=|\/)([^\s&]+)/
22385        );
22386        if (!match || !match[1]) {
22387          console.error('Could not extract YouTube video ID from URL:', videoUrl);
22388          return;
22389        }
22390        videoId = match[1];
22391        playerMode(elm, videoId, playMode, CONSTANTS.PROVIDERS.YOUTUBE, mobile);
22392      } else if (isVimeo >= 0) {
22393        match = videoUrl.match(
22394          /https?:\/\/(?:www\.|player\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/([^\/]*)\/videos\/|album\/(\d+)\/video\/|video\/|)(\d+)(?:$|\/|\?)/
22395        );
22396        if (!match || !match[3]) {
22397          console.error('Could not extract Vimeo video ID from URL:', videoUrl);
22398          return;
22399        }
22400        videoId = match[3];
22401        playerMode(elm, videoId, playMode, CONSTANTS.PROVIDERS.VIMEO, mobile);
22402      } else if (isKontainer >= 0) {
22403        // Store subtitle URLs for use in the Kontainer player.
22404        // Use .attr() instead of .data() to avoid jQuery camelCase quirks with numeric suffixes.
22405        subtitleUrls.url1 = $(elm).closest('.js-video').attr('data-sub-url-da') || null;
22406        subtitleUrls.url2 = $(elm).closest('.js-video').attr('data-sub-url-en') || null;
22407        // Pass full stream URL directly as the video identifier
22408        playerMode(elm, videoUrl, playMode, CONSTANTS.PROVIDERS.KONTAINER, mobile);
22409      } else {
22410        return;
22411      }
22412    };
22413
22414    var playerMode = function (elm, videoId, playMode, service, mobile) {
22415      // check settings and play-mode if play inline or in overlay
22416      if (playMode === CONSTANTS.PLAY_MODES.INLINE) {
22417        playerModeInline(elm);
22418        loadFrame(elm, videoId, playMode, service, mobile);
22419      } else if (playMode === CONSTANTS.PLAY_MODES.OVERLAY) {
22420        playerModeOverlay();
22421        loadFrame(elm, videoId, playMode, service, mobile);
22422      }
22423    };
22424
22425    // if playmode is inline
22426    var playerModeInline = function (elm) {
22427      // Cache DOM queries
22428      var $elm = $(elm);
22429      if ($elm.find(".js-embed").length > 0) {
22430        return; // Already initialized
22431      }
22432
22433      var embedContainerElem = $elm.find(".embed-container");
22434
22435      if (embedContainerElem.length > 0) {
22436        embedContainerElem.addClass("play-start");
22437        embedContainerElem.find("img").remove();
22438        embedContainerElem.append('<div class="js-embed"></div>');
22439        $elm.find(".js-video-play").addClass("is-playing");
22440      }
22441
22442      // append close button (hidden by default, shown when video is stopped/closed)
22443      if (!settings.mq()) {
22444        $elm.find(".video-wrapper")
22445          .append('<button type="button" class="inline-close" aria-label="Close video" tabindex="0" style="display:none">Close</button>');
22446      }
22447
22448      // close btn on inline
22449      $elm.find(".inline-close").on("click keydown", function (e) {
22450        // Support both click and keyboard (Enter/Space)
22451        if (e.type === 'keydown' &&
22452          e.keyCode !== CONSTANTS.KEY_CODES.ENTER &&
22453          e.keyCode !== CONSTANTS.KEY_CODES.SPACE) {
22454          return;
22455        }
22456
22457        e.preventDefault();
22458
22459        // Cache container element
22460        var container = $elm.find(".embed-container");
22461        container.removeClass("play-start playing");
22462        $elm.find(".js-video-play").removeClass("is-playing");
22463
22464        // stop video and cleanup
22465        $elm.find(".js-embed").remove();
22466        $(this).remove();
22467
22468        cleanup();
22469
22470        // stop parent click event
22471        e.stopPropagation();
22472      });
22473    };
22474
22475    // if playmode is overlay
22476    var playerModeOverlay = function () {
22477      // Build overlay HTML more efficiently
22478      var overlayHtml = [
22479        '<div class="overlay">',
22480        '<button type="button" class="overlay-close" aria-label="Close video">Close</button>',
22481        '<div class="video-container"><div class="js-embed"></div></div>',
22482        '</div>'
22483      ].join('');
22484
22485      var overlay = $(overlayHtml);
22486
22487      $("body").append(overlay);
22488
22489      setTimeout(function () {
22490        overlay.addClass("open");
22491      }, CONSTANTS.TIMEOUTS.OVERLAY_TRANSITION);
22492
22493      var closeOverlay = function () {
22494        overlay.removeClass("open");
22495        setTimeout(function () {
22496          overlay.remove();
22497          // Clean up ESC handler when overlay removed
22498          if (eventHandlers.escHandler) {
22499            $(document).off('keyup.videoPlayer', eventHandlers.escHandler);
22500            eventHandlers.escHandler = null;
22501          }
22502        }, CONSTANTS.TIMEOUTS.OVERLAY_CLOSE);
22503      };
22504
22505      // close btn on overlay
22506      overlay.find(".overlay-close").on("click", closeOverlay);
22507
22508      // if click esc then stop video - use namespaced event
22509      eventHandlers.escHandler = function (e) {
22510        if (e.keyCode === CONSTANTS.KEY_CODES.ESC) {
22511          closeOverlay();
22512        }
22513      };
22514      $(document).on('keyup.videoPlayer', eventHandlers.escHandler);
22515    };
22516
22517    // load frames
22518    var loadFrame = function (elm, videoId, playMode, service, mobile) {
22519      setTimeout(function () {
22520        var embedElement;
22521
22522        if (playMode === CONSTANTS.PLAY_MODES.OVERLAY) {
22523          embedElement = $("body").find(".overlay .js-embed")[0];
22524        } else if (playMode === CONSTANTS.PLAY_MODES.INLINE) {
22525          embedElement = elm.find(".js-embed")[0];
22526        }
22527
22528        if (!embedElement) {
22529          console.warn('Could not find embed element for video player');
22530          return;
22531        }
22532
22533        if (service === CONSTANTS.PROVIDERS.YOUTUBE) {
22534          onYouTubeIframeAPIReady(embedElement, videoId, mobile);
22535        } else if (service === CONSTANTS.PROVIDERS.VIMEO) {
22536          onVimeoIframeAPIReady(embedElement, videoId, mobile);
22537        } else if (service === CONSTANTS.PROVIDERS.KONTAINER) {
22538          onKontainerVideoReady(embedElement, videoId, mobile);
22539        }
22540      }, CONSTANTS.TIMEOUTS.FRAME_LOAD);
22541    };
22542
22543    // when youtube api is ready
22544    var onYouTubeIframeAPIReady = function (elm, videoId, mobile) {
22545      if (!elm || !videoId) {
22546        console.error('Invalid element or video ID for YouTube player');
22547        return;
22548      }
22549
22550      try {
22551        var mastHead = $(elm).parents(".mast-head").length;
22552
22553        if (mobile === null || mobile === undefined) {
22554          // check if video is in mast-head
22555          if (mastHead <= 0) {
22556            playerInstances.ytPlayer = new YT.Player(elm, {
22557              width: "100%",
22558              videoId: videoId,
22559              playerVars: {
22560                autoplay: 1,
22561                controls: 1,
22562                showinfo: 0,
22563                rel: 0,
22564                loop: 0,
22565                fs: 0,
22566                cc_load_policty: 0,
22567                iv_load_policy: 3,
22568              },
22569              events: {
22570                onReady: onPlayerReady,
22571                onStateChange: onPlayerStateChange,
22572              },
22573            });
22574          } else {
22575            playerInstances.ytPlayer = new YT.Player(elm, {
22576              width: "100%",
22577              videoId: videoId,
22578              playerVars: {
22579                autoplay: 1,
22580                controls: 1,
22581                showinfo: 0,
22582                rel: 0,
22583                loop: 1,
22584                fs: 0,
22585                cc_load_policty: 0,
22586                iv_load_policy: 3,
22587              },
22588              events: {
22589                onReady: onPlayerReadyHeader,
22590                onStateChange: onPlayerStateChangeHeader,
22591              },
22592            });
22593          }
22594        } else {
22595          playerInstances.ytPlayer = new YT.Player(elm, {
22596            width: "100%",
22597            videoId: videoId,
22598            playerVars: {
22599              autoplay: 0,
22600              controls: 1,
22601              showinfo: 0,
22602              rel: 0,
22603              loop: 0,
22604              fs: 0,
22605              cc_load_policty: 0,
22606              iv_load_policy: 3,
22607            },
22608            events: {
22609              onStateChange: onPlayerStateChange,
22610            },
22611          });
22612        }
22613      } catch (error) {
22614        console.error('Error initializing YouTube player:', error);
22615        playerInstances.ytPlayer = null;
22616      }
22617    };
22618
22619    // play video when player is ready
22620    onPlayerReady = function (event) {
22621      event.target.playVideo();
22622    };
22623    // play & mute video when player is ready
22624    onPlayerReadyHeader = function (event) {
22625      event.target.playVideo();
22626      event.target.mute();
22627    };
22628    // stateChange on video
22629    onPlayerStateChange = function (event) {
22630      var playerContainer = $(event.target.getIframe()).closest(".embed-container");
22631
22632      if (event.data === YT.PlayerState.PLAYING) {
22633        playerContainer.addClass("playing");
22634        playerContainer.find("img").remove();
22635      }
22636
22637      // Capture video metadata
22638      if (!trackingState.videoTitle && event.target.videoTitle) {
22639        trackingState.videoTitle = event.target.videoTitle;
22640      }
22641      var duration = event.target.getDuration();
22642      if (trackingState.videoDuration === 0 && duration > 0) {
22643        trackingState.videoDuration = Math.round(duration);
22644      }
22645
22646      switch (event.data) {
22647        case window.YT.PlayerState.PLAYING || window.YT.PlayerState.UNSTARTED:
22648          playerContainer.addClass("playing");
22649          trackingState.hasVideoEnded = false;
22650
22651          // Only fire video_start once
22652          if (!trackingState.hasVideoStartFired) {
22653            vt.functions.pushToDataLayer({
22654              event: "video_start",
22655              video_provider: "youtube",
22656              video_title: trackingState.videoTitle || event.target.videoTitle || "Unknown Title",
22657              video_duration: trackingState.videoDuration || Math.round(duration),
22658            });
22659            trackingState.hasVideoStartFired = true;
22660            trackingState.firedMilestones = [];
22661          }
22662          startProgressTracking(event.target);
22663          break;
22664
22665        case window.YT.PlayerState.PAUSED:
22666          // Smart pause logic - only fire if not near completion and video hasn't ended
22667          if (!trackingState.hasVideoEnded) {
22668            var currentTime = event.target.getCurrentTime();
22669            var totalDuration = event.target.getDuration();
22670            var percentComplete = (currentTime / totalDuration) * 100;
22671
22672            // Only fire pause if less than 95% complete
22673            if (percentComplete < 95) {
22674              vt.functions.pushToDataLayer({
22675                event: "video_paused",
22676                video_provider: "youtube",
22677                video_title: trackingState.videoTitle || event.target.videoTitle || "Unknown Title",
22678                video_duration: trackingState.videoDuration || Math.round(totalDuration),
22679              });
22680            }
22681          }
22682          stopProgressTracking();
22683          break;
22684
22685        case window.YT.PlayerState.ENDED:
22686          trackingState.hasVideoEnded = true;
22687          event.target.playVideo();
22688          stopProgressTracking();
22689
22690          // Fire complete event if not already fired via milestone
22691          if (!trackingState.firedMilestones.includes(100)) {
22692            vt.functions.pushToDataLayer({
22693              event: "video_complete",
22694              video_provider: "youtube",
22695              video_title: trackingState.videoTitle || event.target.videoTitle || "Unknown Title",
22696              video_duration: trackingState.videoDuration || Math.round(duration),
22697              percentage_watched: 100
22698            });
22699          }
22700
22701          // Reset for replay
22702          trackingState.firedMilestones = [];
22703          trackingState.hasVideoStartFired = false;
22704          trackingState.hasVideoEnded = false;
22705          break;
22706      }
22707    };
22708    // stateChange on video (mast-head)
22709    onPlayerStateChangeHeader = function (event) {
22710      var playerContainer = $(event.target.getIframe()).closest(".embed-container");
22711
22712      // Capture video metadata
22713      if (!trackingState.videoTitle && event.target.videoTitle) {
22714        trackingState.videoTitle = event.target.videoTitle;
22715      }
22716      var duration = event.target.getDuration();
22717      if (trackingState.videoDuration === 0 && duration > 0) {
22718        trackingState.videoDuration = Math.round(duration);
22719      }
22720
22721      switch (event.data) {
22722        case window.YT.PlayerState.PLAYING || window.YT.PlayerState.UNSTARTED:
22723          playerContainer.addClass("playing");
22724          playerContainer.find("img").remove();
22725          trackingState.hasVideoEnded = false;
22726
22727          // Only fire video_start once
22728          if (!trackingState.hasVideoStartFired) {
22729            vt.functions.pushToDataLayer({
22730              event: "video_start",
22731              video_provider: "youtube",
22732              video_title: trackingState.videoTitle || event.target.videoTitle || "Unknown Title",
22733              video_duration: trackingState.videoDuration || Math.round(duration),
22734            });
22735            trackingState.hasVideoStartFired = true;
22736            trackingState.firedMilestones = [];
22737          }
22738          startProgressTracking(event.target);
22739          break;
22740
22741        case window.YT.PlayerState.PAUSED:
22742          // Smart pause logic - only fire if not near completion and video hasn't ended
22743          if (!trackingState.hasVideoEnded) {
22744            var currentTime = event.target.getCurrentTime();
22745            var totalDuration = event.target.getDuration();
22746            var percentComplete = (currentTime / totalDuration) * 100;
22747
22748            // Only fire pause if less than 95% complete
22749            if (percentComplete < 95) {
22750              vt.functions.pushToDataLayer({
22751                event: "video_paused",
22752                video_provider: "youtube",
22753                video_title: trackingState.videoTitle || event.target.videoTitle || "Unknown Title",
22754                video_duration: trackingState.videoDuration || Math.round(totalDuration),
22755              });
22756            }
22757          }
22758          stopProgressTracking();
22759          break;
22760
22761        case window.YT.PlayerState.ENDED:
22762          trackingState.hasVideoEnded = true;
22763          event.target.playVideo();
22764          stopProgressTracking();
22765
22766          // Fire complete event if not already fired via milestone
22767          if (!trackingState.firedMilestones.includes(100)) {
22768            vt.functions.pushToDataLayer({
22769              event: "video_complete",
22770              video_provider: "youtube",
22771              video_title: trackingState.videoTitle || event.target.videoTitle || "Unknown Title",
22772              video_duration: trackingState.videoDuration || Math.round(duration),
22773              percentage_watched: 100
22774            });
22775          }
22776
22777          // Reset for replay
22778          trackingState.firedMilestones = [];
22779          trackingState.hasVideoStartFired = false;
22780          trackingState.hasVideoEnded = false;
22781          break;
22782      }
22783    };
22784
22785    var progressInterval = null;
22786
22787    startProgressTracking = function (target) {
22788      stopProgressTracking();
22789      progressInterval = setInterval(function () {
22790        try {
22791          const current = target.getCurrentTime();
22792          const total = target.getDuration();
22793
22794          if (total === 0) return; // Avoid division by zero
22795
22796          // Store video duration if not already set
22797          if (trackingState.videoDuration === 0 && total > 0) {
22798            trackingState.videoDuration = Math.round(total);
22799          }
22800
22801          const percent = (current / total) * 100;
22802          const breakpoints = [10, 25, 50, 75, 90, 100];
22803
22804          breakpoints.forEach(function (breakpoint) {
22805            // Only fire if we've crossed threshold and haven't fired it yet
22806            if (percent >= breakpoint && !trackingState.firedMilestones.includes(breakpoint)) {
22807              trackingState.firedMilestones.push(breakpoint);
22808
22809              var eventName = breakpoint === 100 ? "video_complete" : "video_progression";
22810              var eventLabel = breakpoint === 100
22811                ? "Video completed - " + (trackingState.videoTitle || "Unknown Title")
22812                : breakpoint === 90
22813                  ? "Video nearly completed - " + (trackingState.videoTitle || "Unknown Title")
22814                  : breakpoint + "% Video played - " + (trackingState.videoTitle || "Unknown Title");
22815
22816              vt.functions.pushToDataLayer({
22817                event: eventName,
22818                video_provider: "youtube",
22819                video_title: trackingState.videoTitle || target.videoTitle || "Unknown Title",
22820                video_duration: trackingState.videoDuration || Math.round(total),
22821                percentage_watched: breakpoint,
22822                eventLabel: eventLabel
22823              });
22824            }
22825          });
22826        } catch (error) {
22827          console.warn('Progress tracking error:', error);
22828        }
22829      }, 500); // Use 500ms for more reliable milestone detection
22830    };
22831
22832    stopProgressTracking = function () {
22833      clearInterval(progressInterval);
22834      progressInterval = null;
22835    };
22836
22837    // on vimeo api is ready
22838    var onVimeoIframeAPIReady = function (elm, videoId, mobile) {
22839      if (!elm || !videoId) {
22840        console.error('Invalid element or video ID for Vimeo player');
22841        return;
22842      }
22843
22844      try {
22845        var playerContainer = $(elm).closest(".embed-container");
22846        var mastHead = $(elm).parents(".mast-head").length;
22847        var contentGallery = $(elm).parents("#content-gallery").length;
22848        var stepList = $(elm).parents(".step-list-content").length;
22849        var shouldLoop = mobile !== null && mastHead > 0;
22850        var shouldAutoplay = typeof mobile === "undefined" || mobile === null;
22851        var options = {
22852          id: videoId,
22853          width: "100%",
22854          title: false,
22855          loop: shouldLoop,
22856          autoplay: shouldAutoplay,
22857        };
22858
22859        playerInstances.vimeoPlayer = new Vimeo.Player(elm, options);
22860        var vimeo = playerInstances.vimeoPlayer;
22861
22862        // check if video is in mast-head
22863        if (mastHead >= 1) {
22864          vimeo.setVolume(0);
22865        }
22866
22867        if (contentGallery >= 1 || stepList >= 1) {
22868          playerContainer.addClass("playing");
22869        }
22870
22871        // click event on mobile autoplay
22872        /*
22873        var mobilePlayContainer = elm.closest(".js-video");
22874        mobilePlayContainer = $(mobilePlayContainer);
22875        mobilePlayContainer.on("click", function (e) {
22876          vimeo.play();
22877        });*/
22878
22879        // Capture video metadata for Vimeo
22880        vimeo.getDuration().then(function (duration) {
22881          if (trackingState.videoDuration === 0 && duration > 0) {
22882            trackingState.videoDuration = Math.round(duration);
22883          }
22884        }).catch(function (error) {
22885          console.warn('Could not get Vimeo duration:', error);
22886        });
22887
22888        // Set video title for Vimeo
22889        if (!trackingState.videoTitle) {
22890          trackingState.videoTitle = videoId;
22891        }
22892
22893        // Vimeo event tracking
22894        vimeo.on("play", function () {
22895          playerContainer.addClass("playing");
22896          playerContainer.find("img").remove();
22897          trackingState.hasVideoEnded = false;
22898
22899          // Only fire video_start once
22900          if (!trackingState.hasVideoStartFired) {
22901            vt.functions.pushToDataLayer({
22902              event: "video_start",
22903              video_provider: "vimeo",
22904              video_title: trackingState.videoTitle || videoId,
22905              video_duration: trackingState.videoDuration || 0,
22906            });
22907            trackingState.hasVideoStartFired = true;
22908            trackingState.firedMilestones = [];
22909          }
22910          startProgressTrackingVimeo(vimeo);
22911        });
22912
22913        vimeo.on("pause", function () {
22914          // Smart pause logic - only fire if not near completion and video hasn't ended
22915          if (!trackingState.hasVideoEnded) {
22916            vimeo.getCurrentTime().then(function (currentTime) {
22917              return vimeo.getDuration().then(function (totalDuration) {
22918                var percentComplete = (currentTime / totalDuration) * 100;
22919
22920                // Only fire pause if less than 95% complete
22921                if (percentComplete < 95) {
22922                  vt.functions.pushToDataLayer({
22923                    event: "video_paused",
22924                    video_provider: "vimeo",
22925                    video_title: trackingState.videoTitle || videoId,
22926                    video_duration: trackingState.videoDuration || Math.round(totalDuration),
22927                  });
22928                }
22929              });
22930            }).catch(function (error) {
22931              console.warn('Error checking video position:', error);
22932            });
22933          }
22934          stopProgressTrackingVimeo();
22935        });
22936
22937        vimeo.on("ended", function () {
22938          trackingState.hasVideoEnded = true;
22939          stopProgressTrackingVimeo();
22940
22941          // Fire complete event if not already fired via milestone
22942          if (!trackingState.firedMilestones.includes(100)) {
22943            vt.functions.pushToDataLayer({
22944              event: "video_complete",
22945              video_provider: "vimeo",
22946              video_title: trackingState.videoTitle || videoId,
22947              video_duration: trackingState.videoDuration || 0,
22948              percentage_watched: 100
22949            });
22950          }
22951
22952          // Reset for replay
22953          trackingState.firedMilestones = [];
22954          trackingState.hasVideoStartFired = false;
22955          trackingState.hasVideoEnded = false;
22956        });
22957
22958        // Progress tracking for Vimeo
22959        var progressIntervalVimeo = null;
22960
22961        function startProgressTrackingVimeo(player) {
22962          stopProgressTrackingVimeo();
22963          progressIntervalVimeo = setInterval(async function () {
22964            try {
22965              const current = await player.getCurrentTime();
22966              const total = await player.getDuration();
22967
22968              if (total === 0) return; // Avoid division by zero
22969
22970              // Store video duration if not already set
22971              if (trackingState.videoDuration === 0 && total > 0) {
22972                trackingState.videoDuration = Math.round(total);
22973              }
22974
22975              const percent = (current / total) * 100;
22976              const breakpoints = [10, 25, 50, 75, 90, 100];
22977
22978              breakpoints.forEach(function (breakpoint) {
22979                // Only fire if we've crossed threshold and haven't fired it yet
22980                if (percent >= breakpoint && !trackingState.firedMilestones.includes(breakpoint)) {
22981                  trackingState.firedMilestones.push(breakpoint);
22982
22983                  var eventName = breakpoint === 100 ? "video_complete" : "video_progression";
22984                  var eventLabel = breakpoint === 100
22985                    ? "Video completed - " + (trackingState.videoTitle || videoId)
22986                    : breakpoint === 90
22987                      ? "Video nearly completed - " + (trackingState.videoTitle || videoId)
22988                      : breakpoint + "% Video played - " + (trackingState.videoTitle || videoId);
22989
22990                  vt.functions.pushToDataLayer({
22991                    event: eventName,
22992                    video_provider: "vimeo",
22993                    video_title: trackingState.videoTitle || videoId,
22994                    video_duration: trackingState.videoDuration || Math.round(total),
22995                    percentage_watched: breakpoint,
22996                    eventLabel: eventLabel
22997                  });
22998                }
22999              });
23000            } catch (error) {
23001              console.warn('Progress tracking error:', error);
23002            }
23003          }, 500); // Use 500ms for more reliable milestone detection
23004        }
23005
23006        function stopProgressTrackingVimeo() {
23007          clearInterval(progressIntervalVimeo);
23008          progressIntervalVimeo = null;
23009        }
23010      } catch (error) {
23011        console.error('Error initializing Vimeo player:', error);
23012        playerInstances.vimeoPlayer = null;
23013      }
23014    };
23015
23016    // Kontainer HLS player using hls.js or native HLS (Safari)
23017    var onKontainerVideoReady = function (elm, videoUrl, mobile) {
23018      if (!elm || !videoUrl) {
23019        console.error('Invalid element or video URL for Kontainer player');
23020        return;
23021      }
23022
23023      try {
23024        var playerContainer = $(elm).closest('.embed-container');
23025        var shouldAutoplay = mobile === null || mobile === undefined;
23026
23027        var video = document.createElement('video');
23028        video.style.width = '100%';
23029        video.style.height = '100%';
23030        video.style.display = 'block';
23031        video.controls = true;
23032        video.autoplay = shouldAutoplay;
23033        video.playsInline = true;
23034
23035        // Required for cross-origin <track> elements (VTT subtitle files) to load without CORS errors
23036        if (subtitleUrls.url1 || subtitleUrls.url2) {
23037          video.crossOrigin = 'anonymous';
23038        }
23039
23040        // Add subtitle tracks if provided via data-sub-url-da / data-sub-url-en
23041        if (subtitleUrls.url1) {
23042          var track1 = document.createElement('track');
23043          track1.kind = 'subtitles';
23044          track1.src = subtitleUrls.url1;
23045          track1.label = 'Dansk';
23046          track1.srclang = 'da';
23047          video.appendChild(track1);
23048        }
23049        if (subtitleUrls.url2) {
23050          var track2 = document.createElement('track');
23051          track2.kind = 'subtitles';
23052          track2.src = subtitleUrls.url2;
23053          track2.label = 'English';
23054          track2.srclang = 'en';
23055          video.appendChild(track2);
23056        }
23057
23058        elm.appendChild(video);
23059
23060        // Activate the first subtitle track — dynamically added tracks default to
23061        // mode "disabled" and the 'default' attribute is unreliable for JS-created tracks.
23062        if (subtitleUrls.url1 && video.textTracks && video.textTracks.length > 0) {
23063          video.textTracks[0].mode = 'showing';
23064        }
23065        playerInstances.kontainerPlayer = video;
23066
23067        var progressIntervalHtml5 = null;
23068
23069        stopProgressTrackingHtml5 = function () {
23070          clearInterval(progressIntervalHtml5);
23071          progressIntervalHtml5 = null;
23072        };
23073
23074        var startProgressTrackingHtml5 = function () {
23075          stopProgressTrackingHtml5();
23076          progressIntervalHtml5 = setInterval(function () {
23077            try {
23078              var current = video.currentTime;
23079              var total = video.duration;
23080              if (!total || total === 0) return;
23081
23082              if (trackingState.videoDuration === 0 && total > 0) {
23083                trackingState.videoDuration = Math.round(total);
23084              }
23085
23086              var percent = (current / total) * 100;
23087              var breakpoints = [10, 25, 50, 75, 90, 100];
23088
23089              breakpoints.forEach(function (breakpoint) {
23090                if (percent >= breakpoint && !trackingState.firedMilestones.includes(breakpoint)) {
23091                  trackingState.firedMilestones.push(breakpoint);
23092
23093                  var eventName = breakpoint === 100 ? 'video_complete' : 'video_progression';
23094                  var eventLabel = breakpoint === 100
23095                    ? 'Video completed - ' + (trackingState.videoTitle || 'Unknown Title')
23096                    : breakpoint === 90
23097                      ? 'Video nearly completed - ' + (trackingState.videoTitle || 'Unknown Title')
23098                      : breakpoint + '% Video played - ' + (trackingState.videoTitle || 'Unknown Title');
23099
23100                  vt.functions.pushToDataLayer({
23101                    event: eventName,
23102                    video_provider: 'kontainer',
23103                    video_title: trackingState.videoTitle || 'Unknown Title',
23104                    video_duration: trackingState.videoDuration || Math.round(total),
23105                    percentage_watched: breakpoint,
23106                    eventLabel: eventLabel
23107                  });
23108                }
23109              });
23110            } catch (error) {
23111              console.warn('Kontainer progress tracking error:', error);
23112            }
23113          }, 500);
23114        };
23115
23116        // Attach HLS source via hls.js or native
23117        if (typeof Hls !== 'undefined' && Hls.isSupported()) {
23118          var hls = new Hls();
23119          playerInstances.kontainerHls = hls;
23120          hls.loadSource(videoUrl);
23121          hls.attachMedia(video);
23122          if (shouldAutoplay) {
23123            hls.on(Hls.Events.MANIFEST_PARSED, function () {
23124              video.play().catch(function (e) {
23125                console.warn('Kontainer autoplay prevented:', e);
23126              });
23127            });
23128          }
23129        } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
23130          // Native HLS support (Safari)
23131          video.src = videoUrl;
23132          if (shouldAutoplay) {
23133            video.play().catch(function (e) {
23134              console.warn('Kontainer autoplay prevented:', e);
23135            });
23136          }
23137        } else {
23138          console.error('HLS is not supported in this browser and hls.js is not loaded.');
23139          return;
23140        }
23141
23142        // Capture metadata
23143        video.addEventListener('loadedmetadata', function () {
23144          if (trackingState.videoDuration === 0 && video.duration > 0) {
23145            trackingState.videoDuration = Math.round(video.duration);
23146          }
23147          if (!trackingState.videoTitle) {
23148            trackingState.videoTitle = videoUrl;
23149          }
23150        });
23151
23152        video.addEventListener('play', function () {
23153          playerContainer.addClass('playing');
23154          playerContainer.find('img').remove();
23155          trackingState.hasVideoEnded = false;
23156
23157          if (!trackingState.hasVideoStartFired) {
23158            vt.functions.pushToDataLayer({
23159              event: 'video_start',
23160              video_provider: 'kontainer',
23161              video_title: trackingState.videoTitle || videoUrl,
23162              video_duration: trackingState.videoDuration || 0
23163            });
23164            trackingState.hasVideoStartFired = true;
23165            trackingState.firedMilestones = [];
23166          }
23167          startProgressTrackingHtml5();
23168        });
23169
23170        video.addEventListener('pause', function () {
23171          if (!trackingState.hasVideoEnded) {
23172            var percentComplete = video.duration > 0 ? (video.currentTime / video.duration) * 100 : 0;
23173            if (percentComplete < 95) {
23174              vt.functions.pushToDataLayer({
23175                event: 'video_paused',
23176                video_provider: 'kontainer',
23177                video_title: trackingState.videoTitle || videoUrl,
23178                video_duration: trackingState.videoDuration || Math.round(video.duration) || 0
23179              });
23180            }
23181          }
23182          stopProgressTrackingHtml5();
23183        });
23184
23185        video.addEventListener('ended', function () {
23186          trackingState.hasVideoEnded = true;
23187          stopProgressTrackingHtml5();
23188
23189          if (!trackingState.firedMilestones.includes(100)) {
23190            vt.functions.pushToDataLayer({
23191              event: 'video_complete',
23192              video_provider: 'kontainer',
23193              video_title: trackingState.videoTitle || videoUrl,
23194              video_duration: trackingState.videoDuration || 0,
23195              percentage_watched: 100
23196            });
23197          }
23198
23199          // Reset for replay
23200          trackingState.firedMilestones = [];
23201          trackingState.hasVideoStartFired = false;
23202          trackingState.hasVideoEnded = false;
23203        });
23204
23205      } catch (error) {
23206        console.error('Error initializing Kontainer player:', error);
23207        playerInstances.kontainerPlayer = null;
23208        playerInstances.kontainerHls = null;
23209      }
23210    };
23211
23212    var loadScript = function (provider) {
23213      // Load only the needed provider's script
23214      if (provider === CONSTANTS.PROVIDERS.YOUTUBE || !provider) {
23215        if (typeof YT === "undefined" || typeof YT.Player === "undefined") {
23216          // Check if script is already loading
23217          if (!$('script[src*="youtube.com/iframe_api"]').length) {
23218            $.getScript("https://www.youtube.com/iframe_api")
23219              .fail(function (jqxhr, settings, exception) {
23220                console.error('Failed to load YouTube API:', exception);
23221              });
23222          }
23223        }
23224      }
23225
23226      if (provider === CONSTANTS.PROVIDERS.VIMEO || !provider) {
23227        if (typeof Vimeo === "undefined" || typeof Vimeo.Player === "undefined") {
23228          // Check if script is already loading
23229          if (!$('script[src*="player.vimeo.com"]').length) {
23230            $.getScript("https://player.vimeo.com/api/player.js")
23231              .fail(function (jqxhr, settings, exception) {
23232                console.error('Failed to load Vimeo API:', exception);
23233              });
23234          }
23235        }
23236      }
23237
23238      if (provider === CONSTANTS.PROVIDERS.KONTAINER ||
23239          (!provider && $(elem).data('videoUrl') && $(elem).data('videoUrl').indexOf('kontainer.com') >= 0)) {
23240        if (typeof Hls === 'undefined') {
23241          if (!$('script[src*="hls.min.js"]').length) {
23242            $.getScript('https://cdn.jsdelivr.net/npm/hls.js@1/dist/hls.min.js')
23243              .fail(function (jqxhr, settings, exception) {
23244                console.error('Failed to load hls.js:', exception);
23245              });
23246          }
23247        }
23248      }
23249    };
23250
23251    var consentGiven = function () {
23252      // Default to false if cookie system not available
23253      if (typeof CookieInformation === "undefined") {
23254        return true; // Allow videos if no cookie system present
23255      }
23256
23257      try {
23258        var consent = CookieInformation.getConsentGivenFor("cookie_cat_marketing");
23259        return Boolean(consent);
23260      } catch (error) {
23261        console.warn('Error checking consent:', error);
23262        return false;
23263      }
23264    };
23265
23266    var playWhenReady = function () {
23267      var videoUrl = $(elem).data('videoUrl') || '';
23268      var isKontainerUrl = videoUrl.indexOf('kontainer.com') >= 0;
23269
23270      if (isKontainerUrl) {
23271        // For Kontainer (HLS), only hls.js or native HLS support is needed
23272        var testVideo = document.createElement('video');
23273        var hasNativeHls = testVideo.canPlayType('application/vnd.apple.mpegurl') !== '';
23274        if (typeof Hls === 'undefined' && !hasNativeHls) {
23275          window.setTimeout(playWhenReady, 100);
23276        } else {
23277          getVideoData($(elem));
23278        }
23279      } else if (
23280        typeof YT == "undefined" ||
23281        typeof YT.Player == "undefined" ||
23282        typeof Vimeo == "undefined" ||
23283        typeof Vimeo.Player == "undefined"
23284      ) {
23285        window.setTimeout(playWhenReady, 100);
23286      } else {
23287        getVideoData($(elem));
23288      }
23289    };
23290
23291    // click event to start video
23292    $(elem)
23293      .find(".js-video-play")
23294      .on("click", function (e) {
23295        if ($(e.target).closest('.prev, .next').length) {
23296          return;
23297        }
23298        // Don't re-initialise if already playing
23299        if ($(this).hasClass('is-playing') || $(this).find('iframe, .js-embed').length) {
23300          return;
23301        }
23302        var videoUrl = $(elem).data('videoUrl') || '';
23303        var isKontainer = videoUrl.indexOf('kontainer.com') >= 0;
23304        if (isKontainer || consentGiven()) {
23305          loadScript(); // Make sure script is loaded, it only load if not already loaded.
23306          playWhenReady();
23307        }
23308      });
23309
23310    // attach event on cookie button
23311    $(elem)
23312      .find(".js-cookie-button")
23313      .on("click", function () {
23314        if (typeof CookieInformation !== "undefined") {
23315          CookieInformation.changeCategoryConsentDecision(
23316            "cookie_cat_marketing"
23317          );
23318          CookieInformation.submitConsent();
23319        }
23320      });
23321
23322    var _initVideoUrl = $(elem).data('videoUrl') || '';
23323    var _isKontainer = _initVideoUrl.indexOf('kontainer.com') >= 0;
23324    if (!_isKontainer && !consentGiven()) {
23325      $(".js-cookie-consent").addClass("show");
23326    } else {
23327      loadScript();
23328    }
23329
23330    // autoplay
23331    if ($(elem).hasClass("js-video-autoplay") && !settings.mq()) {
23332      var videoData = $(elem).data();
23333      getVideoData($(elem));
23334    }
23335
23336    // Store consent handler for cleanup
23337    eventHandlers.consentHandler = function (event) {
23338      try {
23339        if (typeof CookieInformation !== 'undefined' &&
23340          CookieInformation.getConsentGivenFor("cookie_cat_marketing")) {
23341          loadScript();
23342          $(".js-cookie-consent").removeClass("show");
23343        }
23344      } catch (error) {
23345        console.warn('Error handling consent event:', error);
23346      }
23347    };
23348
23349    window.addEventListener(
23350      "CookieInformationConsentGiven",
23351      eventHandlers.consentHandler,
23352      false
23353    );
23354  };
23355})(jQuery);
23356;
23357; (function ($) {
23358    window.vt = window.vt || {};
23359
23360    vt.webdocVideo = function (element) {
23361
23362        var video = function video(el) {
23363            this.element = el;
23364            this.shouldUseLoopingVideo = el.dataset.webdocVideoEnabled == 'True';
23365            this.isPlayer = el.dataset.webdocVideoType == 'player';
23366            this.srcMp4 = el.dataset.webdocVideoUrlMp4;
23367            this.srcWebm = el.dataset.webdocVideoUrlWebm;
23368            this.srcMobile = el.dataset.webdocVideoUrlMobile;
23369            this.poster = el.dataset.webdocVideoPoster;
23370            this.posterPositionClass = el.dataset.posterPosition;
23371            this.videoParent = $(this.element).closest('.mast-head__video-wrapper').get(0);
23372            this.init();
23373        };
23374
23375        video.prototype = {
23376            init: function init() {
23377                if (!this.srcMp4 && !this.srcWebm) return;
23378
23379                if (!this.shouldUseLoopingVideo && !this.isPlayer) {
23380                    this.usePoster(this.poster);
23381
23382                    return;
23383                }
23384
23385                this.createVideoElement(this.srcMp4, this.scrWebm, this.srcMobile);
23386
23387                if (this.isPlayer) {
23388                    this.usePoster(this.poster);
23389                    this.addEventListeners();
23390                }
23391
23392                if (this.isMobile()) {
23393                    window.addEventListener('resize', function () {
23394                        document.documentElement.scrollTop = document.body.scrollTop = 0;
23395                    });
23396                }
23397            },
23398            createVideoElement: function createVideoElement(srcMp4, srcWebm, srcMobile) {
23399                var video = document.createElement('video');
23400                var _this = this;
23401
23402                if (!this.isPlayer && this.shouldUseLoopingVideo) {
23403                    // video.setAttribute('autoplay', true);
23404                    video.setAttribute('playsinline', true);
23405                    video.setAttribute('muted', true);
23406                    video.muted = true;
23407                    video.loop = true;
23408                }
23409
23410                if (srcMp4) {
23411                    var sourceMp4 = document.createElement('source');
23412
23413                    sourceMp4.src = srcMp4;
23414
23415                    // overwrite source with mobile video source is present and mobile
23416                    if (this.isMobile() && srcMobile) {
23417                        sourceMp4.src = srcMobile;
23418                    }
23419                    sourceMp4.type = 'video/mp4';
23420
23421                    video.appendChild(sourceMp4);
23422                }
23423
23424                if (srcWebm) {
23425                    var sourceWebm = document.createElement('source');
23426
23427                    sourceWebm.src = srcWebm;
23428                    sourceWebm.type = 'video/webm';
23429
23430                    video.appendChild(sourceWebm);
23431                }
23432
23433
23434                this.element.appendChild(video);
23435                video.muted = true;
23436                video.play();
23437
23438                if (!this.isPlayer) {
23439                    var checkForLoad = setInterval(function () {
23440                        if (video.readyState >= 3) {
23441                            _this.element.classList.add('webdoc__video--ready');
23442                            if (_this.videoParent.classList.contains('active') || document.body.classList.contains('pageedit')) {
23443                                video.play();
23444                            }
23445
23446                            clearInterval(checkForLoad);
23447                        }
23448                    }, 500);
23449                }
23450            },
23451            usePoster: function usePoster(src) {
23452                if (!src) {
23453                    this.element.classList.add('webdoc__video--ready');
23454                    return;
23455                }
23456
23457                var _this = this;
23458                var imageElement = document.createElement('IMG');
23459                imageElement.src = src;
23460
23461                imageElement.classList.add('webdoc__poster');
23462
23463                if (this.posterPositionClass) {
23464                    imageElement.classList.add(this.posterPositionClass);
23465                }
23466
23467                imageElement.onload = function () {
23468                    _this.element.classList.add('webdoc__video--ready');
23469                }
23470
23471                _this.element.appendChild(imageElement);
23472            },
23473            addEventListeners: function addEventListeners() {
23474                var _this = this;
23475                var playButton = this.videoParent.querySelector('.webdoc__play-button');
23476                var video = this.element.querySelector('video');
23477                var videoPlayingClass = 'webdoc__section--playing';
23478
23479                playButton.addEventListener('click', function (e) {
23480                    e.preventDefault();
23481
23482                    if (!video.paused) {
23483                        _this.videoParent.classList.remove(videoPlayingClass);
23484                        video.pause();
23485                    } else {
23486                        video.play();
23487
23488                        if (video.readyState >= 3) {
23489                            _this.videoParent.classList.add(videoPlayingClass);
23490                        }
23491
23492                        if (_this.isMobile()) {
23493                            if (typeof (video.webkitEnterFullscreen) != "undefined") {
23494                                video.webkitEnterFullscreen();
23495                            } else if (typeof (video.webkitRequestFullscreen) != "undefined") {
23496                                video.webkitRequestFullscreen();
23497                            } else if (typeof (video.mozRequestFullScreen) != "undefined") {
23498                                video.mozRequestFullScreen();
23499                            }
23500                        }
23501                    }
23502                });
23503
23504                video.addEventListener('ended', function (e) {
23505                    e.preventDefault();
23506
23507                    this.currentTime = 0;
23508                    _this.videoParent.classList.remove(videoPlayingClass);
23509                });
23510            },
23511            isMobile: function isMobile() {
23512                var userAgent = navigator.userAgent || navigator.vendor || window.opera;
23513
23514                return (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(userAgent) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|
23514sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(userAgent.substr(0, 4)));
23515            }
23516        };
23517
23518        var videoInstance = new video(element);
23519    }
23520})(jQuery);;
23521; (function ($) {
23522
23523    window.vt = window.vt || {};
23524
23525    vt.initWysiwyg = function () {
23526
23527        var $wysiwyg = $('.wysiwyg'),
23528            quoteSource = $wysiwyg.data('quote-source') || '';
23529
23530        if ($('.pageedit').length > 0) {
23531            return;
23532        }
23533
23534        $('img', $wysiwyg).each(function () {
23535            if ($(this).attr("alt")) {
23536                var caption = this.alt;
23537                $(this).after('<div class="caption">' + caption + '</div>');
23538            }
23539        });
23540
23541        $('.quote', $wysiwyg).each(function () {
23542            if (quoteSource) {
23543                $(this).append('<footer>' + quoteSource + '</footer>');
23544            }
23545        });
23546
23547        $('a[href*=#]:not([href=#],[href*="#!"])', $wysiwyg).click(function (e) {
23548            if (
23549                location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') &&
23550                location.hostname == this.hostname
23551            ) {
23552                var target = $(this.hash);
23553                target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
23554                if (target.length) {
23555                    e.preventDefault();
23556
23557                    $('html,body').animate({
23558                        scrollTop: target.offset().top
23559                    }, 1000);
23560                    return false;
23561                }
23562            }
23563        });
23564
23565    }
23566
23567})(jQuery);;
23568; (function initializer() {
23569    'use strict';
23570
23571    // Ensure that console methods don't break on older browsers
23572    window.vt = window.vt || {};
23573    window.console = window.console || {};
23574    window.console.log = window.console.log || function () { };
23575    window.console.error = window.console.error || function () { };
23576
23577    // Push global pageview event to GTM dataLayer
23578    function pushPageview() {
23579        if (typeof window.dataLayer !== 'undefined' && Array.isArray(window.dataLayer)) {
23580            try {
23581                var pageviewData = {
23582                    event: 'pageview',
23583                    user_id: window.vt.userId || '',
23584                    page_path: window.location.pathname,
23585                    page_title: document.title
23586                };
23587                window.dataLayer.push(pageviewData);
23588            } catch (error) {
23589                console.error('GTM pageview dataLayer push error:', error);
23590            }
23591        } else {
23592            console.warn('dataLayer not available, retrying...');
23593            setTimeout(pushPageview, 100);
23594        }
23595    }
23596
23597    // Call immediately
23598    pushPageview();
23599
23600    /**
23601     * Resolves a nested property from a string path (e.g., "vt.initModal")
23602     * @param {Object} obj - The root object (typically window)
23603     * @param {string} path - Dot-separated property path
23604     * @returns {*} The resolved property value
23605     */
23606    var resolveHandler = function (obj, path) {
23607        return path.split('.').reduce(function (o, prop) {
23608            return o ? o[prop] : undefined;
23609        }, obj);
23610    };
23611
23612    var initializers = document.querySelectorAll('[data-js-initialize]');
23613
23614    for (var i = 0; i < initializers.length; i++) {
23615        var element = initializers[i];
23616        var handlerPath = element.getAttribute('data-js-initialize');
23617
23618        if (!handlerPath) {
23619            continue;
23620        }
23621
23622        try {
23623            var handler = resolveHandler(window, handlerPath);
23624
23625            if (typeof handler === 'function') {
23626                handler(element);
23627            } else {
23628                console.error('Handler is not a function:', handlerPath, element);
23629            }
23630        } catch (ex) {
23631            console.error('Error initializing component:', handlerPath, ex.message, element);
23632        }
23633    }
23634
23635})();
23636;

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.