1 2!function(e){if(!e.hasInitialised){var t={escapeRegExp:function(e){return e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")},hasClass:function(e,t){var i=" ";return 1===e.nodeType&&(i+e.className+i).replace(/[\n\t]/g,i).indexOf(i+t+i)>=0},addClass:function(e,t){e.className+=" "+t},removeClass:function(e,t){var i=new RegExp("\\b"+this.escapeRegExp(t)+"\\b");e.className=e.className.replace(i,"")},interpolateString:function(e,t){var i=/{{([a-z][a-z0-9\-_]*)}}/gi;return e.replace(i,function(e){return t(arguments[1])||""})},getCookie:function(e){var t="; "+document.cookie,i=t.split("; "+e+"=");return 2!=i.length?void 0:i.pop().split(";").shift()},setCookie:function(e,t,i,n,o){var s=new Date;s.setDate(s.getDate()+(i||365));var r=[e+"="+t,"expires="+s.toUTCString(),"path="+(o||"/")];n&&r.push("domain="+n),document.cookie=r.join(";")},deepExtend:function(e,t){for(var i in t)t.hasOwnProperty(i)&&(i in e&&this.isPlainObject(e[i])&&this.isPlainObject(t[i])?this.deepExtend(e[i],t[i]):e[i]=t[i]);return e},throttle:function(e,t){var i=!1;return function(){i||(e.apply(this,arguments),i=!0,setTimeout(function(){i=!1},t))}},hash:function(e){var t,i,n,o=0;if(0===e.length)return o;for(t=0,n=e.length;t<n;++t)i=e.charCodeAt(t),o=(o<<5)-o+i,o|=0;return o},normaliseHex:function(e){return"#"==e[0]&&(e=e.substr(1)),3==e.length&&(e=e[0]+e[0]+e[1]+e[1]+e[2]+e[2]),e},getContrast:function(e){e=this.normaliseHex(e);var t=parseInt(e.substr(0,2),16),i=parseInt(e.substr(2,2),16),n=parseInt(e.substr(4,2),16),o=(299*t+587*i+114*n)/1e3;return o>=128?"#000":"#fff"},getLuminance:function(e){var t=parseInt(this.normaliseHex(e),16),i=38,n=(t>>16)+i,o=(t>>8&255)+i,s=(255&t)+i,r=(16777216+65536*(n<255?n<1?0:n:255)+256*(o<255?o<1?0:o:255)+(s<255?s<1?0:s:255)).toString(16).slice(1);return"#"+r},isMobile:function(){return/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)},isPlainObject:function(e){return"object"==typeof e&&null!==e&&e.constructor==Object}};
2e.status={deny:"deny",allow:"allow",dismiss:"dismiss"},e.transitionEnd=function(){var e=document.createElement("div"),t={t:"transitionend",OT:"oTransitionEnd",msT:"MSTransitionEnd",MozT:"transitionend",WebkitT:"webkitTransitionEnd"};for(var i in t)if(t.hasOwnProperty(i)&&"undefined"!=typeof e.style[i+"ransition"])return t[i];return""}(),e.hasTransition=!!e.transitionEnd;var i=Object.keys(e.status).map(t.escapeRegExp);e.customStyles={},e.Popup=function(){function n(){this.initialise.apply(this,arguments)}function o(e){this.openingTimeout=null,t.removeClass(e,"cc-invisible")}function s(t){t.style.display="none",t.removeEventListener(e.transitionEnd,this.afterTransition),this.afterTransition=null}function r(){var t=this.options.onInitialise.bind(this);if(!window.navigator.cookieEnabled)return t(e.status.deny),!0;if(window.CookiesOK||window.navigator.CookiesOK)return t(e.status.allow),!0;var i=Object.keys(e.status),n=this.getStatus(),o=i.indexOf(n)>=0;return o&&t(n),o}function a(){var e=this.options.position.split("-"),t=[];return e.forEach(function(e){t.push("cc-"+e)}),t}function c(){var e=this.options,i="top"==e.position||"bottom"==e.position?"banner":"floating";t.isMobile()&&(i="floating");var n=["cc-"+i,"cc-type-"+e.type,"cc-theme-"+e.theme];e["static"]&&n.push("cc-static"),n.push.apply(n,a.call(this));p.call(this,this.options.palette);return this.customStyleSelector&&n.push(this.customStyleSelector),n}function l(){var e={},i=this.options;i.showLink||(i.elements.link="",i.elements.messagelink=i.elements.message),Object.keys(i.elements).forEach(function(n){e[n]=t.interpolateString(i.elements[n],function(e){var t=i.content[e];return e&&"string"==typeof t&&t.length?t:""})});var n=i.compliance[i.type];n||(n=i.compliance.info),e.compliance=t.interpolateString(n,function(t){return e[t]});var o=i.layouts[i.layout];return o||(o=i.layouts.basic),t.interpolateString(o,function(t){return e[t]})}function u(i){var n=this.options,o=document.createElement("div"),s=n.container&&1===n.container.nodeType?n.container:document.body;o.innerHTML=i;var r=o.children[0];return r.style.display="none",t.hasClass(r,"cc-window")&&e.hasTransition&&t.addClass(r,"cc-invisible"),this.onButtonClick=h.bind(this),r.addEventListener("click",this.onButtonClick),n.autoAttach&&(s.firstChild?s.insertBefore(r,s.firstChild):s.appendChild(r)),r}function h(n){var o=n.target;if(t.hasClass(o,"cc-btn")){var s=o.className.match(new RegExp("\\bcc-("+i.join("|")+")\\b")),r=s&&s[1]||!1;r&&(this.setStatus(r),this.close(!0))}t.hasClass(o,"cc-close")&&(this.setStatus(e.status.dismiss),this.close(!0)),t.hasClass(o,"cc-revoke")&&this.revokeChoice()}function p(e){var i=t.hash(JSON.stringify(e)),n="cc-color-override-"+i,o=t.isPlainObject(e);return this.customStyleSelector=o?n:null,o&&d(i,e,"."+n),o}function d(i,n,o){if(e.customStyles[i])return void++e.customStyles[i].references;var s={},r=n.popup,a=n.button,c=n.highlight;r&&(r.text=r.text?r.text:t.getContrast(r.background),r.link=r.link?r.link:r.text,s[o+".cc-window"]=["color: "+r.text,"background-color: "+r.background],s[o+".cc-revoke"]=["color: "+r.text,"background-color: "+r.background],s[o+" .cc-link,"+o+" .cc-link:active,"+o+" .cc-link:visited"]=["color: "+r.link],a&&(a.text=a.text?a.text:t.getContrast(a.background),a.border=a.border?a.border:"transparent",s[o+" .cc-btn"]=["color: "+a.text,"border-color: "+a.border,"background-color: "+a.background],"transparent"!=a.background&&(s[o+" .cc-btn:hover, "+o+" .cc-btn:focus"]=["background-color: "+v(a.background)]),c?(c.text=c.text?c.text:t.getContrast(c.background),c.border=c.border?c.border:"transparent",s[o+" .cc-highlight .cc-btn:first-child"]=["color: "+c.text,"border-color: "+c.border,"background-color: "+c.background]):s[o+" .cc-highlight .cc-btn:first-child"]=["color: "+r.text]));var l=document.createElement("style");document.head.appendChild(l),e.customStyles[i]={references:1,element:l.sheet};var u=-1;for(var h in s)s.hasOwnProperty(h)&&l.sheet.insertRule(h+"{"+s[h].join(";")+"}",++u)}function v(e){return e=t.normaliseHex(e),"000000"==e?"#222":t.getLuminance(e)}function f(i){if(t.isPlainObject(i)){var n=t.hash(JSON.stringify(i)),o=e.customStyles[n];if(o&&!--o.references){var s=o.element.ownerNode;s&&s.parentNode&&s.parentNode.removeChild(s),e.customStyles[n]=null}}}function m(e,t){for(var i=0,n=e.length;i<n;++i){var o=e[i];if(o instanceof RegExp&&o.test(t)||"string"==typeof o&&o.length&&o===t)return!0}return!1}function b(){var t=this.setStatus.bind(this),i=this.options.dismissOnTimeout;"number"==typeof i&&i>
2=0&&(this.dismissTimeout=window.setTimeout(function(){t(e.status.dismiss)},Math.floor(i)));var n=this.options.dismissOnScroll;if("number"==typeof n&&n>=0){var o=function(i){window.pageYOffset>Math.floor(n)&&(t(e.status.dismiss),window.removeEventListener("scroll",o),this.onWindowScroll=null)};this.onWindowScroll=o,window.addEventListener("scroll",o)}}function y(){if("info"!=this.options.type&&(this.options.revokable=!0),t.isMobile()&&(this.options.animateRevokable=!1),this.options.revokable){var e=a.call(this);this.options.animateRevokable&&e.push("cc-animate"),this.customStyleSelector&&e.push(this.customStyleSelector);var i=this.options.revokeBtn.replace("{{classes}}",e.join(" "));this.revokeBtn=u.call(this,i);var n=this.revokeBtn;if(this.options.animateRevokable){var o=t.throttle(function(e){var i=!1,o=20,s=window.innerHeight-20;t.hasClass(n,"cc-top")&&e.clientY<o&&(i=!0),t.hasClass(n,"cc-bottom")&&e.clientY>s&&(i=!0),i?t.hasClass(n,"cc-active")||t.addClass(n,"cc-active"):t.hasClass(n,"cc-active")&&t.removeClass(n,"cc-active")},200);this.onMouseMove=o,window.addEventListener("mousemove",o)}}}var g={enabled:!0,container:null,cookie:{name:"cookieconsent_status",path:"/",domain:"",expiryDays:365},onPopupOpen:function(){},onPopupClose:function(){},onInitialise:function(e){},onStatusChange:function(e,t){},onRevokeChoice:function(){},content:{header:"Cookies used on the website!",message:"This website uses cookies to ensure you get the best experience on our website.",dismiss:"Got it!",allow:"Allow cookies",deny:"Decline",link:"Learn more",href:"http://cookie
2sandyou.com",close:"❌"},elements:{header:'<span class="cc-header">{{header}}</span> ',message:'<span id="cookieconsent:desc" class="cc-message">{{message}}</span>',messagelink:'<span id="cookieconsent:desc" class="cc-message">{{message}} <a role=button tabindex="0" class="cc-link" href="{{href}}" target="_blank">{{link}}</a></span>',dismiss:'<a aria-label="dismiss cookie message" role=button tabindex="0" class="cc-btn cc-dismiss">{{dismiss}}</a>',allow:'<a aria-label="allow cookies" role=button tabindex="0" class="cc-btn cc-allow">{{allow}}</a>',deny:'<a aria-label="deny cookies" role=button tabindex="0" class="cc-btn cc-deny">{{deny}}</a>',link:'<a role=button tabindex="0" class="cc-link" href="{{href}}" target="_blank">{{link}}</a>',close:'<span aria-label="dismiss cookie message" role=button tabindex="0" class="cc-close">{{close}}</span>'},window:'<div role="dialog" aria-live="polite" aria-label="cookieconsent" aria-describedby="cookieconsent:desc" class="cc-window {{classes}}"><!--googleoff: all-->{{children}}<!--googleon: all--></div>',revokeBtn:'<div class="cc-revoke {{classes}}">Cookie Policy</div>',compliance:{info:'<div class="cc-compliance">{{dismiss}}</div>',"opt-in":'<div class="cc-compliance cc-highlight">{{dismiss}}{{allow}}</div>',"opt-out":'<div class="cc-compliance cc-highlight">{{deny}}{{dismiss}}</div>'},type:"info",layouts:{basic:"{{messagelink}}{{compliance}}","basic-close":"{{messagelink}}{{compliance}}{{close}}","basic-header":"{{header}}{{message}}{{link}}{{compliance}}"},layout:"basic",position:"bottom",theme:"block","static":!1,palette:null,revokable:!1,animateRevokable:!0,showLink:!0,dismissOnScroll:!1,dismissOnTimeout:!1,autoOpen:!0,autoAttach:!0,whitelistPage:[],blacklistPage:[],overrideHTML:null};return n.prototype.initialise=function(e){this.options&&this.destroy(),t.deepExtend(this.options={},g),t.isPlainObject(e)&&t.deepExtend(this.options,e),r.call(this)&&(this.options.enabled=!1),m(this.options.blacklistPage,location.pathname)&&(this.options.enabled=!1),m(this.options.whitelistPage,location.pathname)&&(this.options.enabled=!0);var i=this.options.window.replace("{{classes}}",c.call(this).join(" ")).replace("{{children}}",l.call(this)),n=this.options.overrideHTML;if("string"==typeof n&&n.length&&(i=n),this.options["static"]){var o=u.call(this,'<div class="cc-grower">'+i+"</div>");o.style.display="",this.element=o.firstChild,this.element.style.display="none",t.addClass(this.element,"cc-invisible")}else this.element=u.call(this,i);b.call(this),y.call(this),this.options.autoOpen&&this.autoOpen()},n.prototype.destroy=function(){this.onButtonClick&&this.element&&(this.element.removeEventListener("click",this.onButtonClick),this.onButtonClick=null),this.dismissTimeout&&(clearTimeout(this.dismissTimeout),this.dismissTimeout=null),this.onWindowScroll&&(window.removeEventListener("scroll",this.onWindowScroll),this.onWindowScroll=null),this.onMouseMove&&(window.removeEventListener("mousemove",this.onMouseMove),this.onMouseMove=null),this.element&&this.element.parentNode&&this.element.parentNode.removeChild(this.element),this.element=null,this.revokeBtn&&this.revokeBtn.parentNode&&this.revokeBtn.parentNode.removeChild(this.revokeBtn),this.revokeBtn=null,f(this.options.palette),this.options=null},n.prototype.open=function(t){if(this.element)return this.isOpen()||(e.hasTransition?this.fadeIn():this.element.style.display="",this.options.revokable&&this.toggleRevokeButton(),this.options.onPopupOpen.call(this)),this},n.prototype.close=function(t){if(this.element)return this.isOpen()&&(e.hasTransition?this.fadeOut():this.element.style.display="none",t&&this.options.revokable&&this.toggleRevokeButton(!0),this.options.onPopupClose.call(this)),this},n.prototype.fadeIn=function(){var i=this.element;if(e.hasTransition&&i&&(this.afterTransition&&s.call(this,i),t.hasClass(i,"cc-invisible"))){if(i.style.display="",this.options["static"]){var n=this.element.clientHeight;this.element.parentNode.style.maxHeight=n+"px"}var r=20;this.openingTimeout=setTimeout(o.bind(this,i),r)}},n.prototype.fadeOut=function(){var i=this.element;e.hasTransition&&i&&(this.openingTimeout&&(clearTimeout(this.openingTimeout),o.bind(this,i)),t.hasClass(i,"cc-invisible")||(this.options["static"]&&(this.element.parentNode.style.maxHeight=""),this.afterTransition=s.bind(this,i),i.addEventListener(e.transitionEnd,this.afterTransition),t.addClass(i,"cc-invisible")))},n.prototype.isOpen=function(){return this.element&&""==this.element.style.display&&(!e.hasTransition||!t.hasClass(this.element,"cc-invisible"))},n.prototype.toggleRevokeButton=function(e){this.revokeBtn&&(this.revokeBtn.style.display=e?"":"none")},n.prototype.revokeChoice=function(e){this.options.enabled=!0,this.clearStatus(),this.options.onRevokeChoice.call(this),e||this.autoOpen()},n.prototype.hasAnswered=function(t){return Object.keys(e.status).indexOf(this.getStatus())>=0}
2,n.prototype.hasConsented=function(t){var i=this.getStatus();return i==e.status.allow||i==e.status.dismiss},n.prototype.autoOpen=function(e){!this.hasAnswered()&&this.options.enabled&&this.open()},n.prototype.setStatus=function(i){var n=this.options.cookie,o=t.getCookie(n.name),s=Object.keys(e.status).indexOf(o)>=0;Object.keys(e.status).indexOf(i)>=0?(t.setCookie(n.name,i,n.expiryDays,n.domain,n.path),this.options.onStatusChange.call(this,i,s)):this.clearStatus()},n.prototype.getStatus=function(){return t.getCookie(this.options.cookie.name)},n.prototype.clearStatus=function(){var e=this.options.cookie;t.setCookie(e.name,"",-1,e.domain,e.path)},n}(),e.Location=function(){function e(e){t.deepExtend(this.options={},s),t.isPlainObject(e)&&t.deepExtend(this.options,e),this.currentServiceIndex=-1}function i(e,t,i){var n,o=document.createElement("script");o.type="text/"+(e.type||"javascript"),o.src=e.src||e,o.async=!1,o.onreadystatechange=o.onload=function(){var e=o.readyState;clearTimeout(n),t.done||e&&!/loaded|complete/.test(e)||(t.done=!0,t(),o.onreadystatechange=o.onload=null)},document.body.appendChild(o),n=setTimeout(function(){t.done=!0,t(),o.onreadystatechange=o.onload=null},i)}function n(e,t,i,n,o){var s=new(window.XMLHttpRequest||window.ActiveXObject)("MSXML2.XMLHTTP.3.0");if(s.open(n?"POST":"GET",e,1),s.setRequestHeader("X-Requested-With","XMLHttpRequest"),s.setRequestHeader("Content-type","application/x-www-form-urlencoded"),Array.isArray(o))for(var r=0,a=o.length;r<a;++r){var c=o[r].split(":",2);s.setRequestHeader(c[0].replace(/^\s+|\s+$/g,""),c[1].replace(/^\s+|\s+$/g,""))}"function"==typeof t&&(s.onreadystatechange=function(){s.readyState>3&&t(s)}),s.send(n)}function o(e){return new Error("Error ["+(e.code||"UNKNOWN")+"]: "+e.error)}var s={timeout:5e3,services:["freegeoip","ipinfo","maxmind"],serviceDefinitions:{freegeoip:function(){return{url:"//freegeoip.net/json/?callback={callback}",isScript:!0,callback:function(e,t){try{var i=JSON.parse(t);return i.error?o(i):{code:i.country_code}}catch(n){return o({error:"Invalid response ("+n+")"})}}}},ipinfo:function(){return{url:"//ipinfo.io",headers:["Accept: application/json"],callback:function(e,t){try{var i=JSON.parse(t);return i.error?o(i):{code:i.country}}catch(n){return o({error:"Invalid response ("+n+")"})}}}},ipinfodb:function(e){return{url:"//api.ipinfodb.com/v3/ip-country/?key={api_key}&format=json&callback={callback}",isScript:!0,callback:function(e,t){try{var i=JSON.parse(t);return"ERROR"==i.statusCode?o({error:i.statusMessage}):{code:i.countryCode}}catch(n){return o({error:"Invalid response ("+n+")"})}}}},maxmind:function(){return{url:"//js.maxmind.com/js/apis/geoip2/v2.1/geoip2.js",isScript:!0,callback:function(e){return window.geoip2?void geoip2.country(function(t){try{e({code:t.country.iso_code})}catch(i){e(o(i))}},function(t){e(o(t))}):void e(new Error("Unexpected response format. The downloaded script should have exported `geoip2` to the global scope"))}}}}};return e.prototype.getNextService=function(){var e;do e=this.getServiceByIdx(++this.currentServiceIndex);while(this.currentServiceIndex<this.options.services.length&&!e);return e},e.prototype.getServiceByIdx=function(e){var i=this.options.services[e];if("function"==typeof i){var n=i();return n.name&&t.deepExtend(n,this.options.serviceDefinitions[n.name](n)),n}return"string"==typeof i?this.options.serviceDefinitions[i]():t.isPlainObject(i)?this.options.serviceDefinitions[i.name](i):null},e.prototype.locate=function(e,t){var i=this.getNextService();return i?(this.callbackComplete=e,this.callbackError=t,void this.runService(i,this.runNextServiceOnError.bind(this))):void t(new Error("No services to run"))},e.prototype.setupUrl=function(e){var t=this.getCurrentServiceOpts();return e.url.replace(/\{(.*?)\}/g,function(i,n){if("callback"===n){var o="callback"+Date.now();return window[o]=function(t){e.__JSONP_DATA=JSON.stringify(t)},o}if(n in t.interpolateUrl)return t.interpolateUrl[n]})},e.prototype.runService=function(e,t){var o=this;if(e&&e.url&&e.callback){var s=e.isScript?i:n,r=this.setupUrl(e);s(r,function(i){var n=i?i.responseText:"";e.__JSONP_DATA&&(n=e.__JSONP_DATA,delete e.__JSONP_DATA),o.runServiceCallback.call(o,t,e,n)},this.options.timeout,e.data,e.headers)}},e.prototype.runServiceCallback=function(e,t,i){var n=this,o=function(t){s||n.onServiceResult.call(n,e,t)},s=t.callback(o,i);s&&this.onServiceResult.call(this,e,s)},e.prototype.onServiceResult=function(e,t){t instanceof Error||t&&t.error?e.call(this,t,null):e.call(this,null,t)},e.prototype.runNextServiceOnError=function(e,t){if(e){this.logError(e);var i=this.getNextService();i?this.runService(i,this.runNextServiceOnError.bind(this)):this.completeService.call(this,this.callbackError,new Error("All services failed"))}else this.completeService.call(this,this.callbackComplete,t)},e.prototype.getCurrentServiceOpts=function(){var e=this.options.services[this.currentServiceIndex];return"string"==typeof e?{name:e}
2:"function"==typeof e?e():t.isPlainObject(e)?e:{}},e.prototype.completeService=function(e,t){this.currentServiceIndex=-1,e&&e(t)},e.prototype.logError=function(e){var t=this.currentServiceIndex,i=this.getServiceByIdx(t);console.error("The service["+t+"] ("+i.url+") responded with the following error",e)},e}(),e.Law=function(){function e(e){this.initialise.apply(this,arguments)}var i={regionalLaw:!0,hasLaw:["AT","BE","BG","HR","CZ","CY","DK","EE","FI","FR","DE","EL","HU","IE","IT","LV","LT","LU","MT","NL","PL","PT","SK","SI","ES","SE","GB","UK"],revokable:["HR","CY","DK","EE","FR","DE","LV","LT","NL","PT","ES"],explicitAction:["HR","IT","ES"]};return e.prototype.initialise=function(e){t.deepExtend(this.options={},i),t.isPlainObject(e)&&t.deepExtend(this.options,e)},e.prototype.get=function(e){var t=this.options;return{hasLaw:t.hasLaw.indexOf(e)>=0,revokable:t.revokable.indexOf(e)>=0,explicitAction:t.explicitAction.indexOf(e)>=0}},e.prototype.applyLaw=function(e,t){var i=this.get(t);return i.hasLaw||(e.enabled=!1),this.options.regionalLaw&&(i.revokable&&(e.revokable=!0),i.explicitAction&&(e.dismissOnScroll=!1,e.dismissOnTimeout=!1)),e},e}(),e.initialise=function(t,i,n){var o=new e.Law(t.law);i||(i=function(){}),n||(n=function(){}),e.getCountryCode(t,function(n){delete t.law,delete t.location,n.code&&(t=o.applyLaw(t,n.code)),i(new e.Popup(t))},function(i){delete t.law,delete t.location,n(i,new e.Popup(t))})},e.getCountryCode=function(t,i,n){if(t.law&&t.law.countryCode)return void i({code:t.law.countryCode});if(t.location){var o=new e.Location(t.location);return void o.locate(function(e){i(e||{})},n)}i({})},e.utils=t,e.hasInitialised=!0,window.cookieconsent=e}}(window.cookieconsent||{}); 3 4// Cookie Consent by Insites. Based on version 3.0.3 5// @see https://cookieconsent.insites.com/ 6window.addEventListener('load', function () { 7 window.cookieconsent.initialise({ 8 palette: { 9 popup: { 10 background: '#eeeeee', 11 }, 12 button: { 13 background: '#162d56', 14 } 15 }, 16 position: 'top', 17 static: true, 18 content: { 19 message: 'De provincie Groningen gebruikt cookies op haar website.', 20 dismiss: 'Verberg deze melding', 21 link: 'Welke cookies plaatsen we?', 22 href: '/cookies', 23 }, 24 elements: { 25 dismiss: '<a tabindex="0" class="cc-btn cc-dismiss">{{dismiss}}</a>', 26 }, 27 onPopupOpen: function () { 28 jQuery(jQuery(this)[0].element).parent().attr('lang', 'nl'); 29 }, 30 }); 31 32 // Make all links clickable when tabbing through a page. This helps with the WCAG guidelines 33 // Select all links on the page and, if they don't have a href, give them an href that does nothing 34 // This function is placed here because it needs to take the 'verberg deze melding' link in account which is created above 35 jQuery('a').each(function () { 36 if (!jQuery(this).attr('href')) { 37 jQuery(this).attr({ 38 href: '#', 39 onClick: 'return false;' 40 }); 41 } 42 }); 43}); 44 45/** 46 * @name MarkerClusterer for Google Maps v3 47 * @version version 1.0.1 48 * @author Luke Mahe 49 * @fileoverview 50 * The library creates and manages per-zoom-level clusters for large amounts of 51 * markers. 52 * <br/> 53 * This is a v3 implementation of the 54 * <a href="http://gmaps-utility-library-dev.googlecode.com/svn/tags/markerclusterer/" 55 * >v2 MarkerClusterer</a>. 56 */ 57 58/** 59 * Licensed under the Apache License, Version 2.0 (the "License"); 60 * you may not use this file except in compliance with the License. 61 * You may obtain a copy of the License at 62 * 63 * http://www.apache.org/licenses/LICENSE-2.0 64 * 65 * Unless required by applicable law or agreed to in writing, software 66 * distributed under the License is distributed on an "AS IS" BASIS, 67 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 68 * See the License for the specific language governing permissions and 69 * limitations under the License. 70 */ 71 72 73/**
74 * A Marker Clusterer that clusters markers. 75 * 76 * @param {google.maps.Map} map The Google map to attach to. 77 * @param {Array.<google.maps.Marker>=} opt_markers Optional markers to add to 78 * the cluster. 79 * @param {Object=} opt_options support the following options: 80 * 'gridSize': (number) The grid size of a cluster in pixels. 81 * 'maxZoom': (number) The maximum zoom level that a marker can be part of a 82 * cluster. 83 * 'zoomOnClick': (boolean) Whether the default behaviour of clicking on a 84 * cluster is to zoom into it. 85 * 'averageCenter': (boolean) Whether the center of each cluster should be 86 * the average of all markers in the cluster. 87 * 'minimumClusterSize': (number) The minimum number of markers to be in a 88 * cluster before the markers are hidden and a count 89 * is shown. 90 * 'styles': (object) An object that has style properties: 91 * 'url': (string) The image url. 92 * 'height': (number) The image height. 93 * 'width': (number) The image width. 94 * 'anchor': (Array) The anchor position of the label text. 95 * 'textColor': (string) The text color. 96 * 'textSize': (number) The text size. 97 * 'backgroundPosition': (string) The position of the backgound x, y. 98 * @constructor 99 * @extends google.maps.OverlayView 100 */ 101function MarkerClusterer(map, opt_markers, opt_options) { 102 // MarkerClusterer implements google.maps.OverlayView interface. We use the 103 // extend function to extend MarkerClusterer with google.maps.OverlayView 104 // because it might not always be available when the code is defined so we 105 // look for it at the last possible moment. If it doesn't exist now then 106 // there is no point going ahead :) 107 this.extend(MarkerClusterer, google.maps.OverlayView); 108 this.map_ = map; 109 110 /** 111 * @type {Array.<google.maps.Marker>} 112 * @private 113 */ 114 this.markers_ = []; 115 116 /** 117 * @type {Array.<Cluster>} 118 */ 119 this.clusters_ = []; 120 121 this.sizes = [53, 56, 66, 78, 90]; 122 123 /** 124 * @private 125 */ 126 this.styles_ = []; 127 128 /** 129 * @type {boolean} 130 * @private 131 */ 132 this.ready_ = false; 133 134 var options = opt_options || {}; 135 136 /** 137 * @type {number} 138 * @private 139 */ 140 this.gridSize_ = options['gridSize'] || 60; 141 142 /** 143 * @private 144 */ 145 this.minClusterSize_ = options['minimumClusterSize'] || 2; 146 147 148 /** 149 * @type {?number} 150 * @private 151 */ 152 this.maxZoom_ = options['maxZoom'] || null; 153 154 this.styles_ = options['styles'] || []; 155 156 /** 157 * @type {string} 158 * @private 159 */ 160 this.imagePath_ = options['imagePath'] || 161 this.MARKER_CLUSTER_IMAGE_PATH_; 162 163 /** 164 * @type {string} 165 * @private 166 */ 167 this.imageExtension_ = options['imageExtension'] || 168 this.MARKER_CLUSTER_IMAGE_EXTENSION_; 169 170 /** 171 * @type {boolean} 172 * @private 173 */ 174 this.zoomOnClick_ = true; 175 176 if (options['zoomOnClick'] != undefined) { 177 this.zoomOnClick_ = options['zoomOnClick']; 178 } 179 180 /** 181 * @type {boolean} 182 * @private 183 */ 184 this.averageCenter_ = false; 185 186 if (options['averageCenter'] != undefined) { 187 this.averageCenter_ = options['averageCenter']; 188 } 189 190 this.setupStyles_(); 191 192 this.setMap(map); 193 194 /** 195 * @type {number} 196 * @private 197 */ 198 this.prevZoom_ = this.map_.getZoom(); 199 200 // Add the map event listeners 201 var that = this; 202 google.maps.event.addListener(this.map_, 'zoom_changed', function() { 203 // Determines map type and prevent illegal zoom levels 204 var zoom = that.map_.getZoom(); 205 var minZoom = that.map_.minZoom || 0; 206 var maxZoom = Math.min(that.map_.maxZoom || 100, 207 that.map_.mapTypes[that.map_.getMapTypeId()].maxZoom); 208 zoom = Math.min(Math.max(zoom,minZoom),maxZoom); 209 210 if (that.prevZoom_ != zoom) { 211 that.prevZoom_ = zoom; 212 that.resetViewport(); 213 } 214 }); 215 216 google.maps.event.addListener(this.map_, 'idle', function() { 217 that.redraw(); 218 }); 219 220 // Finally, add the markers 221 if (opt_markers && (opt_markers.length || Object.keys(opt_markers).length)) { 222 this.addMarkers(opt_markers, false); 223 } 224} 225 226 227/** 228 * The marker cluster image path. 229 * 230 * @type {string} 231 * @private 232 */ 233MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_PATH_ = '../images/m'; 234 235 236/** 237 * The marker cluster image path. 238 * 239 * @type {string} 240 * @private 241 */ 242MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_EXTENSION_ = 'png'; 243 244 245/** 246 * Extends a objects prototype by anothers. 247 * 248 * @param {Object} obj1 The object to be extended. 249 * @param {Object} obj2 The object to extend with. 250 * @return {Object} The new extended object. 251 * @ignore 252 */ 253MarkerClusterer.prototype.extend = function(obj1, obj2) { 254 return (function(object) { 255 for (var property in object.prototype) { 256 this.prototype[property] = object.prototype[property]; 257 } 258 return this; 259 }).apply(obj1, [obj2]); 260}; 261 262 263/** 264 * Implementaion of the interface method. 265 * @ignore 266 */ 267MarkerClusterer.prototype.onAdd = function() { 268 this.setReady_(true); 269}; 270 271/** 272 * Implementaion of the interface method. 273 * @ignore 274 */ 275MarkerClusterer.prototype.draw = function() {}; 276 277/** 278 * Sets up the styles object. 279 * 280 * @private 281 */ 282MarkerClusterer.prototype.setupStyles_ = function() { 283 if (this.styles_.length) { 284 return; 285 } 286 287 for (var i = 0, size; size = this.sizes[i]; i++) { 288 this.styles_.push({ 289 url: this.imagePath_ + (i + 1) + '.' + this.imageExtension_, 290 height: size, 291 width: size 292 }); 293 } 294}; 295 296/** 297 * Fit the map to the bounds of the markers in the clusterer. 298 */ 299MarkerClusterer.prototype.fitMapToMarkers = function() { 300 var markers = this.getMarkers(); 301 var bounds = new google.maps.LatLngBounds(); 302 for (var i = 0, marker; marker = markers[i]; i++) { 303 bounds.extend(marker.getPosition()); 304 } 305 306 this.map_.fitBounds(bounds); 307}; 308 309 310/** 311 * Sets the styles. 312 * 313 * @param {Object} styles The style to set. 314 */ 315MarkerClusterer.prototype.setStyles = function(styles) { 316 this.styles_ = styles; 317}; 318 319 320/** 321 * Gets the styles. 322 * 323 * @return {Object} The styles object. 324 */ 325MarkerClusterer.prototype.getStyles = function() { 326 return this.styles_; 327}; 328 329 330/** 331 * Whether zoom on click is set. 332 * 333 * @return {boolean} True if zoomOnClick_ is set. 334 */ 335MarkerClusterer.prototype.isZoomOnClick = function() { 336 return this.zoomOnClick_; 337}; 338 339/** 340 * Whether average center is set. 341 * 342 * @return {boolean} True if averageCenter_ is set. 343 */ 344MarkerClusterer.prototype.isAverageCenter = function() { 345 return this.averageCenter_; 346}; 347 348 349/** 350 * Returns the array of markers in the clusterer. 351 * 352 * @return {Array.<google.maps.Marker>} The markers. 353 */ 354MarkerClusterer.prototype.getMarkers = function() { 355 return this.markers_; 356}; 357 358 359/** 360 * Returns the number of markers in the clusterer 361 * 362 * @return {Number} The number of markers. 363 */ 364MarkerClusterer.prototype.getTotalMarkers = function() { 365 return this.markers_.length; 366}; 367 368 369/** 370 * Sets the max zoom for the clusterer. 371 * 372 * @param {number} maxZoom The max zoom level. 373 */ 374MarkerClusterer.prototype.setMaxZoom = function(maxZoom) { 375 this.maxZoom_ = maxZoom; 376}; 377 378 379/** 380 * Gets the max zoom for the clusterer. 381 * 382 * @return {number} The max zoom level. 383 */ 384MarkerClusterer.prototype.getMaxZoom = function() { 385 return this.maxZoom_; 386}; 387 388 389/** 390 * The function for calculating the cluster icon image. 391 * 392 * @param {Array.<google.maps.Marker>} markers The markers in the clusterer. 393 * @param {number} numStyles The number of styles available. 394 * @return {Object} A object properties: 'text' (string) and 'index' (number). 395 * @private 396 */ 397MarkerClusterer.prototype.calculator_ = function(markers, numStyles) { 398 var index = 0; 399 var count = markers.length; 400 var dv = count; 401 while (dv !== 0) { 402 dv = parseInt(dv / 10, 10); 403 index++; 404 } 405 406 index = Math.min(index, numStyles); 407 return { 408 text: count, 409 index: index 410 }; 411}; 412 413 414/** 415 * Set the calculator function. 416 * 417 * @param {function(Array, number)} calculator The function to set as the 418 * calculator. The function should return a object properties: 419 * 'text' (string) and 'index' (number). 420 * 421 */ 422MarkerClusterer.prototype.setCalculator = function(calculator) { 423 this.calculator_ = calculator; 424}; 425 426 427/** 428 * Get the calculator function. 429 * 430 * @return {function(Array, number)} the calculator function. 431 */ 432MarkerClusterer.prototype.getCalculator = function() { 433 return this.calculator_; 434}; 435 436 437/** 438 * Add an array of markers to the clusterer. 439 * 440 * @param {Array.<google.maps.Marker>} markers The markers to add. 441 * @param {boolean=}
441 opt_nodraw Whether to redraw the clusters. 442 */ 443MarkerClusterer.prototype.addMarkers = function(markers, opt_nodraw) { 444 if (markers.length) { 445 for (var i = 0, marker; marker = markers[i]; i++) { 446 this.pushMarkerTo_(marker); 447 } 448 } else if (Object.keys(markers).length) { 449 for (var marker in markers) { 450 this.pushMarkerTo_(markers[marker]); 451 } 452 } 453 if (!opt_nodraw) { 454 this.redraw(); 455 } 456}; 457 458 459/** 460 * Pushes a marker to the clusterer. 461 * 462 * @param {google.maps.Marker} marker The marker to add. 463 * @private 464 */ 465MarkerClusterer.prototype.pushMarkerTo_ = function(marker) { 466 marker.isAdded = false; 467 if (marker['draggable']) { 468 // If the marker is draggable add a listener so we update the clusters on 469 // the drag end. 470 var that = this; 471 google.maps.event.addListener(marker, 'dragend', function() { 472 marker.isAdded = false; 473 that.repaint(); 474 }); 475 } 476 this.markers_.push(marker); 477}; 478 479 480/** 481 * Adds a marker to the clusterer and redraws if needed. 482 * 483 * @param {google.maps.Marker} marker The marker to add. 484 * @param {boolean=} opt_nodraw Whether to redraw the clusters. 485 */ 486MarkerClusterer.prototype.addMarker = function(marker, opt_nodraw) { 487 this.pushMarkerTo_(marker); 488 if (!opt_nodraw) { 489 this.redraw(); 490 } 491}; 492 493 494/** 495 * Removes a marker and returns true if removed, false if not 496 * 497 * @param {google.maps.Marker} marker The marker to remove 498 * @return {boolean} Whether the marker was removed or not 499 * @private 500 */ 501MarkerClusterer.prototype.removeMarker_ = function(marker) { 502 var index = -1; 503 if (this.markers_.indexOf) { 504 index = this.markers_.indexOf(marker); 505 } else { 506 for (var i = 0, m; m = this.markers_[i]; i++) { 507 if (m == marker) { 508 index = i; 509 break; 510 } 511 } 512 } 513 514 if (index == -1) { 515 // Marker is not in our list of markers. 516 return false; 517 } 518 519 marker.setMap(null); 520 521 this.markers_.splice(index, 1); 522 523 return true; 524}; 525 526 527/** 528 * Remove a marker from the cluster. 529 * 530 * @param {google.maps.Marker} marker The marker to remove. 531 * @param {boolean=} opt_nodraw Optional boolean to force no redraw. 532 * @return {boolean} True if the marker was removed. 533 */ 534MarkerClusterer.prototype.removeMarker = function(marker, opt_nodraw) { 535 var removed = this.removeMarker_(marker); 536 537 if (!opt_nodraw && removed) { 538 this.resetViewport(); 539 this.redraw(); 540 return true; 541 } else { 542 return false; 543 } 544}; 545 546 547/** 548 * Removes an array of markers from the cluster. 549 * 550 * @param {Array.<google.maps.Marker>} markers The markers to remove. 551 * @param {boolean=} opt_nodraw Optional boolean to force no redraw. 552 */ 553MarkerClusterer.prototype.removeMarkers = function(markers, opt_nodraw) { 554 var removed = false; 555 556 for (var i = 0, marker; marker = markers[i]; i++) { 557 var r = this.removeMarker_(marker); 558 removed = removed || r; 559 } 560 561 if (!opt_nodraw && removed) { 562 this.resetViewport(); 563 this.redraw(); 564 return true; 565 } 566}; 567 568 569/** 570 * Sets the clusterer's ready state. 571 * 572 * @param {boolean} ready The state. 573 * @private 574 */ 575MarkerClusterer.prototype.setReady_ = function(ready) { 576 if (!this.ready_) { 577 this.ready_ = ready; 578 this.createClusters_(); 579 } 580}; 581 582 583/** 584 * Returns the number of clusters in the clusterer. 585 * 586 * @return {number} The number of clusters. 587 */ 588MarkerClusterer.prototype.getTotalClusters = function() { 589 return this.clusters_.length; 590}; 591 592 593/** 594 * Returns the google map that the clusterer is associated with. 595 * 596 * @return {google.maps.Map} The map. 597 */ 598MarkerClusterer.prototype.getMap = function() { 599 return this.map_; 600}; 601 602 603/** 604 * Sets the google map that the clusterer is associated with. 605 * 606 * @param {google.maps.Map} map The map. 607 */ 608MarkerClusterer.prototype.setMap = function(map) { 609 this.map_ = map; 610}; 611 612 613/** 614 * Returns the size of the grid. 615 * 616 * @return {number} The grid size. 617 */ 618MarkerClusterer.prototype.getGridSize = function() { 619 return this.gridSize_; 620}; 621 622 623/** 624 * Sets the size of the grid. 625 * 626 * @param {number} size The grid size. 627 */ 628MarkerClusterer.prototype.setGridSize = function(size) { 629 this.gridSize_ = size; 630}; 631 632 633/** 634 * Returns the min cluster size. 635 * 636 * @return {number} The grid size. 637 */ 638MarkerClusterer.prototype.getMinClusterSize = function() { 639 return this.minClusterSize_; 640}; 641 642/** 643 * Sets the min cluster size. 644 * 645 * @param {number} size The grid size. 646 */ 647MarkerClusterer.prototype.setMinClusterSize = function(size) { 648 this.minClusterSize_ = size; 649}; 650 651 652/** 653 * Extends a bounds object by the grid size. 654 * 655 * @param {google.maps.LatLngBounds} bounds The bounds to extend. 656 * @return {google.maps.LatLngBounds}
656 The extended bounds. 657 */ 658MarkerClusterer.prototype.getExtendedBounds = function(bounds) { 659 var projection = this.getProjection(); 660 661 // Turn the bounds into latlng. 662 var tr = new google.maps.LatLng(bounds.getNorthEast().lat(), 663 bounds.getNorthEast().lng()); 664 var bl = new google.maps.LatLng(bounds.getSouthWest().lat(), 665 bounds.getSouthWest().lng()); 666 667 // Convert the points to pixels and the extend out by the grid size. 668 var trPix = projection.fromLatLngToDivPixel(tr); 669 trPix.x += this.gridSize_; 670 trPix.y -= this.gridSize_; 671 672 var blPix = projection.fromLatLngToDivPixel(bl); 673 blPix.x -= this.gridSize_; 674 blPix.y += this.gridSize_; 675 676 // Convert the pixel points back to LatLng 677 var ne = projection.fromDivPixelToLatLng(trPix); 678 var sw = projection.fromDivPixelToLatLng(blPix); 679 680 // Extend the bounds to contain the new bounds. 681 bounds.extend(ne); 682 bounds.extend(sw); 683 684 return bounds; 685}; 686 687 688/** 689 * Determins if a marker is contained in a bounds. 690 * 691 * @param {google.maps.Marker} marker The marker to check. 692 * @param {google.maps.LatLngBounds} bounds The bounds to check against. 693 * @return {boolean} True if the marker is in the bounds. 694 * @private 695 */ 696MarkerClusterer.prototype.isMarkerInBounds_ = function(marker, bounds) { 697 return bounds.contains(marker.getPosition()); 698}; 699 700 701/** 702 * Clears all clusters and markers from the clusterer. 703 */ 704MarkerClusterer.prototype.clearMarkers = function() { 705 this.resetViewport(true); 706 707 // Set the markers a empty array. 708 this.markers_ = []; 709}; 710 711 712/** 713 * Clears all existing clusters and recreates them. 714 * @param {boolean} opt_hide To also hide the marker. 715 */ 716MarkerClusterer.prototype.resetViewport = function(opt_hide) { 717 // Remove all the clusters 718 for (var i = 0, cluster; cluster = this.clusters_[i]; i++) { 719 cluster.remove(); 720 } 721 722 // Reset the markers to not be added and to be invisible. 723 for (var i = 0, marker; marker = this.markers_[i]; i++) { 724 marker.isAdded = false; 725 if (opt_hide) { 726 marker.setMap(null); 727 } 728 } 729 730 this.clusters_ = []; 731}; 732 733/** 734 * 735 */ 736MarkerClusterer.prototype.repaint = function() { 737 var oldClusters = this.clusters_.slice(); 738 this.clusters_.length = 0; 739 this.resetViewport(); 740 this.redraw(); 741 742 // Remove the old clusters. 743 // Do it in a timeout so the other clusters have been drawn first. 744 window.setTimeout(function() { 745 for (var i = 0, cluster; cluster = oldClusters[i]; i++) { 746 cluster.remove(); 747 } 748 }, 0); 749}; 750 751 752/** 753 * Redraws the clusters. 754 */ 755MarkerClusterer.prototype.redraw = function() { 756 this.createClusters_(); 757}; 758 759 760/** 761 * Calculates the distance between two latlng locations in km. 762 * @see http://www.movable-type.co.uk/scripts/latlong.html 763 * 764 * @param {google.maps.LatLng} p1 The first lat lng point. 765 * @param {google.maps.LatLng} p2 The second lat lng point. 766 * @return {number} The distance between the two points in km. 767 * @private 768 */ 769MarkerClusterer.prototype.distanceBetweenPoints_ = function(p1, p2) { 770 if (!p1 || !p2) { 771 return 0; 772 } 773 774 var R = 6371; // Radius of the Earth in km 775 var dLat = (p2.lat() - p1.lat()) * Math.PI / 180; 776 var dLon = (p2.lng() - p1.lng()) * Math.PI / 180; 777 var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + 778 Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) * 779 Math.sin(dLon / 2) * Math.sin(dLon / 2); 780 var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); 781 var d = R * c; 782 return d; 783}; 784 785 786/** 787 * Add a marker to a cluster, or creates a new cluster. 788 * 789 * @param {google.maps.Marker} marker The marker to add. 790 * @private 791 */ 792MarkerClusterer.prototype.addToClosestCluster_ = function(marker) { 793 var distance = 40000; // Some large number 794 var clusterToAddTo = null; 795 var pos = marker.getPosition(); 796 for (var i = 0, cluster; cluster = this.clusters_[i]; i++) { 797 var center = cluster.getCenter(); 798 if (center) { 799 var d = this.distanceBetweenPoints_(center, marker.getPosition()); 800 if (d < distance) { 801 distance = d; 802 clusterToAddTo = cluster; 803 } 804 } 805 } 806 807 if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)) { 808 clusterToAddTo.addMarker(marker); 809 } else { 810 var cluster = new Cluster(this); 811 cluster.addMarker(marker);
812 this.clusters_.push(cluster); 813 } 814}; 815 816 817/** 818 * Creates the clusters. 819 * 820 * @private 821 */ 822MarkerClusterer.prototype.createClusters_ = function() { 823 if (!this.ready_) { 824 return; 825 } 826 827 // Get our current map view bounds. 828 // Create a new bounds object so we don't affect the map. 829 var mapBounds = new google.maps.LatLngBounds(this.map_.getBounds().getSouthWest(), 830 this.map_.getBounds().getNorthEast()); 831 var bounds = this.getExtendedBounds(mapBounds); 832 833 for (var i = 0, marker; marker = this.markers_[i]; i++) { 834 if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds)) { 835 this.addToClosestCluster_(marker); 836 } 837 } 838}; 839 840 841/** 842 * A cluster that contains markers. 843 * 844 * @param {MarkerClusterer} markerClusterer The markerclusterer that this 845 * cluster is associated with. 846 * @constructor 847 * @ignore 848 */ 849function Cluster(markerClusterer) { 850 this.markerClusterer_ = markerClusterer; 851 this.map_ = markerClusterer.getMap(); 852 this.gridSize_ = markerClusterer.getGridSize(); 853 this.minClusterSize_ = markerClusterer.getMinClusterSize(); 854 this.averageCenter_ = markerClusterer.isAverageCenter(); 855 this.center_ = null; 856 this.markers_ = []; 857 this.bounds_ = null; 858 this.clusterIcon_ = new ClusterIcon(this, markerClusterer.getStyles(), 859 markerClusterer.getGridSize()); 860} 861 862/** 863 * Determins if a marker is already added to the cluster. 864 * 865 * @param {google.maps.Marker} marker The marker to check. 866 * @return {boolean} True if the marker is already added. 867 */ 868Cluster.prototype.isMarkerAlreadyAdded = function(marker) { 869 if (this.markers_.indexOf) { 870 return this.markers_.indexOf(marker) != -1; 871 } else { 872 for (var i = 0, m; m = this.markers_[i]; i++) { 873 if (m == marker) { 874 return true; 875 } 876 } 877 } 878 return false; 879}; 880 881 882/** 883 * Add a marker the cluster. 884 * 885 * @param {google.maps.Marker} marker The marker to add. 886 * @return {boolean} True if the marker was added. 887 */ 888Cluster.prototype.addMarker = function(marker) { 889 if (this.isMarkerAlreadyAdded(marker)) { 890 return false; 891 } 892 893 if (!this.center_) { 894 this.center_ = marker.getPosition(); 895 this.calculateBounds_(); 896 } else { 897 if (this.averageCenter_) { 898 var l = this.markers_.length + 1; 899 var lat = (this.center_.lat() * (l-1) + marker.getPosition().lat()) / l; 900 var lng = (this.center_.lng() * (l-1) + marker.getPosition().lng()) / l; 901 this.center_ = new google.maps.LatLng(lat, lng); 902 this.calculateBounds_(); 903 } 904 } 905 906 marker.isAdded = true; 907 this.markers_.push(marker); 908 909 var len = this.markers_.length; 910 if (len < this.minClusterSize_ && marker.getMap() != this.map_) { 911 // Min cluster size not reached so show the marker. 912 marker.setMap(this.map_); 913 } 914 915 if (len == this.minClusterSize_) { 916 // Hide the markers that were showing. 917 for (var i = 0; i < len; i++) { 918 this.markers_[i].setMap(null); 919 } 920 } 921 922 if (len >= this.minClusterSize_) { 923 marker.setMap(null); 924 } 925 926 this.updateIcon(); 927 return true; 928}; 929 930 931/** 932 * Returns the marker clusterer that the cluster is associated with. 933 * 934 * @return {MarkerClusterer} The associated marker clusterer. 935 */ 936Cluster.prototype.getMarkerClusterer = function() { 937 return this.markerClusterer_; 938}; 939 940 941/** 942 * Returns the bounds of the cluster. 943 * 944 * @return {google.maps.LatLngBounds} the cluster bounds. 945 */ 946Cluster.prototype.getBounds = function() { 947 var bounds = new google.maps.LatLngBounds(this.center_, this.center_); 948 var markers = this.getMarkers(); 949 for (var i = 0, marker; marker = markers[i]; i++) { 950 bounds.extend(marker.getPosition()); 951 } 952 return bounds; 953}; 954 955 956/** 957 * Removes the cluster 958 */ 959Cluster.prototype.remove = function() { 960 this.clusterIcon_.remove(); 961 this.markers_.length = 0; 962 delete this.markers_; 963}; 964 965 966/** 967 * Returns the center of the cluster. 968 * 969 * @return {number} The cluster center. 970 */ 971Cluster.prototype.getSize = function() { 972 return this.markers_.length; 973}; 974 975 976/** 977 * Returns the center of the cluster. 978 * 979 * @return {Array.<google.maps.Marker>} The cluster center. 980 */ 981Cluster.prototype.getMarkers = function() { 982 return this.markers_; 983}; 984 985 986/** 987 * Returns the center of the cluster. 988 * 989 * @return {google.maps.LatLng} The cluster center. 990 */ 991Cluster.prototype.getCenter = function() { 992 return this.center_; 993}; 994 995 996/**
997 * Calculated the extended bounds of the cluster with the grid. 998 * 999 * @private 1000 */ 1001Cluster.prototype.calculateBounds_ = function() { 1002 var bounds = new google.maps.LatLngBounds(this.center_, this.center_); 1003 this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds); 1004}; 1005 1006 1007/** 1008 * Determines if a marker lies in the clusters bounds. 1009 * 1010 * @param {google.maps.Marker} marker The marker to check. 1011 * @return {boolean} True if the marker lies in the bounds. 1012 */ 1013Cluster.prototype.isMarkerInClusterBounds = function(marker) { 1014 return this.bounds_.contains(marker.getPosition()); 1015}; 1016 1017 1018/** 1019 * Returns the map that the cluster is associated with. 1020 * 1021 * @return {google.maps.Map} The map. 1022 */ 1023Cluster.prototype.getMap = function() { 1024 return this.map_; 1025}; 1026 1027 1028/** 1029 * Updates the cluster icon 1030 */ 1031Cluster.prototype.updateIcon = function() { 1032 var zoom = this.map_.getZoom(); 1033 var mz = this.markerClusterer_.getMaxZoom(); 1034 1035 if (mz && zoom > mz) { 1036 // The zoom is greater than our max zoom so show all the markers in cluster. 1037 for (var i = 0, marker; marker = this.markers_[i]; i++) { 1038 marker.setMap(this.map_); 1039 } 1040 return; 1041 } 1042 1043 if (this.markers_.length < this.minClusterSize_) { 1044 // Min cluster size not yet reached. 1045 this.clusterIcon_.hide(); 1046 return; 1047 } 1048 1049 var numStyles = this.markerClusterer_.getStyles().length; 1050 var sums = this.markerClusterer_.getCalculator()(this.markers_, numStyles); 1051 this.clusterIcon_.setCenter(this.center_); 1052 this.clusterIcon_.setSums(sums); 1053 this.clusterIcon_.show(); 1054}; 1055 1056 1057/** 1058 * A cluster icon 1059 * 1060 * @param {Cluster} cluster The cluster to be associated with. 1061 * @param {Object} styles An object that has style properties: 1062 * 'url': (string) The image url. 1063 * 'height': (number) The image height. 1064 * 'width': (number) The image width. 1065 * 'anchor': (Array) The anchor position of the label text. 1066 * 'textColor': (string) The text color. 1067 * 'textSize': (number) The text size. 1068 * 'backgroundPosition: (string) The background postition x, y. 1069 * @param {number=} opt_padding Optional padding to apply to the cluster icon. 1070 * @constructor 1071 * @extends google.maps.OverlayView 1072 * @ignore 1073 */ 1074function ClusterIcon(cluster, styles, opt_padding) { 1075 cluster.getMarkerClusterer().extend(ClusterIcon, google.maps.OverlayView); 1076 1077 this.styles_ = styles; 1078 this.padding_ = opt_padding || 0; 1079 this.cluster_ = cluster; 1080 this.center_ = null; 1081 this.map_ = cluster.getMap(); 1082 this.div_ = null; 1083 this.sums_ = null; 1084 this.visible_ = false; 1085 1086 this.setMap(this.map_); 1087} 1088 1089 1090/** 1091 * Triggers the clusterclick event and zoom's if the option is set. 1092 */ 1093ClusterIcon.prototype.triggerClusterClick = function() { 1094 var markerClusterer = this.cluster_.getMarkerClusterer(); 1095 1096 // Trigger the clusterclick event. 1097 google.maps.event.trigger(markerClusterer, 'clusterclick', this.cluster_); 1098 1099 if (markerClusterer.isZoomOnClick()) { 1100 // Zoom into the cluster. 1101 this.map_.fitBounds(this.cluster_.getBounds()); 1102 } 1103}; 1104 1105 1106/** 1107 * Adding the cluster icon to the dom. 1108 * @ignore 1109 */ 1110ClusterIcon.prototype.onAdd = function() { 1111 this.div_ = document.createElement('DIV'); 1112 if (this.visible_) { 1113 var pos = this.getPosFromLatLng_(this.center_); 1114 this.div_.style.cssText = this.createCss(pos); 1115 this.div_.innerHTML = this.sums_.text; 1116 } 1117 1118 var panes = this.getPanes(); 1119 panes.overlayMouseTarget.appendChild(this.div_); 1120 1121 var that = this; 1122 google.maps.event.addDomListener(this.div_, 'click', function() { 1123 that.triggerClusterClick(); 1124 }); 1125}; 1126 1127 1128/** 1129 * Returns the position to place the div dending on the latlng. 1130 * 1131 * @param {google.maps.LatLng} latlng The position in latlng. 1132 * @return {google.maps.Point} The position in pixels. 1133 * @private 1134 */ 1135ClusterIcon.prototype.getPosFromLatLng_ = function(latlng) { 1136 var pos = this.getProjection().fromLatLngToDivPixel(latlng); 1137 pos.x -= parseInt(this.width_ / 2, 10); 1138 pos.y -= parseInt(this.height_ / 2, 10); 1139 return pos; 1140}; 1141 1142 1143/** 1144 * Draw the icon. 1145 * @ignore 1146 */ 1147ClusterIcon.prototype.draw = function() { 1148 if (this.visible_) { 1149 var pos = this.getPosFromLatLng_(this.center_); 1150 this.div_.style.top = pos.y + 'px'; 1151 this.div_.style.left = pos.x + 'px'; 1152 } 1153}; 1154 1155 1156/** 1157 * Hide the icon. 1158 */ 1159ClusterIcon.prototype.hide = function() { 1160 if (this.div_) { 1161 this.div_.style.display = 'none'; 1162 } 1163 this.visible_ = false;
1164}; 1165 1166 1167/** 1168 * Position and show the icon. 1169 */ 1170ClusterIcon.prototype.show = function() { 1171 if (this.div_) { 1172 var pos = this.getPosFromLatLng_(this.center_); 1173 this.div_.style.cssText = this.createCss(pos); 1174 this.div_.style.display = ''; 1175 } 1176 this.visible_ = true; 1177}; 1178 1179 1180/** 1181 * Remove the icon from the map 1182 */ 1183ClusterIcon.prototype.remove = function() { 1184 this.setMap(null); 1185}; 1186 1187 1188/** 1189 * Implementation of the onRemove interface. 1190 * @ignore 1191 */ 1192ClusterIcon.prototype.onRemove = function() { 1193 if (this.div_ && this.div_.parentNode) { 1194 this.hide(); 1195 this.div_.parentNode.removeChild(this.div_); 1196 this.div_ = null; 1197 } 1198}; 1199 1200 1201/** 1202 * Set the sums of the icon. 1203 * 1204 * @param {Object} sums The sums containing: 1205 * 'text': (string) The text to display in the icon. 1206 * 'index': (number) The style index of the icon. 1207 */ 1208ClusterIcon.prototype.setSums = function(sums) { 1209 this.sums_ = sums; 1210 this.text_ = sums.text; 1211 this.index_ = sums.index; 1212 if (this.div_) { 1213 this.div_.innerHTML = sums.text; 1214 } 1215 1216 this.useStyle(); 1217}; 1218 1219 1220/** 1221 * Sets the icon to the the styles. 1222 */ 1223ClusterIcon.prototype.useStyle = function() { 1224 var index = Math.max(0, this.sums_.index - 1); 1225 index = Math.min(this.styles_.length - 1, index); 1226 var style = this.styles_[index]; 1227 this.url_ = style['url']; 1228 this.height_ = style['height']; 1229 this.width_ = style['width']; 1230 this.textColor_ = style['textColor']; 1231 this.anchor_ = style['anchor']; 1232 this.textSize_ = style['textSize']; 1233 this.backgroundPosition_ = style['backgroundPosition']; 1234}; 1235 1236 1237/** 1238 * Sets the center of the icon. 1239 * 1240 * @param {google.maps.LatLng} center The latlng to set as the center. 1241 */ 1242ClusterIcon.prototype.setCenter = function(center) { 1243 this.center_ = center; 1244}; 1245 1246 1247/** 1248 * Create the css text based on the position of the icon. 1249 * 1250 * @param {google.maps.Point} pos The position. 1251 * @return {string} The css style text. 1252 */ 1253ClusterIcon.prototype.createCss = function(pos) { 1254 var style = []; 1255 style.push('background-image:url(' + this.url_ + ');'); 1256 var backgroundPosition = this.backgroundPosition_ ? this.backgroundPosition_ : '0 0'; 1257 style.push('background-position:' + backgroundPosition + ';'); 1258 1259 if (typeof this.anchor_ === 'object') { 1260 if (typeof this.anchor_[0] === 'number' && this.anchor_[0] > 0 && 1261 this.anchor_[0] < this.height_) { 1262 style.push('height:' + (this.height_ - this.anchor_[0]) + 1263 'px; padding-top:' + this.anchor_[0] + 'px;'); 1264 } else { 1265 style.push('height:' + this.height_ + 'px; line-height:' + this.height_ + 1266 'px;'); 1267 } 1268 if (typeof this.anchor_[1] === 'number' && this.anchor_[1] > 0 && 1269 this.anchor_[1] < this.width_) { 1270 style.push('width:' + (this.width_ - this.anchor_[1]) + 1271 'px; padding-left:' + this.anchor_[1] + 'px;'); 1272 } else { 1273 style.push('width:' + this.width_ + 'px; text-align:center;'); 1274 } 1275 } else { 1276 style.push('height:' + this.height_ + 'px; line-height:' + 1277 this.height_ + 'px; width:' + this.width_ + 'px; text-align:center;'); 1278 } 1279 1280 var txtColor = this.textColor_ ? this.textColor_ : 'black'; 1281 var txtSize = this.textSize_ ? this.textSize_ : 11; 1282 1283 style.push('cursor:pointer; top:' + pos.y + 'px; left:' + 1284 pos.x + 'px; color:' + txtColor + '; position:absolute; font-size:' + 1285 txtSize + 'px; font-family:Arial,sans-serif; font-weight:bold'); 1286 return style.join(''); 1287}; 1288 1289window.addEventListener('load', function() { 1290 jQuery.fn.extend({ 1291 multipleActiveFilter: function(filterType) { 1292 var element = filterType === 'tags' ? 'li' : 'ul'; 1293 var _this = this; 1294 1295 this.find('.filter input').on('change', loading); 1296 this.find('.wizard input').on('change', change); 1297 1298 function loading() { 1299 _this.find('.overzicht').addClass('is-loading'); 1300 window.setTimeout(filter, 500); 1301 } 1302 1303 function filter() { 1304 // reset everything 1305 _this.find('article.category, article.category ' + element).removeClass('hide'); 1306 1307 // filter links 1308 var filters = []; 1309 _this.find('.filter input:checked').each(function(i, n) { 1310 filterType === 'tags' 1311 ? filters.push('article.category ' + element + ':has(a[data-categories*="' + _this.find(n).attr('id') + '"])') 1312 : filters.push('article.category ' + element + '[data-categories*="' + _this.find(n).attr('id') + '"]') 1313 }); 1314 1315 _this.find('.filter input').each(function(i, n) { 1316 this.setAttribute('aria-checked', this.checked == true); 1317 }); 1318 1319 if (filters.length) { 1320 _this.find('article.category ' + element).not( filters.join(',') ).addClass('hide'); 1321 } 1322 1323 // hide empty themes 1324 _this.find('article.category').each(function(i, n) { 1325 if (_this.find(n).find(element + ':not(.hide)').length === 0) { 1326 _this.find(n).addClass('hide'); 1327 } 1328 }); 1329 1330 _this.find('.overzicht').removeClass('is-loading'); 1331 } 1332 1333 function change() { 1334 var type = _this.find('.wizard input:checked').attr('id'); 1335 1336 // reset everything 1337 _this.find('.selectie').addClass('hide'); 1338 1339 // show filtered option 1340 _this.find('.selectie[data-type=' + type + ']').removeClass('hide'); 1341 } 1342 } 1343 }); 1344}); 1345 1346window.addEventListener('load', function () { 1347 jQuery.fn.extend({ 1348 singleActiveFilter: function () { 1349 var _this = this; 1350 const tabs = _this.find('.wizard button.tab'); 1351 1352 // Apply onclick handler to all tab buttons 1353 tabs.on('click', function(event) { 1354 const target = jQuery(event.target); 1355 const datacontent = target.data('content'); 1356 1357 // reset everything 1358 tabs.removeClass('active'); 1359 _this.find('.selection').addClass('hide'); 1360 1361 // Set clicked button as active 1362 target.addClass('active'); 1363 // show content for selected option 1364 _this.find('.selection[data-content=' + datacontent + ']').removeClass('hide'); 1365 }); 1366 } 1367 }); 1368 1369 jQuery('section.single-filter').each(function () { 1370 jQuery(this).singleActiveFilter(); 1371 }); 1372}); 1373
vendor: 16,892 bytes, line 1374
1374!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.datepicker=t():e.datepicker=t()}(window,(function(){return function(e){var t={};function n(a){if(t[a])return t[a].exports;var r=t[a]={i:a,l:!1,exports:{}};return e[a].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=e,n.c=t,n.d=function(e,t,a){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:a})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var a=Object.create(null);if(n.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(a,r,function(t){return e[t]}.bind(null,r));return a},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";n.r(t);var a=[],r=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],i=["January","February","March","April","May","June","July","August","September","October","November","December"],o={t:"top",r:"right",b:"bottom",l:"left",c:"centered"};function s(){}var l=["click","focusin","keydown","input"];function d(e){l.forEach((function(t){e.addEventListener(t,e===document?L:Y)}))}function c(e){return Array.isArray(e)?e.map(c):"[object Object]"===x(e)?Object.keys(e).reduce((function(t,n){return t[n]=c(e[n]),t}),{}):e}function u(e,t){var n=e.calendar.querySelector(".qs-overlay"),a=n&&!n.classList.contains("qs-hidden");t=t||new Date(e.currentYear,e.currentMonth),e.calendar.innerHTML=[h(t,e,a),f(t,e,a),v(e,a)].join(""),a&&window.requestAnimationFrame((function(){M(!0,e)}))}function h(e,t,n){return['<div class="qs-controls'+(n?" qs-blur":"")+'">','<div class="qs-arrow qs-left"></div>','<div class="qs-month-year'+(t.disableYearOverlay?" qs-disabled-year-overlay":"")+'">','<span class="qs-month">'+t.months[e.getMonth()]+"</span>",'<span class="qs-year">'+e.getFullYear()+"</span>","</div>",'<div class="qs-arrow qs-right"></div>',"</div>"].join("")}function f(e,t,n){var a=t.currentMonth,r=t.currentYear,i=t.dateSelected,o=t.maxDate,s=t.minDate,l=t.showAllDates,d=t.days,c=t.disabledDates,u=t.startDay,h=t.weekendIndices,f=t.events,v=t.getRange?t.getRange():{},m=+v.start,y=+v.end,p=g(new Date(e).setDate(1)),w=p.getDay()-u,D=w<0?7:0;p.setMonth(p.getMonth()+1),p.setDate(0);var b=p.getDate(),q=[],S=D+7*((w+b)/7|0);S+=(w+b)%7?7:0;for(var M=1;M<=S;M++){var E=(M-1)%7,x=d[E],C=M-(w>=0?w:7+w),L=new Date(r,a,C),Y=f[+L],j=C<1||C>b,O=j?C<1?-1:1:0,P=j&&!l,k=P?"":L.getDate(),N=+L==+i,_=E===h[0]||E===h[1],I=m!==y,A="qs-square "+x;Y&&!P&&(A+=" qs-event"),j&&(A+=" qs-outside-current-month"),!l&&j||(A+=" qs-num"),N&&(A+=" qs-active"),(c[+L]||t.disabler(L)||_&&t.noWeekends||s&&+L<+s||o&&+L>+o)&&!P&&(A+=" qs-disabled"),+g(new Date)==+L&&(A+=" qs-current"),+L===m&&y&&I&&(A+=" qs-range-start"),+L>m&&+L<y&&(A+=" qs-range-middle"),+L===y&&m&&I&&(A+=" qs-range-end"),P&&(A+=" qs-empty",k=""),q.push('<div class="'+A+'" data-direction="'+O+'">'+k+"</div>")}var R=d.map((function(e){return'<div class="qs-square qs-day">'+e+"</div>"})).concat(q);return R.unshift('<div class="qs-squares'+(n?" qs-blur":"")+'">'),R.push("</div>"),R.join("")}function v(e,t){var n=e.overlayPlaceholder,a=e.overlayButton;return['<div class="qs-overlay'+(t?"":" qs-hidden")+'">',"<div>",'<input class="qs-overlay-year" placeholder="'+n+'" inputmode="numeric" />','<div class="qs-close">✕</div>',"</div>",'<div class="qs-overlay-month-container">'+e.overlayMonths.map((function(e,t){return'<div class="qs-overlay-month" data-month-num="'+t+'">'+e+"</div>"})).join("")+"</div>",'<div class="qs-submit qs-disabled">'+a+"</div>","</div>"].join("")}function m(e,t,n){var a=t.el,r=t.calendar.querySelector(".qs-active"),i=e.textContent,o=t.sibling;(a.disabled||a.readOnly)&&t.respectDisabledReadOnly||(t.dateSelected=n?void 0:new Date(t.currentYear,t.currentMonth,i),r&&r.classList.remove("qs-active"),n||e.classList.add("qs-active"),p(a,t,n),n||q(t),o&&(y({instance:t,deselect:n}),t.first&&!o.dateSelected&&(o.currentYear=t.currentYear,o.currentMonth=t.currentMonth,o.currentMonthName=t.currentMonthName),u(t),u(o)),t.onSelect(t,n?void 0:new Date(t.dateSelected)))}function y(e){var t=e.instance.first?e.instance:e.instance.sibling,n=t.sibling;t===e.instance?e.deselect?(t.minDate=t.originalMinDate,n.minDate=n.originalMinDate):n.minDate=t.dateSelected:e.deselect?(n.maxDate=n.originalMaxDate,t.maxDate=t.originalMaxDate):t.maxDate=n.dateSelected}function p(e,t,n){if(!t.nonInput)return n?e.value="":t.formatter!==s?t.formatter(e,t.dateSelected,t):void(e.value=t.dateSelected.toDateString())}function w(e,t,n,a){n||a?(n&&(t.currentYear=+n),a&&(t.currentMonth=+a)):(t.currentMonth+=e.contains("qs-right")?1:-1,12===t.currentMonth?(t.currentMonth=0,t.currentYear++):-1===t.currentMonth&&(t.currentMonth=11,t.currentYear--)),t.currentMonthName=t.months[t.currentMonth],u(t),t.onMonthChange(t)}function D(e){if(!e.noPosition){var t=e.position.top,n=e.position.right;if(e.position.centered)return e.calendarContainer.classList.add("qs-centered");var a=e.positionedEl.getBoundingClientRect(),r=e.el.getBoundingClientRect(),i=e.calendarContainer.getBoundingClientRect(),o=r.top-a.top+(t?-1*i.height:r.height)+"px",s=r.left-a.left+(n?r.width-i.width:0)+"px";e.calendarContainer.style.setProperty("top",o),e.calendarContainer.style.setProperty("left",s)}}function b(e){return"[object Date]"===x(e)&&"Invalid Date"!==e.toString()}function g(e){if(b(e)||"number"==typeof e&&!isNaN(e)){var t=new Date(+e);return new Date(t.getFullYear(),t.getMonth(),t.getDate())}}function q(e){e.disabled||!e.calendarContainer.classList.contains("qs-hidden")&&!e.alwaysShow&&("overlay"!==e.defaultView&&M(!0,e),e.calendarContainer.classList.add("qs-hidden"),e.onHide(e))}function S(e){e.disabled||(e.calendarContainer.classList.remove("qs-hidden"),"overlay"===e.defaultView&&M(!1,e),D(e),e.onShow(e))}function M(e,t){var n=t.calendar,a=n.querySelector(".qs-overlay"),r=a.querySelector(".qs-overlay-year"),i=n.querySelector(".qs-controls"),o=n.querySelector(".qs-squares");e?(a.classList.add("qs-hidden"),i.classList.remove("qs-blur"),o.classList.remove("qs-blur"),r.value=""):(a.classList.remove("qs-hidden"),i.classList.add("qs-blur"),o.classList.add("qs-blur"),r.focus())}function E(e,t,n,a){var r=isNaN(+(new Date).setFullYear(t.value||void 0)),i=r?null:t.value;if(13===e.which||13===e.keyCode||"click"===e.type)a?w(null,n,i,a):r||t.classList.contains("qs-disabled")||w(null,n,i);else if(n.calendar.contains(t)){n.calendar.querySelector(".qs-submit").classList[r?"add":"remove"]("qs-disabled")}}function x(e){return{}.toString.call(e)}function C(e){a.forEach((function(t){t!==e&&q(t)}))}function L(e){if(!e.__qs_shadow_dom){var t=e.which||e.keyCode,n=e.type,r=e.target,o=r.classList,s=a.filter((function(e){return e.calendar.contains(r)||e.el===r}))[0],l=s&&s.calendar.contains(r);if(!(s&&s.isMobile&&s.disableMobile))if("click"===n){if(!s)return a.forEach(q);if(s.disabled)return;var d=s.calendar,c=s.calendarContainer,h=s.disableYearOverlay,f=s.nonInput,v=d.querySelector(".qs-overlay-year"),y=!!d.querySelector(".qs-hidden"),p=d.querySelector(".qs-month-year").contains(r),D=r.dataset.monthNum;if(s.noPosition&&!l)(c.classList.contains("qs-hidden")?S:q)(s);else if(o.contains("qs-arrow"))w(o,s);else if(p||o.contains("qs-close"))h||M(!y,s);else if(D)E(e,v,s,D);else{if(o.contains("qs-disabled"))return;if(o.contains("qs-num")){var b=r.textContent,g=+r.dataset.direction,x=new Date(s.currentYear,s.currentMonth+g,b);if(g){s.currentYear=x.getFullYear(),s.currentMonth=x.getMonth(),s.currentMonthName=i[s.currentMonth],u(s);for(var L,Y=s.calendar.querySelectorAll('[data-direction="0"]'),j=0;!L;){var O=Y[j];O.textContent===b&&(L=O),j++}r=L}return void(+x==+s.dateSelected?m(r,s,!0):r.classList.contains("qs-disabled")||m(r,s))}o.contains("qs-submit")?E(e,v,s):f&&r===s.el&&(S(s),C(s))}}else if("focusin"===n&&s)S(s),C(s);else if("keydown"===n&&9===t&&s)q(s);else if("keydown"===n&&s&&!s.disabled){var P=!s.calendar.querySelector(".qs-overlay").classList.contains("qs-hidden");13===t&&P&&l?E(e,r,s):27===t&&P&&l&&M(!0,s)}else if("input"===n){if(!s||!s.calendar.contains(r))return;var k=s.calendar.querySelector(".qs-submit"),N=r.value.split("").reduce((function(e,t){return e||"0"!==t?e+(t.match(/[0-9]/)?t:""):""}),"").slice(0,4);r.value=N,k.classList[4===N.length?"remove":"add"]("qs-disabled")}}}function Y(e){L(e),e.__qs_shadow_dom=!0}function j(e,t){l.forEach((function(n){e.removeEventListener(n,t)}))}function O(){S(this)}function P(){q(this)}function k(e,t){var n=g(e),a=this.currentYear,r=this.currentMonth,i=this.sibling;if(null==e)return this.dateSelected=void 0,p(this.el,this,!0),i&&(y({instance:this,deselect:!0}),u(i)),u(this),this;if(!b(e))throw new Error("`setDate` needs a JavaScript Date object.");if(this.disabledDates[+n]||n<this.minDate||n>this.maxDate)throw new Error("You can't manually set a date that's disabled.");this.dateSelected=n,t&&(this.currentYear=n.getFullYear(),this.currentMonth=n.getMonth(),this.currentMonthName=this.months[n.getMonth()]),p(this.el,this),i&&(y({instance:this}),u(i));var o=a===n.getFullYear()&&r===n.getMonth();return o||t?u(this,n):o||u(this,new Date(a,r,1)),this}function N(e){return I(this,e,!0)}function _(e){return I(this,e)}function I(e,t,n){var a=e.dateSelected,r=e.first,i=e.sibling,o=e.minDate,s=e.maxDate,l=g(t),d=n?"Min":"Max";function c(){return"original"+d+"Date"}function h(){return d.toLowerCase()+"Date"}function f(){return"set"+d}function v(){throw new Error("Out-of-range date passed to "+f())}if(null==t)e[c()]=void 0,i?(i[c()]=void 0,n?(r&&!a||!r&&!i.dateSelected)&&(e.minDate=void 0,i.minDate=void 0):(r&&!i.dateSelected||!r&&!a)&&(e.maxDate=void 0,i.maxDate=void 0)):e[h()]=void 0;else{if(!b(t))throw new Error("Invalid date passed to "+f());i?((r&&n&&l>(a||s)||r&&!n&&l<(i.dateSelected||o)||!r&&n&&l>(i.dateSelected||s)||!r&&!n&&l<(a||o))&&v(),e[c()]=l,i[c()]=l,(n&&(r&&!a||!r&&!i.dateSelected)||!n&&(r&&!i.dateSelected||!r&&!a))&&(e[h()]=l,i[h()]=l)):((n&&l>(a||s)||!n&&l<(a||o))&&v(),e[h()]=l)}return i&&u(i),u(e),e}function A(){var e=this.first?this:this.sibling,t=e.sibling;return{start:e.dateSelected,end:t.dateSelected}}function R(){var e=this.shadowDom,t=this.positionedEl,n=this.calendarContainer,r=this.sibling,i=this;this.inlinePosition&&(a.some((function(e){return e!==i&&e.positionedEl===t}))||t.style.setProperty("position",null));n.remove(),a=a.filter((function(e){return e!==i})),r&&delete r.sibling,a.length||j(document,L);var o=a.some((function(t){return t.shadowDom===e}));for(var s in e&&!o&&j(e,Y),this)delete this[s];a.length||l.forEach((function(e){document.removeEventListener(e,L)}))}function F(e,t){var n=new Date(e);if(!b(n))throw new Error("Invalid date passed to `navigate`");this.currentYear=n.getFullYear(),this.currentMonth=n.getMonth(),u(this),t&&this.onMonthChange(this)}function B(){var e=!this.calendarContainer.classList.contains("qs-hidden"),t=!this.calendarContainer.querySelector(".qs-overlay").classList.contains("qs-hidden");e&&M(t,this)}t.default=function(e,t){var n=function(e,t){var n,l,d=function(e){var t=c(e);t.events&&(t.events=t.events.reduce((function(e,t){if(!b(t))throw new Error('"options.events" must only contain valid JavaScript Date objects.');return e[+g(t)]=!0,e}),{}));["startDate","dateSelected","minDate","maxDate"].forEach((function(e){var n=t[e];if(n&&!b(n))throw new Error('"options.'+e+'" needs to be a valid JavaScript Date object.');t[e]=g(n)}));var n=t.position,i=t.maxDate,l=t.minDate,d=t.dateSelected,u=t.overlayPlaceholder,h=t.overlayButton,f=t.startDay,v=t.id;if(t.startDate=g(t.startDate||d||new Date),t.disabledDates=(t.disabledDates||[]).reduce((function(e,t){var n=+g(t);if(!b(t))throw new Error('You supplied an invalid date to "options.disabledDates".');if(n===+g(d))throw new Error('"disabledDates" cannot contain the same date as "dateSelected".');return e[n]=1,e}),{}),t.hasOwnProperty("id")&&null==v)throw new Error("`id` cannot be `null` or `undefined`");if(null!=v){var m=a.filter((function(e){return e.id===v}));if(m.length>1)throw new Error("Only two datepickers can share an id.");m.length?(t.second=!0,t.sibling=m[0]):t.first=!0}var y=["tr","tl","br","bl","c"].some((function(e){return n===e}));if(n&&!y)throw new Error('"options.position" must be one of the following: tl, tr, bl, br, or c.');function p(e){throw new Error('"dateSelected" in options is '+(e?"less":"greater")+' than "'+(e||"max")+'Date".')}if(t.position=function(e){var t=e[0],n=e[1],a={};a[o[t]]=1,n&&(a[o[n]]=1);return a}(n||"bl"),i<l)throw new Error('"maxDate" in options is less than "minDate".');d&&(l>d&&p("min"),i<d&&p());if(["onSelect","onShow","onHide","onMonthChange","formatter","disabler"].forEach((function(e){"function"!=typeof t[e]&&(t[e]=s)})),["customDays","customMonths","customOverlayMonths"].forEach((function(e,n){var a=t[e],r=n?12:7;if(a){if(!Array.isArray(a)||a.length!==r||a.some((function(e){return"string"!=typeof e})))throw new Error('"'+e+'" must be an array with '+r+" strings.");t[n?n<2?"months":"overlayMonths":"days"]=a}})),f&&f>0&&f<7){var w=(t.customDays||r).slice(),D=w.splice(0,f);t.customDays=w.concat(D),t.startDay=+f,t.weekendIndices=[w.length-1,w.length]}else t.startDay=0,t.weekendIndices=[6,0];"string"!=typeof u&&delete t.overlayPlaceholder;"string"!=typeof h&&delete t.overlayButton;var q=t.defaultView;if(q&&"calendar"!==q&&"overlay"!==q)throw new Error('options.defaultView must either be "calendar" or "overlay".');return t.defaultView=q||"calendar",t}(t||{startDate:g(new Date),position:"bl",defaultView:"calendar"}),u=e;if("string"==typeof u)u="#"===u[0]?document.getElementById(u.slice(1)):document.querySelector(u);else{if("[object ShadowRoot]"===x(u))throw new Error("Using a shadow DOM as your selector is not supported.");for(var h,f=u.parentNode;!h;){var v=x(f);"[object HTMLDocument]"===v?h=!0:"[object ShadowRoot]"===v?(h=!0,n=f,l=f.host):f=f.parentNode}}if(!u)throw new Error("No selector / element found.");if(a.some((function(e){return e.el===u})))throw new Error("A datepicker already exists on that element.");var m=u===document.body,y=n?u.parentElement||n:m?document.body:u.parentElement,w=n?u.parentElement||l:y,D=document.createElement("div"),q=document.createElement("div");D.className="qs-datepicker-container qs-hidden",q.className="qs-datepicker";var M={shadowDom:n,customElement:l,positionedEl:w,el:u,parent:y,nonInput:"INPUT"!==u.nodeName,noPosition:m,position:!m&&d.position,startDate:d.startDate,dateSelected:d.dateSelected,disabledDates:d.disabledDates,minDate:d.minDate,maxDate:d.maxDate,noWeekends:!!d.noWeekends,weekendIndices:d.weekendIndices,calendarContainer:D,calendar:q,currentMonth:(d.startDate||d.dateSelected).getMonth(),currentMonthName:(d.months||i)[(d.startDate||d.dateSelected).getMonth()],currentYear:(d.startDate||d.dateSelected).getFullYear(),events:d.events||{},defaultView:d.defaultView,setDate:k,remove:R,setMin:N,setMax:_,show:O,hide:P,navigate:F,toggleOverlay:B,onSelect:d.onSelect,onShow:d.onShow,onHide:d.onHide,onMonthChange:d.onMonthChange,formatter:d.formatter,disabler:d.disabler,months:d.months||i,days:d.customDays||r,startDay:d.startDay,overlayMonths:d.overlayMonths||(d.months||i).map((function(e){return e.slice(0,3)})),overlayPlaceholder:d.overlayPlaceholder||"4-digit year",overlayButton:d.overlayButton||"Submit",disableYearOverlay:!!d.disableYearOverlay,disableMobile:!!d.disableMobile,isMobile:"ontouchstart"in window,alwaysShow:!!d.alwaysShow,id:d.id,showAllDates:!!d.showAllDates,respectDisabledReadOnly:!!d.respectDisabledReadOnly,first:d.first,second:d.second};if(d.sibling){var E=d.sibling,C=M,L=E.minDate||C.minDate,Y=E.maxDate||C.maxDate;C.sibling=E,E.sibling=C,E.minDate=L,E.maxDate=Y,C.minDate=L,C.maxDate=Y,E.originalMinDate=L,E.originalMaxDate=Y,C.originalMinDate=L,C.originalMaxDate=Y,E.getRange=A,C.getRange=A}d.dateSelected&&p(u,M);var j=getComputedStyle(w).position;m||j&&"static"!==j||(M.inlinePosition=!0,w.style.setProperty("position","relative"));var I=a.filter((function(e){return e.positionedEl===M.positionedEl}));I.some((function(e){return e.inlinePosition}))&&(M.inlinePosition=!0,I.forEach((function(e){e.inlinePosition=!0})));D.appendChild(q),y.appendChild(D),M.alwaysShow&&S(M);return M}(e,t);if(a.length||d(document),n.shadowDom&&(a.some((function(e){return e.shadowDom===n.shadowDom}))||d(n.shadowDom)),a.push(n),n.second){var l=n.sibling;y({instance:n,deselect:!n.dateSelected}),y({instance:l,deselect:!l.dateSelected}),u(l)}return u(n,n.startDate||n.dateSelected),n.alwaysShow&&D(n),n}}]).default}));
1374
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.