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 ? ' ' : // 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) ? ' ' : ''); 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) ? ' ' : '') + 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">‹</li>' + 8403 '<li data-view="years current"></li>' + 8404 '<li data-view="years next">›</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">‹</li>' + 8411 '<li data-view="year current"></li>' + 8412 '<li data-view="year next">›</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">‹</li>' + 8419 '<li data-view="month current"></li>' + 8420 '<li data-view="month next">›</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> </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.