PageSourceSearch

https://www.schweizerhofzermatt.ch/wp-content/plugins/bookwize-liq…?ver=0962f400a43c89e4c047707e8dfb1776

js schweizerhofzermatt.ch collected 2026-09-30 20:18:53 UTC 326,438 bytes, 7,311 lines download raw bytes

vendor: 5,019 bytes, lines 1-4
1/*! Magnific Popup - v1.1.0 - 2016-02-20
2* http://dimsemenov.com/plugins/magnific-popup/
3* Copyright (c) 2016 Dmitry Semenov; */
4!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):window.jQuery||window.Zepto)}(function(a){var b,c,d,e,f,g,h="Close",i="BeforeClose",j="AfterClose",k="BeforeAppend",l="MarkupParse",m="Open",n="Change",o="mfp",p="."+o,q="mfp-ready",r="mfp-removing",s="mfp-prevent-close",t=function(){},u=!!window.jQuery,v=a(window),w=function(a,c){b.ev.on(o+a+p,c)},x=function(b,c,d,e){var f=document.createElement("div");return f.className="mfp-"+b,d&&(f.innerHTML=d),e?c&&c.appendChild(f):(f=a(f),c&&f.appendTo(c)),f},y=function(c,d){b.ev.triggerHandler(o+c,d),b.st.callbacks&&(c=c.charAt(0).toLowerCase()+c.slice(1),b.st.callbacks[c]&&b.st.callbacks[c].apply(b,a.isArray(d)?d:[d]))},z=function(c){return c===g&&b.currTemplate.closeBtn||(b.currTemplate.closeBtn=a(b.st.closeMarkup.replace("%title%",b.st.tClose)),g=c),b.currTemplate.closeBtn},A=function(){a.magnificPopup.instance||(b=new t,b.init(),a.magnificPopup.instance=b)},B=function(){var a=document.createElement("p").style,b=["ms","O","Moz","Webkit"];if(void 0!==a.transition)return!0;for(;b.length;)if(b.pop()+"Transition"in a)return!0;return!1};t.prototype={constructor:t,init:function(){var c=navigator.appVersion;b.isLowIE=b.isIE8=document.all&&!document.addEventListener,b.isAndroid=/android/gi.test(c),b.isIOS=/iphone|ipad|ipod/gi.test(c),b.supportsTransition=B(),b.probablyMobile=b.isAndroid||b.isIOS||/(Opera Mini)|Kindle|webOS|BlackBerry|(Opera Mobi)|(Windows Phone)|IEMobile/i.test(navigator.userAgent),d=a(document),b.popupsCache={}},open:function(c){var e;if(c.isObj===!1){b.items=c.items.toArray(),b.index=0;var g,h=c.items;for(e=0;e<h.length;e++)if(g=h[e],g.parsed&&(g=g.el[0]),g===c.el[0]){b.index=e;break}}else b.items=a.isArray(c.items)?c.items:[c.items],b.index=c.index||0;if(b.isOpen)return void b.updateItemHTML();b.types=[],f="",c.mainEl&&c.mainEl.length?b.ev=c.mainEl.eq(0):b.ev=d,c.key?(b.popupsCache[c.key]||(b.popupsCache[c.key]={}),b.currTemplate=b.popupsCache[c.key]):b.currTemplate={},b.st=a.extend(!0,{},a.magnificPopup.defaults,c),b.fixedContentPos="auto"===b.st.fixedContentPos?!b.probablyMobile:b.st.fixedContentPos,b.st.modal&&(b.st.closeOnContentClick=!1,b.st.closeOnBgClick=!1,b.st.showCloseBtn=!1,b.st.enableEscapeKey=!1),b.bgOverlay||(b.bgOverlay=x("bg").on("click"+p,function(){b.close()}),b.wrap=x("wrap").attr("tabindex",-1).on("click"+p,function(a){b._checkIfClose(a.target)&&b.close()}),b.container=x("container",b.wrap)),b.contentContainer=x("content"),b.st.preloader&&(b.preloader=x("preloader",b.container,b.st.tLoading));var i=a.magnificPopup.modules;for(e=0;e<i.length;e++){var j=i[e];j=j.charAt(0).toUpperCase()+j.slice(1),b["init"+j].call(b)}y("BeforeOpen"),b.st.showCloseBtn&&(b.st.closeBtnInside?(w(l,function(a,b,c,d){c.close_replaceWith=z(d.type)}),f+=" mfp-close-btn-in"):b.wrap.append(z())),b.st.alignTop&&(f+=" mfp-align-top"),b.fixedContentPos?b.wrap.css({overflow:b.st.overflowY,overflowX:"hidden",overflowY:b.st.overflowY}):b.wrap.css({top:v.scrollTop(),position:"absolute"}),(b.st.fixedBgPos===!1||"auto"===b.st.fixedBgPos&&!b.fixedContentPos)&&b.bgOverlay.css({height:d.height(),position:"absolute"}),b.st.enableEscapeKey&&d.on("keyup"+p,function(a){27===a.keyCode&&b.close()}),v.on("resize"+p,function(){b.updateSize()}),b.st.closeOnContentClick||(f+=" mfp-auto-cursor"),f&&b.wrap.addClass(f);var k=b.wH=v.height(),n={};if(b.fixedContentPos&&b._hasScrollBar(k)){var o=b._getScrollbarSize();o&&(n.marginRight=o)}b.fixedContentPos&&(b.isIE7?a("body, html").css("overflow","hidden"):n.overflow="hidden");var r=b.st.mainClass;return b.isIE7&&(r+=" mfp-ie7"),r&&b._addClassToMFP(r),b.updateItemHTML(),y("BuildControls"),a("html").css(n),b.bgOverlay.add(b.wrap).prependTo(b.st.prependTo||a(document.body)),b._lastFocusedEl=document.activeElement,setTimeout(function(){b.content?(b._addClassToMFP(q),b._setFocus()):b.bgOverlay.addClass(q),d.on("focusin"+p,b._onFocusIn)},16),b.isOpen=!0,b.updateSize(k),y(m),c},close:function(){b.isOpen&&(y(i),b.isOpen=!1,b.st.removalDelay&&!b.isLowIE&&b.supportsTransition?(b._addClassToMFP(r),setTimeout(function(){b._close()},b.st.removalDelay)):b._close())},_close:function(){y(h);var c=r+" "+q+" ";if(b.bgOverlay.detach(),b.wrap.detach(),b.container.empty(),b.st.mainClass&&(c+=b.st.mainClass+" "),b._removeClassFromMFP(c),b.fixedContentPos){var e={marginRight:""};b.isIE7?a("body, html").css("overflow",""):e.overflow="",a("html").css(e)}d.off("keyup"+p+" focusin"+p),b.ev.off(p),b.wrap.attr("class","mfp-wrap").removeAttr("style"),b.bgOverlay.attr("class","mfp-bg"),b.container.attr("class","mfp-container"),!b.st.showCloseBtn||b.st.closeBtnInside&&b.currTemplate[b.currItem.type]!==!0||b.currTemplate.closeBtn&&b.currTemplate.closeBtn.detach(),b.st.autoFocusLast&&b._lastFocusedEl&&a(b._lastFocusedEl).focus(),b.currItem=null,b.content=null,b.currTemplate=null,b.prevHeight=0,y(j)}
4,updateSize:function(a){if(b.isIOS){var c=document.documentElement.clientWidth/window.innerWidth,d=window.innerHeight*c;b.wrap.css("height",d),b.wH=d}else b.wH=a||v.height();b.fixedContentPos||b.wrap.css("height",b.wH),y("Resize")},updateItemHTML:function(){var c=b.items[b.index];b.contentContainer.detach(),b.content&&b.content.detach(),c.parsed||(c=b.parseEl(b.index));var d=c.type;if(y("BeforeChange",[b.currItem?b.currItem.type:"",d]),b.currItem=c,!b.currTemplate[d]){var f=b.st[d]?b.st[d].markup:!1;y("FirstMarkupParse",f),f?b.currTemplate[d]=a(f):b.currTemplate[d]=!0}e&&e!==c.type&&b.container.removeClass("mfp-"+e+"-holder");var g=b["get"+d.charAt(0).toUpperCase()+d.slice(1)](c,b.currTemplate[d]);b.appendContent(g,d),c.preloaded=!0,y(n,c),e=c.type,b.container.prepend(b.contentContainer),y("AfterChange")},appendContent:function(a,c){b.content=a,a?b.st.showCloseBtn&&b.st.closeBtnInside&&b.currTemplate[c]===!0?b.content.find(".mfp-close").length||b.content.append(z()):b.content=a:b.content="",y(k),b.container.addClass("mfp-"+c+"-holder"),b.contentContainer.append(b.content)},parseEl:function(c){var d,e=b.items[c];if(e.tagName?e={el:a(e)}:(d=e.type,e={data:e,src:e.src}),e.el){for(var f=b.types,g=0;g<f.length;g++)if(e.el.hasClass("mfp-"+f[g])){d=f[g];break}e.src=e.el.attr("data-mfp-src"),e.src||(e.src=e.el.attr("href"))}return e.type=d||b.st.type||"inline",e.index=c,e.parsed=!0,b.items[c]=e,y("ElementParse",e),b.items[c]},addGroup:function(a,c){var d=function(d){d.mfpEl=this,b._openClick(d,a,c)};c||(c={});var e="click.magnificPopup";c.mainEl=a,c.items?(c.isObj=!0,a.off(e).on(e,d)):(c.isObj=!1,c.delegate?a.off(e).on(e,c.delegate,d):(c.items=a,a.off(e).on(e,d)))},_openClick:function(c,d,e){var f=void 0!==e.midClick?e.midClick:a.magnificPopup.defaults.midClick;if(f||!(2===c.which||c.ctrlKey||c.metaKey||c.altKey||c.shiftKey)){var g=void 0!==e.disableOn?e.disableOn:a.magnificPopup.defaults.disableOn;if(g)if(a.isFunction(g)){if(!g.call(b))return!0}else if(v.width()<g)return!0;c.type&&(c.preventDefault(),b.isOpen&&c.stopPropagation()),e.el=a(c.mfpEl),e.delegate&&(e.items=d.find(e.delegate)),b.open(e)}},updateStatus:function(a,d){if(b.preloader){c!==a&&b.container.removeClass("mfp-s-"+c),d||"loading"!==a||(d=b.st.tLoading);var e={status:a,text:d};y("UpdateStatus",e),a=e.status,d=e.text,b.preloader.html(d),b.preloader.find("a").on("click",function(a){a.stopImmediatePropagation()}),b.container.addClass("mfp-s-"+a),c=a}},_checkIfClose:function(c){if(!a(c).hasClass(s)){var d=b.st.closeOnContentClick,e=b.st.closeOnBgClick;if(d&&e)return!0;if(!b.content||a(c).hasClass("mfp-close")||b.preloader&&c===b.preloader[0])return!0;if(c===b.content[0]||a.contains(b.content[0],c)){if(d)return!0}else if(e&&a.contains(document,c))return!0;return!1}},_addClassToMFP:function(a){b.bgOverlay.addClass(a),b.wrap.addClass(a)},_removeClassFromMFP:function(a){this.bgOverlay.removeClass(a),b.wrap.removeClass(a)},_hasScrollBar:function(a){return(b.isIE7?d.height():document.body.scrollHeight)>(a||v.height())},_setFocus:function(){(b.st.focus?b.content.find(b.st.focus).eq(0):b.wrap).focus()},_onFocusIn:function(c){return c.target===b.wrap[0]||a.contains(b.wrap[0],c.target)?void 0:(b._setFocus(),!1)},_parseMarkup:function(b,c,d){var e;d.data&&(c=a.extend(d.data,c)),y(l,[b,c,d]),a.each(c,function(c,d){if(void 0===d||d===!1)return!0;if(e=c.split("_"),e.length>1){var f=b.find(p+"-"+e[0]);if(f.length>0){var g=e[1];"replaceWith"===g?f[0]!==d[0]&&f.replaceWith(d):"img"===g?f.is("img")?f.attr("src",d):f.replaceWith(a("<img>").attr("src",d).attr("class",f.attr("class"))):f.attr(e[1],d)}}else b.find(p+"-"+c).html(d)})},_getScrollbarSize:function(){if(void 0===b.scrollbarSize){var a=document.createElement("div");a.style.cssText="width: 99px; height: 99px; overflow: scroll; position: absolute; top: -9999px;",document.body.appendChild(a),b.scrollbarSize=a.offsetWidth-a.clientWidth,document.body.removeChild(a)}return b.scrollbarSize}},a.magnificPopup={instance:null,proto:t.prototype,modules:[],open:function(b,c){return A(),b=b?a.extend(!0,{},b):{},b.isObj=!0,b.index=c||0,this.instance.open(b)},close:function(){return a.magnificPopup.instance&&a.magnificPopup.instance.close()},registerModule:function(b,c){c.options&&(a.magnificPopup.defaults[b]=c.options),a.extend(this.proto,c.proto),this.modules.push(b)},defaults:{disableOn:0,key:null,midClick:!1,mainClass:"",preloader:!0,focus:"",closeOnContentClick:!1,closeOnBgClick:!0,closeBtnInside:!0,showCloseBtn:!0,enableEscapeKey:!0,modal:!1,alignTop:!1,removalDelay:0,prependTo:null,fixedContentPos:"auto",fixedBgPos:"auto",overflowY:"auto",closeMarkup:'<button title="%title%" type="button" class="mfp-close">&#215;</button>',tClose:"Close (Esc)",tLoading:"Loading...",autoFocusLast:!0}},a.fn.magnificPopup=function(c){A();var d=a(this);if("string"==typeof c)if("open"===c){var e,f=u?d.data("magnificPopup"):d[0].magnificPopup,g=parseInt(arguments[1],10)||0;f.items?e=f.items[g]:(e=d,f.delegate&&(e=e.find(f.delegate)),e=e.eq(g)),b._openClick({mfpEl:e},d,f)}else b.isOpen&&b[c].apply(b,Array.prototype.slice.call(arguments,1
4));else c=a.extend(!0,{},c),u?d.data("magnificPopup",c):d[0].magnificPopup=c,b.addGroup(d,c);return d};var C,D,E,F="inline",G=function(){E&&(D.after(E.addClass(C)).detach(),E=null)};a.magnificPopup.registerModule(F,{options:{hiddenClass:"hide",markup:"",tNotFound:"Content not found"},proto:{initInline:function(){b.types.push(F),w(h+"."+F,function(){G()})},getInline:function(c,d){if(G(),c.src){var e=b.st.inline,f=a(c.src);if(f.length){var g=f[0].parentNode;g&&g.tagName&&(D||(C=e.hiddenClass,D=x(C),C="mfp-"+C),E=f.after(D).detach().removeClass(C)),b.updateStatus("ready")}else b.updateStatus("error",e.tNotFound),f=a("<div>");return c.inlineElement=f,f}return b.updateStatus("ready"),b._parseMarkup(d,{},c),d}}});var H,I="ajax",J=function(){H&&a(document.body).removeClass(H)},K=function(){J(),b.req&&b.req.abort()};a.magnificPopup.registerModule(I,{options:{settings:null,cursor:"mfp-ajax-cur",tError:'<a href="%url%">The content</a> could not be loaded.'},proto:{initAjax:function(){b.types.push(I),H=b.st.ajax.cursor,w(h+"."+I,K),w("BeforeChange."+I,K)},getAjax:function(c){H&&a(document.body).addClass(H),b.updateStatus("loading");var d=a.extend({url:c.src,success:function(d,e,f){var g={data:d,xhr:f};y("ParseAjax",g),b.appendContent(a(g.data),I),c.finished=!0,J(),b._setFocus(),setTimeout(function(){b.wrap.addClass(q)},16),b.updateStatus("ready"),y("AjaxContentAdded")},error:function(){J(),c.finished=c.loadError=!0,b.updateStatus("error",b.st.ajax.tError.replace("%url%",c.src))}},b.st.ajax.settings);return b.req=a.ajax(d),""}}});var L,M=function(c){if(c.data&&void 0!==c.data.title)return c.data.title;
4var d=b.st.image.titleSrc;if(d){if(a.isFunction(d))return d.call(b,c);if(c.el)return c.el.attr(d)||""}return""};a.magnificPopup.registerModule("image",{options:{markup:'<div class="mfp-figure"><div class="mfp-close"></div><figure><div class="mfp-img"></div><figcaption><div class="mfp-bottom-bar"><div class="mfp-title"></div><div class="mfp-counter"></div></div></figcaption></figure></div>',cursor:"mfp-zoom-out-cur",titleSrc:"title",verticalFit:!0,tError:'<a href="%url%">The image</a> could not be loaded.'},proto:{initImage:function(){var c=b.st.image,d=".image";b.types.push("image"),w(m+d,function(){"image"===b.currItem.type&&c.cursor&&a(document.body).addClass(c.cursor)}),w(h+d,function(){c.cursor&&a(document.body).removeClass(c.cursor),v.off("resize"+p)}),w("Resize"+d,b.resizeImage),b.isLowIE&&w("AfterChange",b.resizeImage)},resizeImage:function(){var a=b.currItem;if(a&&a.img&&b.st.image.verticalFit){var c=0;b.isLowIE&&(c=parseInt(a.img.css("padding-top"),10)+parseInt(a.img.css("padding-bottom"),10)),a.img.css("max-height",b.wH-c)}},_onImageHasSize:function(a){a.img&&(a.hasSize=!0,L&&clearInterval(L),a.isCheckingImgSize=!1,y("ImageHasSize",a),a.imgHidden&&(b.content&&b.content.removeClass("mfp-loading"),a.imgHidden=!1))},findImageSize:function(a){var c=0,d=a.img[0],e=function(f){L&&clearInterval(L),L=setInterval(function(){return d.naturalWidth>0?void b._onImageHasSize(a):(c>200&&clearInterval(L),c++,void(3===c?e(10):40===c?e(50):100===c&&e(500)))},f)};e(1)},getImage:function(c,d){var e=0,f=function(){c&&(c.img[0].complete?(c.img.off(".mfploader"),c===b.currItem&&(b._onImageHasSize(c),b.updateStatus("ready")),c.hasSize=!0,c.loaded=!0,y("ImageLoadComplete")):(e++,200>e?setTimeout(f,100):g()))},g=function(){c&&(c.img.off(".mfploader"),c===b.currItem&&(b._onImageHasSize(c),b.updateStatus("error",h.tError.replace("%url%",c.src))),c.hasSize=!0,c.loaded=!0,c.loadError=!0)},h=b.st.image,i=d.find(".mfp-img");if(i.length){var j=document.createElement("img");j.className="mfp-img",c.el&&c.el.find("img").length&&(j.alt=c.el.find("img").attr("alt")),c.img=a(j).on("load.mfploader",f).on("error.mfploader",g),j.src=c.src,i.is("img")&&(c.img=c.img.clone()),j=c.img[0],j.naturalWidth>0?c.hasSize=!0:j.width||(c.hasSize=!1)}return b._parseMarkup(d,{title:M(c),img_replaceWith:c.img},c),b.resizeImage(),c.hasSize?(L&&clearInterval(L),c.loadError?(d.addClass("mfp-loading"),b.updateStatus("error",h.tError.replace("%url%",c.src))):(d.removeClass("mfp-loading"),b.updateStatus("ready")),d):(b.updateStatus("loading"),c.loading=!0,c.hasSize||(c.imgHidden=!0,d.addClass("mfp-loading"),b.findImageSize(c)),d)}}});var N,O=function(){return void 0===N&&(N=void 0!==document.createElement("p").style.MozTransform),N};a.magnificPopup.registerModule("zoom",{options:{enabled:!1,easing:"ease-in-out",duration:300,opener:function(a){return a.is("img")?a:a.find("img")}},proto:{initZoom:function(){var a,c=b.st.zoom,d=".zoom";if(c.enabled&&b.supportsTransition){var e,f,g=c.duration,j=function(a){var b=a.clone().removeAttr("style").removeAttr("class").addClass("mfp-animated-image"),d="all "+c.duration/1e3+"s "+c.easing,e={position:"fixed",zIndex:9999,left:0,top:0,"-webkit-backface-visibility":"hidden"},f="transition";return e["-webkit-"+f]=e["-moz-"+f]=e["-o-"+f]=e[f]=d,b.css(e),b},k=function(){b.content.css("visibility","visible")};w("BuildControls"+d,function(){if(b._allowZoom()){if(clearTimeout(e),b.content.css("visibility","hidden"),a=b._getItemToZoom(),!a)return void k();f=j(a),f.css(b._getOffset()),b.wrap.append(f),e=setTimeout(function(){f.css(b._getOffset(!0)),e=setTimeout(function(){k(),setTimeout(function(){f.remove(),a=f=null,y("ZoomAnimationEnded")},16)},g)},16)}}),w(i+d,function(){if(b._allowZoom()){if(clearTimeout(e),b.st.removalDelay=g,!a){if(a=b._getItemToZoom(),!a)return;f=j(a)}f.css(b._getOffset(!0)),b.wrap.append(f),b.content.css("visibility","hidden"),setTimeout(function(){f.css(b._getOffset())},16)}}),w(h+d,function(){b._allowZoom()&&(k(),f&&f.remove(),a=null)})}},_allowZoom:function(){return"image"===b.currItem.type},_getItemToZoom:function(){return b.currItem.hasSize?b.currItem.img:!1},_getOffset:function(c){var d;d=c?b.currItem.img:b.st.zoom.opener(b.currItem.el||b.currItem);var e=d.offset(),f=parseInt(d.css("padding-top"),10),g=parseInt(d.css("padding-bottom"),10);e.top-=a(window).scrollTop()-f;var h={width:d.width(),height:(u?d.innerHeight():d[0].offsetHeight)-g-f};return O()?h["-moz-transform"]=h.transform="translate("+e.left+"px,"+e.top+"px)":(h.left=e.left,h.top=e.top),h}}});
4var P="iframe",Q="//about:blank",R=function(a){if(b.currTemplate[P]){var c=b.currTemplate[P].find("iframe");c.length&&(a||(c[0].src=Q),b.isIE8&&c.css("display",a?"block":"none"))}};a.magnificPopup.registerModule(P,{options:{markup:'<div class="mfp-iframe-scaler"><div class="mfp-close"></div><iframe class="mfp-iframe" src="//about:blank" frameborder="0" allowfullscreen></iframe></div>',srcAction:"iframe_src",patterns:{youtube:{index:"youtube.com",id:"v=",src:"//www.youtube.com/embed/%id%?autoplay=1"},vimeo:{index:"vimeo.com/",id:"/",src:"//player.vimeo.com/video/%id%?autoplay=1"},gmaps:{index:"//maps.google.",src:"%id%&output=embed"}}},proto:{initIframe:function(){b.types.push(P),w("BeforeChange",function(a,b,c){b!==c&&(b===P?R():c===P&&R(!0))}),w(h+"."+P,function(){R()})},getIframe:function(c,d){var e=c.src,f=b.st.iframe;a.each(f.patterns,function(){return e.indexOf(this.index)>-1?(this.id&&(e="string"==typeof this.id?e.substr(e.lastIndexOf(this.id)+this.id.length,e.length):this.id.call(this,e)),e=this.src.replace("%id%",e),!1):void 0});var g={};return f.srcAction&&(g[f.srcAction]=e),b._parseMarkup(d,g,c),b.updateStatus("ready"),d}}});var S=function(a){var c=b.items.length;return a>c-1?a-c:0>a?c+a:a},T=function(a,b,c){return a.replace(/%curr%/gi,b+1).replace(/%total%/gi,c)};a.magnificPopup.registerModule("gallery",{options:{enabled:!1,arrowMarkup:'<button title="%title%" type="button" class="mfp-arrow mfp-arrow-%dir%"></button>',preload:[0,2],navigateByImgClick:!0,arrows:!0,tPrev:"Previous (Left arrow key)",tNext:"Next (Right arrow key)",tCounter:"%curr% of %total%"},proto:{initGallery:function(){var c=b.st.gallery,e=".mfp-gallery";return b.direction=!0,c&&c.enabled?(f+=" mfp-gallery",w(m+e,function(){c.navigateByImgClick&&b.wrap.on("click"+e,".mfp-img",function(){return b.items.length>1?(b.next(),!1):void 0}),d.on("keydown"+e,function(a){37===a.keyCode?b.prev():39===a.keyCode&&b.next()})}),w("UpdateStatus"+e,function(a,c){c.text&&(c.text=T(c.text,b.currItem.index,b.items.length))}),w(l+e,function(a,d,e,f){var g=b.items.length;e.counter=g>1?T(c.tCounter,f.index,g):""}),w("BuildControls"+e,function(){if(b.items.length>1&&c.arrows&&!b.arrowLeft){var d=c.arrowMarkup,e=b.arrowLeft=a(d.replace(/%title%/gi,c.tPrev).replace(/%dir%/gi,"left")).addClass(s),f=b.arrowRight=a(d.replace(/%title%/gi,c.tNext).replace(/%dir%/gi,"right")).addClass(s);e.click(function(){b.prev()}),f.click(function(){b.next()}),b.container.append(e.add(f))}}),w(n+e,function(){b._preloadTimeout&&clearTimeout(b._preloadTimeout),b._preloadTimeout=setTimeout(function(){b.preloadNearbyImages(),b._preloadTimeout=null},16)}),void w(h+e,function(){d.off(e),b.wrap.off("click"+e),b.arrowRight=b.arrowLeft=null})):!1},next:function(){b.direction=!0,b.index=S(b.index+1),b.updateItemHTML()},prev:function(){b.direction=!1,b.index=S(b.index-1),b.updateItemHTML()},goTo:function(a){b.direction=a>=b.index,b.index=a,b.updateItemHTML()},preloadNearbyImages:function(){var a,c=b.st.gallery.preload,d=Math.min(c[0],b.items.length),e=Math.min(c[1],b.items.length);for(a=1;a<=(b.direction?e:d);a++)b._preloadItem(b.index+a);for(a=1;a<=(b.direction?d:e);a++)b._preloadItem(b.index-a)},_preloadItem:function(c){if(c=S(c),!b.items[c].preloaded){var d=b.items[c];d.parsed||(d=b.parseEl(c)),y("LazyLoad",d),"image"===d.type&&(d.img=a('<img class="mfp-img" />').on("load.mfploader",function(){d.hasSize=!0}).on("error.mfploader",function(){d.hasSize=!0,d.loadError=!0,y("LazyLoadError",d)}).attr("src",d.src)),d.preloaded=!0}}}});var U="retina";a.magnificPopup.registerModule(U,{options:{replaceSrc:function(a){return a.src.replace(/\.\w+$/,function(a){return"@2x"+a})},ratio:1},proto:{initRetina:function(){if(window.devicePixelRatio>1){var a=b.st.retina,c=a.ratio;c=isNaN(c)?c():c,c>1&&(w("ImageHasSize."+U,function(a,b){b.img.css({"max-width":b.img[0].naturalWidth/c,width:"100%"})}),w("ElementParse."+U,function(b,d){d.src=a.replaceSrc(d,c)}))}}}}),A()});
5/*
6     _ _      _       _
7 ___| (_) ___| | __  (_)___
8/ __| | |/ __| |/ /  | / __|
9\__ \ | | (__|   < _ | \__ \
10|___/_|_|\___|_|\_(_)/ |___/
11                   |__/
12
13 Version: 1.5.9
14  Author: Ken Wheeler
15 Website: http://kenwheeler.github.io
16    Docs: http://kenwheeler.github.io/slick
17    Repo: http://github.com/kenwheeler/slick
18  Issues: http://github.com/kenwheeler/slick/issues
19
20 */
21!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,useTransform:!1
vendor: 38,833 bytes, lines 21-22
21,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,sliding:!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),e.options.vertical===!1?d[e.animType]="translate3d("+b+"px, 0px, 0px)":d[e.animType]="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={};b.options.fade===!1?c[b.transitionType]=b.transformType+" "+b.options.speed+"ms "+b.options.cssEase:c[b.transitionType]="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)):(a.currentSlide-1===0&&(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.$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=d.slideCount%d.options.slidesToScroll!==0,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.slick",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.$slidesCache=b.$slides,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.slidesToScroll,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.slidesToScroll,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=b.slideWidth*b.options.slidesToShow*-1,e=d*b.options.slidesToShow*-1),b.slideCount%b.options.slidesToScroll!==0&&a+b.options.slidesToScroll>b.slideCount&&b.slideCount>b.options.slidesToShow&&(a>b.slideCount?(b.slideOffset=(b.options.slidesToShow-(a-b.slideCount))*b.slideWidth*-1,e=(b.options.slidesToShow-(a-b.slideCount))*d*-1):(b.slideOffset=b.slideCount%b.options.slidesToScroll*b.slideWidth*-1,e=b.slideCount%b.options.slidesToScroll*d*-1))):a+b.options.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?a*b.slideWidth*-1+b.slideOffset:a*d*-1+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=b.options.rtl===!0?f[0]?-1*(b.$slideTrack.width()-f[0].offsetLeft-f.width()):0:f[0]?-1*f[0].offsetLeft:0,b.options.centerMode===!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+1),c=b.options.rtl===!0?f[0]?-1*(b.$slideTrack.width()-f[0].offsetLeft-f.width()):0: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"},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},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",null),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 d,e,c=this;e=c.slideCount-c.options.slidesToShow,c.options.infinite||(c.slideCount<=c.options.slidesToShow?c.currentSlide=0:c.currentSlide>e&&(c.currentSlide=e)),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,void d.reinit())},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.$slideTrack.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=b.slideWidth*d*-1,b.options.rtl===!0?a(e).css({position:"relative",right:c,top:0,zIndex:b.options.zIndex-2,opacity:0}):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=a.options.useTransform&&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.options.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");b.$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),void c.asNavFor(e)):void c.slideHandler(e)},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)?void(i.options.fade===!1&&(d=i.currentSlide,c!==!0?i.animateSlide(g,function(){i.postSlide(d);
22}):i.postSlide(d))):i.options.infinite===!1&&i.options.centerMode===!0&&(0>a||a>i.slideCount-i.options.slidesToScroll)?void(i.options.fade===!1&&(d=i.currentSlide,c!==!0?i.animateSlide(g,function(){i.postSlide(d)}):i.postSlide(d))):(i.options.autoplay===!0&&clearInterval(i.autoPlayTimer),e=0>d?i.slideCount%i.options.slidesToScroll!==0?i.slideCount-i.slideCount%i.options.slidesToScroll:i.slideCount+d:d>=i.slideCount?i.slideCount%i.options.slidesToScroll!==0?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)})):i.postSlide(e),void i.animateHeight()):void(c!==!0?i.animateSlide(h,function(){i.postSlide(e)}):i.postSlide(e))))},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(a){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>4&&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.options.vertical===!1?b.swipeLeft=d+f*g:b.swipeLeft=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):void b.setCSS(b.swipeLeft)):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,void(b.dragging=!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;a.$slideTrack.find(".slick-active").attr({"aria-hidden":"false"}).find("a, input, button, select").attr({tabindex:"0"})},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 f,g,a=this,c=arguments[0],d=Array.prototype.slice.call(arguments,1),e=a.length;for(f=0;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}});
22
vendor: 40,580 bytes, lines 23-1122
23/*
24== malihu jquery custom scrollbar plugin == 
25Version: 3.1.5 
26Plugin URI: http://manos.malihu.gr/jquery-custom-content-scroller 
27Author: malihu
28Author URI: http://manos.malihu.gr
29License: MIT License (MIT)
30*/
31
32/*
33Copyright Manos Malihutsakis (email: [email protected])
34
35Permission is hereby granted, free of charge, to any person obtaining a copy
36of this software and associated documentation files (the "Software"), to deal
37in the Software without restriction, including without limitation the rights
38to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
39copies of the Software, and to permit persons to whom the Software is
40furnished to do so, subject to the following conditions:
41
42The above copyright notice and this permission notice shall be included in
43all copies or substantial portions of the Software.
44
45THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
46IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
47FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
48AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
49LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
50OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
51THE SOFTWARE.
52*/
53
54/*
55The code below is fairly long, fully commented and should be normally used in development. 
56For production, use either the minified jquery.mCustomScrollbar.min.js script or 
57the production-ready jquery.mCustomScrollbar.concat.min.js which contains the plugin 
58and dependencies (minified). 
59*/
60
61(function(factory){
62	if(typeof define==="function" && define.amd){
63		define(["jquery"],factory);
64	}else if(typeof module!=="undefined" && module.exports){
65		module.exports=factory;
66	}else{
67		factory(jQuery,window,document);
68	}
69}(function($){
70(function(init){
71	var _rjs=typeof define==="function" && define.amd, /* RequireJS */
72		_njs=typeof module !== "undefined" && module.exports, /* NodeJS */
73		_dlp=("https:"==document.location.protocol) ? "https:" : "http:", /* location protocol */
74		_url="cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.1.13/jquery.mousewheel.min.js";
75	if(!_rjs){
76		if(_njs){
77			require("jquery-mousewheel")($);
78		}else{
79			/* load jquery-mousewheel plugin (via CDN) if it's not present or not loaded via RequireJS 
80			(works when mCustomScrollbar fn is called on window load) */
81			$.event.special.mousewheel || $("head").append(decodeURI("%3Cscript src="+_dlp+"//"+_url+"%3E%3C/script%3E"));
82		}
83	}
84	init();
85}(function(){
86	
87	/* 
88	----------------------------------------
89	PLUGIN NAMESPACE, PREFIX, DEFAULT SELECTOR(S) 
90	----------------------------------------
91	*/
92	
93	var pluginNS="mCustomScrollbar",
94		pluginPfx="mCS",
95		defaultSelector=".mCustomScrollbar",
96	
97	
98		
99	
100	
101	/* 
102	----------------------------------------
103	DEFAULT OPTIONS 
104	----------------------------------------
105	*/
106	
107		defaults={
108			/*
109			set element/content width/height programmatically 
110			values: boolean, pixels, percentage 
111				option						default
112				-------------------------------------
113				setWidth					false
114				setHeight					false
115			*/
116			/*
117			set the initial css top property of content  
118			values: string (e.g. "-100px", "10%" etc.)
119			*/
120			setTop:0,
121			/*
122			set the initial css left property of content  
123			values: string (e.g. "-100px", "10%" etc.)
124			*/
125			setLeft:0,
126			/* 
127			scrollbar axis (vertical and/or horizontal scrollbars) 
128			values (string): "y", "x", "yx"
129			*/
130			axis:"y",
131			/*
132			position of scrollbar relative to content  
133			values (string): "inside", "outside" ("outside" requires elements with position:relative)
134			*/
135			scrollbarPosition:"inside",
136			/*
137			scrolling inertia
138			values: integer (milliseconds)
139			*/
140			scrollInertia:950,
141			/* 
142			auto-adjust scrollbar dragger length
143			values: boolean
144			*/
145			autoDraggerLength:true,
146			/*
147			auto-hide scrollbar when idle 
148			values: boolean
149				option						default
150				-------------------------------------
151				autoHideScrollbar			false
152			*/
153			/*
154			auto-expands scrollbar on mouse-over and dragging
155			values: boolean
156				option						default
157				-------------------------------------
158				autoExpandScrollbar			false
159			*/
160			/*
161			always show scrollbar, even when there's nothing to scroll 
162			values: integer (0=disable, 1=always show dragger rail and buttons, 2=always show dragger rail, dragger and buttons), boolean
163			*/
164			alwaysShowScrollbar:0,
165			/*
166			scrolling always snaps to a multiple of this number in pixels
167			values: integer, array ([y,x])
168				option						default
169				-------------------------------------
170				snapAmount					null
171			*/
172			/*
173			when snapping, snap with this number in pixels as an offset 
174			values: integer
175			*/
176			snapOffset:0,
177			/* 
178			mouse-wheel scrolling
179			*/
180			mouseWheel:{
181				/* 
182				enable mouse-wheel scrolling
183				values: boolean
184				*/
185				enable:true,
186				/* 
187				scrolling amount in pixels
188				values: "auto", integer 
189				*/
190				scrollAmount:"auto",
191				/* 
192				mouse-wheel scrolling axis 
193				the default scrolling direction when both vertical and horizontal scrollbars are present 
194				values (string): "y", "x" 
195				*/
196				axis:"y",
197				/* 
198				prevent the default behaviour which automatically scrolls the parent element(s) when end of scrolling is reached 
199				values: boolean
200					option						default
201					-------------------------------------
202					preventDefault				null
203				*/
204				/*
205				the reported mouse-wheel delta value. The number of lines (translated to pixels) one wheel notch scrolls.  
206				values: "auto", integer 
207				"auto" uses the default OS/browser value 
208				*/
209				deltaFactor:"auto",
210				/*
211				normalize mouse-wheel delta to -1 or 1 (disables mouse-wheel acceleration) 
212				values: boolean
213					option						default
214					-------------------------------------
215					normalizeDelta				null
216				*/
217				/*
218				invert mouse-wheel scrolling direction 
219				values: boolean
220					option						default
221					-------------------------------------
222					invert						null
223				*/
224				/*
225				the tags that disable mouse-wheel when cursor is over them
226				*/
227				disableOver:["select","option","keygen","datalist","textarea"]
228			},
229			/* 
230			scrollbar buttons
231			*/
232			scrollButtons:{ 
233				/*
234				enable scrollbar buttons
235				values: boolean
236					option						default
237					-------------------------------------
238					enable						null
239				*/
240				/*
241				scrollbar buttons scrolling type 
242				values (string): "stepless", "stepped"
243				*/
244				scrollType:"stepless",
245				/*
246				scrolling amount in pixels
247				values: "auto", integer 
248				*/
249				scrollAmount:"auto"
250				/*
251				tabindex of the scrollbar buttons
252				values: false, integer
253					option						default
254					-------------------------------------
255					tabindex					null
256				*/
257			},
258			/* 
259			keyboard scrolling
260			*/
261			keyboard:{ 
262				/*
263				enable scrolling via keyboard
264				values: boolean
265				*/
266				enable:true,
267				/*
268				keyboard scrolling type 
269				values (string): "stepless", "stepped"
270				*/
271				scrollType:"stepless",
272				/*
273				scrolling amount in pixels
274				values: "auto", integer 
275				*/
276				scrollAmount:"auto"
277			},
278			/*
279			enable content touch-swipe scrolling 
280			values: boolean, integer, string (number)
281			integer values define the axis-specific minimum amount required for scrolling momentum
282			*/
283			contentTouchScroll:25,
284			/*
285			enable/disable document (default) touch-swipe scrolling 
286			*/
287			documentTouchScroll:true,
288			/*
289			advanced option parameters
290			*/
291			advanced:{
292				/*
293				auto-expand content horizontally (for "x" or "yx" axis) 
294				values: boolean, integer (the value 2 forces the non scrollHeight/scrollWidth method, the value 3 forces the scrollHeight/scrollWidth method)
295					option						default
296					-------------------------------------
297					autoExpandHorizontalScroll	null
298				*/
299				/*
300				auto-scroll to elements with focus
301				*/
302				autoScrollOnFocus:"input,textarea,select,button,datalist,keygen,a[tabindex],area,object,[contenteditable='true']",
303				/*
304				auto-update scrollbars on content, element or viewport resize 
305				should be true for fluid layouts/elements, adding/removing content dynamically, hiding/showing elements, content with images etc. 
306				values: boolean
307				*/
308				updateOnContentResize:true,
309				/*
310				auto-update scrollbars each time each image inside the element is fully loaded 
311				values: "auto", boolean
312				*/
313				updateOnImageLoad:"auto",
314				/*
315				auto-update scrollbars based on the amount and size changes of specific selectors 
316				useful when you need to update the scrollbar(s) automatically, each time a type of element is added, removed or changes its size 
317				values: boolean, string (e.g. "ul li" will auto-update scrollbars each time list-items inside the element are changed) 
318				a value of true (boolean) will auto-update scrollbars each time any element is changed
319					option						default
320					-------------------------------------
321					updateOnSelectorChange		null
322				*/
323				/*
324				extra selectors that'll allow scrollbar dragging upon mousemove/up, pointermove/up, touchend etc. (e.g. "selector-1, selector-2")
325					option						default
326					-------------------------------------
327					extraDraggableSelectors		null
328				*/
329				/*
330				extra selectors that'll release scrollbar dragging upon mouseup, pointerup, touchend etc. (e.g. "selector-1, selector-2")
331					option						default
332					-------------------------------------
333					releaseDraggableSelectors	null
334				*/
335				/*
336				auto-update timeout 
337				values: integer (milliseconds)
338				*/
339				autoUpdateTimeout:60
340			},
341			/* 
342			scrollbar theme 
343			values: string (see CSS/plugin URI for a list of ready-to-use themes)
344			*/
345			theme:"light",
346			/*
347			user defined callback functions
348			*/
349			callbacks:{
350				/*
351				Available callbacks: 
352					callback					default
353					-------------------------------------
354					onCreate					null
355					onInit						null
356					onScrollStart				null
357					onScroll					null
358					onTotalScroll				null
359					onTotalScrollBack			null
360					whileScrolling				null
361					onOverflowY					null
362					onOverflowX					null
363					onOverflowYNone				null
364					onOverflowXNone				null
365					onImageLoad					null
366					onSelectorChange			null
367					onBeforeUpdate				null
368					onUpdate					null
369				*/
370				onTotalScrollOffset:0,
371				onTotalScrollBackOffset:0,
372				alwaysTriggerOffsets:true
373			}
374			/*
375			add scrollbar(s) on all elements matching the current selector, now and in the future 
376			values: boolean, string 
377			string values: "on" (enable), "once" (disable after first invocation), "off" (disable)
378			liveSelector values: string (selector)
379				option						default
380				-------------------------------------
381				live						false
382				liveSelector				null
383			*/
384		},
385	
386	
387	
388	
389	
390	/* 
391	----------------------------------------
392	VARS, CONSTANTS 
393	----------------------------------------
394	*/
395	
396		totalInstances=0, /* plugin instances amount */
397		liveTimers={}, /* live option timers */
398		oldIE=(window.attachEvent && !window.addEventListener) ? 1 : 0, /* detect IE < 9 */
399		touchActive=false,touchable, /* global touch vars (for touch and pointer events) */
400		/* general plugin classes */
401		classes=[
402			"mCSB_dragger_onDrag","mCSB_scrollTools_onDrag","mCS_img_loaded","mCS_disabled","mCS_destroyed","mCS_no_scrollbar",
403			"mCS-autoHide","mCS-dir-rtl","mCS_no_scrollbar_y","mCS_no_scrollbar_x","mCS_y_hidden","mCS_x_hidden","mCSB_draggerContainer",
404			"mCSB_buttonUp","mCSB_buttonDown","mCSB_buttonLeft","mCSB_buttonRight"
405		],
406		
407	
408	
409	
410	
411	/* 
412	----------------------------------------
413	METHODS 
414	----------------------------------------
415	*/
416	
417		methods={
418			
419			/* 
420			plugin initialization method 
421			creates the scrollbar(s), plugin data object and options
422			----------------------------------------
423			*/
424			
425			init:function(options){
426				
427				var options=$.extend(true,{},defaults,options),
428					selector=_selector.call(this); /* validate selector */
429				
430				/* 
431				if live option is enabled, monitor for elements matching the current selector and 
432				apply scrollbar(s) when found (now and in the future) 
433				*/
434				if(options.live){
435					var liveSelector=options.liveSelector || this.selector || defaultSelector, /* live selector(s) */
436						$liveSelector=$(liveSelector); /* live selector(s) as jquery object */
437					if(options.live==="off"){
438						/* 
439						disable live if requested 
440						usage: $(selector).mCustomScrollbar({live:"off"}); 
441						*/
442						removeLiveTimers(liveSelector);
443						return;
444					}
445					liveTimers[liveSelector]=setTimeout(function(){
446						/* call mCustomScrollbar fn on live selector(s) every half-second */
447						$liveSelector.mCustomScrollbar(options);
448						if(options.live==="once" && $liveSelector.length){
449							/* disable live after first invocation */
450							removeLiveTimers(liveSelector);
451						}
452					},500);
453				}else{
454					removeLiveTimers(liveSelector);
455				}
456				
457				/* options backward compatibility (for versions < 3.0.0) and normalization */
458				options.setWidth=(options.set_width) ? options.set_width : options.setWidth;
459				options.setHeight=(options.set_height) ? options.set_height : options.setHeight;
460				options.axis=(options.horizontalScroll) ? "x" : _findAxis(options.axis);
461				options.scrollInertia=options.scrollInertia>0 && options.scrollInertia<17 ? 17 : options.scrollInertia;
462				if(typeof options.mouseWheel!=="object" &&  options.mouseWheel==true){ /* old school mouseWheel option (non-object) */
463					options.mouseWheel={enable:true,scrollAmount:"auto",axis:"y",preventDefault:false,deltaFactor:"auto",normalizeDelta:false,invert:false}
464				}
465				options.mouseWheel.scrollAmount=!options.mouseWheelPixels ? options.mouseWheel.scrollAmount : options.mouseWheelPixels;
466				options.mouseWheel.normalizeDelta=!options.advanced.normalizeMouseWheelDelta ? options.mouseWheel.normalizeDelta : options.advanced.normalizeMouseWheelDelta;
467				options.scrollButtons.scrollType=_findScrollButtonsType(options.scrollButtons.scrollType); 
468				
469				_theme(options); /* theme-specific options */
470				
471				/* plugin constructor */
472				return $(selector).each(function(){
473					
474					var $this=$(this);
475					
476					if(!$this.data(pluginPfx)){ /* prevent multiple instantiations */
477					
478						/* store options and create objects in jquery data */
479						$this.data(pluginPfx,{
480							idx:++totalInstances, /* instance index */
481							opt:options, /* options */
482							scrollRatio:{y:null,x:null}, /* scrollbar to content ratio */
483							overflowed:null, /* overflowed axis */
484							contentReset:{y:null,x:null}, /* object to check when content resets */
485							bindEvents:false, /* object to check if events are bound */
486							tweenRunning:false, /* object to check if tween is running */
487							sequential:{}, /* sequential scrolling object */
488							langDir:$this.css("direction"), /* detect/store direction (ltr or rtl) */
489							cbOffsets:null, /* object to check whether callback offsets always trigger */
490							/* 
491							object to check how scrolling events where last triggered 
492							"internal" (default - triggered by this script), "external" (triggered by other scripts, e.g. via scrollTo method) 
493							usage: object.data("mCS").trigger
494							*/
495							trigger:null,
496							/* 
497							object to check for changes in elements in order to call the update method automatically 
498							*/
499							poll:{size:{o:0,n:0},img:{o:0,n:0},change:{o:0,n:0}}
500						});
501						
502						var d=$this.data(pluginPfx),o=d.opt,
503							/* HTML data attributes */
504							htmlDataAxis=$this.data("mcs-axis"),htmlDataSbPos=$this.data("mcs-scrollbar-position"),htmlDataTheme=$this.data("mcs-theme");
505						 
506						if(htmlDataAxis){o.axis=htmlDataAxis;} /* usage example: data-mcs-axis="y" */
507						if(htmlDataSbPos){o.scrollbarPosition=htmlDataSbPos;} /* usage example: data-mcs-scrollbar-position="outside" */
508						if(htmlDataTheme){ /* usage example: data-mcs-theme="minimal" */
509							o.theme=htmlDataTheme;
510							_theme(o); /* theme-specific options */
511						}
512						
513						_pluginMarkup.call(this); /* add plugin markup */
514						
515						if(d && o.callbacks.onCreate && typeof o.callbacks.onCreate==="function"){o.callbacks.onCreate.call(this);} /* callbacks: onCreate */
516						
517						$("#mCSB_"+d.idx+"_container img:not(."+classes[2]+")").addClass(classes[2]); /* flag loaded images */
518						
519						methods.update.call(null,$this); /* call the update method */
520					
521					}
522					
523				});
524				
525			},
526			/* ---------------------------------------- */
527			
528			
529			
530			/* 
531			plugin update method 
532			updates content and scrollbar(s) values, events and status 
533			----------------------------------------
534			usage: $(selector).mCustomScrollbar("update");
535			*/
536			
537			update:function(el,cb){
538				
539				var selector=el || _selector.call(this); /* validate selector */
540				
541				return $(selector).each(function(){
542					
543					var $this=$(this);
544					
545					if($this.data(pluginPfx)){ /* check if plugin has initialized */
546						
547						var d=$this.data(pluginPfx),o=d.opt,
548							mCSB_container=$("#mCSB_"+d.idx+"_container"),
549							mCustomScrollBox=$("#mCSB_"+d.idx),
550							mCSB_dragger=[$("#mCSB_"+d.idx+"_dragger_vertical"),$("#mCSB_"+d.idx+"_dragger_horizontal")];
551						
552						if(!mCSB_container.length){return;}
553						
554						if(d.tweenRunning){_stop($this);} /* stop any running tweens while updating */
555						
556						if(cb && d && o.callbacks.onBeforeUpdate && typeof o.callbacks.onBeforeUpdate==="function"){o.callbacks.onBeforeUpdate.call(this);} /* callbacks: onBeforeUpdate */
557						
558						/* if element was disabled or destroyed, remove class(es) */
559						if($this.hasClass(classes[3])){$this.removeClass(classes[3]);}
560						if($this.hasClass(classes[4])){$this.removeClass(classes[4]);}
561						
562						/* css flexbox fix, detect/set max-height */
563						mCustomScrollBox.css("max-height","none");
564						if(mCustomScrollBox.height()!==$this.height()){mCustomScrollBox.css("max-height",$this.height());}
565						
566						_expandContentHorizontally.call(this); /* expand content horizontally */
567						
568						if(o.axis!=="y" && !o.advanced.autoExpandHorizontalScroll){
569							mCSB_container.css("width",_contentWidth(mCSB_container));
570						}
571						
572						d.overflowed=_overflowed.call(this); /* determine if scrolling is required */
573						
574						_scrollbarVisibility.call(this); /* show/hide scrollbar(s) */
575						
576						/* auto-adjust scrollbar dragger length analogous to content */
577						if(o.autoDraggerLength){_setDraggerLength.call(this);}
578						
579						_scrollRatio.call(this); /* calculate and store scrollbar to content ratio */
580						
581						_bindEvents.call(this); /* bind scrollbar events */
582						
583						/* reset scrolling position and/or events */
584						var to=[Math.abs(mCSB_container[0].offsetTop),Math.abs(mCSB_container[0].offsetLeft)];
585						if(o.axis!=="x"){ /* y/yx axis */
586							if(!d.overflowed[0]){ /* y scrolling is not required */
587								_resetContentPosition.call(this); /* reset content position */
588								if(o.axis==="y"){
589									_unbindEvents.call(this);
590								}else if(o.axis==="yx" && d.overflowed[1]){
591									_scrollTo($this,to[1].toString(),{dir:"x",dur:0,overwrite:"none"});
592								}
593							}else if(mCSB_dragger[0].height()>mCSB_dragger[0].parent().height()){
594								_resetContentPosition.call(this); /* reset content position */
595							}else{ /* y scrolling is required */
596								_scrollTo($this,to[0].toString(),{dir:"y",dur:0,overwrite:"none"});
597								d.contentReset.y=null;
598							}
599						}
600						if(o.axis!=="y"){ /* x/yx axis */
601							if(!d.overflowed[1]){ /* x scrolling is not required */
602								_resetContentPosition.call(this); /* reset content position */
603								if(o.axis==="x"){
604									_unbindEvents.call(this);
605								}else if(o.axis==="yx" && d.overflowed[0]){
606									_scrollTo($this,to[0].toString(),{dir:"y",dur:0,overwrite:"none"});
607								}
608							}else if(mCSB_dragger[1].width()>mCSB_dragger[1].parent().width()){
609								_resetContentPosition.call(this); /* reset content position */
610							}else{ /* x scrolling is required */
611								_scrollTo($this,to[1].toString(),{dir:"x",dur:0,overwrite:"none"});
612								d.contentReset.x=null;
613							}
614						}
615						
616						/* callbacks: onImageLoad, onSelectorChange, onUpdate */
617						if(cb && d){
618							if(cb===2 && o.callbacks.onImageLoad && typeof o.callbacks.onImageLoad==="function"){
619								o.callbacks.onImageLoad.call(this);
620							}else if(cb===3 && o.callbacks.onSelectorChange && typeof o.callbacks.onSelectorChange==="function"){
621								o.callbacks.onSelectorChange.call(this);
622							}else if(o.callbacks.onUpdate && typeof o.callbacks.onUpdate==="function"){
623								o.callbacks.onUpdate.call(this);
624							}
625						}
626						
627						_autoUpdate.call(this); /* initialize automatic updating (for dynamic content, fluid layouts etc.) */
628						
629					}
630					
631				});
632				
633			},
634			/* ---------------------------------------- */
635			
636			
637			
638			/* 
639			plugin scrollTo method 
640			triggers a scrolling event to a specific value
641			----------------------------------------
642			usage: $(selector).mCustomScrollbar("scrollTo",value,options);
643			*/
644		
645			scrollTo:function(val,options){
646				
647				/* prevent silly things like $(selector).mCustomScrollbar("scrollTo",undefined); */
648				if(typeof val=="undefined" || val==null){return;}
649				
650				var selector=_selector.call(this); /* validate selector */
651				
652				return $(selector).each(function(){
653					
654					var $this=$(this);
655					
656					if($this.data(pluginPfx)){ /* check if plugin has initialized */
657					
658						var d=$this.data(pluginPfx),o=d.opt,
659							/* method default options */
660							methodDefaults={
661								trigger:"external", /* method is by default triggered externally (e.g. from other scripts) */
662								scrollInertia:o.scrollInertia, /* scrolling inertia (animation duration) */
663								scrollEasing:"mcsEaseInOut", /* animation easing */
664								moveDragger:false, /* move dragger instead of content */
665								timeout:60, /* scroll-to delay */
666								callbacks:true, /* enable/disable callbacks */
667								onStart:true,
668								onUpdate:true,
669								onComplete:true
670							},
671							methodOptions=$.extend(true,{},methodDefaults,options),
672							to=_arr.call(this,val),dur=methodOptions.scrollInertia>0 && methodOptions.scrollInertia<17 ? 17 : methodOptions.scrollInertia;
673						
674						/* translate yx values to actual scroll-to positions */
675						to[0]=_to.call(this,to[0],"y");
676						to[1]=_to.call(this,to[1],"x");
677						
678						/* 
679						check if scroll-to value moves the dragger instead of content. 
680						Only pixel values apply on dragger (e.g. 100, "100px", "-=100" etc.) 
681						*/
682						if(methodOptions.moveDragger){
683							to[0]*=d.scrollRatio.y;
684							to[1]*=d.scrollRatio.x;
685						}
686						
687						methodOptions.dur=_isTabHidden() ? 0 : dur; //skip animations if browser tab is hidden
688						
689						setTimeout(function(){ 
690							/* do the scrolling */
691							if(to[0]!==null && typeof to[0]!=="undefined" && o.axis!=="x" && d.overflowed[0]){ /* scroll y */
692								methodOptions.dir="y";
693								methodOptions.overwrite="all";
694								_scrollTo($this,to[0].toString(),methodOptions);
695							}
696							if(to[1]!==null && typeof to[1]!=="undefined" && o.axis!=="y" && d.overflowed[1]){ /* scroll x */
697								methodOptions.dir="x";
698								methodOptions.overwrite="none";
699								_scrollTo($this,to[1].toString(),methodOptions);
700							}
701						},methodOptions.timeout);
702						
703					}
704					
705				});
706				
707			},
708			/* ---------------------------------------- */
709			
710			
711			
712			/*
713			plugin stop method 
714			stops scrolling animation
715			----------------------------------------
716			usage: $(selector).mCustomScrollbar("stop");
717			*/
718			stop:function(){
719				
720				var selector=_selector.call(this); /* validate selector */
721				
722				return $(selector).each(function(){
723					
724					var $this=$(this);
725					
726					if($this.data(pluginPfx)){ /* check if plugin has initialized */
727										
728						_stop($this);
729					
730					}
731					
732				});
733				
734			},
735			/* ---------------------------------------- */
736			
737			
738			
739			/*
740			plugin disable method 
741			temporarily disables the scrollbar(s) 
742			----------------------------------------
743			usage: $(selector).mCustomScrollbar("disable",reset); 
744			reset (boolean): resets content position to 0 
745			*/
746			disable:function(r){
747				
748				var selector=_selector.call(this); /* validate selector */
749				
750				return $(selector).each(function(){
751					
752					var $this=$(this);
753					
754					if($this.data(pluginPfx)){ /* check if plugin has initialized */
755						
756						var d=$this.data(pluginPfx);
757						
758						_autoUpdate.call(this,"remove"); /* remove automatic updating */
759						
760						_unbindEvents.call(this); /* unbind events */
761						
762						if(r){_resetContentPosition.call(this);} /* reset content position */
763						
764						_scrollbarVisibility.call(this,true); /* show/hide scrollbar(s) */
765						
766						$this.addClass(classes[3]); /* add disable class */
767					
768					}
769					
770				});
771				
772			},
773			/* ---------------------------------------- */
774			
775			
776			
777			/*
778			plugin destroy method 
779			completely removes the scrollbar(s) and returns the element to its original state
780			----------------------------------------
781			usage: $(selector).mCustomScrollbar("destroy"); 
782			*/
783			destroy:function(){
784				
785				var selector=_selector.call(this); /* validate selector */
786				
787				return $(selector).each(function(){
788					
789					var $this=$(this);
790					
791					if($this.data(pluginPfx)){ /* check if plugin has initialized */
792					
793						var d=$this.data(pluginPfx),o=d.opt,
794							mCustomScrollBox=$("#mCSB_"+d.idx),
795							mCSB_container=$("#mCSB_"+d.idx+"_container"),
796							scrollbar=$(".mCSB_"+d.idx+"_scrollbar");
797					
798						if(o.live){removeLiveTimers(o.liveSelector || $(selector).selector);} /* remove live timers */
799						
800						_autoUpdate.call(this,"remove"); /* remove automatic updating */
801						
802						_unbindEvents.call(this); /* unbind events */
803						
804						_resetContentPosition.call(this); /* reset content position */
805						
806						$this.removeData(pluginPfx); /* remove plugin data object */
807						
808						_delete(this,"mcs"); /* delete callbacks object */
809						
810						/* remove plugin markup */
811						scrollbar.remove(); /* remove scrollbar(s) first (those can be either inside or outside plugin's inner wrapper) */
812						mCSB_container.find("img."+classes[2]).removeClass(classes[2]); /* remove loaded images flag */
813						mCustomScrollBox.replaceWith(mCSB_container.contents()); /* replace plugin's inner wrapper with the original content */
814						/* remove plugin classes from the element and add destroy class */
815						$this.removeClass(pluginNS+" _"+pluginPfx+"_"+d.idx+" "+classes[6]+" "+classes[7]+" "+classes[5]+" "+classes[3]).addClass(classes[4]);
816					
817					}
818					
819				});
820				
821			}
822			/* ---------------------------------------- */
823			
824		},
825	
826	
827	
828	
829		
830	/* 
831	----------------------------------------
832	FUNCTIONS
833	----------------------------------------
834	*/
835	
836		/* validates selector (if selector is invalid or undefined uses the default one) */
837		_selector=function(){
838			return (typeof $(this)!=="object" || $(this).length<1) ? defaultSelector : this;
839		},
840		/* -------------------- */
841		
842		
843		/* changes options according to theme */
844		_theme=function(obj){
845			var fixedSizeScrollbarThemes=["rounded","rounded-dark","rounded-dots","rounded-dots-dark"],
846				nonExpandedScrollbarThemes=["rounded-dots","rounded-dots-dark","3d","3d-dark","3d-thick","3d-thick-dark","inset","inset-dark","inset-2","inset-2-dark","inset-3","inset-3-dark"],
847				disabledScrollButtonsThemes=["minimal","minimal-dark"],
848				enabledAutoHideScrollbarThemes=["minimal","minimal-dark"],
849				scrollbarPositionOutsideThemes=["minimal","minimal-dark"];
850			obj.autoDraggerLength=$.inArray(obj.theme,fixedSizeScrollbarThemes) > -1 ? false : obj.autoDraggerLength;
851			obj.autoExpandScrollbar=$.inArray(obj.theme,nonExpandedScrollbarThemes) > -1 ? false : obj.autoExpandScrollbar;
852			obj.scrollButtons.enable=$.inArray(obj.theme,disabledScrollButtonsThemes) > -1 ? false : obj.scrollButtons.enable;
853			obj.autoHideScrollbar=$.inArray(obj.theme,enabledAutoHideScrollbarThemes) > -1 ? true : obj.autoHideScrollbar;
854			obj.scrollbarPosition=$.inArray(obj.theme,scrollbarPositionOutsideThemes) > -1 ? "outside" : obj.scrollbarPosition;
855		},
856		/* -------------------- */
857		
858		
859		/* live option timers removal */
860		removeLiveTimers=function(selector){
861			if(liveTimers[selector]){
862				clearTimeout(liveTimers[selector]);
863				_delete(liveTimers,selector);
864			}
865		},
866		/* -------------------- */
867		
868		
869		/* normalizes axis option to valid values: "y", "x", "yx" */
870		_findAxis=function(val){
871			return (val==="yx" || val==="xy" || val==="auto") ? "yx" : (val==="x" || val==="horizontal") ? "x" : "y";
872		},
873		/* -------------------- */
874		
875		
876		/* normalizes scrollButtons.scrollType option to valid values: "stepless", "stepped" */
877		_findScrollButtonsType=function(val){
878			return (val==="stepped" || val==="pixels" || val==="step" || val==="click") ? "stepped" : "stepless";
879		},
880		/* -------------------- */
881		
882		
883		/* generates plugin markup */
884		_pluginMarkup=function(){
885			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
886				expandClass=o.autoExpandScrollbar ? " "+classes[1]+"_expand" : "",
887				scrollbar=["<div id='mCSB_"+d.idx+"_scrollbar_vertical' class='mCSB_scrollTools mCSB_"+d.idx+"_scrollbar mCS-"+o.theme+" mCSB_scrollTools_vertical"+expandClass+"'><div class='"+classes[12]+"'><div id='mCSB_"+d.idx+"_dragger_vertical' class='mCSB_dragger' style='position:absolute;'><div class='mCSB_dragger_bar' /></div><div class='mCSB_draggerRail' /></div></div>","<div id='mCSB_"+d.idx+"_scrollbar_horizontal' class='mCSB_scrollTools mCSB_"+d.idx+"_scrollbar mCS-"+o.theme+" mCSB_scrollTools_horizontal"+expandClass+"'><div class='"+classes[12]+"'><div id='mCSB_"+d.idx+"_dragger_horizontal' class='mCSB_dragger' style='position:absolute;'><div class='mCSB_dragger_bar' /></div><div class='mCSB_draggerRail' /></div></div>"],
888				wrapperClass=o.axis==="yx" ? "mCSB_vertical_horizontal" : o.axis==="x" ? "mCSB_horizontal" : "mCSB_vertical",
889				scrollbars=o.axis==="yx" ? scrollbar[0]+scrollbar[1] : o.axis==="x" ? scrollbar[1] : scrollbar[0],
890				contentWrapper=o.axis==="yx" ? "<div id='mCSB_"+d.idx+"_container_wrapper' class='mCSB_container_wrapper' />" : "",
891				autoHideClass=o.autoHideScrollbar ? " "+classes[6] : "",
892				scrollbarDirClass=(o.axis!=="x" && d.langDir==="rtl") ? " "+classes[7] : "";
893			if(o.setWidth){$this.css("width",o.setWidth);} /* set element width */
894			if(o.setHeight){$this.css("height",o.setHeight);} /* set element height */
895			o.setLeft=(o.axis!=="y" && d.langDir==="rtl") ? "989999px" : o.setLeft; /* adjust left position for rtl direction */
896			$this.addClass(pluginNS+" _"+pluginPfx+"_"+d.idx+autoHideClass+scrollbarDirClass).wrapInner("<div id='mCSB_"+d.idx+"' class='mCustomScrollBox mCS-"+o.theme+" "+wrapperClass+"'><div id='mCSB_"+d.idx+"_container' class='mCSB_container' style='position:relative; top:"+o.setTop+"; left:"+o.setLeft+";' dir='"+d.langDir+"' /></div>");
897			var mCustomScrollBox=$("#mCSB_"+d.idx),
898				mCSB_container=$("#mCSB_"+d.idx+"_container");
899			if(o.axis!=="y" && !o.advanced.autoExpandHorizontalScroll){
900				mCSB_container.css("width",_contentWidth(mCSB_container));
901			}
902			if(o.scrollbarPosition==="outside"){
903				if($this.css("position")==="static"){ /* requires elements with non-static position */
904					$this.css("position","relative");
905				}
906				$this.css("overflow","visible");
907				mCustomScrollBox.addClass("mCSB_outside").after(scrollbars);
908			}else{
909				mCustomScrollBox.addClass("mCSB_inside").append(scrollbars);
910				mCSB_container.wrap(contentWrapper);
911			}
912			_scrollButtons.call(this); /* add scrollbar buttons */
913			/* minimum dragger length */
914			var mCSB_dragger=[$("#mCSB_"+d.idx+"_dragger_vertical"),$("#mCSB_"+d.idx+"_dragger_horizontal")];
915			mCSB_dragger[0].css("min-height",mCSB_dragger[0].height());
916			mCSB_dragger[1].css("min-width",mCSB_dragger[1].width());
917		},
918		/* -------------------- */
919		
920		
921		/* calculates content width */
922		_contentWidth=function(el){
923			var val=[el[0].scrollWidth,Math.max.apply(Math,el.children().map(function(){return $(this).outerWidth(true);}).get())],w=el.parent().width();
924			return val[0]>w ? val[0] : val[1]>w ? val[1] : "100%";
925		},
926		/* -------------------- */
927		
928		
929		/* expands content horizontally */
930		_expandContentHorizontally=function(){
931			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
932				mCSB_container=$("#mCSB_"+d.idx+"_container");
933			if(o.advanced.autoExpandHorizontalScroll && o.axis!=="y"){
934				/* calculate scrollWidth */
935				mCSB_container.css({"width":"auto","min-width":0,"overflow-x":"scroll"});
936				var w=Math.ceil(mCSB_container[0].scrollWidth);
937				if(o.advanced.autoExpandHorizontalScroll===3 || (o.advanced.autoExpandHorizontalScroll!==2 && w>mCSB_container.parent().width())){
938					mCSB_container.css({"width":w,"min-width":"100%","overflow-x":"inherit"});
939				}else{
940					/* 
941					wrap content with an infinite width div and set its position to absolute and width to auto. 
942					Setting width to auto before calculating the actual width is important! 
943					We must let the browser set the width as browser zoom values are impossible to calculate.
944					*/
945					mCSB_container.css({"overflow-x":"inherit","position":"absolute"})
946						.wrap("<div class='mCSB_h_wrapper' style='position:relative; left:0; width:999999px;' />")
947						.css({ /* set actual width, original position and un-wrap */
948							/* 
949							get the exact width (with decimals) and then round-up. 
950							Using jquery outerWidth() will round the width value which will mess up with inner elements that have non-integer width
951							*/
952							"width":(Math.ceil(mCSB_container[0].getBoundingClientRect().right+0.4)-Math.floor(mCSB_container[0].getBoundingClientRect().left)),
953							"min-width":"100%",
954							"position":"relative"
955						}).unwrap();
956				}
957			}
958		},
959		/* -------------------- */
960		
961		
962		/* adds scrollbar buttons */
963		_scrollButtons=function(){
964			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
965				mCSB_scrollTools=$(".mCSB_"+d.idx+"_scrollbar:first"),
966				tabindex=!_isNumeric(o.scrollButtons.tabindex) ? "" : "tabindex='"+o.scrollButtons.tabindex+"'",
967				btnHTML=[
968					"<a href='#' class='"+classes[13]+"' "+tabindex+" />",
969					"<a href='#' class='"+classes[14]+"' "+tabindex+" />",
970					"<a href='#' class='"+classes[15]+"' "+tabindex+" />",
971					"<a href='#' class='"+classes[16]+"' "+tabindex+" />"
972				],
973				btn=[(o.axis==="x" ? btnHTML[2] : btnHTML[0]),(o.axis==="x" ? btnHTML[3] : btnHTML[1]),btnHTML[2],btnHTML[3]];
974			if(o.scrollButtons.enable){
975				mCSB_scrollTools.prepend(btn[0]).append(btn[1]).next(".mCSB_scrollTools").prepend(btn[2]).append(btn[3]);
976			}
977		},
978		/* -------------------- */
979		
980		
981		/* auto-adjusts scrollbar dragger length */
982		_setDraggerLength=function(){
983			var $this=$(this),d=$this.data(pluginPfx),
984				mCustomScrollBox=$("#mCSB_"+d.idx),
985				mCSB_container=$("#mCSB_"+d.idx+"_container"),
986				mCSB_dragger=[$("#mCSB_"+d.idx+"_dragger_vertical"),$("#mCSB_"+d.idx+"_dragger_horizontal")],
987				ratio=[mCustomScrollBox.height()/mCSB_container.outerHeight(false),mCustomScrollBox.width()/mCSB_container.outerWidth(false)],
988				l=[
989					parseInt(mCSB_dragger[0].css("min-height")),Math.round(ratio[0]*mCSB_dragger[0].parent().height()),
990					parseInt(mCSB_dragger[1].css("min-width")),Math.round(ratio[1]*mCSB_dragger[1].parent().width())
991				],
992				h=oldIE && (l[1]<l[0]) ? l[0] : l[1],w=oldIE && (l[3]<l[2]) ? l[2] : l[3];
993			mCSB_dragger[0].css({
994				"height":h,"max-height":(mCSB_dragger[0].parent().height()-10)
995			}).find(".mCSB_dragger_bar").css({"line-height":l[0]+"px"});
996			mCSB_dragger[1].css({
997				"width":w,"max-width":(mCSB_dragger[1].parent().width()-10)
998			});
999		},
1000		/* -------------------- */
1001		
1002		
1003		/* calculates scrollbar to content ratio */
1004		_scrollRatio=function(){
1005			var $this=$(this),d=$this.data(pluginPfx),
1006				mCustomScrollBox=$("#mCSB_"+d.idx),
1007				mCSB_container=$("#mCSB_"+d.idx+"_container"),
1008				mCSB_dragger=[$("#mCSB_"+d.idx+"_dragger_vertical"),$("#mCSB_"+d.idx+"_dragger_horizontal")],
1009				scrollAmount=[mCSB_container.outerHeight(false)-mCustomScrollBox.height(),mCSB_container.outerWidth(false)-mCustomScrollBox.width()],
1010				ratio=[
1011					scrollAmount[0]/(mCSB_dragger[0].parent().height()-mCSB_dragger[0].height()),
1012					scrollAmount[1]/(mCSB_dragger[1].parent().width()-mCSB_dragger[1].width())
1013				];
1014			d.scrollRatio={y:ratio[0],x:ratio[1]};
1015		},
1016		/* -------------------- */
1017		
1018		
1019		/* toggles scrolling classes */
1020		_onDragClasses=function(el,action,xpnd){
1021			var expandClass=xpnd ? classes[0]+"_expanded" : "",
1022				scrollbar=el.closest(".mCSB_scrollTools");
1023			if(action==="active"){
1024				el.toggleClass(classes[0]+" "+expandClass); scrollbar.toggleClass(classes[1]); 
1025				el[0]._draggable=el[0]._draggable ? 0 : 1;
1026			}else{
1027				if(!el[0]._draggable){
1028					if(action==="hide"){
1029						el.removeClass(classes[0]); scrollbar.removeClass(classes[1]);
1030					}else{
1031						el.addClass(classes[0]); scrollbar.addClass(classes[1]);
1032					}
1033				}
1034			}
1035		},
1036		/* -------------------- */
1037		
1038		
1039		/* checks if content overflows its container to determine if scrolling is required */
1040		_overflowed=function(){
1041			var $this=$(this),d=$this.data(pluginPfx),
1042				mCustomScrollBox=$("#mCSB_"+d.idx),
1043				mCSB_container=$("#mCSB_"+d.idx+"_container"),
1044				contentHeight=d.overflowed==null ? mCSB_container.height() : mCSB_container.outerHeight(false),
1045				contentWidth=d.overflowed==null ? mCSB_container.width() : mCSB_container.outerWidth(false),
1046				h=mCSB_container[0].scrollHeight,w=mCSB_container[0].scrollWidth;
1047			if(h>contentHeight){contentHeight=h;}
1048			if(w>contentWidth){contentWidth=w;}
1049			return [contentHeight>mCustomScrollBox.height(),contentWidth>mCustomScrollBox.width()];
1050		},
1051		/* -------------------- */
1052		
1053		
1054		/* resets content position to 0 */
1055		_resetContentPosition=function(){
1056			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
1057				mCustomScrollBox=$("#mCSB_"+d.idx),
1058				mCSB_container=$("#mCSB_"+d.idx+"_container"),
1059				mCSB_dragger=[$("#mCSB_"+d.idx+"_dragger_vertical"),$("#mCSB_"+d.idx+"_dragger_horizontal")];
1060			_stop($this); /* stop any current scrolling before resetting */
1061			if((o.axis!=="x" && !d.overflowed[0]) || (o.axis==="y" && d.overflowed[0])){ /* reset y */
1062				mCSB_dragger[0].add(mCSB_container).css("top",0);
1063				_scrollTo($this,"_resetY");
1064			}
1065			if((o.axis!=="y" && !d.overflowed[1]) || (o.axis==="x" && d.overflowed[1])){ /* reset x */
1066				var cx=dx=0;
1067				if(d.langDir==="rtl"){ /* adjust left position for rtl direction */
1068					cx=mCustomScrollBox.width()-mCSB_container.outerWidth(false);
1069					dx=Math.abs(cx/d.scrollRatio.x);
1070				}
1071				mCSB_container.css("left",cx);
1072				mCSB_dragger[1].css("left",dx);
1073				_scrollTo($this,"_resetX");
1074			}
1075		},
1076		/* -------------------- */
1077		
1078		
1079		/* binds scrollbar events */
1080		_bindEvents=function(){
1081			var $this=$(this),d=$this.data(pluginPfx),o=d.opt;
1082			if(!d.bindEvents){ /* check if events are already bound */
1083				_draggable.call(this);
1084				if(o.contentTouchScroll){_contentDraggable.call(this);}
1085				_selectable.call(this);
1086				if(o.mouseWheel.enable){ /* bind mousewheel fn when plugin is available */
1087					function _mwt(){
1088						mousewheelTimeout=setTimeout(function(){
1089							if(!$.event.special.mousewheel){
1090								_mwt();
1091							}else{
1092								clearTimeout(mousewheelTimeout);
1093								_mousewheel.call($this[0]);
1094							}
1095						},100);
1096					}
1097					var mousewheelTimeout;
1098					_mwt();
1099				}
1100				_draggerRail.call(this);
1101				_wrapperScroll.call(this);
1102				if(o.advanced.autoScrollOnFocus){_focus.call(this);}
1103				if(o.scrollButtons.enable){_buttons.call(this);}
1104				if(o.keyboard.enable){_keyboard.call(this);}
1105				d.bindEvents=true;
1106			}
1107		},
1108		/* -------------------- */
1109		
1110		
1111		/* unbinds scrollbar events */
1112		_unbindEvents=function(){
1113			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
1114				namespace=pluginPfx+"_"+d.idx,
1115				sb=".mCSB_"+d.idx+"_scrollbar",
1116				sel=$("#mCSB_"+d.idx+",#mCSB_"+d.idx+"_container,#mCSB_"+d.idx+"_container_wrapper,"+sb+" ."+classes[12]+",#mCSB_"+d.idx+"_dragger_vertical,#mCSB_"+d.idx+"_dragger_horizontal,"+sb+">a"),
1117				mCSB_container=$("#mCSB_"+d.idx+"_container");
1118			if(o.advanced.releaseDraggableSelectors){sel.add($(o.advanced.releaseDraggableSelectors));}
1119			if(o.advanced.extraDraggableSelectors){sel.add($(o.advanced.extraDraggableSelectors));}
1120			if(d.bindEvents){ /* check if events are bound */
1121				/* unbind namespaced events from document/selectors */
1122				
1122$(document).add($(!_canAccessIFrame() || top.document
vendor: 3,898 bytes, lines 1122-1214
1122)).unbind("."+namespace);
1123				sel.each(function(){
1124					$(this).unbind("."+namespace);
1125				});
1126				/* clear and delete timeouts/objects */
1127				clearTimeout($this[0]._focusTimeout); _delete($this[0],"_focusTimeout");
1128				clearTimeout(d.sequential.step); _delete(d.sequential,"step");
1129				clearTimeout(mCSB_container[0].onCompleteTimeout); _delete(mCSB_container[0],"onCompleteTimeout");
1130				d.bindEvents=false;
1131			}
1132		},
1133		/* -------------------- */
1134		
1135		
1136		/* toggles scrollbar visibility */
1137		_scrollbarVisibility=function(disabled){
1138			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
1139				contentWrapper=$("#mCSB_"+d.idx+"_container_wrapper"),
1140				content=contentWrapper.length ? contentWrapper : $("#mCSB_"+d.idx+"_container"),
1141				scrollbar=[$("#mCSB_"+d.idx+"_scrollbar_vertical"),$("#mCSB_"+d.idx+"_scrollbar_horizontal")],
1142				mCSB_dragger=[scrollbar[0].find(".mCSB_dragger"),scrollbar[1].find(".mCSB_dragger")];
1143			if(o.axis!=="x"){
1144				if(d.overflowed[0] && !disabled){
1145					scrollbar[0].add(mCSB_dragger[0]).add(scrollbar[0].children("a")).css("display","block");
1146					content.removeClass(classes[8]+" "+classes[10]);
1147				}else{
1148					if(o.alwaysShowScrollbar){
1149						if(o.alwaysShowScrollbar!==2){mCSB_dragger[0].css("display","none");}
1150						content.removeClass(classes[10]);
1151					}else{
1152						scrollbar[0].css("display","none");
1153						content.addClass(classes[10]);
1154					}
1155					content.addClass(classes[8]);
1156				}
1157			}
1158			if(o.axis!=="y"){
1159				if(d.overflowed[1] && !disabled){
1160					scrollbar[1].add(mCSB_dragger[1]).add(scrollbar[1].children("a")).css("display","block");
1161					content.removeClass(classes[9]+" "+classes[11]);
1162				}else{
1163					if(o.alwaysShowScrollbar){
1164						if(o.alwaysShowScrollbar!==2){mCSB_dragger[1].css("display","none");}
1165						content.removeClass(classes[11]);
1166					}else{
1167						scrollbar[1].css("display","none");
1168						content.addClass(classes[11]);
1169					}
1170					content.addClass(classes[9]);
1171				}
1172			}
1173			if(!d.overflowed[0] && !d.overflowed[1]){
1174				$this.addClass(classes[5]);
1175			}else{
1176				$this.removeClass(classes[5]);
1177			}
1178		},
1179		/* -------------------- */
1180		
1181		
1182		/* returns input coordinates of pointer, touch and mouse events (relative to document) */
1183		_coordinates=function(e){
1184			var t=e.type,o=e.target.ownerDocument!==document && frameElement!==null ? [$(frameElement).offset().top,$(frameElement).offset().left] : null,
1185				io=_canAccessIFrame() && e.target.ownerDocument!==top.document && frameElement!==null ? [$(e.view.frameElement).offset().top,$(e.view.frameElement).offset().left] : [0,0];
1186			switch(t){
1187				case "pointerdown": case "MSPointerDown": case "pointermove": case "MSPointerMove": case "pointerup": case "MSPointerUp":
1188					return o ? [e.originalEvent.pageY-o[0]+io[0],e.originalEvent.pageX-o[1]+io[1],false] : [e.originalEvent.pageY,e.originalEvent.pageX,false];
1189					break;
1190				case "touchstart": case "touchmove": case "touchend":
1191					var touch=e.originalEvent.touches[0] || e.originalEvent.changedTouches[0],
1192						touches=e.originalEvent.touches.length || e.originalEvent.changedTouches.length;
1193					return e.target.ownerDocument!==document ? [touch.screenY,touch.screenX,touches>1] : [touch.pageY,touch.pageX,touches>1];
1194					break;
1195				default:
1196					return o ? [e.pageY-o[0]+io[0],e.pageX-o[1]+io[1],false] : [e.pageY,e.pageX,false];
1197			}
1198		},
1199		/* -------------------- */
1200		
1201		
1202		/* 
1203		SCROLLBAR DRAG EVENTS
1204		scrolls content via scrollbar dragging 
1205		*/
1206		_draggable=function(){
1207			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
1208				namespace=pluginPfx+"_"+d.idx,
1209				draggerId=["mCSB_"+d.idx+"_dragger_vertical","mCSB_"+d.idx+"_dragger_horizontal"],
1210				mCSB_container=$("#mCSB_"+d.idx+"_container"),
1211				mCSB_dragger=$("#"+draggerId[0]+",#"+draggerId[1]),
1212				draggable,dragY,dragX,
1213				rds=o.advanced.releaseDraggableSelectors ? mCSB_dragger.add($(o.advanced.releaseDraggableSelectors)) : mCSB_dragger,
1214				
1214eds=o.advanced.extraDraggableSelectors ? $(!_canAccessIFrame() || top.document).add($(o.advanced.extraDraggableSelectors)) : $(!_canAccessIFrame() || top.document
vendor: 48,256 bytes, lines 1214-2480
1214);
1215			mCSB_dragger.bind("contextmenu."+namespace,function(e){
1216				e.preventDefault(); //prevent right click
1217			}).bind("mousedown."+namespace+" touchstart."+namespace+" pointerdown."+namespace+" MSPointerDown."+namespace,function(e){
1218				e.stopImmediatePropagation();
1219				e.preventDefault();
1220				if(!_mouseBtnLeft(e)){return;} /* left mouse button only */
1221				touchActive=true;
1222				if(oldIE){document.onselectstart=function(){return false;}} /* disable text selection for IE < 9 */
1223				_iframe.call(mCSB_container,false); /* enable scrollbar dragging over iframes by disabling their events */
1224				_stop($this);
1225				draggable=$(this);
1226				var offset=draggable.offset(),y=_coordinates(e)[0]-offset.top,x=_coordinates(e)[1]-offset.left,
1227					h=draggable.height()+offset.top,w=draggable.width()+offset.left;
1228				if(y<h && y>0 && x<w && x>0){
1229					dragY=y; 
1230					dragX=x;
1231				}
1232				_onDragClasses(draggable,"active",o.autoExpandScrollbar); 
1233			}).bind("touchmove."+namespace,function(e){
1234				e.stopImmediatePropagation();
1235				e.preventDefault();
1236				var offset=draggable.offset(),y=_coordinates(e)[0]-offset.top,x=_coordinates(e)[1]-offset.left;
1237				_drag(dragY,dragX,y,x);
1238			});
1239			$(document).add(eds).bind("mousemove."+namespace+" pointermove."+namespace+" MSPointerMove."+namespace,function(e){
1240				if(draggable){
1241					var offset=draggable.offset(),y=_coordinates(e)[0]-offset.top,x=_coordinates(e)[1]-offset.left;
1242					if(dragY===y && dragX===x){return;} /* has it really moved? */
1243					_drag(dragY,dragX,y,x);
1244				}
1245			}).add(rds).bind("mouseup."+namespace+" touchend."+namespace+" pointerup."+namespace+" MSPointerUp."+namespace,function(e){
1246				if(draggable){
1247					_onDragClasses(draggable,"active",o.autoExpandScrollbar); 
1248					draggable=null;
1249				}
1250				touchActive=false;
1251				if(oldIE){document.onselectstart=null;} /* enable text selection for IE < 9 */
1252				_iframe.call(mCSB_container,true); /* enable iframes events */
1253			});
1254			function _drag(dragY,dragX,y,x){
1255				mCSB_container[0].idleTimer=o.scrollInertia<233 ? 250 : 0;
1256				if(draggable.attr("id")===draggerId[1]){
1257					var dir="x",to=((draggable[0].offsetLeft-dragX)+x)*d.scrollRatio.x;
1258				}else{
1259					var dir="y",to=((draggable[0].offsetTop-dragY)+y)*d.scrollRatio.y;
1260				}
1261				_scrollTo($this,to.toString(),{dir:dir,drag:true});
1262			}
1263		},
1264		/* -------------------- */
1265		
1266		
1267		/* 
1268		TOUCH SWIPE EVENTS
1269		scrolls content via touch swipe 
1270		Emulates the native touch-swipe scrolling with momentum found in iOS, Android and WP devices 
1271		*/
1272		_contentDraggable=function(){
1273			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
1274				namespace=pluginPfx+"_"+d.idx,
1275				mCustomScrollBox=$("#mCSB_"+d.idx),
1276				mCSB_container=$("#mCSB_"+d.idx+"_container"),
1277				mCSB_dragger=[$("#mCSB_"+d.idx+"_dragger_vertical"),$("#mCSB_"+d.idx+"_dragger_horizontal")],
1278				draggable,dragY,dragX,touchStartY,touchStartX,touchMoveY=[],touchMoveX=[],startTime,runningTime,endTime,distance,speed,amount,
1279				durA=0,durB,overwrite=o.axis==="yx" ? "none" : "all",touchIntent=[],touchDrag,docDrag,
1280				iframe=mCSB_container.find("iframe"),
1281				events=[
1282					"touchstart."+namespace+" pointerdown."+namespace+" MSPointerDown."+namespace, //start
1283					"touchmove."+namespace+" pointermove."+namespace+" MSPointerMove."+namespace, //move
1284					"touchend."+namespace+" pointerup."+namespace+" MSPointerUp."+namespace //end
1285				],
1286				touchAction=document.body.style.touchAction!==undefined && document.body.style.touchAction!=="";
1287			mCSB_container.bind(events[0],function(e){
1288				_onTouchstart(e);
1289			}).bind(events[1],function(e){
1290				_onTouchmove(e);
1291			});
1292			mCustomScrollBox.bind(events[0],function(e){
1293				_onTouchstart2(e);
1294			}).bind(events[2],function(e){
1295				_onTouchend(e);
1296			});
1297			if(iframe.length){
1298				iframe.each(function(){
1299					$(this).bind("load",function(){
1300						/* bind events on accessible iframes */
1301						if(_canAccessIFrame(this)){
1302							$(this.contentDocument || this.contentWindow.document).bind(events[0],function(e){
1303								_onTouchstart(e);
1304								_onTouchstart2(e);
1305							}).bind(events[1],function(e){
1306								_onTouchmove(e);
1307							}).bind(events[2],function(e){
1308								_onTouchend(e);
1309							});
1310						}
1311					});
1312				});
1313			}
1314			function _onTouchstart(e){
1315				if(!_pointerTouch(e) || touchActive || _coordinates(e)[2]){touchable=0; return;}
1316				touchable=1; touchDrag=0; docDrag=0; draggable=1;
1317				$this.removeClass("mCS_touch_action");
1318				var offset=mCSB_container.offset();
1319				dragY=_coordinates(e)[0]-offset.top;
1320				dragX=_coordinates(e)[1]-offset.left;
1321				touchIntent=[_coordinates(e)[0],_coordinates(e)[1]];
1322			}
1323			function _onTouchmove(e){
1324				if(!_pointerTouch(e) || touchActive || _coordinates(e)[2]){return;}
1325				if(!o.documentTouchScroll){e.preventDefault();} 
1326				e.stopImmediatePropagation();
1327				if(docDrag && !touchDrag){return;}
1328				if(draggable){
1329					runningTime=_getTime();
1330					var offset=mCustomScrollBox.offset(),y=_coordinates(e)[0]-offset.top,x=_coordinates(e)[1]-offset.left,
1331						easing="mcsLinearOut";
1332					touchMoveY.push(y);
1333					touchMoveX.push(x);
1334					touchIntent[2]=Math.abs(_coordinates(e)[0]-touchIntent[0]); touchIntent[3]=Math.abs(_coordinates(e)[1]-touchIntent[1]);
1335					if(d.overflowed[0]){
1336						var limit=mCSB_dragger[0].parent().height()-mCSB_dragger[0].height(),
1337							prevent=((dragY-y)>0 && (y-dragY)>-(limit*d.scrollRatio.y) && (touchIntent[3]*2<touchIntent[2] || o.axis==="yx"));
1338					}
1339					if(d.overflowed[1]){
1340						var limitX=mCSB_dragger[1].parent().width()-mCSB_dragger[1].width(),
1341							preventX=((dragX-x)>0 && (x-dragX)>-(limitX*d.scrollRatio.x) && (touchIntent[2]*2<touchIntent[3] || o.axis==="yx"));
1342					}
1343					if(prevent || preventX){ /* prevent native document scrolling */
1344						if(!touchAction){e.preventDefault();} 
1345						touchDrag=1;
1346					}else{
1347						docDrag=1;
1348						$this.addClass("mCS_touch_action");
1349					}
1350					if(touchAction){e.preventDefault();} 
1351					amount=o.axis==="yx" ? [(dragY-y),(dragX-x)] : o.axis==="x" ? [null,(dragX-x)] : [(dragY-y),null];
1352					mCSB_container[0].idleTimer=250;
1353					if(d.overflowed[0]){_drag(amount[0],durA,easing,"y","all",true);}
1354					if(d.overflowed[1]){_drag(amount[1],durA,easing,"x",overwrite,true);}
1355				}
1356			}
1357			function _onTouchstart2(e){
1358				if(!_pointerTouch(e) || touchActive || _coordinates(e)[2]){touchable=0; return;}
1359				touchable=1;
1360				e.stopImmediatePropagation();
1361				_stop($this);
1362				startTime=_getTime();
1363				var offset=mCustomScrollBox.offset();
1364				touchStartY=_coordinates(e)[0]-offset.top;
1365				touchStartX=_coordinates(e)[1]-offset.left;
1366				touchMoveY=[]; touchMoveX=[];
1367			}
1368			function _onTouchend(e){
1369				if(!_pointerTouch(e) || touchActive || _coordinates(e)[2]){return;}
1370				draggable=0;
1371				e.stopImmediatePropagation();
1372				touchDrag=0; docDrag=0;
1373				endTime=_getTime();
1374				var offset=mCustomScrollBox.offset(),y=_coordinates(e)[0]-offset.top,x=_coordinates(e)[1]-offset.left;
1375				if((endTime-runningTime)>30){return;}
1376				speed=1000/(endTime-startTime);
1377				var easing="mcsEaseOut",slow=speed<2.5,
1378					diff=slow ? [touchMoveY[touchMoveY.length-2],touchMoveX[touchMoveX.length-2]] : [0,0];
1379				distance=slow ? [(y-diff[0]),(x-diff[1])] : [y-touchStartY,x-touchStartX];
1380				var absDistance=[Math.abs(distance[0]),Math.abs(distance[1])];
1381				speed=slow ? [Math.abs(distance[0]/4),Math.abs(distance[1]/4)] : [speed,speed];
1382				var a=[
1383					Math.abs(mCSB_container[0].offsetTop)-(distance[0]*_m((absDistance[0]/speed[0]),speed[0])),
1384					Math.abs(mCSB_container[0].offsetLeft)-(distance[1]*_m((absDistance[1]/speed[1]),speed[1]))
1385				];
1386				amount=o.axis==="yx" ? [a[0],a[1]] : o.axis==="x" ? [null,a[1]] : [a[0],null];
1387				durB=[(absDistance[0]*4)+o.scrollInertia,(absDistance[1]*4)+o.scrollInertia];
1388				var md=parseInt(o.contentTouchScroll) || 0; /* absolute minimum distance required */
1389				amount[0]=absDistance[0]>md ? amount[0] : 0;
1390				amount[1]=absDistance[1]>md ? amount[1] : 0;
1391				if(d.overflowed[0]){_drag(amount[0],durB[0],easing,"y",overwrite,false);}
1392				if(d.overflowed[1]){_drag(amount[1],durB[1],easing,"x",overwrite,false);}
1393			}
1394			function _m(ds,s){
1395				var r=[s*1.5,s*2,s/1.5,s/2];
1396				if(ds>90){
1397					return s>4 ? r[0] : r[3];
1398				}else if(ds>60){
1399					return s>3 ? r[3] : r[2];
1400				}else if(ds>30){
1401					return s>8 ? r[1] : s>6 ? r[0] : s>4 ? s : r[2];
1402				}else{
1403					return s>8 ? s : r[3];
1404				}
1405			}
1406			function _drag(amount,dur,easing,dir,overwrite,drag){
1407				if(!amount){return;}
1408				_scrollTo($this,amount.toString(),{dur:dur,scrollEasing:easing,dir:dir,overwrite:overwrite,drag:drag});
1409			}
1410		},
1411		/* -------------------- */
1412		
1413		
1414		/* 
1415		SELECT TEXT EVENTS 
1416		scrolls content when text is selected 
1417		*/
1418		_selectable=function(){
1419			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,seq=d.sequential,
1420				namespace=pluginPfx+"_"+d.idx,
1421				mCSB_container=$("#mCSB_"+d.idx+"_container"),
1422				wrapper=mCSB_container.parent(),
1423				action;
1424			mCSB_container.bind("mousedown."+namespace,function(e){
1425				if(touchable){return;}
1426				if(!action){action=1; touchActive=true;}
1427			}).add(document).bind("mousemove."+namespace,function(e){
1428				if(!touchable && action && _sel()){
1429					var offset=mCSB_container.offset(),
1430						y=_coordinates(e)[0]-offset.top+mCSB_container[0].offsetTop,x=_coordinates(e)[1]-offset.left+mCSB_container[0].offsetLeft;
1431					if(y>0 && y<wrapper.height() && x>0 && x<wrapper.width()){
1432						if(seq.step){_seq("off",null,"stepped");}
1433					}else{
1434						if(o.axis!=="x" && d.overflowed[0]){
1435							if(y<0){
1436								_seq("on",38);
1437							}else if(y>wrapper.height()){
1438								_seq("on",40);
1439							}
1440						}
1441						if(o.axis!=="y" && d.overflowed[1]){
1442							if(x<0){
1443								_seq("on",37);
1444							}else if(x>wrapper.width()){
1445								_seq("on",39);
1446							}
1447						}
1448					}
1449				}
1450			}).bind("mouseup."+namespace+" dragend."+namespace,function(e){
1451				if(touchable){return;}
1452				if(action){action=0; _seq("off",null);}
1453				touchActive=false;
1454			});
1455			function _sel(){
1456				return 	window.getSelection ? window.getSelection().toString() : 
1457						document.selection && document.selection.type!="Control" ? document.selection.createRange().text : 0;
1458			}
1459			function _seq(a,c,s){
1460				seq.type=s && action ? "stepped" : "stepless";
1461				seq.scrollAmount=10;
1462				_sequentialScroll($this,a,c,"mcsLinearOut",s ? 60 : null);
1463			}
1464		},
1465		/* -------------------- */
1466		
1467		
1468		/* 
1469		MOUSE WHEEL EVENT
1470		scrolls content via mouse-wheel 
1471		via mouse-wheel plugin (https://github.com/brandonaaron/jquery-mousewheel)
1472		*/
1473		_mousewheel=function(){
1474			if(!$(this).data(pluginPfx)){return;} /* Check if the scrollbar is ready to use mousewheel events (issue: #185) */
1475			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
1476				namespace=pluginPfx+"_"+d.idx,
1477				mCustomScrollBox=$("#mCSB_"+d.idx),
1478				mCSB_dragger=[$("#mCSB_"+d.idx+"_dragger_vertical"),$("#mCSB_"+d.idx+"_dragger_horizontal")],
1479				iframe=$("#mCSB_"+d.idx+"_container").find("iframe");
1480			if(iframe.length){
1481				iframe.each(function(){
1482					$(this).bind("load",function(){
1483						/* bind events on accessible iframes */
1484						if(_canAccessIFrame(this)){
1485							$(this.contentDocument || this.contentWindow.document).bind("mousewheel."+namespace,function(e,delta){
1486								_onMousewheel(e,delta);
1487							});
1488						}
1489					});
1490				});
1491			}
1492			mCustomScrollBox.bind("mousewheel."+namespace,function(e,delta){
1493				_onMousewheel(e,delta);
1494			});
1495			function _onMousewheel(e,delta){
1496				_stop($this);
1497				if(_disableMousewheel($this,e.target)){return;} /* disables mouse-wheel when hovering specific elements */
1498				var deltaFactor=o.mouseWheel.deltaFactor!=="auto" ? parseInt(o.mouseWheel.deltaFactor) : (oldIE && e.deltaFactor<100) ? 100 : e.deltaFactor || 100,
1499					dur=o.scrollInertia;
1500				if(o.axis==="x" || o.mouseWheel.axis==="x"){
1501					var dir="x",
1502						px=[Math.round(deltaFactor*d.scrollRatio.x),parseInt(o.mouseWheel.scrollAmount)],
1503						amount=o.mouseWheel.scrollAmount!=="auto" ? px[1] : px[0]>=mCustomScrollBox.width() ? mCustomScrollBox.width()*0.9 : px[0],
1504						contentPos=Math.abs($("#mCSB_"+d.idx+"_container")[0].offsetLeft),
1505						draggerPos=mCSB_dragger[1][0].offsetLeft,
1506						limit=mCSB_dragger[1].parent().width()-mCSB_dragger[1].width(),
1507						dlt=o.mouseWheel.axis==="y" ? (e.deltaY || delta) : e.deltaX;
1508				}else{
1509					var dir="y",
1510						px=[Math.round(deltaFactor*d.scrollRatio.y),parseInt(o.mouseWheel.scrollAmount)],
1511						amount=o.mouseWheel.scrollAmount!=="auto" ? px[1] : px[0]>=mCustomScrollBox.height() ? mCustomScrollBox.height()*0.9 : px[0],
1512						contentPos=Math.abs($("#mCSB_"+d.idx+"_container")[0].offsetTop),
1513						draggerPos=mCSB_dragger[0][0].offsetTop,
1514						limit=mCSB_dragger[0].parent().height()-mCSB_dragger[0].height(),
1515						dlt=e.deltaY || delta;
1516				}
1517				if((dir==="y" && !d.overflowed[0]) || (dir==="x" && !d.overflowed[1])){return;}
1518				if(o.mouseWheel.invert || e.webkitDirectionInvertedFromDevice){dlt=-dlt;}
1519				if(o.mouseWheel.normalizeDelta){dlt=dlt<0 ? -1 : 1;}
1520				if((dlt>0 && draggerPos!==0) || (dlt<0 && draggerPos!==limit) || o.mouseWheel.preventDefault){
1521					e.stopImmediatePropagation();
1522					e.preventDefault();
1523				}
1524				if(e.deltaFactor<5 && !o.mouseWheel.normalizeDelta){
1525					//very low deltaFactor values mean some kind of delta acceleration (e.g. osx trackpad), so adjusting scrolling accordingly
1526					amount=e.deltaFactor; dur=17;
1527				}
1528				_scrollTo($this,(contentPos-(dlt*amount)).toString(),{dir:dir,dur:dur});
1529			}
1530		},
1531		/* -------------------- */
1532		
1533		
1534		/* checks if iframe can be accessed */
1535		_canAccessIFrameCache=new Object(),
1536		_canAccessIFrame=function(iframe){
1537		    var result=false,cacheKey=false,html=null;
1538		    if(iframe===undefined){
1539				cacheKey="#empty";
1540		    }else if($(iframe).attr("id")!==undefined){
1541				cacheKey=$(iframe).attr("id");
1542		    }
1543			if(cacheKey!==false && _canAccessIFrameCache[cacheKey]!==undefined){
1544				return _canAccessIFrameCache[cacheKey];
1545			}
1546			if(!iframe){
1547				try{
1548					var doc=top.document;
1549					html=doc.body.innerHTML;
1550				}catch(err){/* do nothing */}
1551				result=(html!==null);
1552			}else{
1553				try{
1554					var doc=iframe.contentDocument || iframe.contentWindow.document;
1555					html=doc.body.innerHTML;
1556				}catch(err){/* do nothing */}
1557				result=(html!==null);
1558			}
1559			if(cacheKey!==false){_canAccessIFrameCache[cacheKey]=result;}
1560			return result;
1561		},
1562		/* -------------------- */
1563		
1564		
1565		/* switches iframe's pointer-events property (drag, mousewheel etc. over cross-domain iframes) */
1566		_iframe=function(evt){
1567			var el=this.find("iframe");
1568			if(!el.length){return;} /* check if content contains iframes */
1569			var val=!evt ? "none" : "auto";
1570			el.css("pointer-events",val); /* for IE11, iframe's display property should not be "block" */
1571		},
1572		/* -------------------- */
1573		
1574		
1575		/* disables mouse-wheel when hovering specific elements like select, datalist etc. */
1576		_disableMousewheel=function(el,target){
1577			var tag=target.nodeName.toLowerCase(),
1578				tags=el.data(pluginPfx).opt.mouseWheel.disableOver,
1579				/* elements that require focus */
1580				focusTags=["select","textarea"];
1581			return $.inArray(tag,tags) > -1 && !($.inArray(tag,focusTags) > -1 && !$(target).is(":focus"));
1582		},
1583		/* -------------------- */
1584		
1585		
1586		/* 
1587		DRAGGER RAIL CLICK EVENT
1588		scrolls content via dragger rail 
1589		*/
1590		_draggerRail=function(){
1591			var $this=$(this),d=$this.data(pluginPfx),
1592				namespace=pluginPfx+"_"+d.idx,
1593				mCSB_container=$("#mCSB_"+d.idx+"_container"),
1594				wrapper=mCSB_container.parent(),
1595				mCSB_draggerContainer=$(".mCSB_"+d.idx+"_scrollbar ."+classes[12]),
1596				clickable;
1597			mCSB_draggerContainer.bind("mousedown."+namespace+" touchstart."+namespace+" pointerdown."+namespace+" MSPointerDown."+namespace,function(e){
1598				touchActive=true;
1599				if(!$(e.target).hasClass("mCSB_dragger")){clickable=1;}
1600			}).bind("touchend."+namespace+" pointerup."+namespace+" MSPointerUp."+namespace,function(e){
1601				touchActive=false;
1602			}).bind("click."+namespace,function(e){
1603				if(!clickable){return;}
1604				clickable=0;
1605				if($(e.target).hasClass(classes[12]) || $(e.target).hasClass("mCSB_draggerRail")){
1606					_stop($this);
1607					var el=$(this),mCSB_dragger=el.find(".mCSB_dragger");
1608					if(el.parent(".mCSB_scrollTools_horizontal").length>0){
1609						if(!d.overflowed[1]){return;}
1610						var dir="x",
1611							clickDir=e.pageX>mCSB_dragger.offset().left ? -1 : 1,
1612							to=Math.abs(mCSB_container[0].offsetLeft)-(clickDir*(wrapper.width()*0.9));
1613					}else{
1614						if(!d.overflowed[0]){return;}
1615						var dir="y",
1616							clickDir=e.pageY>mCSB_dragger.offset().top ? -1 : 1,
1617							to=Math.abs(mCSB_container[0].offsetTop)-(clickDir*(wrapper.height()*0.9));
1618					}
1619					_scrollTo($this,to.toString(),{dir:dir,scrollEasing:"mcsEaseInOut"});
1620				}
1621			});
1622		},
1623		/* -------------------- */
1624		
1625		
1626		/* 
1627		FOCUS EVENT
1628		scrolls content via element focus (e.g. clicking an input, pressing TAB key etc.)
1629		*/
1630		_focus=function(){
1631			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
1632				namespace=pluginPfx+"_"+d.idx,
1633				mCSB_container=$("#mCSB_"+d.idx+"_container"),
1634				wrapper=mCSB_container.parent();
1635			mCSB_container.bind("focusin."+namespace,function(e){
1636				var el=$(document.activeElement),
1637					nested=mCSB_container.find(".mCustomScrollBox").length,
1638					dur=0;
1639				if(!el.is(o.advanced.autoScrollOnFocus)){return;}
1640				_stop($this);
1641				clearTimeout($this[0]._focusTimeout);
1642				$this[0]._focusTimer=nested ? (dur+17)*nested : 0;
1643				$this[0]._focusTimeout=setTimeout(function(){
1644					var	to=[_childPos(el)[0],_childPos(el)[1]],
1645						contentPos=[mCSB_container[0].offsetTop,mCSB_container[0].offsetLeft],
1646						isVisible=[
1647							(contentPos[0]+to[0]>=0 && contentPos[0]+to[0]<wrapper.height()-el.outerHeight(false)),
1648							(contentPos[1]+to[1]>=0 && contentPos[0]+to[1]<wrapper.width()-el.outerWidth(false))
1649						],
1650						overwrite=(o.axis==="yx" && !isVisible[0] && !isVisible[1]) ? "none" : "all";
1651					if(o.axis!=="x" && !isVisible[0]){
1652						_scrollTo($this,to[0].toString(),{dir:"y",scrollEasing:"mcsEaseInOut",overwrite:overwrite,dur:dur});
1653					}
1654					if(o.axis!=="y" && !isVisible[1]){
1655						_scrollTo($this,to[1].toString(),{dir:"x",scrollEasing:"mcsEaseInOut",overwrite:overwrite,dur:dur});
1656					}
1657				},$this[0]._focusTimer);
1658			});
1659		},
1660		/* -------------------- */
1661		
1662		
1663		/* sets content wrapper scrollTop/scrollLeft always to 0 */
1664		_wrapperScroll=function(){
1665			var $this=$(this),d=$this.data(pluginPfx),
1666				namespace=pluginPfx+"_"+d.idx,
1667				wrapper=$("#mCSB_"+d.idx+"_container").parent();
1668			wrapper.bind("scroll."+namespace,function(e){
1669				if(wrapper.scrollTop()!==0 || wrapper.scrollLeft()!==0){
1670					$(".mCSB_"+d.idx+"_scrollbar").css("visibility","hidden"); /* hide scrollbar(s) */
1671				}
1672			});
1673		},
1674		/* -------------------- */
1675		
1676		
1677		/* 
1678		BUTTONS EVENTS
1679		scrolls content via up, down, left and right buttons 
1680		*/
1681		_buttons=function(){
1682			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,seq=d.sequential,
1683				namespace=pluginPfx+"_"+d.idx,
1684				sel=".mCSB_"+d.idx+"_scrollbar",
1685				btn=$(sel+">a");
1686			btn.bind("contextmenu."+namespace,function(e){
1687				e.preventDefault(); //prevent right click
1688			}).bind("mousedown."+namespace+" touchstart."+namespace+" pointerdown."+namespace+" MSPointerDown."+namespace+" mouseup."+namespace+" touchend."+namespace+" pointerup."+namespace+" MSPointerUp."+namespace+" mouseout."+namespace+" pointerout."+namespace+" MSPointerOut."+namespace+" click."+namespace,function(e){
1689				e.preventDefault();
1690				if(!_mouseBtnLeft(e)){return;} /* left mouse button only */
1691				var btnClass=$(this).attr("class");
1692				seq.type=o.scrollButtons.scrollType;
1693				switch(e.type){
1694					case "mousedown": case "touchstart": case "pointerdown": case "MSPointerDown":
1695						if(seq.type==="stepped"){return;}
1696						touchActive=true;
1697						d.tweenRunning=false;
1698						_seq("on",btnClass);
1699						break;
1700					case "mouseup": case "touchend": case "pointerup": case "MSPointerUp":
1701					case "mouseout": case "pointerout": case "MSPointerOut":
1702						if(seq.type==="stepped"){return;}
1703						touchActive=false;
1704						if(seq.dir){_seq("off",btnClass);}
1705						break;
1706					case "click":
1707						if(seq.type!=="stepped" || d.tweenRunning){return;}
1708						_seq("on",btnClass);
1709						break;
1710				}
1711				function _seq(a,c){
1712					seq.scrollAmount=o.scrollButtons.scrollAmount;
1713					_sequentialScroll($this,a,c);
1714				}
1715			});
1716		},
1717		/* -------------------- */
1718		
1719		
1720		/* 
1721		KEYBOARD EVENTS
1722		scrolls content via keyboard 
1723		Keys: up arrow, down arrow, left arrow, right arrow, PgUp, PgDn, Home, End
1724		*/
1725		_keyboard=function(){
1726			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,seq=d.sequential,
1727				namespace=pluginPfx+"_"+d.idx,
1728				mCustomScrollBox=$("#mCSB_"+d.idx),
1729				mCSB_container=$("#mCSB_"+d.idx+"_container"),
1730				wrapper=mCSB_container.parent(),
1731				editables="input,textarea,select,datalist,keygen,[contenteditable='true']",
1732				iframe=mCSB_container.find("iframe"),
1733				events=["blur."+namespace+" keydown."+namespace+" keyup."+namespace];
1734			if(iframe.length){
1735				iframe.each(function(){
1736					$(this).bind("load",function(){
1737						/* bind events on accessible iframes */
1738						if(_canAccessIFrame(this)){
1739							$(this.contentDocument || this.contentWindow.document).bind(events[0],function(e){
1740								_onKeyboard(e);
1741							});
1742						}
1743					});
1744				});
1745			}
1746			mCustomScrollBox.attr("tabindex","0").bind(events[0],function(e){
1747				_onKeyboard(e);
1748			});
1749			function _onKeyboard(e){
1750				switch(e.type){
1751					case "blur":
1752						if(d.tweenRunning && seq.dir){_seq("off",null);}
1753						break;
1754					case "keydown": case "keyup":
1755						var code=e.keyCode ? e.keyCode : e.which,action="on";
1756						if((o.axis!=="x" && (code===38 || code===40)) || (o.axis!=="y" && (code===37 || code===39))){
1757							/* up (38), down (40), left (37), right (39) arrows */
1758							if(((code===38 || code===40) && !d.overflowed[0]) || ((code===37 || code===39) && !d.overflowed[1])){return;}
1759							if(e.type==="keyup"){action="off";}
1760							if(!$(document.activeElement).is(editables)){
1761								e.preventDefault();
1762								e.stopImmediatePropagation();
1763								_seq(action,code);
1764							}
1765						}else if(code===33 || code===34){
1766							/* PgUp (33), PgDn (34) */
1767							if(d.overflowed[0] || d.overflowed[1]){
1768								e.preventDefault();
1769								e.stopImmediatePropagation();
1770							}
1771							if(e.type==="keyup"){
1772								_stop($this);
1773								var keyboardDir=code===34 ? -1 : 1;
1774								if(o.axis==="x" || (o.axis==="yx" && d.overflowed[1] && !d.overflowed[0])){
1775									var dir="x",to=Math.abs(mCSB_container[0].offsetLeft)-(keyboardDir*(wrapper.width()*0.9));
1776								}else{
1777									var dir="y",to=Math.abs(mCSB_container[0].offsetTop)-(keyboardDir*(wrapper.height()*0.9));
1778								}
1779								_scrollTo($this,to.toString(),{dir:dir,scrollEasing:"mcsEaseInOut"});
1780							}
1781						}else if(code===35 || code===36){
1782							/* End (35), Home (36) */
1783							if(!$(document.activeElement).is(editables)){
1784								if(d.overflowed[0] || d.overflowed[1]){
1785									e.preventDefault();
1786									e.stopImmediatePropagation();
1787								}
1788								if(e.type==="keyup"){
1789									if(o.axis==="x" || (o.axis==="yx" && d.overflowed[1] && !d.overflowed[0])){
1790										var dir="x",to=code===35 ? Math.abs(wrapper.width()-mCSB_container.outerWidth(false)) : 0;
1791									}else{
1792										var dir="y",to=code===35 ? Math.abs(wrapper.height()-mCSB_container.outerHeight(false)) : 0;
1793									}
1794									_scrollTo($this,to.toString(),{dir:dir,scrollEasing:"mcsEaseInOut"});
1795								}
1796							}
1797						}
1798						break;
1799				}
1800				function _seq(a,c){
1801					seq.type=o.keyboard.scrollType;
1802					seq.scrollAmount=o.keyboard.scrollAmount;
1803					if(seq.type==="stepped" && d.tweenRunning){return;}
1804					_sequentialScroll($this,a,c);
1805				}
1806			}
1807		},
1808		/* -------------------- */
1809		
1810		
1811		/* scrolls content sequentially (used when scrolling via buttons, keyboard arrows etc.) */
1812		_sequentialScroll=function(el,action,trigger,e,s){
1813			var d=el.data(pluginPfx),o=d.opt,seq=d.sequential,
1814				mCSB_container=$("#mCSB_"+d.idx+"_container"),
1815				once=seq.type==="stepped" ? true : false,
1816				steplessSpeed=o.scrollInertia < 26 ? 26 : o.scrollInertia, /* 26/1.5=17 */
1817				steppedSpeed=o.scrollInertia < 1 ? 17 : o.scrollInertia;
1818			switch(action){
1819				case "on":
1820					seq.dir=[
1821						(trigger===classes[16] || trigger===classes[15] || trigger===39 || trigger===37 ? "x" : "y"),
1822						(trigger===classes[13] || trigger===classes[15] || trigger===38 || trigger===37 ? -1 : 1)
1823					];
1824					_stop(el);
1825					if(_isNumeric(trigger) && seq.type==="stepped"){return;}
1826					_on(once);
1827					break;
1828				case "off":
1829					_off();
1830					if(once || (d.tweenRunning && seq.dir)){
1831						_on(true);
1832					}
1833					break;
1834			}
1835			
1836			/* starts sequence */
1837			function _on(once){
1838				if(o.snapAmount){seq.scrollAmount=!(o.snapAmount instanceof Array) ? o.snapAmount : seq.dir[0]==="x" ? o.snapAmount[1] : o.snapAmount[0];} /* scrolling snapping */
1839				var c=seq.type!=="stepped", /* continuous scrolling */
1840					t=s ? s : !once ? 1000/60 : c ? steplessSpeed/1.5 : steppedSpeed, /* timer */
1841					m=!once ? 2.5 : c ? 7.5 : 40, /* multiplier */
1842					contentPos=[Math.abs(mCSB_container[0].offsetTop),Math.abs(mCSB_container[0].offsetLeft)],
1843					ratio=[d.scrollRatio.y>10 ? 10 : d.scrollRatio.y,d.scrollRatio.x>10 ? 10 : d.scrollRatio.x],
1844					amount=seq.dir[0]==="x" ? contentPos[1]+(seq.dir[1]*(ratio[1]*m)) : contentPos[0]+(seq.dir[1]*(ratio[0]*m)),
1845					px=seq.dir[0]==="x" ? contentPos[1]+(seq.dir[1]*parseInt(seq.scrollAmount)) : contentPos[0]+(seq.dir[1]*parseInt(seq.scrollAmount)),
1846					to=seq.scrollAmount!=="auto" ? px : amount,
1847					easing=e ? e : !once ? "mcsLinear" : c ? "mcsLinearOut" : "mcsEaseInOut",
1848					onComplete=!once ? false : true;
1849				if(once && t<17){
1850					to=seq.dir[0]==="x" ? contentPos[1] : contentPos[0];
1851				}
1852				_scrollTo(el,to.toString(),{dir:seq.dir[0],scrollEasing:easing,dur:t,onComplete:onComplete});
1853				if(once){
1854					seq.dir=false;
1855					return;
1856				}
1857				clearTimeout(seq.step);
1858				seq.step=setTimeout(function(){
1859					_on();
1860				},t);
1861			}
1862			/* stops sequence */
1863			function _off(){
1864				clearTimeout(seq.step);
1865				_delete(seq,"step");
1866				_stop(el);
1867			}
1868		},
1869		/* -------------------- */
1870		
1871		
1872		/* returns a yx array from value */
1873		_arr=function(val){
1874			var o=$(this).data(pluginPfx).opt,vals=[];
1875			if(typeof val==="function"){val=val();} /* check if the value is a single anonymous function */
1876			/* check if value is object or array, its length and create an array with yx values */
1877			if(!(val instanceof Array)){ /* object value (e.g. {y:"100",x:"100"}, 100 etc.) */
1878				vals[0]=val.y ? val.y : val.x || o.axis==="x" ? null : val;
1879				vals[1]=val.x ? val.x : val.y || o.axis==="y" ? null : val;
1880			}else{ /* array value (e.g. [100,100]) */
1881				vals=val.length>1 ? [val[0],val[1]] : o.axis==="x" ? [null,val[0]] : [val[0],null];
1882			}
1883			/* check if array values are anonymous functions */
1884			if(typeof vals[0]==="function"){vals[0]=vals[0]();}
1885			if(typeof vals[1]==="function"){vals[1]=vals[1]();}
1886			return vals;
1887		},
1888		/* -------------------- */
1889		
1890		
1891		/* translates values (e.g. "top", 100, "100px", "#id") to actual scroll-to positions */
1892		_to=function(val,dir){
1893			if(val==null || typeof val=="undefined"){return;}
1894			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
1895				mCSB_container=$("#mCSB_"+d.idx+"_container"),
1896				wrapper=mCSB_container.parent(),
1897				t=typeof val;
1898			if(!dir){dir=o.axis==="x" ? "x" : "y";}
1899			var contentLength=dir==="x" ? mCSB_container.outerWidth(false)-wrapper.width() : mCSB_container.outerHeight(false)-wrapper.height(),
1900				contentPos=dir==="x" ? mCSB_container[0].offsetLeft : mCSB_container[0].offsetTop,
1901				cssProp=dir==="x" ? "left" : "top";
1902			switch(t){
1903				case "function": /* this currently is not used. Consider removing it */
1904					return val();
1905					break;
1906				case "object": /* js/jquery object */
1907					var obj=val.jquery ? val : $(val);
1908					if(!obj.length){return;}
1909					return dir==="x" ? _childPos(obj)[1] : _childPos(obj)[0];
1910					break;
1911				case "string": case "number":
1912					if(_isNumeric(val)){ /* numeric value */
1913						return Math.abs(val);
1914					}else if(val.indexOf("%")!==-1){ /* percentage value */
1915						return Math.abs(contentLength*parseInt(val)/100);
1916					}else if(val.indexOf("-=")!==-1){ /* decrease value */
1917						return Math.abs(contentPos-parseInt(val.split("-=")[1]));
1918					}else if(val.indexOf("+=")!==-1){ /* inrease value */
1919						var p=(contentPos+parseInt(val.split("+=")[1]));
1920						return p>=0 ? 0 : Math.abs(p);
1921					}else if(val.indexOf("px")!==-1 && _isNumeric(val.split("px")[0])){ /* pixels string value (e.g. "100px") */
1922						return Math.abs(val.split("px")[0]);
1923					}else{
1924						if(val==="top" || val==="left"){ /* special strings */
1925							return 0;
1926						}else if(val==="bottom"){
1927							return Math.abs(wrapper.height()-mCSB_container.outerHeight(false));
1928						}else if(val==="right"){
1929							return Math.abs(wrapper.width()-mCSB_container.outerWidth(false));
1930						}else if(val==="first" || val==="last"){
1931							var obj=mCSB_container.find(":"+val);
1932							return dir==="x" ? _childPos(obj)[1] : _childPos(obj)[0];
1933						}else{
1934							if($(val).length){ /* jquery selector */
1935								return dir==="x" ? _childPos($(val))[1] : _childPos($(val))[0];
1936							}else{ /* other values (e.g. "100em") */
1937								mCSB_container.css(cssProp,val);
1938								methods.update.call(null,$this[0]);
1939								return;
1940							}
1941						}
1942					}
1943					break;
1944			}
1945		},
1946		/* -------------------- */
1947		
1948		
1949		/* calls the update method automatically */
1950		_autoUpdate=function(rem){
1951			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
1952				mCSB_container=$("#mCSB_"+d.idx+"_container");
1953			if(rem){
1954				/* 
1955				removes autoUpdate timer 
1956				usage: _autoUpdate.call(this,"remove");
1957				*/
1958				clearTimeout(mCSB_container[0].autoUpdate);
1959				_delete(mCSB_container[0],"autoUpdate");
1960				return;
1961			}
1962			upd();
1963			function upd(){
1964				clearTimeout(mCSB_container[0].autoUpdate);
1965				if($this.parents("html").length===0){
1966					/* check element in dom tree */
1967					$this=null;
1968					return;
1969				}
1970				mCSB_container[0].autoUpdate=setTimeout(function(){
1971					/* update on specific selector(s) length and size change */
1972					if(o.advanced.updateOnSelectorChange){
1973						d.poll.change.n=sizesSum();
1974						if(d.poll.change.n!==d.poll.change.o){
1975							d.poll.change.o=d.poll.change.n;
1976							doUpd(3);
1977							return;
1978						}
1979					}
1980					/* update on main element and scrollbar size changes */
1981					if(o.advanced.updateOnContentResize){
1982						d.poll.size.n=$this[0].scrollHeight+$this[0].scrollWidth+mCSB_container[0].offsetHeight+$this[0].offsetHeight+$this[0].offsetWidth;
1983						if(d.poll.size.n!==d.poll.size.o){
1984							d.poll.size.o=d.poll.size.n;
1985							doUpd(1);
1986							return;
1987						}
1988					}
1989					/* update on image load */
1990					if(o.advanced.updateOnImageLoad){
1991						if(!(o.advanced.updateOnImageLoad==="auto" && o.axis==="y")){ //by default, it doesn't run on vertical content
1992							d.poll.img.n=mCSB_container.find("img").length;
1993							if(d.poll.img.n!==d.poll.img.o){
1994								d.poll.img.o=d.poll.img.n;
1995								mCSB_container.find("img").each(function(){
1996									imgLoader(this);
1997								});
1998								return;
1999							}
2000						}
2001					}
2002					if(o.advanced.updateOnSelectorChange || o.advanced.updateOnContentResize || o.advanced.updateOnImageLoad){upd();}
2003				},o.advanced.autoUpdateTimeout);
2004			}
2005			/* a tiny image loader */
2006			function imgLoader(el){
2007				if($(el).hasClass(classes[2])){doUpd(); return;}
2008				var img=new Image();
2009				function createDelegate(contextObject,delegateMethod){
2010					return function(){return delegateMethod.apply(contextObject,arguments);}
2011				}
2012				function imgOnLoad(){
2013					this.onload=null;
2014					$(el).addClass(classes[2]);
2015					doUpd(2);
2016				}
2017				img.onload=createDelegate(img,imgOnLoad);
2018				img.src=el.src;
2019			}
2020			/* returns the total height and width sum of all elements matching the selector */
2021			function sizesSum(){
2022				if(o.advanced.updateOnSelectorChange===true){o.advanced.updateOnSelectorChange="*";}
2023				var total=0,sel=mCSB_container.find(o.advanced.updateOnSelectorChange);
2024				if(o.advanced.updateOnSelectorChange && sel.length>0){sel.each(function(){total+=this.offsetHeight+this.offsetWidth;});}
2025				return total;
2026			}
2027			/* calls the update method */
2028			function doUpd(cb){
2029				clearTimeout(mCSB_container[0].autoUpdate);
2030				methods.update.call(null,$this[0],cb);
2031			}
2032		},
2033		/* -------------------- */
2034		
2035		
2036		/* snaps scrolling to a multiple of a pixels number */
2037		_snapAmount=function(to,amount,offset){
2038			return (Math.round(to/amount)*amount-offset); 
2039		},
2040		/* -------------------- */
2041		
2042		
2043		/* stops content and scrollbar animations */
2044		_stop=function(el){
2045			var d=el.data(pluginPfx),
2046				sel=$("#mCSB_"+d.idx+"_container,#mCSB_"+d.idx+"_container_wrapper,#mCSB_"+d.idx+"_dragger_vertical,#mCSB_"+d.idx+"_dragger_horizontal");
2047			sel.each(function(){
2048				_stopTween.call(this);
2049			});
2050		},
2051		/* -------------------- */
2052		
2053		
2054		/* 
2055		ANIMATES CONTENT 
2056		This is where the actual scrolling happens
2057		*/
2058		_scrollTo=function(el,to,options){
2059			var d=el.data(pluginPfx),o=d.opt,
2060				defaults={
2061					trigger:"internal",
2062					dir:"y",
2063					scrollEasing:"mcsEaseOut",
2064					drag:false,
2065					dur:o.scrollInertia,
2066					overwrite:"all",
2067					callbacks:true,
2068					onStart:true,
2069					onUpdate:true,
2070					onComplete:true
2071				},
2072				options=$.extend(defaults,options),
2073				dur=[options.dur,(options.drag ? 0 : options.dur)],
2074				mCustomScrollBox=$("#mCSB_"+d.idx),
2075				mCSB_container=$("#mCSB_"+d.idx+"_container"),
2076				wrapper=mCSB_container.parent(),
2077				totalScrollOffsets=o.callbacks.onTotalScrollOffset ? _arr.call(el,o.callbacks.onTotalScrollOffset) : [0,0],
2078				totalScrollBackOffsets=o.callbacks.onTotalScrollBackOffset ? _arr.call(el,o.callbacks.onTotalScrollBackOffset) : [0,0];
2079			d.trigger=options.trigger;
2080			if(wrapper.scrollTop()!==0 || wrapper.scrollLeft()!==0){ /* always reset scrollTop/Left */
2081				$(".mCSB_"+d.idx+"_scrollbar").css("visibility","visible");
2082				wrapper.scrollTop(0).scrollLeft(0);
2083			}
2084			if(to==="_resetY" && !d.contentReset.y){
2085				/* callbacks: onOverflowYNone */
2086				if(_cb("onOverflowYNone")){o.callbacks.onOverflowYNone.call(el[0]);}
2087				d.contentReset.y=1;
2088			}
2089			if(to==="_resetX" && !d.contentReset.x){
2090				/* callbacks: onOverflowXNone */
2091				if(_cb("onOverflowXNone")){o.callbacks.onOverflowXNone.call(el[0]);}
2092				d.contentReset.x=1;
2093			}
2094			if(to==="_resetY" || to==="_resetX"){return;}
2095			if((d.contentReset.y || !el[0].mcs) && d.overflowed[0]){
2096				/* callbacks: onOverflowY */
2097				if(_cb("onOverflowY")){o.callbacks.onOverflowY.call(el[0]);}
2098				d.contentReset.x=null;
2099			}
2100			if((d.contentReset.x || !el[0].mcs) && d.overflowed[1]){
2101				/* callbacks: onOverflowX */
2102				if(_cb("onOverflowX")){o.callbacks.onOverflowX.call(el[0]);}
2103				d.contentReset.x=null;
2104			}
2105			if(o.snapAmount){ /* scrolling snapping */
2106				var snapAmount=!(o.snapAmount instanceof Array) ? o.snapAmount : options.dir==="x" ? o.snapAmount[1] : o.snapAmount[0];
2107				to=_snapAmount(to,snapAmount,o.snapOffset);
2108			}
2109			switch(options.dir){
2110				case "x":
2111					var mCSB_dragger=$("#mCSB_"+d.idx+"_dragger_horizontal"),
2112						property="left",
2113						contentPos=mCSB_container[0].offsetLeft,
2114						limit=[
2115							mCustomScrollBox.width()-mCSB_container.outerWidth(false),
2116							mCSB_dragger.parent().width()-mCSB_dragger.width()
2117						],
2118						scrollTo=[to,to===0 ? 0 : (to/d.scrollRatio.x)],
2119						tso=totalScrollOffsets[1],
2120						tsbo=totalScrollBackOffsets[1],
2121						totalScrollOffset=tso>0 ? tso/d.scrollRatio.x : 0,
2122						totalScrollBackOffset=tsbo>0 ? tsbo/d.scrollRatio.x : 0;
2123					break;
2124				case "y":
2125					var mCSB_dragger=$("#mCSB_"+d.idx+"_dragger_vertical"),
2126						property="top",
2127						contentPos=mCSB_container[0].offsetTop,
2128						limit=[
2129							mCustomScrollBox.height()-mCSB_container.outerHeight(false),
2130							mCSB_dragger.parent().height()-mCSB_dragger.height()
2131						],
2132						scrollTo=[to,to===0 ? 0 : (to/d.scrollRatio.y)],
2133						tso=totalScrollOffsets[0],
2134						tsbo=totalScrollBackOffsets[0],
2135						totalScrollOffset=tso>0 ? tso/d.scrollRatio.y : 0,
2136						totalScrollBackOffset=tsbo>0 ? tsbo/d.scrollRatio.y : 0;
2137					break;
2138			}
2139			if(scrollTo[1]<0 || (scrollTo[0]===0 && scrollTo[1]===0)){
2140				scrollTo=[0,0];
2141			}else if(scrollTo[1]>=limit[1]){
2142				scrollTo=[limit[0],limit[1]];
2143			}else{
2144				scrollTo[0]=-scrollTo[0];
2145			}
2146			if(!el[0].mcs){
2147				_mcs();  /* init mcs object (once) to make it available before callbacks */
2148				if(_cb("onInit")){o.callbacks.onInit.call(el[0]);} /* callbacks: onInit */
2149			}
2150			clearTimeout(mCSB_container[0].onCompleteTimeout);
2151			_tweenTo(mCSB_dragger[0],property,Math.round(scrollTo[1]),dur[1],options.scrollEasing);
2152			if(!d.tweenRunning && ((contentPos===0 && scrollTo[0]>=0) || (contentPos===limit[0] && scrollTo[0]<=limit[0]))){return;}
2153			_tweenTo(mCSB_container[0],property,Math.round(scrollTo[0]),dur[0],options.scrollEasing,options.overwrite,{
2154				onStart:function(){
2155					if(options.callbacks && options.onStart && !d.tweenRunning){
2156						/* callbacks: onScrollStart */
2157						if(_cb("onScrollStart")){_mcs(); o.callbacks.onScrollStart.call(el[0]);}
2158						d.tweenRunning=true;
2159						_onDragClasses(mCSB_dragger);
2160						d.cbOffsets=_cbOffsets();
2161					}
2162				},onUpdate:function(){
2163					if(options.callbacks && options.onUpdate){
2164						/* callbacks: whileScrolling */
2165						if(_cb("whileScrolling")){_mcs(); o.callbacks.whileScrolling.call(el[0]);}
2166					}
2167				},onComplete:function(){
2168					if(options.callbacks && options.onComplete){
2169						if(o.axis==="yx"){clearTimeout(mCSB_container[0].onCompleteTimeout);}
2170						var t=mCSB_container[0].idleTimer || 0;
2171						mCSB_container[0].onCompleteTimeout=setTimeout(function(){
2172							/* callbacks: onScroll, onTotalScroll, onTotalScrollBack */
2173							if(_cb("onScroll")){_mcs(); o.callbacks.onScroll.call(el[0]);}
2174							if(_cb("onTotalScroll") && scrollTo[1]>=limit[1]-totalScrollOffset && d.cbOffsets[0]){_mcs(); o.callbacks.onTotalScroll.call(el[0]);}
2175							if(_cb("onTotalScrollBack") && scrollTo[1]<=totalScrollBackOffset && d.cbOffsets[1]){_mcs(); o.callbacks.onTotalScrollBack.call(el[0]);}
2176							d.tweenRunning=false;
2177							mCSB_container[0].idleTimer=0;
2178							_onDragClasses(mCSB_dragger,"hide");
2179						},t);
2180					}
2181				}
2182			});
2183			/* checks if callback function exists */
2184			function _cb(cb){
2185				return d && o.callbacks[cb] && typeof o.callbacks[cb]==="function";
2186			}
2187			/* checks whether callback offsets always trigger */
2188			function _cbOffsets(){
2189				return [o.callbacks.alwaysTriggerOffsets || contentPos>=limit[0]+tso,o.callbacks.alwaysTriggerOffsets || contentPos<=-tsbo];
2190			}
2191			/* 
2192			populates object with useful values for the user 
2193			values: 
2194				content: this.mcs.content
2195				content top position: this.mcs.top 
2196				content left position: this.mcs.left 
2197				dragger top position: this.mcs.draggerTop 
2198				dragger left position: this.mcs.draggerLeft 
2199				scrolling y percentage: this.mcs.topPct 
2200				scrolling x percentage: this.mcs.leftPct 
2201				scrolling direction: this.mcs.direction
2202			*/
2203			function _mcs(){
2204				var cp=[mCSB_container[0].offsetTop,mCSB_container[0].offsetLeft], /* content position */
2205					dp=[mCSB_dragger[0].offsetTop,mCSB_dragger[0].offsetLeft], /* dragger position */
2206					cl=[mCSB_container.outerHeight(false),mCSB_container.outerWidth(false)], /* content length */
2207					pl=[mCustomScrollBox.height(),mCustomScrollBox.width()]; /* content parent length */
2208				el[0].mcs={
2209					content:mCSB_container, /* original content wrapper as jquery object */
2210					top:cp[0],left:cp[1],draggerTop:dp[0],draggerLeft:dp[1],
2211					topPct:Math.round((100*Math.abs(cp[0]))/(Math.abs(cl[0])-pl[0])),leftPct:Math.round((100*Math.abs(cp[1]))/(Math.abs(cl[1])-pl[1])),
2212					direction:options.dir
2213				};
2214				/* 
2215				this refers to the original element containing the scrollbar(s)
2216				usage: this.mcs.top, this.mcs.leftPct etc. 
2217				*/
2218			}
2219		},
2220		/* -------------------- */
2221		
2222		
2223		/* 
2224		CUSTOM JAVASCRIPT ANIMATION TWEEN 
2225		Lighter and faster than jquery animate() and css transitions 
2226		Animates top/left properties and includes easings 
2227		*/
2228		_tweenTo=function(el,prop,to,duration,easing,overwrite,callbacks){
2229			if(!el._mTween){el._mTween={top:{},left:{}};}
2230			var callbacks=callbacks || {},
2231				onStart=callbacks.onStart || function(){},onUpdate=callbacks.onUpdate || function(){},onComplete=callbacks.onComplete || function(){},
2232				startTime=_getTime(),_delay,progress=0,from=el.offsetTop,elStyle=el.style,_request,tobj=el._mTween[prop];
2233			if(prop==="left"){from=el.offsetLeft;}
2234			var diff=to-from;
2235			tobj.stop=0;
2236			if(overwrite!=="none"){_cancelTween();}
2237			_startTween();
2238			function _step(){
2239				if(tobj.stop){return;}
2240				if(!progress){onStart.call();}
2241				progress=_getTime()-startTime;
2242				_tween();
2243				if(progress>=tobj.time){
2244					tobj.time=(progress>tobj.time) ? progress+_delay-(progress-tobj.time) : progress+_delay-1;
2245					if(tobj.time<progress+1){tobj.time=progress+1;}
2246				}
2247				if(tobj.time<duration){tobj.id=_request(_step);}else{onComplete.call();}
2248			}
2249			function _tween(){
2250				if(duration>0){
2251					tobj.currVal=_ease(tobj.time,from,diff,duration,easing);
2252					elStyle[prop]=Math.round(tobj.currVal)+"px";
2253				}else{
2254					elStyle[prop]=to+"px";
2255				}
2256				onUpdate.call();
2257			}
2258			function _startTween(){
2259				_delay=1000/60;
2260				tobj.time=progress+_delay;
2261				_request=(!window.requestAnimationFrame) ? function(f){_tween(); return setTimeout(f,0.01);} : window.requestAnimationFrame;
2262				tobj.id=_request(_step);
2263			}
2264			function _cancelTween(){
2265				if(tobj.id==null){return;}
2266				if(!window.requestAnimationFrame){clearTimeout(tobj.id);
2267				}else{window.cancelAnimationFrame(tobj.id);}
2268				tobj.id=null;
2269			}
2270			function _ease(t,b,c,d,type){
2271				switch(type){
2272					case "linear": case "mcsLinear":
2273						return c*t/d + b;
2274						break;
2275					case "mcsLinearOut":
2276						t/=d; t--; return c * Math.sqrt(1 - t*t) + b;
2277						break;
2278					case "easeInOutSmooth":
2279						t/=d/2;
2280						if(t<1) return c/2*t*t + b;
2281						t--;
2282						return -c/2 * (t*(t-2) - 1) + b;
2283						break;
2284					case "easeInOutStrong":
2285						t/=d/2;
2286						if(t<1) return c/2 * Math.pow( 2, 10 * (t - 1) ) + b;
2287						t--;
2288						return c/2 * ( -Math.pow( 2, -10 * t) + 2 ) + b;
2289						break;
2290					case "easeInOut": case "mcsEaseInOut":
2291						t/=d/2;
2292						if(t<1) return c/2*t*t*t + b;
2293						t-=2;
2294						return c/2*(t*t*t + 2) + b;
2295						break;
2296					case "easeOutSmooth":
2297						t/=d; t--;
2298						return -c * (t*t*t*t - 1) + b;
2299						break;
2300					case "easeOutStrong":
2301						return c * ( -Math.pow( 2, -10 * t/d ) + 1 ) + b;
2302						break;
2303					case "easeOut": case "mcsEaseOut": default:
2304						var ts=(t/=d)*t,tc=ts*t;
2305						return b+c*(0.499999999999997*tc*ts + -2.5*ts*ts + 5.5*tc + -6.5*ts + 4*t);
2306				}
2307			}
2308		},
2309		/* -------------------- */
2310		
2311		
2312		/* returns current time */
2313		_getTime=function(){
2314			if(window.performance && window.performance.now){
2315				return window.performance.now();
2316			}else{
2317				if(window.performance && window.performance.webkitNow){
2318					return window.performance.webkitNow();
2319				}else{
2320					if(Date.now){return Date.now();}else{return new Date().getTime();}
2321				}
2322			}
2323		},
2324		/* -------------------- */
2325		
2326		
2327		/* stops a tween */
2328		_stopTween=function(){
2329			var el=this;
2330			if(!el._mTween){el._mTween={top:{},left:{}};}
2331			var props=["top","left"];
2332			for(var i=0; i<props.length; i++){
2333				var prop=props[i];
2334				if(el._mTween[prop].id){
2335					if(!window.requestAnimationFrame){clearTimeout(el._mTween[prop].id);
2336					}else{window.cancelAnimationFrame(el._mTween[prop].id);}
2337					el._mTween[prop].id=null;
2338					el._mTween[prop].stop=1;
2339				}
2340			}
2341		},
2342		/* -------------------- */
2343		
2344		
2345		/* deletes a property (avoiding the exception thrown by IE) */
2346		_delete=function(c,m){
2347			try{delete c[m];}catch(e){c[m]=null;}
2348		},
2349		/* -------------------- */
2350		
2351		
2352		/* detects left mouse button */
2353		_mouseBtnLeft=function(e){
2354			return !(e.which && e.which!==1);
2355		},
2356		/* -------------------- */
2357		
2358		
2359		/* detects if pointer type event is touch */
2360		_pointerTouch=function(e){
2361			var t=e.originalEvent.pointerType;
2362			return !(t && t!=="touch" && t!==2);
2363		},
2364		/* -------------------- */
2365		
2366		
2367		/* checks if value is numeric */
2368		_isNumeric=function(val){
2369			return !isNaN(parseFloat(val)) && isFinite(val);
2370		},
2371		/* -------------------- */
2372		
2373		
2374		/* returns element position according to content */
2375		_childPos=function(el){
2376			var p=el.parents(".mCSB_container");
2377			return [el.offset().top-p.offset().top,el.offset().left-p.offset().left];
2378		},
2379		/* -------------------- */
2380		
2381		
2382		/* checks if browser tab is hidden/inactive via Page Visibility API */
2383		_isTabHidden=function(){
2384			var prop=_getHiddenProp();
2385			if(!prop) return false;
2386			return document[prop];
2387			function _getHiddenProp(){
2388				var pfx=["webkit","moz","ms","o"];
2389				if("hidden" in document) return "hidden"; //natively supported
2390				for(var i=0; i<pfx.length; i++){ //prefixed
2391				    if((pfx[i]+"Hidden") in document) 
2392				        return pfx[i]+"Hidden";
2393				}
2394				return null; //not supported
2395			}
2396		};
2397		/* -------------------- */
2398		
2399	
2400	
2401	
2402	
2403	/* 
2404	----------------------------------------
2405	PLUGIN SETUP 
2406	----------------------------------------
2407	*/
2408	
2409	/* plugin constructor functions */
2410	$.fn[pluginNS]=function(method){ /* usage: $(selector).mCustomScrollbar(); */
2411		if(methods[method]){
2412			return methods[method].apply(this,Array.prototype.slice.call(arguments,1));
2413		}else if(typeof method==="object" || !method){
2414			return methods.init.apply(this,arguments);
2415		}else{
2416			$.error("Method "+method+" does not exist");
2417		}
2418	};
2419	$[pluginNS]=function(method){ /* usage: $.mCustomScrollbar(); */
2420		if(methods[method]){
2421			return methods[method].apply(this,Array.prototype.slice.call(arguments,1));
2422		}else if(typeof method==="object" || !method){
2423			return methods.init.apply(this,arguments);
2424		}else{
2425			$.error("Method "+method+" does not exist");
2426		}
2427	};
2428	
2429	/* 
2430	allow setting plugin default options. 
2431	usage: $.mCustomScrollbar.defaults.scrollInertia=500; 
2432	to apply any changed default options on default selectors (below), use inside document ready fn 
2433	e.g.: $(document).ready(function(){ $.mCustomScrollbar.defaults.scrollInertia=500; });
2434	*/
2435	$[pluginNS].defaults=defaults;
2436	
2437	/* 
2438	add window object (window.mCustomScrollbar) 
2439	usage: if(window.mCustomScrollbar){console.log("custom scrollbar plugin loaded");}
2440	*/
2441	window[pluginNS]=true;
2442	
2443	$(window).bind("load",function(){
2444		
2445		$(defaultSelector)[pluginNS](); /* add scrollbars automatically on default selector */
2446		
2447		/* extend jQuery expressions */
2448		$.extend($.expr[":"],{
2449			/* checks if element is within scrollable viewport */
2450			mcsInView:$.expr[":"].mcsInView || function(el){
2451				var $el=$(el),content=$el.parents(".mCSB_container"),wrapper,cPos;
2452				if(!content.length){return;}
2453				wrapper=content.parent();
2454				cPos=[content[0].offsetTop,content[0].offsetLeft];
2455				return 	cPos[0]+_childPos($el)[0]>=0 && cPos[0]+_childPos($el)[0]<wrapper.height()-$el.outerHeight(false) && 
2456						cPos[1]+_childPos($el)[1]>=0 && cPos[1]+_childPos($el)[1]<wrapper.width()-$el.outerWidth(false);
2457			},
2458			/* checks if element or part of element is in view of scrollable viewport */
2459			mcsInSight:$.expr[":"].mcsInSight || function(el,i,m){
2460				var $el=$(el),elD,content=$el.parents(".mCSB_container"),wrapperView,pos,wrapperViewPct,
2461					pctVals=m[3]==="exact" ? [[1,0],[1,0]] : [[0.9,0.1],[0.6,0.4]];
2462				if(!content.length){return;}
2463				elD=[$el.outerHeight(false),$el.outerWidth(false)];
2464				pos=[content[0].offsetTop+_childPos($el)[0],content[0].offsetLeft+_childPos($el)[1]];
2465				wrapperView=[content.parent()[0].offsetHeight,content.parent()[0].offsetWidth];
2466				wrapperViewPct=[elD[0]<wrapperView[0] ? pctVals[0] : pctVals[1],elD[1]<wrapperView[1] ? pctVals[0] : pctVals[1]];
2467				return 	pos[0]-(wrapperView[0]*wrapperViewPct[0][0])<0 && pos[0]+elD[0]-(wrapperView[0]*wrapperViewPct[0][1])>=0 && 
2468						pos[1]-(wrapperView[1]*wrapperViewPct[1][0])<0 && pos[1]+elD[1]-(wrapperView[1]*wrapperViewPct[1][1])>=0;
2469			},
2470			/* checks if element is overflowed having visible scrollbar(s) */
2471			mcsOverflow:$.expr[":"].mcsOverflow || function(el){
2472				var d=$(el).data(pluginPfx);
2473				if(!d){return;}
2474				return d.overflowed[0] || d.overflowed[1];
2475			}
2476		});
2477	
2478	});
2479
2480}))}));
2480
2481// THIS FILE IS GENERATED - DO NOT EDIT!
2482/*!mobile-detect v1.4.5 2021-03-13*/
2483/*global module:false, define:false*/
2484/*jshint latedef:false*/
2485/*!@license Copyright 2013, Heinrich Goebl, License: MIT, see https://github.com/hgoebl/mobile-detect.js*/
2486(function (define, undefined) {
2487define(function () {
2488    'use strict';
2489
2490    var impl = {};
2491
2492    impl.mobileDetectRules = {
2493    "phones": {
2494        "iPhone": "\\biPhone\\b|\\biPod\\b",
2495        "BlackBerry": "BlackBerry|\\bBB10\\b|rim[0-9]+|\\b(BBA100|BBB100|BBD100|BBE100|BBF100|STH100)\\b-[0-9]+",
2496        "Pixel": "; \\bPixel\\b",
2497        "HTC": "HTC|HTC.*(Sensation|Evo|Vision|Explorer|6800|8100|8900|A7272|S510e|C110e|Legend|Desire|T8282)|APX515CKT|Qtek9090|APA9292KT|HD_mini|Sensation.*Z710e|PG86100|Z715e|Desire.*(A8181|HD)|ADR6200|ADR6400L|ADR6425|001HT|Inspire 4G|Android.*\\bEVO\\b|T-Mobile G1|Z520m|Android [0-9.]+; Pixel",
2498        "Nexus": "Nexus One|Nexus S|Galaxy.*Nexus|Android.*Nexus.*Mobile|Nexus 4|Nexus 5|Nexus 5X|Nexus 6",
2499        "Dell": "Dell[;]? (Streak|Aero|Venue|Venue Pro|Flash|Smoke|Mini 3iX)|XCD28|XCD35|\\b001DL\\b|\\b101DL\\b|\\bGS01\\b",
2500        "Motorola": "Motorola|DROIDX|DROID BIONIC|\\bDroid\\b.*Build|Android.*Xoom|HRI39|MOT-|A1260|A1680|A555|A853|A855|A953|A955|A956|Motorola.*ELECTRIFY|Motorola.*i1|i867|i940|MB200|MB300|MB501|MB502|MB508|MB511|MB520|MB525|MB526|MB611|MB612|MB632|MB810|MB855|MB860|MB861|MB865|MB870|ME501|ME502|ME511|ME525|ME600|ME632|ME722|ME811|ME860|ME863|ME865|MT620|MT710|MT716|MT720|MT810|MT870|MT917|Motorola.*TITANIUM|WX435|WX445|XT300|XT301|XT311|XT316|XT317|XT319|XT320|XT390|XT502|XT530|XT531|XT532|XT535|XT603|XT610|XT611|XT615|XT681|XT701|XT702|XT711|XT720|XT800|XT806|XT860|XT862|XT875|XT882|XT883|XT894|XT901|XT907|XT909|XT910|XT912|XT928|XT926|XT915|XT919|XT925|XT1021|\\bMoto E\\b|XT1068|XT1092|XT1052",
2501        "Samsung": 
vendor: 4,913 bytes, line 2501
2501"\\bSamsung\\b|SM-G950F|SM-G955F|SM-G9250|GT-19300|SGH-I337|BGT-S5230|GT-B2100|GT-B2700|GT-B2710|GT-B3210|GT-B3310|GT-B3410|GT-B3730|GT-B3740|GT-B5510|GT-B5512|GT-B5722|GT-B6520|GT-B7300|GT-B7320|GT-B7330|GT-B7350|GT-B7510|GT-B7722|GT-B7800|GT-C3010|GT-C3011|GT-C3060|GT-C3200|GT-C3212|GT-C3212I|GT-C3262|GT-C3222|GT-C3300|GT-C3300K|GT-C3303|GT-C3303K|GT-C3310|GT-C3322|GT-C3330|GT-C3350|GT-C3500|GT-C3510|GT-C3530|GT-C3630|GT-C3780|GT-C5010|GT-C5212|GT-C6620|GT-C6625|GT-C6712|GT-E1050|GT-E1070|GT-E1075|GT-E1080|GT-E1081|GT-E1085|GT-E1087|GT-E1100|GT-E1107|GT-E1110|GT-E1120|GT-E1125|GT-E1130|GT-E1160|GT-E1170|GT-E1175|GT-E1180|GT-E1182|GT-E1200|GT-E1210|GT-E1225|GT-E1230|GT-E1390|GT-E2100|GT-E2120|GT-E2121|GT-E2152|GT-E2220|GT-E2222|GT-E2230|GT-E2232|GT-E2250|GT-E2370|GT-E2550|GT-E2652|GT-E3210|GT-E3213|GT-I5500|GT-I5503|GT-I5700|GT-I5800|GT-I5801|GT-I6410|GT-I6420|GT-I7110|GT-I7410|GT-I7500|GT-I8000|GT-I8150|GT-I8160|GT-I8190|GT-I8320|GT-I8330|GT-I8350|GT-I8530|GT-I8700|GT-I8703|GT-I8910|GT-I9000|GT-I9001|GT-I9003|GT-I9010|GT-I9020|GT-I9023|GT-I9070|GT-I9082|GT-I9100|GT-I9103|GT-I9220|GT-I9250|GT-I9300|GT-I9305|GT-I9500|GT-I9505|GT-M3510|GT-M5650|GT-M7500|GT-M7600|GT-M7603|GT-M8800|GT-M8910|GT-N7000|GT-S3110|GT-S3310|GT-S3350|GT-S3353|GT-S3370|GT-S3650|GT-S3653|GT-S3770|GT-S3850|GT-S5210|GT-S5220|GT-S5229|GT-S5230|GT-S5233|GT-S5250|GT-S5253|GT-S5260|GT-S5263|GT-S5270|GT-S5300|GT-S5330|GT-S5350|GT-S5360|GT-S5363|GT-S5369|GT-S5380|GT-S5380D|GT-S5560|GT-S5570|GT-S5600|GT-S5603|GT-S5610|GT-S5620|GT-S5660|GT-S5670|GT-S5690|GT-S5750|GT-S5780|GT-S5830|GT-S5839|GT-S6102|GT-S6500|GT-S7070|GT-S7200|GT-S7220|GT-S7230|GT-S7233|GT-S7250|GT-S7500|GT-S7530|GT-S7550|GT-S7562|GT-S7710|GT-S8000|GT-S8003|GT-S8500|GT-S8530|GT-S8600|SCH-A310|SCH-A530|SCH-A570|SCH-A610|SCH-A630|SCH-A650|SCH-A790|SCH-A795|SCH-A850|SCH-A870|SCH-A890|SCH-A930|SCH-A950|SCH-A970|SCH-A990|SCH-I100|SCH-I110|SCH-I400|SCH-I405|SCH-I500|SCH-I510|SCH-I515|SCH-I600|SCH-I730|SCH-I760|SCH-I770|SCH-I830|SCH-I910|SCH-I920|SCH-I959|SCH-LC11|SCH-N150|SCH-N300|SCH-R100|SCH-R300|SCH-R351|SCH-R400|SCH-R410|SCH-T300|SCH-U310|SCH-U320|SCH-U350|SCH-U360|SCH-U365|SCH-U370|SCH-U380|SCH-U410|SCH-U430|SCH-U450|SCH-U460|SCH-U470|SCH-U490|SCH-U540|SCH-U550|SCH-U620|SCH-U640|SCH-U650|SCH-U660|SCH-U700|SCH-U740|SCH-U750|SCH-U810|SCH-U820|SCH-U900|SCH-U940|SCH-U960|SCS-26UC|SGH-A107|SGH-A117|SGH-A127|SGH-A137|SGH-A157|SGH-A167|SGH-A177|SGH-A187|SGH-A197|SGH-A227|SGH-A237|SGH-A257|SGH-A437|SGH-A517|SGH-A597|SGH-A637|SGH-A657|SGH-A667|SGH-A687|SGH-A697|SGH-A707|SGH-A717|SGH-A727|SGH-A737|SGH-A747|SGH-A767|SGH-A777|SGH-A797|SGH-A817|SGH-A827|SGH-A837|SGH-A847|SGH-A867|SGH-A877|SGH-A887|SGH-A897|SGH-A927|SGH-B100|SGH-B130|SGH-B200|SGH-B220|SGH-C100|SGH-C110|SGH-C120|SGH-C130|SGH-C140|SGH-C160|SGH-C170|SGH-C180|SGH-C200|SGH-C207|SGH-C210|SGH-C225|SGH-C230|SGH-C417|SGH-C450|SGH-D307|SGH-D347|SGH-D357|SGH-D407|SGH-D415|SGH-D780|SGH-D807|SGH-D980|SGH-E105|SGH-E200|SGH-E315|SGH-E316|SGH-E317|SGH-E335|SGH-E590|SGH-E635|SGH-E715|SGH-E890|SGH-F300|SGH-F480|SGH-I200|SGH-I300|SGH-I320|SGH-I550|SGH-I577|SGH-I600|SGH-I607|SGH-I617|SGH-I627|SGH-I637|SGH-I677|SGH-I700|SGH-I717|SGH-I727|SGH-i747M|SGH-I777|SGH-I780|SGH-I827|SGH-I847|SGH-I857|SGH-I896|SGH-I897|SGH-I900|SGH-I907|SGH-I917|SGH-I927|SGH-I937|SGH-I997|SGH-J150|SGH-J200|SGH-L170|SGH-L700|SGH-M110|SGH-M150|SGH-M200|SGH-N105|SGH-N500|SGH-N600|SGH-N620|SGH-N625|SGH-N700|SGH-N710|SGH-P107|SGH-P207|SGH-P300|SGH-P310|SGH-P520|SGH-P735|SGH-P777|SGH-Q105|SGH-R210|SGH-R220|SGH-R225|SGH-S105|SGH-S307|SGH-T109|SGH-T119|SGH-T139|SGH-T209|SGH-T219|SGH-T229|SGH-T239|SGH-T249|SGH-T259|SGH-T309|SGH-T319|SGH-T329|SGH-T339|SGH-T349|SGH-T359|SGH-T369|SGH-T379|SGH-T409|SGH-T429|SGH-T439|SGH-T459|SGH-T469|SGH-T479|SGH-T499|SGH-T509|SGH-T519|SGH-T539|SGH-T559|SGH-T589|SGH-T609|SGH-T619|SGH-T629|SGH-T639|SGH-T659|SGH-T669|SGH-T679|SGH-T709|SGH-T719|SGH-T729|SGH-T739|SGH-T746|SGH-T749|SGH-T759|SGH-T769|SGH-T809|SGH-T819|SGH-T839|SGH-T919|SGH-T929|SGH-T939|SGH-T959|SGH-T989|SGH-U100|SGH-U200|SGH-U800|SGH-V205|SGH-V206|SGH-X100|SGH-X105|SGH-X120|SGH-X140|SGH-X426|SGH-X427|SGH-X475|SGH-X495|SGH-X497|SGH-X507|SGH-X600|SGH-X610|SGH-X620|SGH-X630|SGH-X700|SGH-X820|SGH-X890|SGH-Z130|SGH-Z150|SGH-Z170|SGH-ZX10|SGH-ZX20|SHW-M110|SPH-A120|SPH-A400|SPH-A420|SPH-A460|SPH-A500|SPH-A560|SPH-A600|SPH-A620|SPH-A660|SPH-A700|SPH-A740|SPH-A760|SPH-A790|SPH-A800|SPH-A820|SPH-A840|SPH-A880|SPH-A900|SPH-A940|SPH-A960|SPH-D600|SPH-D700|SPH-D710|SPH-D720|SPH-I300|SPH-I325|SPH-I330|SPH-I350|SPH-I500|SPH-I600|SPH-I700|SPH-L700|SPH-M100|SPH-M220|SPH-M240|SPH-M300|SPH-M305|SPH-M320|SPH-M330|SPH-M350|SPH-M360|SPH-M370|SPH-M380|SPH-M510|SPH-M540|SPH-M550|SPH-M560|SPH-M570|SPH-M580|SPH-M610|SPH-M620|SPH-M630|SPH-M800|SPH-M810|SPH-M850|SPH-M900|SPH-M910|SPH-M920|SPH-M930|SPH-N100|SPH-N200|SPH-N240|SPH-N300|SPH-N400|SPH-Z400|SWC-E100|SCH-i909|GT-N7100|GT-N7105|SCH-I535|SM-N900A|SGH-I317|SGH-T999L|GT-S5360B|GT-
vendor: 63,412 bytes, lines 2501-3491
2501I8262|GT-S6802|GT-S6312|GT-S6310|GT-S5312|GT-S5310|GT-I9105|GT-I8510|GT-S6790N|SM-G7105|SM-N9005|GT-S5301|GT-I9295|GT-I9195|SM-C101|GT-S7392|GT-S7560|GT-B7610|GT-I5510|GT-S7582|GT-S7530E|GT-I8750|SM-G9006V|SM-G9008V|SM-G9009D|SM-G900A|SM-G900D|SM-G900F|SM-G900H|SM-G900I|SM-G900J|SM-G900K|SM-G900L|SM-G900M|SM-G900P|SM-G900R4|SM-G900S|SM-G900T|SM-G900V|SM-G900W8|SHV-E160K|SCH-P709|SCH-P729|SM-T2558|GT-I9205|SM-G9350|SM-J120F|SM-G920F|SM-G920V|SM-G930F|SM-N910C|SM-A310F|GT-I9190|SM-J500FN|SM-G903F|SM-J330F|SM-G610F|SM-G981B|SM-G892A|SM-A530F",
2502        "LG": "\\bLG\\b;|LG[- ]?(C800|C900|E400|E610|E900|E-900|F160|F180K|F180L|F180S|730|855|L160|LS740|LS840|LS970|LU6200|MS690|MS695|MS770|MS840|MS870|MS910|P500|P700|P705|VM696|AS680|AS695|AX840|C729|E970|GS505|272|C395|E739BK|E960|L55C|L75C|LS696|LS860|P769BK|P350|P500|P509|P870|UN272|US730|VS840|VS950|LN272|LN510|LS670|LS855|LW690|MN270|MN510|P509|P769|P930|UN200|UN270|UN510|UN610|US670|US740|US760|UX265|UX840|VN271|VN530|VS660|VS700|VS740|VS750|VS910|VS920|VS930|VX9200|VX11000|AX840A|LW770|P506|P925|P999|E612|D955|D802|MS323|M257)|LM-G710",
2503        "Sony": "SonyST|SonyLT|SonyEricsson|SonyEricssonLT15iv|LT18i|E10i|LT28h|LT26w|SonyEricssonMT27i|C5303|C6902|C6903|C6906|C6943|D2533|SOV34|601SO|F8332",
2504        "Asus": "Asus.*Galaxy|PadFone.*Mobile",
2505        "Xiaomi": "^(?!.*\\bx11\\b).*xiaomi.*$|POCOPHONE F1|MI 8|Redmi Note 9S|Redmi Note 5A Prime|N2G47H|M2001J2G|M2001J2I|M1805E10A|M2004J11G|M1902F1G|M2002J9G|M2004J19G|M2003J6A1G",
2506        "NokiaLumia": "Lumia [0-9]{3,4}",
2507        "Micromax": "Micromax.*\\b(A210|A92|A88|A72|A111|A110Q|A115|A116|A110|A90S|A26|A51|A35|A54|A25|A27|A89|A68|A65|A57|A90)\\b",
2508        "Palm": "PalmSource|Palm",
2509        "Vertu": "Vertu|Vertu.*Ltd|Vertu.*Ascent|Vertu.*Ayxta|Vertu.*Constellation(F|Quest)?|Vertu.*Monika|Vertu.*Signature",
2510        "Pantech": "PANTECH|IM-A850S|IM-A840S|IM-A830L|IM-A830K|IM-A830S|IM-A820L|IM-A810K|IM-A810S|IM-A800S|IM-T100K|IM-A725L|IM-A780L|IM-A775C|IM-A770K|IM-A760S|IM-A750K|IM-A740S|IM-A730S|IM-A720L|IM-A710K|IM-A690L|IM-A690S|IM-A650S|IM-A630K|IM-A600S|VEGA PTL21|PT003|P8010|ADR910L|P6030|P6020|P9070|P4100|P9060|P5000|CDM8992|TXT8045|ADR8995|IS11PT|P2030|P6010|P8000|PT002|IS06|CDM8999|P9050|PT001|TXT8040|P2020|P9020|P2000|P7040|P7000|C790",
2511        "Fly": "IQ230|IQ444|IQ450|IQ440|IQ442|IQ441|IQ245|IQ256|IQ236|IQ255|IQ235|IQ245|IQ275|IQ240|IQ285|IQ280|IQ270|IQ260|IQ250",
2512        "Wiko": "KITE 4G|HIGHWAY|GETAWAY|STAIRWAY|DARKSIDE|DARKFULL|DARKNIGHT|DARKMOON|SLIDE|WAX 4G|RAINBOW|BLOOM|SUNSET|GOA(?!nna)|LENNY|BARRY|IGGY|OZZY|CINK FIVE|CINK PEAX|CINK PEAX 2|CINK SLIM|CINK SLIM 2|CINK +|CINK KING|CINK PEAX|CINK SLIM|SUBLIM",
2513        "iMobile": "i-mobile (IQ|i-STYLE|idea|ZAA|Hitz)",
2514        "SimValley": "\\b(SP-80|XT-930|SX-340|XT-930|SX-310|SP-360|SP60|SPT-800|SP-120|SPT-800|SP-140|SPX-5|SPX-8|SP-100|SPX-8|SPX-12)\\b",
2515        "Wolfgang": "AT-B24D|AT-AS50HD|AT-AS40W|AT-AS55HD|AT-AS45q2|AT-B26D|AT-AS50Q",
2516        "Alcatel": "Alcatel",
2517        "Nintendo": "Nintendo (3DS|Switch)",
2518        "Amoi": "Amoi",
2519        "INQ": "INQ",
2520        "OnePlus": "ONEPLUS",
2521        "GenericPhone": "Tapatalk|PDA;|SAGEM|\\bmmp\\b|pocket|\\bpsp\\b|symbian|Smartphone|smartfon|treo|up.browser|up.link|vodafone|\\bwap\\b|nokia|Series40|Series60|S60|SonyEricsson|N900|MAUI.*WAP.*Browser"
2522    },
2523    "tablets": {
2524        "iPad": "iPad|iPad.*Mobile",
2525        "NexusTablet": "Android.*Nexus[\\s]+(7|9|10)",
2526        "GoogleTablet": "Android.*Pixel C",
2527        "SamsungTablet": "SAMSUNG.*Tablet|Galaxy.*Tab|SC-01C|GT-P1000|GT-P1003|GT-P1010|GT-P3105|GT-P6210|GT-P6800|GT-P6810|GT-P7100|GT-P7300|GT-P7310|GT-P7500|GT-P7510|SCH-I800|SCH-I815|SCH-I905|SGH-I957|SGH-I987|SGH-T849|SGH-T859|SGH-T869|SPH-P100|GT-P3100|GT-P3108|GT-P3110|GT-P5100|GT-P5110|GT-P6200|GT-P7320|GT-P7511|GT-N8000|GT-P8510|SGH-I497|SPH-P500|SGH-T779|SCH-I705|SCH-I915|GT-N8013|GT-P3113|GT-P5113|GT-P8110|GT-N8010|GT-N8005|GT-N8020|GT-P1013|GT-P6201|GT-P7501|GT-N5100|GT-N5105|GT-N5110|SHV-E140K|SHV-E140L|SHV-E140S|SHV-E150S|SHV-E230K|SHV-E230L|SHV-E230S|SHW-M180K|SHW-M180L|SHW-M180S|SHW-M180W|SHW-M300W|SHW-M305W|SHW-M380K|SHW-M380S|SHW-M380W|SHW-M430W|SHW-M480K|SHW-M480S|SHW-M480W|SHW-M485W|SHW-M486W|SHW-M500W|GT-I9228|SCH-P739|SCH-I925|GT-I9200|GT-P5200|GT-P5210|GT-P5210X|SM-T311|SM-T310|SM-T310X|SM-T210|SM-T210R|SM-T211|SM-P600|SM-P601|SM-P605|SM-P900|SM-P901|SM-T217|SM-T217A|SM-T217S|SM-P6000|SM-T3100|SGH-I467|XE500|SM-T110|GT-P5220|GT-I9200X|GT-N5110X|GT-N5120|SM-P905|SM-T111|SM-T2105|SM-T315|SM-T320|SM-T320X|SM-T321|SM-T520|SM-T525|SM-T530NU|SM-T230NU|SM-T330NU|SM-T900|XE500T1C|SM-P605V|SM-P905V|SM-T337V|SM-T537V|SM-T707V|SM-T807V|SM-P600X|SM-P900X|SM-T210X|SM-T230|SM-T230X|SM-T325|GT-P7503|SM-T531|SM-T330|SM-T530|SM-T705|SM-T705C|SM-T535|SM-T331|SM-T800|SM-T700|SM-T537|SM-T807|SM-P907A|SM-T337A|SM-T537A|SM-T707A|SM-T807A|SM-T237|SM-T807P|SM-P607T|SM-T217T|SM-T337T|SM-T807T|SM-T116NQ|SM-T116BU|SM-P550|SM-T350|SM-T550|SM-T9000|SM-P9000|SM-T705Y|SM-T805|GT-P3113|SM-T710|SM-T810|SM-T815|SM-T360|SM-T533|SM-T113|SM-T335|SM-T715|SM-T560|SM-T670|SM-T677|SM-T377|SM-T567|SM-T357T|SM-T555|SM-T561|SM-T713|SM-T719|SM-T813|SM-T819|SM-T580|SM-T355Y?|SM-T280|SM-T817A|SM-T820|SM-W700|SM-P580|SM-T587|SM-P350|SM-P555M|SM-P355M|SM-T113NU|SM-T815Y|SM-T585|SM-T285|SM-T825|SM-W708|SM-T835|SM-T830|SM-T837V|SM-T720|SM-T510|SM-T387V|SM-P610|SM-T290|SM-T515|SM-T590|SM-T595|SM-T725|SM-T817P|SM-P585N0|SM-T395|SM-T295|SM-T865|SM-P610N|SM-P615|SM-T970|SM-T380|SM-T5950|SM-T905|SM-T231|SM-T500|SM-T860",
2528        "Kindle": "Kindle|Silk.*Accelerated|Android.*\\b(KFOT|KFTT|KFJWI|KFJWA|KFOTE|KFSOWI|KFTHWI|KFTHWA|KFAPWI|KFAPWA|WFJWAE|KFSAWA|KFSAWI|KFASWI|KFARWI|KFFOWI|KFGIWI|KFMEWI)\\b|Android.*Silk\/[0-9.]+ like Chrome\/[0-9.]+ (?!Mobile)",
2529        "SurfaceTablet": "Windows NT [0-9.]+; ARM;.*(Tablet|ARMBJS)",
2530        "HPTablet": "HP Slate (7|8|10)|HP ElitePad 900|hp-tablet|EliteBook.*Touch|HP 8|Slate 21|HP SlateBook 10",
2531        "AsusTablet": "^.*PadFone((?!Mobile).)*$|Transformer|TF101|TF101G|TF300T|TF300TG|TF300TL|TF700T|TF700KL|TF701T|TF810C|ME171|ME301T|ME302C|ME371MG|ME370T|ME372MG|ME172V|ME173X|ME400C|Slider SL101|\\bK00F\\b|\\bK00C\\b|\\bK00E\\b|\\bK00L\\b|TX201LA|ME176C|ME102A|\\bM80TA\\b|ME372CL|ME560CG|ME372CG|ME302KL| K010 | K011 | K017 | K01E |ME572C|ME103K|ME170C|ME171C|\\bME70C\\b|ME581C|ME581CL|ME8510C|ME181C|P01Y|PO1MA|P01Z|\\bP027\\b|\\bP024\\b|\\bP00C\\b",
2532        "BlackBerryTablet": "PlayBook|RIM Tablet",
2533        "HTCtablet": "HTC_Flyer_P512|HTC Flyer|HTC Jetstream|HTC-P715a|HTC EVO View 4G|PG41200|PG09410",
2534        "MotorolaTablet": "xoom|sholest|MZ615|MZ605|MZ505|MZ601|MZ602|MZ603|MZ604|MZ606|MZ607|MZ608|MZ609|MZ615|MZ616|MZ617",
2535        "NookTablet": "Android.*Nook|NookColor|nook browser|BNRV200|BNRV200A|BNTV250|BNTV250A|BNTV400|BNTV600|LogicPD Zoom2",
2536        "AcerTablet": "Android.*; \\b(A100|A101|A110|A200|A210|A211|A500|A501|A510|A511|A700|A701|W500|W500P|W501|W501P|W510|W511|W700|G100|G100W|B1-A71|B1-710|B1-711|A1-810|A1-811|A1-830)\\b|W3-810|\\bA3-A10\\b|\\bA3-A11\\b|\\bA3-A20\\b|\\bA3-A30|A3-A40",
2537        "ToshibaTablet": "Android.*(AT100|AT105|AT200|AT205|AT270|AT275|AT300|AT305|AT1S5|AT500|AT570|AT700|AT830)|TOSHIBA.*FOLIO",
2538        "LGTablet": "\\bL-06C|LG-V909|LG-V900|LG-V700|LG-V510|LG-V500|LG-V410|LG-V400|LG-VK810\\b",
2539        "FujitsuTablet": "Android.*\\b(F-01D|F-02F|F-05E|F-10D|M532|Q572)\\b",
2540        "PrestigioTablet": "PMP3170B|PMP3270B|PMP3470B|PMP7170B|PMP3370B|PMP3570C|PMP5870C|PMP3670B|PMP5570C|PMP5770D|PMP3970B|PMP3870C|PMP5580C|PMP5880D|PMP5780D|PMP5588C|PMP7280C|PMP7280C3G|PMP7280|PMP7880D|PMP5597D|PMP5597|PMP7100D|PER3464|PER3274|PER3574|PER3884|PER5274|PER5474|PMP5097CPRO|PMP5097|PMP7380D|PMP5297C|PMP5297C_QUAD|PMP812E|PMP812E3G|PMP812F|PMP810E|PMP880TD|PMT3017|PMT3037|PMT3047|PMT3057|PMT7008|PMT5887|PMT5001|PMT5002",
2541        "LenovoTablet": "Lenovo TAB|Idea(Tab|Pad)( A1|A10| K1|)|ThinkPad([ ]+)?Tablet|YT3-850M|YT3-X90L|YT3-X90F|YT3-X90X|Lenovo.*(S2109|S2110|S5000|S6000|K3011|A3000|A3500|A1000|A2107|A2109|A1107|A5500|A7600|B6000|B8000|B8080)(-|)(FL|F|HV|H|)|TB-X103F|TB-X304X|TB-X304F|TB-X304L|TB-X505F|TB-X505L|TB-X505X|TB-X605F|TB-X605L|TB-8703F|TB-8703X|TB-8703N|TB-8704N|TB-8704F|TB-8704X|TB-8704V|TB-7304F|TB-7304I|TB-7304X|Tab2A7-10F|Tab2A7-20F|TB2-X30L|YT3-X50L|YT3-X50F|YT3-X50M|YT-X705F|YT-X703F|YT-X703L|YT-X705L|YT-X705X|TB2-X30F|TB2-X30L|TB2-X30M|A2107A-F|A2107A-H|TB3-730F|TB3-730M|TB3-730X|TB-7504F|TB-7504X|TB-X704F|TB-X104F|TB3-X70F|TB-X705F|TB-8504F|TB3-X70L|TB3-710F|TB-X704L",
2542        "DellTablet": "Venue 11|Venue 8|Venue 7|Dell Streak 10|Dell Streak 7",
2543        "YarvikTablet": "Android.*\\b(TAB210|TAB211|TAB224|TAB250|TAB260|TAB264|TAB310|TAB360|TAB364|TAB410|TAB411|TAB420|TAB424|TAB450|TAB460|TAB461|TAB464|TAB465|TAB467|TAB468|TAB07-100|TAB07-101|TAB07-150|TAB07-151|TAB07-152|TAB07-200|TAB07-201-3G|TAB07-210|TAB07-211|TAB07-212|TAB07-214|TAB07-220|TAB07-400|TAB07-485|TAB08-150|TAB08-200|TAB08-201-3G|TAB08-201-30|TAB09-100|TAB09-211|TAB09-410|TAB10-150|TAB10-201|TAB10-211|TAB10-400|TAB10-410|TAB13-201|TAB274EUK|TAB275EUK|TAB374EUK|TAB462EUK|TAB474EUK|TAB9-200)\\b",
2544        "MedionTablet": "Android.*\\bOYO\\b|LIFE.*(P9212|P9514|P9516|S9512)|LIFETAB",
2545        "ArnovaTablet": "97G4|AN10G2|AN7bG3|AN7fG3|AN8G3|AN8cG3|AN7G3|AN9G3|AN7dG3|AN7dG3ST|AN7dG3ChildPad|AN10bG3|AN10bG3DT|AN9G2",
2546        "IntensoTablet": "INM8002KP|INM1010FP|INM805ND|Intenso Tab|TAB1004",
2547        "IRUTablet": "M702pro",
2548        "MegafonTablet": "MegaFon V9|\\bZTE V9\\b|Android.*\\bMT7A\\b",
2549        "EbodaTablet": "E-Boda (Supreme|Impresspeed|Izzycomm|Essential)",
2550        "AllViewTablet": "Allview.*(Viva|Alldro|City|Speed|All TV|Frenzy|Quasar|Shine|TX1|AX1|AX2)",
2551        "ArchosTablet": "\\b(101G9|80G9|A101IT)\\b|Qilive 97R|Archos5|\\bARCHOS (70|79|80|90|97|101|FAMILYPAD|)(b|c|)(G10| Cobalt| TITANIUM(HD|)| Xenon| Neon|XSK| 2| XS 2| PLATINUM| CARBON|GAMEPAD)\\b",
2552        "AinolTablet": "NOVO7|NOVO8|NOVO10|Novo7Aurora|Novo7Basic|NOVO7PALADIN|novo9-Spark",
2553        "NokiaLumiaTablet": "Lumia 2520",
2554        "SonyTablet": "Sony.*Tablet|Xperia Tablet|Sony Tablet S|SO-03E|SGPT12|SGPT13|SGPT114|SGPT121|SGPT122|SGPT123|SGPT111|SGPT112|SGPT113|SGPT131|SGPT132|SGPT133|SGPT211|SGPT212|SGPT213|SGP311|SGP312|SGP321|EBRD1101|EBRD1102|EBRD1201|SGP351|SGP341|SGP511|SGP512|SGP521|SGP541|SGP551|SGP621|SGP641|SGP612|SOT31|SGP771|SGP611|SGP612|SGP712",
2555        "PhilipsTablet": "\\b(PI2010|PI3000|PI3100|PI3105|PI3110|PI3205|PI3210|PI3900|PI4010|PI7000|PI7100)\\b",
2556        "CubeTablet": "Android.*(K8GT|U9GT|U10GT|U16GT|U17GT|U18GT|U19GT|U20GT|U23GT|U30GT)|CUBE U8GT",
2557        "CobyTablet": "MID1042|MID1045|MID1125|MID1126|MID7012|MID7014|MID7015|MID7034|MID7035|MID7036|MID7042|MID7048|MID7127|MID8042|MID8048|MID8127|MID9042|MID9740|MID9742|MID7022|MID7010",
2558        "MIDTablet": "M9701|M9000|M9100|M806|M1052|M806|T703|MID701|MID713|MID710|MID727|MID760|MID830|MID728|MID933|MID125|MID810|MID732|MID120|MID930|MID800|MID731|MID900|MID100|MID820|MID735|MID980|MID130|MID833|MID737|MID960|MID135|MID860|MID736|MID140|MID930|MID835|MID733|MID4X10",
2559        "MSITablet": "MSI \\b(Primo 73K|Primo 73L|Primo 81L|Primo 77|Primo 93|Primo 75|Primo 76|Primo 73|Primo 81|Primo 91|Primo 90|Enjoy 71|Enjoy 7|Enjoy 10)\\b",
2560        "SMiTTablet": "Android.*(\\bMID\\b|MID-560|MTV-T1200|MTV-PND531|MTV-P1101|MTV-PND530)",
2561        "RockChipTablet": "Android.*(RK2818|RK2808A|RK2918|RK3066)|RK2738|RK2808A",
2562        "FlyTablet": "IQ310|Fly Vision",
2563        "bqTablet": "Android.*(bq)?.*\\b(Elcano|Curie|Edison|Maxwell|Kepler|Pascal|Tesla|Hypatia|Platon|Newton|Livingstone|Cervantes|Avant|Aquaris ([E|M]10|M8))\\b|Maxwell.*Lite|Maxwell.*Plus",
2564        "HuaweiTablet": "MediaPad|MediaPad 7 Youth|IDEOS S7|S7-201c|S7-202u|S7-101|S7-103|S7-104|S7-105|S7-106|S7-201|S7-Slim|M2-A01L|BAH-L09|BAH-W09|AGS-L09|CMR-AL19",
2565        "NecTablet": "\\bN-06D|\\bN-08D",
2566        "PantechTablet": "Pantech.*P4100",
2567        "BronchoTablet": "Broncho.*(N701|N708|N802|a710)",
2568        "VersusTablet": "TOUCHPAD.*[78910]|\\bTOUCHTAB\\b",
2569        "ZyncTablet": "z1000|Z99 2G|z930|z990|z909|Z919|z900",
2570        "PositivoTablet": "TB07STA|TB10STA|TB07FTA|TB10FTA",
2571        "NabiTablet": "Android.*\\bNabi",
2572        "KoboTablet": "Kobo Touch|\\bK080\\b|\\bVox\\b Build|\\bArc\\b Build",
2573        "DanewTablet": "DSlide.*\\b(700|701R|702|703R|704|802|970|971|972|973|974|1010|1012)\\b",
2574        "TexetTablet": "NaviPad|TB-772A|TM-7045|TM-7055|TM-9750|TM-7016|TM-7024|TM-7026|TM-7041|TM-7043|TM-7047|TM-8041|TM-9741|TM-9747|TM-9748|TM-9751|TM-7022|TM-7021|TM-7020|TM-7011|TM-7010|TM-7023|TM-7025|TM-7037W|TM-7038W|TM-7027W|TM-9720|TM-9725|TM-9737W|TM-1020|TM-9738W|TM-9740|TM-9743W|TB-807A|TB-771A|TB-727A|TB-725A|TB-719A|TB-823A|TB-805A|TB-723A|TB-715A|TB-707A|TB-705A|TB-709A|TB-711A|TB-890HD|TB-880HD|TB-790HD|TB-780HD|TB-770HD|TB-721HD|TB-710HD|TB-434HD|TB-860HD|TB-840HD|TB-760HD|TB-750HD|TB-740HD|TB-730HD|TB-722HD|TB-720HD|TB-700HD|TB-500HD|TB-470HD|TB-431HD|TB-430HD|TB-506|TB-504|TB-446|TB-436|TB-416|TB-146SE|TB-126SE",
2575        "PlaystationTablet": "Playstation.*(Portable|Vita)",
2576        "TrekstorTablet": "ST10416-1|VT10416-1|ST70408-1|ST702xx-1|ST702xx-2|ST80208|ST97216|ST70104-2|VT10416-2|ST10216-2A|SurfTab",
2577        "PyleAudioTablet": "\\b(PTBL10CEU|PTBL10C|PTBL72BC|PTBL72BCEU|PTBL7CEU|PTBL7C|PTBL92BC|PTBL92BCEU|PTBL9CEU|PTBL9CUK|PTBL9C)\\b",
2578        "AdvanTablet": "Android.* \\b(E3A|T3X|T5C|T5B|T3E|T3C|T3B|T1J|T1F|T2A|T1H|T1i|E1C|T1-E|T5-A|T4|E1-B|T2Ci|T1-B|T1-D|O1-A|E1-A|T1-A|T3A|T4i)\\b ",
2579        "DanyTechTablet": "Genius Tab G3|Genius Tab S2|Genius Tab Q3|Genius Tab G4|Genius Tab Q4|Genius Tab G-II|Genius TAB GII|Genius TAB GIII|Genius Tab S1",
2580        "GalapadTablet": "Android [0-9.]+; [a-z-]+; \\bG1\\b",
2581        "MicromaxTablet": "Funbook|Micromax.*\\b(P250|P560|P360|P362|P600|P300|P350|P500|P275)\\b",
2582        "KarbonnTablet": "Android.*\\b(A39|A37|A34|ST8|ST10|ST7|Smart Tab3|Smart Tab2)\\b",
2583        "AllFineTablet": "Fine7 Genius|Fine7 Shine|Fine7 Air|Fine8 Style|Fine9 More|Fine10 Joy|Fine11 Wide",
2584        "PROSCANTablet": "\\b(PEM63|PLT1023G|PLT1041|PLT1044|PLT1044G|PLT1091|PLT4311|PLT4311PL|PLT4315|PLT7030|PLT7033|PLT7033D|PLT7035|PLT7035D|PLT7044K|PLT7045K|PLT7045KB|PLT7071KG|PLT7072|PLT7223G|PLT7225G|PLT7777G|PLT7810K|PLT7849G|PLT7851G|PLT7852G|PLT8015|PLT8031|PLT8034|PLT8036|PLT8080K|PLT8082|PLT8088|PLT8223G|PLT8234G|PLT8235G|PLT8816K|PLT9011|PLT9045K|PLT9233G|PLT9735|PLT9760G|PLT9770G)\\b",
2585        "YONESTablet": "BQ1078|BC1003|BC1077|RK9702|BC9730|BC9001|IT9001|BC7008|BC7010|BC708|BC728|BC7012|BC7030|BC7027|BC7026",
2586        "ChangJiaTablet": "TPC7102|TPC7103|TPC7105|TPC7106|TPC7107|TPC7201|TPC7203|TPC7205|TPC7210|TPC7708|TPC7709|TPC7712|TPC7110|TPC8101|TPC8103|TPC8105|TPC8106|TPC8203|TPC8205|TPC8503|TPC9106|TPC9701|TPC97101|TPC97103|TPC97105|TPC97106|TPC97111|TPC97113|TPC97203|TPC97603|TPC97809|TPC97205|TPC10101|TPC10103|TPC10106|TPC10111|TPC10203|TPC10205|TPC10503",
2587        "GUTablet": "TX-A1301|TX-M9002|Q702|kf026",
2588        "PointOfViewTablet": "TAB-P506|TAB-navi-7-3G-M|TAB-P517|TAB-P-527|TAB-P701|TAB-P703|TAB-P721|TAB-P731N|TAB-P741|TAB-P825|TAB-P905|TAB-P925|TAB-PR945|TAB-PL1015|TAB-P1025|TAB-PI1045|TAB-P1325|TAB-PROTAB[0-9]+|TAB-PROTAB25|TAB-PROTAB26|TAB-PROTAB27|TAB-PROTAB26XL|TAB-PROTAB2-IPS9|TAB-PROTAB30-IPS9|TAB-PROTAB25XXL|TAB-PROTAB26-IPS10|TAB-PROTAB30-IPS10",
2589        "OvermaxTablet": "OV-(SteelCore|NewBase|Basecore|Baseone|Exellen|Quattor|EduTab|Solution|ACTION|BasicTab|TeddyTab|MagicTab|Stream|TB-08|TB-09)|Qualcore 1027",
2590        "HCLTablet": "HCL.*Tablet|Connect-3G-2.0|Connect-2G-2.0|ME Tablet U1|ME Tablet U2|ME Tablet G1|ME Tablet X1|ME Tablet Y2|ME Tablet Sync",
2591        "DPSTablet": "DPS Dream 9|DPS Dual 7",
2592        "VistureTablet": "V97 HD|i75 3G|Visture V4( HD)?|Visture V5( HD)?|Visture V10",
2593        "CrestaTablet": "CTP(-)?810|CTP(-)?818|CTP(-)?828|CTP(-)?838|CTP(-)?888|CTP(-)?978|CTP(-)?980|CTP(-)?987|CTP(-)?988|CTP(-)?989",
2594        "MediatekTablet": "\\bMT8125|MT8389|MT8135|MT8377\\b",
2595        "ConcordeTablet": "Concorde([ ]+)?Tab|ConCorde ReadMan",
2596        "GoCleverTablet": "GOCLEVER TAB|A7GOCLEVER|M1042|M7841|M742|R1042BK|R1041|TAB A975|TAB A7842|TAB A741|TAB A741L|TAB M723G|TAB M721|TAB A1021|TAB I921|TAB R721|TAB I720|TAB T76|TAB R70|TAB R76.2|TAB R106|TAB R83.2|TAB M813G|TAB I721|GCTA722|TAB I70|TAB I71|TAB S73|TAB R73|TAB R74|TAB R93|TAB R75|TAB R76.1|TAB A73|TAB A93|TAB A93.2|TAB T72|TAB R83|TAB R974|TAB R973|TAB A101|TAB A103|TAB A104|TAB A104.2|R105BK|M713G|A972BK|TAB A971|TAB R974.2|TAB R104|TAB R83.3|TAB A1042",
2597        "ModecomTablet": "FreeTAB 9000|FreeTAB 7.4|FreeTAB 7004|FreeTAB 7800|FreeTAB 2096|FreeTAB 7.5|FreeTAB 1014|FreeTAB 1001 |FreeTAB 8001|FreeTAB 9706|FreeTAB 9702|FreeTAB 7003|FreeTAB 7002|FreeTAB 1002|FreeTAB 7801|FreeTAB 1331|FreeTAB 1004|FreeTAB 8002|FreeTAB 8014|FreeTAB 9704|FreeTAB 1003",
2598        "VoninoTablet": "\\b(Argus[ _]?S|Diamond[ _]?79HD|Emerald[ _]?78E|Luna[ _]?70C|Onyx[ _]?S|Onyx[ _]?Z|Orin[ _]?HD|Orin[ _]?S|Otis[ _]?S|SpeedStar[ _]?S|Magnet[ _]?M9|Primus[ _]?94[ _]?3G|Primus[ _]?94HD|Primus[ _]?QS|Android.*\\bQ8\\b|Sirius[ _]?EVO[ _]?QS|Sirius[ _]?QS|Spirit[ _]?S)\\b",
2599        "ECSTablet": "V07OT2|TM105A|S10OT1|TR10CS1",
2600        "StorexTablet": "eZee[_']?(Tab|Go)[0-9]+|TabLC7|Looney Tunes Tab",
2601        "VodafoneTablet": "SmartTab([ ]+)?[0-9]+|SmartTabII10|SmartTabII7|VF-1497|VFD 1400",
2602        "EssentielBTablet": "Smart[ ']?TAB[ ]+?[0-9]+|Family[ ']?TAB2",
2603        "RossMoorTablet": "RM-790|RM-997|RMD-878G|RMD-974R|RMT-705A|RMT-701|RME-601|RMT-501|RMT-711",
2604        "iMobileTablet": "i-mobile i-note",
2605        "TolinoTablet": "tolino tab [0-9.]+|tolino shine",
2606        "AudioSonicTablet": "\\bC-22Q|T7-QC|T-17B|T-17P\\b",
2607        "AMPETablet": "Android.* A78 ",
2608        "SkkTablet": "Android.* (SKYPAD|PHOENIX|CYCLOPS)",
2609        "TecnoTablet": "TECNO P9|TECNO DP8D",
2610        "JXDTablet": "Android.* \\b(F3000|A3300|JXD5000|JXD3000|JXD2000|JXD300B|JXD300|S5800|S7800|S602b|S5110b|S7300|S5300|S602|S603|S5100|S5110|S601|S7100a|P3000F|P3000s|P101|P200s|P1000m|P200m|P9100|P1000s|S6600b|S908|P1000|P300|S18|S6600|S9100)\\b",
2611        "iJoyTablet": "Tablet (Spirit 7|Essentia|Galatea|Fusion|Onix 7|Landa|Titan|Scooby|Deox|Stella|Themis|Argon|Unique 7|Sygnus|Hexen|Finity 7|Cream|Cream X2|Jade|Neon 7|Neron 7|Kandy|Scape|Saphyr 7|Rebel|Biox|Rebel|Rebel 8GB|Myst|Draco 7|Myst|Tab7-004|Myst|Tadeo Jones|Tablet Boing|Arrow|Draco Dual Cam|Aurix|Mint|Amity|Revolution|Finity 9|Neon 9|T9w|Amity 4GB Dual Cam|Stone 4GB|Stone 8GB|Andromeda|Silken|X2|Andromeda II|Halley|Flame|Saphyr 9,7|Touch 8|Planet|Triton|Unique 10|Hexen 10|Memphis 4GB|Memphis 8GB|Onix 10)",
2612        "FX2Tablet": "FX2 PAD7|FX2 PAD10",
2613        "XoroTablet": "KidsPAD 701|PAD[ ]?712|PAD[ ]?714|PAD[ ]?716|PAD[ ]?717|PAD[ ]?718|PAD[ ]?720|PAD[ ]?721|PAD[ ]?722|PAD[ ]?790|PAD[ ]?792|PAD[ ]?900|PAD[ ]?9715D|PAD[ ]?9716DR|PAD[ ]?9718DR|PAD[ ]?9719QR|PAD[ ]?9720QR|TelePAD1030|Telepad1032|TelePAD730|TelePAD731|TelePAD732|TelePAD735Q|TelePAD830|TelePAD9730|TelePAD795|MegaPAD 1331|MegaPAD 1851|MegaPAD 2151",
2614        "ViewsonicTablet": "ViewPad 10pi|ViewPad 10e|ViewPad 10s|ViewPad E72|ViewPad7|ViewPad E100|ViewPad 7e|ViewSonic VB733|VB100a",
2615        "VerizonTablet": "QTAQZ3|QTAIR7|QTAQTZ3|QTASUN1|QTASUN2|QTAXIA1",
2616        "OdysTablet": "LOOX|XENO10|ODYS[ -](Space|EVO|Xpress|NOON)|\\bXELIO\\b|Xelio10Pro|XELIO7PHONETAB|XELIO10EXTREME|XELIOPT2|NEO_QUAD10",
2617        "CaptivaTablet": "CAPTIVA PAD",
2618        "IconbitTablet": "NetTAB|NT-3702|NT-3702S|NT-3702S|NT-3603P|NT-3603P|NT-0704S|NT-0704S|NT-3805C|NT-3805C|NT-0806C|NT-0806C|NT-0909T|NT-0909T|NT-0907S|NT-0907S|NT-0902S|NT-0902S",
2619        "TeclastTablet": "T98 4G|\\bP80\\b|\\bX90HD\\b|X98 Air|X98 Air 3G|\\bX89\\b|P80 3G|\\bX80h\\b|P98 Air|\\bX89HD\\b|P98 3G|\\bP90HD\\b|P89 3G|X98 3G|\\bP70h\\b|P79HD 3G|G18d 3G|\\bP79HD\\b|\\bP89s\\b|\\bA88\\b|\\bP10HD\\b|\\bP19HD\\b|G18 3G|\\bP78HD\\b|\\bA78\\b|\\bP75\\b|G17s 3G|G17h 3G|\\bP85t\\b|\\bP90\\b|\\bP11\\b|\\bP98t\\b|\\bP98HD\\b|\\bG18d\\b|\\bP85s\\b|\\bP11HD\\b|\\bP88s\\b|\\bA80HD\\b|\\bA80se\\b|\\bA10h\\b|\\bP89\\b|\\bP78s\\b|\\bG18\\b|\\bP85\\b|\\bA70h\\b|\\bA70\\b|\\bG17\\b|\\bP18\\b|\\bA80s\\b|\\bA11s\\b|\\bP88HD\\b|\\bA80h\\b|\\bP76s\\b|\\bP76h\\b|\\bP98\\b|\\bA10HD\\b|\\bP78\\b|\\bP88\\b|\\bA11\\b|\\bA10t\\b|\\bP76a\\b|\\bP76t\\b|\\bP76e\\b|\\bP85HD\\b|\\bP85a\\b|\\bP86\\b|\\bP75HD\\b|\\bP76v\\b|\\bA12\\b|\\bP75a\\b|\\bA15\\b|\\bP76Ti\\b|\\bP81HD\\b|\\bA10\\b|\\bT760VE\\b|\\bT720HD\\b|\\bP76\\b|\\bP73\\b|\\bP71\\b|\\bP72\\b|\\bT720SE\\b|\\bC520Ti\\b|\\bT760\\b|\\bT720VE\\b|T720-3GE|T720-WiFi",
2620        "OndaTablet": "\\b(V975i|Vi30|VX530|V701|Vi60|V701s|Vi50|V801s|V719|Vx610w|VX610W|V819i|Vi10|VX580W|Vi10|V711s|V813|V811|V820w|V820|Vi20|V711|VI30W|V712|V891w|V972|V819w|V820w|Vi60|V820w|V711|V813s|V801|V819|V975s|V801|V819|V819|V818|V811|V712|V975m|V101w|V961w|V812|V818|V971|V971s|V919|V989|V116w|V102w|V973|Vi40)\\b[\\s]+|V10 \\b4G\\b",
2621        "JaytechTablet": "TPC-PA762",
2622        "BlaupunktTablet": "Endeavour 800NG|Endeavour 1010",
2623        "DigmaTablet": "\\b(iDx10|iDx9|iDx8|iDx7|iDxD7|iDxD8|iDsQ8|iDsQ7|iDsQ8|iDsD10|iDnD7|3TS804H|iDsQ11|iDj7|iDs10)\\b",
2624        "EvolioTablet": "ARIA_Mini_wifi|Aria[ _]Mini|Evolio X10|Evolio X7|Evolio X8|\\bEvotab\\b|\\bNeura\\b",
2625        "LavaTablet": "QPAD E704|\\bIvoryS\\b|E-TAB IVORY|\\bE-TAB\\b",
2626        "AocTablet": "MW0811|MW0812|MW0922|MTK8382|MW1031|MW0831|MW0821|MW0931|MW0712",
2627        "MpmanTablet": "MP11 OCTA|MP10 OCTA|MPQC1114|MPQC1004|MPQC994|MPQC974|MPQC973|MPQC804|MPQC784|MPQC780|\\bMPG7\\b|MPDCG75|MPDCG71|MPDC1006|MP101DC|MPDC9000|MPDC905|MPDC706HD|MPDC706|MPDC705|MPDC110|MPDC100|MPDC99|MPDC97|MPDC88|MPDC8|MPDC77|MP709|MID701|MID711|MID170|MPDC703|MPQC1010",
2628        "CelkonTablet": "CT695|CT888|CT[\\s]?910|CT7 Tab|CT9 Tab|CT3 Tab|CT2 Tab|CT1 Tab|C820|C720|\\bCT-1\\b",
2629        "WolderTablet": "miTab \\b(DIAMOND|SPACE|BROOKLYN|NEO|FLY|MANHATTAN|FUNK|EVOLUTION|SKY|GOCAR|IRON|GENIUS|POP|MINT|EPSILON|BROADWAY|JUMP|HOP|LEGEND|NEW AGE|LINE|ADVANCE|FEEL|FOLLOW|LIKE|LINK|LIVE|THINK|FREEDOM|CHICAGO|CLEVELAND|BALTIMORE-GH|IOWA|BOSTON|SEATTLE|PHOENIX|DALLAS|IN 101|MasterChef)\\b",
2630        "MediacomTablet": "M-MPI10C3G|M-SP10EG|M-SP10EGP|M-SP10HXAH|M-SP7HXAH|M-SP10HXBH|M-SP8HXAH|M-SP8MXA",
2631        "MiTablet": "\\bMI PAD\\b|\\bHM NOTE 1W\\b",
2632        "NibiruTablet": "Nibiru M1|Nibiru Jupiter One",
2633        "NexoTablet": "NEXO NOVA|NEXO 10|NEXO AVIO|NEXO FREE|NEXO GO|NEXO EVO|NEXO 3G|NEXO SMART|NEXO KIDDO|NEXO MOBI",
2634        "LeaderTablet": "TBLT10Q|TBLT10I|TBL-10WDKB|TBL-10WDKBO2013|TBL-W230V2|TBL-W450|TBL-W500|SV572|TBLT7I|TBA-AC7-8G|TBLT79|TBL-8W16|TBL-10W32|TBL-10WKB|TBL-W100",
2635        "UbislateTablet": "UbiSlate[\\s]?7C",
2636        "PocketBookTablet": "Pocketbook",
2637        "KocasoTablet": "\\b(TB-1207)\\b",
2638        "HisenseTablet": "\\b(F5281|E2371)\\b",
2639        "Hudl": "Hudl HT7S3|Hudl 2",
2640        "TelstraTablet": "T-Hub2",
2641        "GenericTablet": "Android.*\\b97D\\b|Tablet(?!.*PC)|BNTV250A|MID-WCDMA|LogicPD Zoom2|\\bA7EB\\b|CatNova8|A1_07|CT704|CT1002|\\bM721\\b|rk30sdk|\\bEVOTAB\\b|M758A|ET904|ALUMIUM10|Smartfren Tab|Endeavour 1010|Tablet-PC-4|Tagi Tab|\\bM6pro\\b|CT1020W|arc 10HD|\\bTP750\\b|\\bQTAQZ3\\b|WVT101|TM1088|KT107"
2642    },
2643    "oss": {
2644        "AndroidOS": "Android",
2645        "BlackBerryOS": "blackberry|\\bBB10\\b|rim tablet os",
2646        "PalmOS": "PalmOS|avantgo|blazer|elaine|hiptop|palm|plucker|xiino",
2647        "SymbianOS": "Symbian|SymbOS|Series60|Series40|SYB-[0-9]+|\\bS60\\b",
2648        "WindowsMobileOS": "Windows CE.*(PPC|Smartphone|Mobile|[0-9]{3}x[0-9]{3})|Windows Mobile|Windows Phone [0-9.]+|WCE;",
2649        "WindowsPhoneOS": "Windows Phone 10.0|Windows Phone 8.1|Windows Phone 8.0|Windows Phone OS|XBLWP7|ZuneWP7|Windows NT 6.[23]; ARM;",
2650        "iOS": "\\biPhone.*Mobile|\\biPod|\\biPad|AppleCoreMedia",
2651        "iPadOS": "CPU OS 13",
2652        "SailfishOS": "Sailfish",
2653        "MeeGoOS": "MeeGo",
2654        "MaemoOS": "Maemo",
2655        "JavaOS": "J2ME\/|\\bMIDP\\b|\\bCLDC\\b",
2656        "webOS": "webOS|hpwOS",
2657        "badaOS": "\\bBada\\b",
2658        "BREWOS": "BREW"
2659    },
2660    "uas": {
2661        "Chrome": "\\bCrMo\\b|CriOS|Android.*Chrome\/[.0-9]* (Mobile)?",
2662        "Dolfin": "\\bDolfin\\b",
2663        "Opera": "Opera.*Mini|Opera.*Mobi|Android.*Opera|Mobile.*OPR\/[0-9.]+$|Coast\/[0-9.]+",
2664        "Skyfire": "Skyfire",
2665        "Edge": "\\bEdgiOS\\b|Mobile Safari\/[.0-9]* Edge",
2666        "IE": "IEMobile|MSIEMobile",
2667        "Firefox": "fennec|firefox.*maemo|(Mobile|Tablet).*Firefox|Firefox.*Mobile|FxiOS",
2668        "Bolt": "bolt",
2669        "TeaShark": "teashark",
2670        "Blazer": "Blazer",
2671        "Safari": "Version((?!\\bEdgiOS\\b).)*Mobile.*Safari|Safari.*Mobile|MobileSafari",
2672        "WeChat": "\\bMicroMessenger\\b",
2673        "UCBrowser": "UC.*Browser|UCWEB",
2674        "baiduboxapp": "baiduboxapp",
2675        "baidubrowser": "baidubrowser",
2676        "DiigoBrowser": "DiigoBrowser",
2677        "Mercury": "\\bMercury\\b",
2678        "ObigoBrowser": "Obigo",
2679        "NetFront": "NF-Browser",
2680        "GenericBrowser": "NokiaBrowser|OviBrowser|OneBrowser|TwonkyBeamBrowser|SEMC.*Browser|FlyFlow|Minimo|NetFront|Novarra-Vision|MQQBrowser|MicroMessenger",
2681        "PaleMoon": "Android.*PaleMoon|Mobile.*PaleMoon"
2682    },
2683    "props": {
2684        "Mobile": "Mobile\/[VER]",
2685        "Build": "Build\/[VER]",
2686        "Version": "Version\/[VER]",
2687        "VendorID": "VendorID\/[VER]",
2688        "iPad": "iPad.*CPU[a-z ]+[VER]",
2689        "iPhone": "iPhone.*CPU[a-z ]+[VER]",
2690        "iPod": "iPod.*CPU[a-z ]+[VER]",
2691        "Kindle": "Kindle\/[VER]",
2692        "Chrome": [
2693            "Chrome\/[VER]",
2694            "CriOS\/[VER]",
2695            "CrMo\/[VER]"
2696        ],
2697        "Coast": [
2698            "Coast\/[VER]"
2699        ],
2700        "Dolfin": "Dolfin\/[VER]",
2701        "Firefox": [
2702            "Firefox\/[VER]",
2703            "FxiOS\/[VER]"
2704        ],
2705        "Fennec": "Fennec\/[VER]",
2706        "Edge": "Edge\/[VER]",
2707        "IE": [
2708            "IEMobile\/[VER];",
2709            "IEMobile [VER]",
2710            "MSIE [VER];",
2711            "Trident\/[0-9.]+;.*rv:[VER]"
2712        ],
2713        "NetFront": "NetFront\/[VER]",
2714        "NokiaBrowser": "NokiaBrowser\/[VER]",
2715        "Opera": [
2716            " OPR\/[VER]",
2717            "Opera Mini\/[VER]",
2718            "Version\/[VER]"
2719        ],
2720        "Opera Mini": "Opera Mini\/[VER]",
2721        "Opera Mobi": "Version\/[VER]",
2722        "UCBrowser": [
2723            "UCWEB[VER]",
2724            "UC.*Browser\/[VER]"
2725        ],
2726        "MQQBrowser": "MQQBrowser\/[VER]",
2727        "MicroMessenger": "MicroMessenger\/[VER]",
2728        "baiduboxapp": "baiduboxapp\/[VER]",
2729        "baidubrowser": "baidubrowser\/[VER]",
2730        "SamsungBrowser": "SamsungBrowser\/[VER]",
2731        "Iron": "Iron\/[VER]",
2732        "Safari": [
2733            "Version\/[VER]",
2734            "Safari\/[VER]"
2735        ],
2736        "Skyfire": "Skyfire\/[VER]",
2737        "Tizen": "Tizen\/[VER]",
2738        "Webkit": "webkit[ \/][VER]",
2739        "PaleMoon": "PaleMoon\/[VER]",
2740        "SailfishBrowser": "SailfishBrowser\/[VER]",
2741        "Gecko": "Gecko\/[VER]",
2742        "Trident": "Trident\/[VER]",
2743        "Presto": "Presto\/[VER]",
2744        "Goanna": "Goanna\/[VER]",
2745        "iOS": " \\bi?OS\\b [VER][ ;]{1}",
2746        "Android": "Android [VER]",
2747        "Sailfish": "Sailfish [VER]",
2748        "BlackBerry": [
2749            "BlackBerry[\\w]+\/[VER]",
2750            "BlackBerry.*Version\/[VER]",
2751            "Version\/[VER]"
2752        ],
2753        "BREW": "BREW [VER]",
2754        "Java": "Java\/[VER]",
2755        "Windows Phone OS": [
2756            "Windows Phone OS [VER]",
2757            "Windows Phone [VER]"
2758        ],
2759        "Windows Phone": "Windows Phone [VER]",
2760        "Windows CE": "Windows CE\/[VER]",
2761        "Windows NT": "Windows NT [VER]",
2762        "Symbian": [
2763            "SymbianOS\/[VER]",
2764            "Symbian\/[VER]"
2765        ],
2766        "webOS": [
2767            "webOS\/[VER]",
2768            "hpwOS\/[VER];"
2769        ]
2770    },
2771    "utils": {
2772        "Bot": "Googlebot|facebookexternalhit|Google-AMPHTML|s~amp-validator|AdsBot-Google|Google Keyword Suggestion|Facebot|YandexBot|YandexMobileBot|bingbot|ia_archiver|AhrefsBot|Ezooms|GSLFbot|WBSearchBot|Twitterbot|TweetmemeBot|Twikle|PaperLiBot|Wotbox|UnwindFetchor|Exabot|MJ12bot|YandexImages|TurnitinBot|Pingdom|contentkingapp|AspiegelBot",
2773        "MobileBot": "Googlebot-Mobile|AdsBot-Google-Mobile|YahooSeeker\/M1A1-R2D2",
2774        "DesktopMode": "WPDesktop",
2775        "TV": "SonyDTV|HbbTV",
2776        "WebKit": "(webkit)[ \/]([\\w.]+)",
2777        "Console": "\\b(Nintendo|Nintendo WiiU|Nintendo 3DS|Nintendo Switch|PLAYSTATION|Xbox)\\b",
2778        "Watch": "SM-V700"
2779    }
2780};
2781
2782    // following patterns come from http://detectmobilebrowsers.com/
2783    impl.detectMobileBrowsers = {
2784        fullPattern: /(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/i,
2785        shortPattern: /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 )|sy(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,
2786        tabletPattern: /android|ipad|playbook|silk/i
2787    };
2788
2789    var hasOwnProp = Object.prototype.hasOwnProperty,
2790        isArray;
2791
2792    impl.FALLBACK_PHONE = 'UnknownPhone';
2793    impl.FALLBACK_TABLET = 'UnknownTablet';
2794    impl.FALLBACK_MOBILE = 'UnknownMobile';
2795
2796    isArray = ('isArray' in Array) ?
2797        Array.isArray : function (value) { return Object.prototype.toString.call(value) === '[object Array]'; };
2798
2799    function equalIC(a, b) {
2800        return a != null && b != null && a.toLowerCase() === b.toLowerCase();
2801    }
2802
2803    function containsIC(array, value) {
2804        var valueLC, i, len = array.length;
2805        if (!len || !value) {
2806            return false;
2807        }
2808        valueLC = value.toLowerCase();
2809        for (i = 0; i < len; ++i) {
2810            if (valueLC === array[i].toLowerCase()) {
2811                return true;
2812            }
2813        }
2814        return false;
2815    }
2816
2817    function convertPropsToRegExp(object) {
2818        for (var key in object) {
2819            if (hasOwnProp.call(object, key)) {
2820                object[key] = new RegExp(object[key], 'i');
2821            }
2822        }
2823    }
2824
2825    function prepareUserAgent(userAgent) {
2826        return (userAgent || '').substr(0, 500); // mitigate vulnerable to ReDoS
2827    }
2828
2829    (function init() {
2830        var key, values, value, i, len, verPos, mobileDetectRules = impl.mobileDetectRules;
2831        for (key in mobileDetectRules.props) {
2832            if (hasOwnProp.call(mobileDetectRules.props, key)) {
2833                values = mobileDetectRules.props[key];
2834                if (!isArray(values)) {
2835                    values = [values];
2836                }
2837                len = values.length;
2838                for (i = 0; i < len; ++i) {
2839                    value = values[i];
2840                    verPos = value.indexOf('[VER]');
2841                    if (verPos >= 0) {
2842                        value = value.substring(0, verPos) + '([\\w._\\+]+)' + value.substring(verPos + 5);
2843                    }
2844                    values[i] = new RegExp(value, 'i');
2845                }
2846                mobileDetectRules.props[key] = values;
2847            }
2848        }
2849        convertPropsToRegExp(mobileDetectRules.oss);
2850        convertPropsToRegExp(mobileDetectRules.phones);
2851        convertPropsToRegExp(mobileDetectRules.tablets);
2852        convertPropsToRegExp(mobileDetectRules.uas);
2853        convertPropsToRegExp(mobileDetectRules.utils);
2854
2855        // copy some patterns to oss0 which are tested first (see issue#15)
2856        mobileDetectRules.oss0 = {
2857            WindowsPhoneOS: mobileDetectRules.oss.WindowsPhoneOS,
2858            WindowsMobileOS: mobileDetectRules.oss.WindowsMobileOS
2859        };
2860    }());
2861
2862    /**
2863     * Test userAgent string against a set of rules and find the first matched key.
2864     * @param {Object} rules (key is String, value is RegExp)
2865     * @param {String} userAgent the navigator.userAgent (or HTTP-Header 'User-Agent').
2866     * @returns {String|null} the matched key if found, otherwise <tt>null</tt>
2867     * @private
2868     */
2869    impl.findMatch = function(rules, userAgent) {
2870        for (var key in rules) {
2871            if (hasOwnProp.call(rules, key)) {
2872                if (rules[key].test(userAgent)) {
2873                    return key;
2874                }
2875            }
2876        }
2877        return null;
2878    };
2879
2880    /**
2881     * Test userAgent string against a set of rules and return an array of matched keys.
2882     * @param {Object} rules (key is String, value is RegExp)
2883     * @param {String} userAgent the navigator.userAgent (or HTTP-Header 'User-Agent').
2884     * @returns {Array} an array of matched keys, may be empty when there is no match, but not <tt>null</tt>
2885     * @private
2886     */
2887    impl.findMatches = function(rules, userAgent) {
2888        var result = [];
2889        for (var key in rules) {
2890            if (hasOwnProp.call(rules, key)) {
2891                if (rules[key].test(userAgent)) {
2892                    result.push(key);
2893                }
2894            }
2895        }
2896        return result;
2897    };
2898
2899    /**
2900     * Check the version of the given property in the User-Agent.
2901     *
2902     * @param {String} propertyName
2903     * @param {String} userAgent
2904     * @return {String} version or <tt>null</tt> if version not found
2905     * @private
2906     */
2907    impl.getVersionStr = function (propertyName, userAgent) {
2908        var props = impl.mobileDetectRules.props, patterns, i, len, match;
2909        if (hasOwnProp.call(props, propertyName)) {
2910            patterns = props[propertyName];
2911            len = patterns.length;
2912            for (i = 0; i < len; ++i) {
2913                match = patterns[i].exec(userAgent);
2914                if (match !== null) {
2915                    return match[1];
2916                }
2917            }
2918        }
2919        return null;
2920    };
2921
2922    /**
2923     * Check the version of the given property in the User-Agent.
2924     * Will return a float number. (eg. 2_0 will return 2.0, 4.3.1 will return 4.31)
2925     *
2926     * @param {String} propertyName
2927     * @param {String} userAgent
2928     * @return {Number} version or <tt>NaN</tt> if version not found
2929     * @private
2930     */
2931    impl.getVersion = function (propertyName, userAgent) {
2932        var version = impl.getVersionStr(propertyName, userAgent);
2933        return version ? impl.prepareVersionNo(version) : NaN;
2934    };
2935
2936    /**
2937     * Prepare the version number.
2938     *
2939     * @param {String} version
2940     * @return {Number} the version number as a floating number
2941     * @private
2942     */
2943    impl.prepareVersionNo = function (version) {
2944        var numbers;
2945
2946        numbers = version.split(/[a-z._ \/\-]/i);
2947        if (numbers.length === 1) {
2948            version = numbers[0];
2949        }
2950        if (numbers.length > 1) {
2951            version = numbers[0] + '.';
2952            numbers.shift();
2953            version += numbers.join('');
2954        }
2955        return Number(version);
2956    };
2957
2958    impl.isMobileFallback = function (userAgent) {
2959        return impl.detectMobileBrowsers.fullPattern.test(userAgent) ||
2960            impl.detectMobileBrowsers.shortPattern.test(userAgent.substr(0,4));
2961    };
2962
2963    impl.isTabletFallback = function (userAgent) {
2964        return impl.detectMobileBrowsers.tabletPattern.test(userAgent);
2965    };
2966
2967    impl.prepareDetectionCache = function (cache, userAgent, maxPhoneWidth) {
2968        if (cache.mobile !== undefined) {
2969            return;
2970        }
2971        var phone, tablet, phoneSized;
2972
2973        // first check for stronger tablet rules, then phone (see issue#5)
2974        tablet = impl.findMatch(impl.mobileDetectRules.tablets, userAgent);
2975        if (tablet) {
2976            cache.mobile = cache.tablet = tablet;
2977            cache.phone = null;
2978            return; // unambiguously identified as tablet
2979        }
2980
2981        phone = impl.findMatch(impl.mobileDetectRules.phones, userAgent);
2982        if (phone) {
2983            cache.mobile = cache.phone = phone;
2984            cache.tablet = null;
2985            return; // unambiguously identified as phone
2986        }
2987
2988        // our rules haven't found a match -> try more general fallback rules
2989        if (impl.isMobileFallback(userAgent)) {
2990            phoneSized = MobileDetect.isPhoneSized(maxPhoneWidth);
2991            if (phoneSized === undefined) {
2992                cache.mobile = impl.FALLBACK_MOBILE;
2993                cache.tablet = cache.phone = null;
2994            } else if (phoneSized) {
2995                cache.mobile = cache.phone = impl.FALLBACK_PHONE;
2996                cache.tablet = null;
2997            } else {
2998                cache.mobile = cache.tablet = impl.FALLBACK_TABLET;
2999                cache.phone = null;
3000            }
3001        } else if (impl.isTabletFallback(userAgent)) {
3002            cache.mobile = cache.tablet = impl.FALLBACK_TABLET;
3003            cache.phone = null;
3004        } else {
3005            // not mobile at all!
3006            cache.mobile = cache.tablet = cache.phone = null;
3007        }
3008    };
3009
3010    // t is a reference to a MobileDetect instance
3011    impl.mobileGrade = function (t) {
3012        // impl note:
3013        // To keep in sync w/ Mobile_Detect.php easily, the following code is tightly aligned to the PHP version.
3014        // When changes are made in Mobile_Detect.php, copy this method and replace:
3015        //     $this-> / t.
3016        //     self::MOBILE_GRADE_(.) / '$1'
3017        //     , self::VERSION_TYPE_FLOAT / (nothing)
3018        //     isIOS() / os('iOS')
3019        //     [reg] / (nothing)   <-- jsdelivr complaining about unescaped unicode character U+00AE
3020        var $isMobile = t.mobile() !== null;
3021
3022        if (
3023            // Apple iOS 3.2-5.1 - Tested on the original iPad (4.3 / 5.0), iPad 2 (4.3), iPad 3 (5.1), original iPhone (3.1), iPhone 3 (3.2), 3GS (4.3), 4 (4.3 / 5.0), and 4S (5.1)
3024            t.os('iOS') && t.version('iPad')>=4.3 ||
3025            t.os('iOS') && t.version('iPhone')>=3.1 ||
3026            t.os('iOS') && t.version('iPod')>=3.1 ||
3027
3028            // Android 2.1-2.3 - Tested on the HTC Incredible (2.2), original Droid (2.2), HTC Aria (2.1), Google Nexus S (2.3). Functional on 1.5 & 1.6 but performance may be sluggish, tested on Google G1 (1.5)
3029            // Android 3.1 (Honeycomb)  - Tested on the Samsung Galaxy Tab 10.1 and Motorola XOOM
3030            // Android 4.0 (ICS)  - Tested on a Galaxy Nexus. Note: transition performance can be poor on upgraded devices
3031            // Android 4.1 (Jelly Bean)  - Tested on a Galaxy Nexus and Galaxy 7
3032            ( t.version('Android')>2.1 && t.is('Webkit') ) ||
3033
3034            // Windows Phone 7-7.5 - Tested on the HTC Surround (7.0) HTC Trophy (7.5), LG-E900 (7.5), Nokia Lumia 800
3035            t.version('Windows Phone OS')>=7.0 ||
3036
3037            // Blackberry 7 - Tested on BlackBerry Torch 9810
3038            // Blackberry 6.0 - Tested on the Torch 9800 and Style 9670
3039            t.is('BlackBerry') && t.version('BlackBerry')>=6.0 ||
3040            // Blackberry Playbook (1.0-2.0) - Tested on PlayBook
3041            t.match('Playbook.*Tablet') ||
3042
3043            // Palm WebOS (1.4-2.0) - Tested on the Palm Pixi (1.4), Pre (1.4), Pre 2 (2.0)
3044            ( t.version('webOS')>=1.4 && t.match('Palm|Pre|Pixi') ) ||
3045            // Palm WebOS 3.0  - Tested on HP TouchPad
3046            t.match('hp.*TouchPad') ||
3047
3048            // Firefox Mobile (12 Beta) - Tested on Android 2.3 device
3049            ( t.is('Firefox') && t.version('Firefox')>=12 ) ||
3050
3051            // Chrome for Android - Tested on Android 4.0, 4.1 device
3052            ( t.is('Chrome') && t.is('AndroidOS') && t.version('Android')>=4.0 ) ||
3053
3054            // Skyfire 4.1 - Tested on Android 2.3 device
3055            ( t.is('Skyfire') && t.version('Skyfire')>=4.1 && t.is('AndroidOS') && t.version('Android')>=2.3 ) ||
3056
3057            // Opera Mobile 11.5-12: Tested on Android 2.3
3058            ( t.is('Opera') && t.version('Opera Mobi')>11 && t.is('AndroidOS') ) ||
3059
3060            // Meego 1.2 - Tested on Nokia 950 and N9
3061            t.is('MeeGoOS') ||
3062
3063            // Tizen (pre-release) - Tested on early hardware
3064            t.is('Tizen') ||
3065
3066            // Samsung Bada 2.0 - Tested on a Samsung Wave 3, Dolphin browser
3067            // @todo: more tests here!
3068            t.is('Dolfin') && t.version('Bada')>=2.0 ||
3069
3070            // UC Browser - Tested on Android 2.3 device
3071            ( (t.is('UC Browser') || t.is('Dolfin')) && t.version('Android')>=2.3 ) ||
3072
3073            // Kindle 3 and Fire  - Tested on the built-in WebKit browser for each
3074            ( t.match('Kindle Fire') ||
3075                t.is('Kindle') && t.version('Kindle')>=3.0 ) ||
3076
3077            // Nook Color 1.4.1 - Tested on original Nook Color, not Nook Tablet
3078            t.is('AndroidOS') && t.is('NookTablet') ||
3079
3080            // Chrome Desktop 11-21 - Tested on OS X 10.7 and Windows 7
3081            t.version('Chrome')>=11 && !$isMobile ||
3082
3083            // Safari Desktop 4-5 - Tested on OS X 10.7 and Windows 7
3084            t.version('Safari')>=5.0 && !$isMobile ||
3085
3086            // Firefox Desktop 4-13 - Tested on OS X 10.7 and Windows 7
3087            t.version('Firefox')>=4.0 && !$isMobile ||
3088
3089            // Internet Explorer 7-9 - Tested on Windows XP, Vista and 7
3090            t.version('MSIE')>=7.0 && !$isMobile ||
3091
3092            // Opera Desktop 10-12 - Tested on OS X 10.7 and Windows 7
3093            // @reference: http://my.opera.com/community/openweb/idopera/
3094            t.version('Opera')>=10 && !$isMobile
3095
3096            ){
3097            return 'A';
3098        }
3099
3100        if (
3101            t.os('iOS') && t.version('iPad')<4.3 ||
3102            t.os('iOS') && t.version('iPhone')<3.1 ||
3103            t.os('iOS') && t.version('iPod')<3.1 ||
3104
3105            // Blackberry 5.0: Tested on the Storm 2 9550, Bold 9770
3106            t.is('Blackberry') && t.version('BlackBerry')>=5 && t.version('BlackBerry')<6 ||
3107
3108            //Opera Mini (5.0-6.5) - Tested on iOS 3.2/4.3 and Android 2.3
3109            ( t.version('Opera Mini')>=5.0 && t.version('Opera Mini')<=6.5 &&
3110                (t.version('Android')>=2.3 || t.is('iOS')) ) ||
3111
3112            // Nokia Symbian^3 - Tested on Nokia N8 (Symbian^3), C7 (Symbian^3), also works on N97 (Symbian^1)
3113            t.match('NokiaN8|NokiaC7|N97.*Series60|Symbian/3') ||
3114
3115            // @todo: report this (tested on Nokia N71)
3116            t.version('Opera Mobi')>=11 && t.is('SymbianOS')
3117            ){
3118            return 'B';
3119        }
3120
3121        if (
3122        // Blackberry 4.x - Tested on the Curve 8330
3123            t.version('BlackBerry')<5.0 ||
3124            // Windows Mobile - Tested on the HTC Leo (WinMo 5.2)
3125            t.match('MSIEMobile|Windows CE.*Mobile') || t.version('Windows Mobile')<=5.2
3126
3127            ){
3128            return 'C';
3129        }
3130
3131        //All older smartphone platforms and featurephones - Any device that doesn't support media queries
3132        //will receive the basic, C grade experience.
3133        return 'C';
3134    };
3135
3136    impl.detectOS = function (ua) {
3137        return impl.findMatch(impl.mobileDetectRules.oss0, ua) ||
3138            impl.findMatch(impl.mobileDetectRules.oss, ua);
3139    };
3140
3141    impl.getDeviceSmallerSide = function () {
3142        return window.screen.width < window.screen.height ?
3143            window.screen.width :
3144            window.screen.height;
3145    };
3146
3147    /**
3148     * Constructor for MobileDetect object.
3149     * <br>
3150     * Such an object will keep a reference to the given user-agent string and cache most of the detect queries.<br>
3151     * <div style="background-color: #d9edf7; border: 1px solid #bce8f1; color: #3a87ad; padding: 14px; border-radius: 2px; margin-top: 20px">
3152     *     <strong>Find information how to download and install:</strong>
3153     *     <a href="https://github.com/hgoebl/mobile-detect.js/">github.com/hgoebl/mobile-detect.js/</a>
3154     * </div>
3155     *
3156     * @example <pre>
3157     *     var md = new MobileDetect(window.navigator.userAgent);
3158     *     if (md.mobile()) {
3159     *         location.href = (md.mobileGrade() === 'A') ? '/mobile/' : '/lynx/';
3160     *     }
3161     * </pre>
3162     *
3163     * @param {string} userAgent typically taken from window.navigator.userAgent or http_header['User-Agent']
3164     * @param {number} [maxPhoneWidth=600] <strong>only for browsers</strong> specify a value for the maximum
3165     *        width of smallest device side (in logical "CSS" pixels) until a device detected as mobile will be handled
3166     *        as phone.
3167     *        This is only used in cases where the device cannot be classified as phone or tablet.<br>
3168     *        See <a href="http://developer.android.com/guide/practices/screens_support.html">Declaring Tablet Layouts
3169     *        for Android</a>.<br>
3170     *        If you provide a value < 0, then this "fuzzy" check is disabled.
3171     * @constructor
3172     * @global
3173     */
3174    function MobileDetect(userAgent, maxPhoneWidth) {
3175        this.ua = prepareUserAgent(userAgent);
3176        this._cache = {};
3177        //600dp is typical 7" tablet minimum width
3178        this.maxPhoneWidth = maxPhoneWidth || 600;
3179    }
3180
3181    MobileDetect.prototype = {
3182        constructor: MobileDetect,
3183
3184        /**
3185         * Returns the detected phone or tablet type or <tt>null</tt> if it is not a mobile device.
3186         * <br>
3187         * For a list of possible return values see {@link MobileDetect#phone} and {@link MobileDetect#tablet}.<br>
3188         * <br>
3189         * If the device is not detected by the regular expressions from Mobile-Detect, a test is made against
3190         * the patterns of <a href="http://detectmobilebrowsers.com/">detectmobilebrowsers.com</a>. If this test
3191         * is positive, a value of <code>UnknownPhone</code>, <code>UnknownTablet</code> or
3192         * <code>UnknownMobile</code> is returned.<br>
3193         * When used in browser, the decision whether phone or tablet is made based on <code>screen.width/height</code>.<br>
3194         * <br>
3195         * When used server-side (node.js), there is no way to tell the difference between <code>UnknownTablet</code>
3196         * and <code>UnknownMobile</code>, so you will get <code>UnknownMobile</code> here.<br>
3197         * Be aware that since v1.0.0 in this special case you will get <code>UnknownMobile</code> only for:
3198         * {@link MobileDetect#mobile}, not for {@link MobileDetect#phone} and {@link MobileDetect#tablet}.
3199         * In versions before v1.0.0 all 3 methods returned <code>UnknownMobile</code> which was tedious to use.
3200         * <br>
3201         * In most cases you will use the return value just as a boolean.
3202         *
3203         * @returns {String} the key for the phone family or tablet family, e.g. "Nexus".
3204         * @function MobileDetect#mobile
3205         */
3206        mobile: function () {
3207            impl.prepareDetectionCache(this._cache, this.ua, this.maxPhoneWidth);
3208            return this._cache.mobile;
3209        },
3210
3211        /**
3212         * Returns the detected phone type/family string or <tt>null</tt>.
3213         * <br>
3214         * The returned tablet (family or producer) is one of following keys:<br>
3215         * <br><tt>iPhone, BlackBerry, Pixel, HTC, Nexus, Dell, Motorola, Samsung, LG, Sony, Asus,
3216         * Xiaomi, NokiaLumia, Micromax, Palm, Vertu, Pantech, Fly, Wiko, iMobile,
3217         * SimValley, Wolfgang, Alcatel, Nintendo, Amoi, INQ, OnePlus, GenericPhone</tt><br>
3218         * <br>
3219         * If the device is not detected by the regular expressions from Mobile-Detect, a test is made against
3220         * the patterns of <a href="http://detectmobilebrowsers.com/">detectmobilebrowsers.com</a>. If this test
3221         * is positive, a value of <code>UnknownPhone</code> or <code>UnknownMobile</code> is returned.<br>
3222         * When used in browser, the decision whether phone or tablet is made based on <code>screen.width/height</code>.<br>
3223         * <br>
3224         * When used server-side (node.js), there is no way to tell the difference between <code>UnknownTablet</code>
3225         * and <code>UnknownMobile</code>, so you will get <code>null</code> here, while {@link MobileDetect#mobile}
3226         * will return <code>UnknownMobile</code>.<br>
3227         * Be aware that since v1.0.0 in this special case you will get <code>UnknownMobile</code> only for:
3228         * {@link MobileDetect#mobile}, not for {@link MobileDetect#phone} and {@link MobileDetect#tablet}.
3229         * In versions before v1.0.0 all 3 methods returned <code>UnknownMobile</code> which was tedious to use.
3230         * <br>
3231         * In most cases you will use the return value just as a boolean.
3232         *
3233         * @returns {String} the key of the phone family or producer, e.g. "iPhone"
3234         * @function MobileDetect#phone
3235         */
3236        phone: function () {
3237            impl.prepareDetectionCache(this._cache, this.ua, this.maxPhoneWidth);
3238            return this._cache.phone;
3239        },
3240
3241        /**
3242         * Returns the detected tablet type/family string or <tt>null</tt>.
3243         * <br>
3244         * The returned tablet (family or producer) is one of following keys:<br>
3245         * <br><tt>iPad, NexusTablet, GoogleTablet, SamsungTablet, Kindle, SurfaceTablet,
3246         * HPTablet, AsusTablet, BlackBerryTablet, HTCtablet, MotorolaTablet, NookTablet,
3247         * AcerTablet, ToshibaTablet, LGTablet, FujitsuTablet, PrestigioTablet,
3248         * LenovoTablet, DellTablet, YarvikTablet, MedionTablet, ArnovaTablet,
3249         * IntensoTablet, IRUTablet, MegafonTablet, EbodaTablet, AllViewTablet,
3250         * ArchosTablet, AinolTablet, NokiaLumiaTablet, SonyTablet, PhilipsTablet,
3251         * CubeTablet, CobyTablet, MIDTablet, MSITablet, SMiTTablet, RockChipTablet,
3252         * FlyTablet, bqTablet, HuaweiTablet, NecTablet, PantechTablet, BronchoTablet,
3253         * VersusTablet, ZyncTablet, PositivoTablet, NabiTablet, KoboTablet, DanewTablet,
3254         * TexetTablet, PlaystationTablet, TrekstorTablet, PyleAudioTablet, AdvanTablet,
3255         * DanyTechTablet, GalapadTablet, MicromaxTablet, KarbonnTablet, AllFineTablet,
3256         * PROSCANTablet, YONESTablet, ChangJiaTablet, GUTablet, PointOfViewTablet,
3257         * OvermaxTablet, HCLTablet, DPSTablet, VistureTablet, CrestaTablet,
3258         * MediatekTablet, ConcordeTablet, GoCleverTablet, ModecomTablet, VoninoTablet,
3259         * ECSTablet, StorexTablet, VodafoneTablet, EssentielBTablet, RossMoorTablet,
3260         * iMobileTablet, TolinoTablet, AudioSonicTablet, AMPETablet, SkkTablet,
3261         * TecnoTablet, JXDTablet, iJoyTablet, FX2Tablet, XoroTablet, ViewsonicTablet,
3262         * VerizonTablet, OdysTablet, CaptivaTablet, IconbitTablet, TeclastTablet,
3263         * OndaTablet, JaytechTablet, BlaupunktTablet, DigmaTablet, EvolioTablet,
3264         * LavaTablet, AocTablet, MpmanTablet, CelkonTablet, WolderTablet, MediacomTablet,
3265         * MiTablet, NibiruTablet, NexoTablet, LeaderTablet, UbislateTablet,
3266         * PocketBookTablet, KocasoTablet, HisenseTablet, Hudl, TelstraTablet,
3267         * GenericTablet</tt><br>
3268         * <br>
3269         * If the device is not detected by the regular expressions from Mobile-Detect, a test is made against
3270         * the patterns of <a href="http://detectmobilebrowsers.com/">detectmobilebrowsers.com</a>. If this test
3271         * is positive, a value of <code>UnknownTablet</code> or <code>UnknownMobile</code> is returned.<br>
3272         * When used in browser, the decision whether phone or tablet is made based on <code>screen.width/height</code>.<br>
3273         * <br>
3274         * When used server-side (node.js), there is no way to tell the difference between <code>UnknownTablet</code>
3275         * and <code>UnknownMobile</code>, so you will get <code>null</code> here, while {@link MobileDetect#mobile}
3276         * will return <code>UnknownMobile</code>.<br>
3277         * Be aware that since v1.0.0 in this special case you will get <code>UnknownMobile</code> only for:
3278         * {@link MobileDetect#mobile}, not for {@link MobileDetect#phone} and {@link MobileDetect#tablet}.
3279         * In versions before v1.0.0 all 3 methods returned <code>UnknownMobile</code> which was tedious to use.
3280         * <br>
3281         * In most cases you will use the return value just as a boolean.
3282         *
3283         * @returns {String} the key of the tablet family or producer, e.g. "SamsungTablet"
3284         * @function MobileDetect#tablet
3285         */
3286        tablet: function () {
3287            impl.prepareDetectionCache(this._cache, this.ua, this.maxPhoneWidth);
3288            return this._cache.tablet;
3289        },
3290
3291        /**
3292         * Returns the (first) detected user-agent string or <tt>null</tt>.
3293         * <br>
3294         * The returned user-agent is one of following keys:<br>
3295         * <br><tt>Chrome, Dolfin, Opera, Skyfire, Edge, IE, Firefox, Bolt, TeaShark, Blazer,
3296         * Safari, WeChat, UCBrowser, baiduboxapp, baidubrowser, DiigoBrowser, Mercury,
3297         * ObigoBrowser, NetFront, GenericBrowser, PaleMoon</tt><br>
3298         * <br>
3299         * In most cases calling {@link MobileDetect#userAgent} will be sufficient. But there are rare
3300         * cases where a mobile device pretends to be more than one particular browser. You can get the
3301         * list of all matches with {@link MobileDetect#userAgents} or check for a particular value by
3302         * providing one of the defined keys as first argument to {@link MobileDetect#is}.
3303         *
3304         * @returns {String} the key for the detected user-agent or <tt>null</tt>
3305         * @function MobileDetect#userAgent
3306         */
3307        userAgent: function () {
3308            if (this._cache.userAgent === undefined) {
3309                this._cache.userAgent = impl.findMatch(impl.mobileDetectRules.uas, this.ua);
3310            }
3311            return this._cache.userAgent;
3312        },
3313
3314        /**
3315         * Returns all detected user-agent strings.
3316         * <br>
3317         * The array is empty or contains one or more of following keys:<br>
3318         * <br><tt>Chrome, Dolfin, Opera, Skyfire, Edge, IE, Firefox, Bolt, TeaShark, Blazer,
3319         * Safari, WeChat, UCBrowser, baiduboxapp, baidubrowser, DiigoBrowser, Mercury,
3320         * ObigoBrowser, NetFront, GenericBrowser, PaleMoon</tt><br>
3321         * <br>
3322         * In most cases calling {@link MobileDetect#userAgent} will be sufficient. But there are rare
3323         * cases where a mobile device pretends to be more than one particular browser. You can get the
3324         * list of all matches with {@link MobileDetect#userAgents} or check for a particular value by
3325         * providing one of the defined keys as first argument to {@link MobileDetect#is}.
3326         *
3327         * @returns {Array} the array of detected user-agent keys or <tt>[]</tt>
3328         * @function MobileDetect#userAgents
3329         */
3330        userAgents: function () {
3331            if (this._cache.userAgents === undefined) {
3332                this._cache.userAgents = impl.findMatches(impl.mobileDetectRules.uas, this.ua);
3333            }
3334            return this._cache.userAgents;
3335        },
3336
3337        /**
3338         * Returns the detected operating system string or <tt>null</tt>.
3339         * <br>
3340         * The operating system is one of following keys:<br>
3341         * <br><tt>AndroidOS, BlackBerryOS, PalmOS, SymbianOS, WindowsMobileOS, WindowsPhoneOS,
3342         * iOS, iPadOS, SailfishOS, MeeGoOS, MaemoOS, JavaOS, webOS, badaOS, BREWOS</tt><br>
3343         *
3344         * @returns {String} the key for the detected operating system.
3345         * @function MobileDetect#os
3346         */
3347        os: function () {
3348            if (this._cache.os === undefined) {
3349                this._cache.os = impl.detectOS(this.ua);
3350            }
3351            return this._cache.os;
3352        },
3353
3354        /**
3355         * Get the version (as Number) of the given property in the User-Agent.
3356         * <br>
3357         * Will return a float number. (eg. 2_0 will return 2.0, 4.3.1 will return 4.31)
3358         *
3359         * @param {String} key a key defining a thing which has a version.<br>
3360         *        You can use one of following keys:<br>
3361         * <br><tt>Mobile, Build, Version, VendorID, iPad, iPhone, iPod, Kindle, Chrome, Coast,
3362         * Dolfin, Firefox, Fennec, Edge, IE, NetFront, NokiaBrowser, Opera, Opera Mini,
3363         * Opera Mobi, UCBrowser, MQQBrowser, MicroMessenger, baiduboxapp, baidubrowser,
3364         * SamsungBrowser, Iron, Safari, Skyfire, Tizen, Webkit, PaleMoon,
3365         * SailfishBrowser, Gecko, Trident, Presto, Goanna, iOS, Android, Sailfish,
3366         * BlackBerry, BREW, Java, Windows Phone OS, Windows Phone, Windows CE, Windows
3367         * NT, Symbian, webOS</tt><br>
3368         *
3369         * @returns {Number} the version as float or <tt>NaN</tt> if User-Agent doesn't contain this version.
3370         *          Be careful when comparing this value with '==' operator!
3371         * @function MobileDetect#version
3372         */
3373        version: function (key) {
3374            return impl.getVersion(key, this.ua);
3375        },
3376
3377        /**
3378         * Get the version (as String) of the given property in the User-Agent.
3379         * <br>
3380         *
3381         * @param {String} key a key defining a thing which has a version.<br>
3382         *        You can use one of following keys:<br>
3383         * <br><tt>Mobile, Build, Version, VendorID, iPad, iPhone, iPod, Kindle, Chrome, Coast,
3384         * Dolfin, Firefox, Fennec, Edge, IE, NetFront, NokiaBrowser, Opera, Opera Mini,
3385         * Opera Mobi, UCBrowser, MQQBrowser, MicroMessenger, baiduboxapp, baidubrowser,
3386         * SamsungBrowser, Iron, Safari, Skyfire, Tizen, Webkit, PaleMoon,
3387         * SailfishBrowser, Gecko, Trident, Presto, Goanna, iOS, Android, Sailfish,
3388         * BlackBerry, BREW, Java, Windows Phone OS, Windows Phone, Windows CE, Windows
3389         * NT, Symbian, webOS</tt><br>
3390         *
3391         * @returns {String} the "raw" version as String or <tt>null</tt> if User-Agent doesn't contain this version.
3392         *
3393         * @function MobileDetect#versionStr
3394         */
3395        versionStr: function (key) {
3396            return impl.getVersionStr(key, this.ua);
3397        },
3398
3399        /**
3400         * Global test key against userAgent, os, phone, tablet and some other properties of userAgent string.
3401         *
3402         * @param {String} key the key (case-insensitive) of a userAgent, an operating system, phone or
3403         *        tablet family.<br>
3404         *        For a complete list of possible values, see {@link MobileDetect#userAgent},
3405         *        {@link MobileDetect#os}, {@link MobileDetect#phone}, {@link MobileDetect#tablet}.<br>
3406         *        Additionally you have following keys:<br>
3407         * <br><tt>Bot, MobileBot, DesktopMode, TV, WebKit, Console, Watch</tt><br>
3408         *
3409         * @returns {boolean} <tt>true</tt> when the given key is one of the defined keys of userAgent, os, phone,
3410         *                    tablet or one of the listed additional keys, otherwise <tt>false</tt>
3411         * @function MobileDetect#is
3412         */
3413        is: function (key) {
3414            return containsIC(this.userAgents(), key) ||
3415                   equalIC(key, this.os()) ||
3416                   equalIC(key, this.phone()) ||
3417                   equalIC(key, this.tablet()) ||
3418                   containsIC(impl.findMatches(impl.mobileDetectRules.utils, this.ua), key);
3419        },
3420
3421        /**
3422         * Do a quick test against navigator::userAgent.
3423         *
3424         * @param {String|RegExp} pattern the pattern, either as String or RegExp
3425         *                        (a string will be converted to a case-insensitive RegExp).
3426         * @returns {boolean} <tt>true</tt> when the pattern matches, otherwise <tt>false</tt>
3427         * @function MobileDetect#match
3428         */
3429        match: function (pattern) {
3430            if (!(pattern instanceof RegExp)) {
3431                pattern = new RegExp(pattern, 'i');
3432            }
3433            return pattern.test(this.ua);
3434        },
3435
3436        /**
3437         * Checks whether the mobile device can be considered as phone regarding <code>screen.width</code>.
3438         * <br>
3439         * Obviously this method makes sense in browser environments only (not for Node.js)!
3440         * @param {number} [maxPhoneWidth] the maximum logical pixels (aka. CSS-pixels) to be considered as phone.<br>
3441         *        The argument is optional and if not present or falsy, the value of the constructor is taken.
3442         * @returns {boolean|undefined} <code>undefined</code> if screen size wasn't detectable, else <code>true</code>
3443         *          when screen.width is less or equal to maxPhoneWidth, otherwise <code>false</code>.<br>
3444         *          Will always return <code>undefined</code> server-side.
3445         */
3446        isPhoneSized: function (maxPhoneWidth) {
3447            return MobileDetect.isPhoneSized(maxPhoneWidth || this.maxPhoneWidth);
3448        },
3449
3450        /**
3451         * Returns the mobile grade ('A', 'B', 'C').
3452         *
3453         * @returns {String} one of the mobile grades ('A', 'B', 'C').
3454         * @function MobileDetect#mobileGrade
3455         */
3456        mobileGrade: function () {
3457            if (this._cache.grade === undefined) {
3458                this._cache.grade = impl.mobileGrade(this);
3459            }
3460            return this._cache.grade;
3461        }
3462    };
3463
3464    // environment-dependent
3465    if (typeof window !== 'undefined' && window.screen) {
3466        MobileDetect.isPhoneSized = function (maxPhoneWidth) {
3467            return maxPhoneWidth < 0 ? undefined : impl.getDeviceSmallerSide() <= maxPhoneWidth;
3468        };
3469    } else {
3470        MobileDetect.isPhoneSized = function () {};
3471    }
3472
3473    // should not be replaced by a completely new object - just overwrite existing methods
3474    MobileDetect._impl = impl;
3475    
3476    MobileDetect.version = '1.4.5 2021-03-13';
3477
3478    return MobileDetect;
3479}); // end of call of define()
3480})((function (undefined) {
3481    if (typeof module !== 'undefined' && module.exports) {
3482        return function (factory) { module.exports = factory(); };
3483    } else if (typeof define === 'function' && define.amd) {
3484        return define;
3485    } else if (typeof window !== 'undefined') {
3486        return function (factory) { window.MobileDetect = factory(); };
3487    } else {
3488        // please file a bug if you get this error!
3489        throw new Error('unknown environment');
3490    }
3491})());
3491
3492// @license http://opensource.org/licenses/MIT
3493// copyright Paul Irish 2015
3494
3495
3496// Date.now() is supported everywhere except IE8. For IE8 we use the Date.now polyfill
3497//   github.com/Financial-Times/polyfill-service/blob/master/polyfills/Date.now/polyfill.js
3498// as Safari 6 doesn't have support for NavigationTiming, we use a Date.now() timestamp for relative values
3499
3500// if you want values similar to what you'd get with real perf.now, place this towards the head of the page
3501// but in reality, you're just getting the delta between now() calls, so it's not terribly important where it's placed
3502
3503
3504(function () {
3505
3506	if ("performance" in window == false) {
3507		window.performance = {};
3508	}
3509
3510	Date.now = (Date.now || function () {  // thanks IE8
3511		return new Date().getTime();
3512	});
3513
3514	if ("now" in window.performance == false) {
3515
3516		var nowOffset = Date.now();
3517
3518		if (performance.timing && performance.timing.navigationStart) {
3519			nowOffset = performance.timing.navigationStart
3520		}
3521
3522		window.performance.now = function now() {
3523			return Date.now() - nowOffset;
3524		}
3525	}
3526
3527})();
3528/*** Copyright 2013 Teun Duynstee Licensed under the Apache License, Version 2.0 ***/
3529firstBy = function () {
3530	function n(n, t) {
3531		if ("function" != typeof n) {
3532			var r = n;
3533			n = function (n, t) {
3534				return n[r] < t[r] ? -1 : n[r] > t[r] ? 1 : 0
3535			}
3536		}
3537		if (1 === n.length) {
3538			var u = n;
3539			n = function (n, t) {
3540				return u(n) < u(t) ? -1 : u(n) > u(t) ? 1 : 0
3541			}
3542		}
3543		return -1 === t ? function (t, r) {
3544			return -n(t, r)
3545		} : n
3546	}
3547
3548	function t(t, u) {
3549		return t = n(t, u), t.thenBy = r, t
3550	}
3551
3552	function r(r, u) {
3553		var f = this;
3554		return r = n(r, u), t(function (n, t) {
3555			return f(n, t) || r(n, t)
3556		})
3557	}
3558
3559	return t
3560}();
3561// Global hp object
3562window.hp = {};
3563
3564hp.addClass = function ($el, cssClass) {
3565
3566	if (typeof $el === 'undefined' || typeof cssClass === 'undefined') {
3567
3568		return false;
3569
3570	}
3571
3572
3573
3574	if ($el.is(cssClass)) {
3575
3576		return false;
3577
3578	}
3579
3580	// remove classes starting with `hp-`
3581
3582	$el.attr('class', function (i, c) {
3583
3584		return c.replace(/(^|\s)hp-\S+/g, '');
3585
3586	});
3587
3588
3589
3590	$el.addClass(cssClass);
3591
3592};
3593
3594
3595hp.anchorLink = function (params) {
3596
3597	var options = $.extend({}, params);
3598
3599	var hash = window.location.hash.replace('#!', '');
3600
3601	var $window = $(window);
3602
3603	var hashIsLocked = false;
3604
3605
3606
3607	function _setHash(newHash) {
3608
3609		if (hashIsLocked) {
3610
3611			return false;
3612
3613		}
3614
3615		window.location.hash = '#!' + newHash;
3616
3617	}
3618
3619
3620
3621	function _setActive(index) {
3622
3623		$('[data-hp-anchor-link]').removeClass('active');
3624
3625		var $link = $('[data-hp-anchor-link]:eq(' + index + ')');
3626
3627
3628
3629		if ($link.length) {
3630
3631			$link.addClass('active');
3632
3633			_setHash($link.data('hpAnchorLink'));
3634
3635		}
3636
3637	}
3638
3639
3640
3641	$(document).on('click', '[data-hp-anchor-link]', function (e) {
3642
3643
3644
3645		var anchorLink = $(this).data('hpAnchorLink');
3646
3647		var $target = $($(this).data('hpAnchorTarget'));
3648
3649		if (!$target.length || !anchorLink || anchorLink === '') {
3650
3651			return true;
3652
3653		}
3654
3655
3656
3657		$('[data-hp-anchor-link]').removeClass('active');
3658
3659		$(this).addClass('active');
3660
3661
3662
3663		var offset = $target.offset().top;
3664
3665		if (options.header instanceof jQuery) {
3666
3667			offset -= options.header.height();
3668
3669		}
3670
3671
3672
3673		hashIsLocked = true; // to prevent the on scroll event to change the hash
3674
3675		hp.scrollTo({
3676
3677			offset: offset,
3678
3679			callback: function () {
3680
3681				hashIsLocked = false;
3682
3683				_setHash(anchorLink);
3684
3685			}
3686
3687		});
3688
3689	});
3690
3691
3692
3693	$window.on('scroll', function () {
3694
3695		if ($window.scrollTop() <= 50 && hashIsLocked === false) {
3696
3697			_setActive(0);
3698
3699		}
3700
3701	});
3702
3703
3704
3705	$window.on('load', function () {
3706
3707		if (hash.length) {
3708
3709			$('[data-hp-anchor-link="' + hash + '"]').trigger('click');
3710
3711		}
3712
3713	});
3714
3715};
3716
3717
3718hp.browserVersion = function () {
3719
3720
3721
3722	var ua = navigator.userAgent, tem, M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
3723
3724	if (/trident/i.test(M[1])) {
3725
3726		tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
3727
3728		return {name: 'IE', version: (tem[1] || '')};
3729
3730	}
3731
3732	if (M[1] === 'Chrome') {
3733
3734		tem = ua.match(/\bOPR\/(\d+)/)
3735
3736		if (tem != null) {
3737
3738			return {name: 'Opera', version: tem[1]};
3739
3740		}
3741
3742	}
3743
3744	M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?'];
3745
3746	if ((tem = ua.match(/version\/(\d+)/i)) != null) {
3747
3748		M.splice(1, 1, tem[1]);
3749
3750	}
3751
3752	return {
3753
3754		name: M[0],
3755
3756		version: M[1]
3757
3758	};
3759
3760};
3761
3762
3763hp.calculateTop = function ($element, margin) {
3764
3765	margin = margin || 120;
3766
3767	return $element.parent().offset().top - $(window).scrollTop() + margin;
3768
3769};
3770
3771
3772hp.clearCache = function () {
3773
3774	// clear overloaded IBE cache
3775
3776	BookwizeLiquid.cache.clear();
3777
3778
3779
3780	if (hp.isCheckRatesPage()) {
3781
3782		window.location.href = IBE.Config.websiteUrl;
3783
3784	} else {
3785
3786		window.location.hash = '';
3787
3788		window.location.href = hp.removeParam('clearCache', window.location.href);
3789
3790		window.location.reload();
3791
3792	}
3793
3794};
3795
3796
3797hp.closeOverlays = function () {
3798
3799
3800
3801	var $overlay = $('.hp-toggle--open');
3802
3803
3804
3805	try {
3806
3807
3808
3809		$overlay.fadeOut().removeClass('hp-toggle--open');
3810
3811		$('.hp-backdrop').remove();
3812
3813		$('.datepick-popup').remove();
3814
3815        $('body').removeClass('step1-open');
3816
3817
3818
3819		BookwizeLiquid.popup.close();
3820
3821		if($('.single-gallery__slider')){
3822
3823            $('.single-gallery__slider').slick('refresh');
3824
3825		}
3826
3827
3828
3829		if (BookwizeLiquid.isMultiProperty === true) {
3830
3831			ko.utils.arrayForEach(BookwizeLiquid.vm.step2.properties(), function (item) {
3832
3833				item.removeLastRoom();
3834
3835			});
3836
3837		} else {
3838
3839			BookwizeLiquid.vm.step2.removeLastRoom();
3840
3841		}
3842
3843	} catch (e) {
3844
3845		// we do not anything to log here
3846
3847	}
3848
3849
3850
3851	$overlay.trigger('hp/overlayClosed', {
3852
3853		source: $overlay
3854
3855	});
3856
3857};
3858
3859
3860
3861
3862
3863
3864// ci=2014-08-08&co=2014-08-12&r=2^3/3/0/0^4/3/0^1/0&couponCode=&board=&rp=
3865
3866hp.deepLink = function (sessionData, returnType) {
3867
3868	var data = ko.toJS(sessionData);
3869
3870	var preferences = {
3871
3872		ci: data.checkIn,
3873
3874		co: data.checkOut,
3875
3876		board: data.board,
3877
3878		lang: data.lang,
3879
3880		cur: data.currency,
3881
3882		couponCode: data.couponCode
3883
3884	};
3885
3886	var rooms = '';
3887
3888	ko.utils.arrayForEach(data.rooms, function (item) {
3889
3890		var adults = ko.utils.arrayFilter(item, function (guest) {
3891
3892			return guest.ageCategory === 'Adult';
3893
3894		});
3895
3896		var children = ko.utils.arrayFilter(item, function (guest) {
3897
3898			return guest.ageCategory === 'Children';
3899
3900		});
3901
3902		var infants = ko.utils.arrayFilter(item, function (guest) {
3903
3904			return guest.ageCategory === 'Infant';
3905
3906		});
3907
3908
3909
3910		if (adults.length && adults[0].pax > 0) {
3911
3912			rooms += adults[0].pax;
3913
3914		}
3915
3916
3917
3918		if ((children.length && children[0].pax > 0) || (infants.length && infants[0].pax > 0)) {
3919
3920			rooms += '/';
3921
3922		}
3923
3924		if (children.length && children[0].pax > 0) {
3925
3926			for (var i = 0; i < children[0].pax; i++) {
3927
3928				rooms += children[0].fromAge + '/';
3929
3930			}
3931
3932		}
3933
3934		if (infants.length && infants[0].pax > 0) {
3935
3936			for (var i = 0; i < infants[0].pax; i++) {
3937
3938				rooms += infants[0].fromAge + '/';
3939
3940			}
3941
3942		}
3943
3944		rooms += '^';
3945
3946	});
3947
3948
3949
3950	rooms = rooms.slice(0, -1);
3951
3952	// preferences.r = rooms;
3953
3954	preferences.r = 1;
3955
3956
3957
3958	return (typeof returnType === 'undefined') ? $.param(preferences) : preferences;
3959
3960};
3961
3962
3963hp.disable = function ($el) {
3964
3965	if (typeof $el === 'undefined' || $el.length == 0) {
3966
3967		return false;
3968
3969	}
3970
3971	$el.attr('disabled', 'disabled');
3972
3973};
3974
3975
3976hp.dropdown = function () {
3977
3978
3979
3980	$(document).on('click', '.hp-dropdown-toggle', function (e) {
3981
3982		$($(this).data('hpDropdown')).toggleClass('active');
3983
3984	});
3985
3986
3987
3988	$(document).on('click', '.hp-dropdown li', function (e) {
3989
3990		$(this).parent().removeClass('active');
3991
3992	});
3993
3994
3995
3996	$(window).on('scroll', function(){
3997
3998		$('.hp-dropdown.active').removeClass('active');
3999
4000	});
4001
4002};
4003
4004
4005hp.enable = function ($el) {
4006
4007	if (typeof $el === 'undefined' || $el.length == 0) {
4008
4009		return false;
4010
4011	}
4012
4013	var t = setTimeout(function () {
4014
4015		$el.removeAttr('disabled');
4016
4017		clearTimeout(t);
4018
4019	}, 377);
4020
4021};
4022
4023
4024hp.escapeKeyUp = function () {
4025
4026	$(document).keyup(function (e) {
4027
4028		if (e.keyCode == 27) {
4029
4030			hp.closeOverlays();
4031
4032			return true;
4033
4034		}
4035
4036	});
4037
4038};
4039
4040
4041
4042
4043hp.getHotelCredentialsFromRoomId = function (id, properties) {
4044
4045	var returnValue = {};
4046
4047
4048
4049	var hotel = hp.getHotelInfoFromRoomId(id, properties);
4050
4051
4052
4053	if (hotel) {
4054
4055		var params = ko.utils.arrayFilter(properties, function (item) {
4056
4057			return item.hotelId == hotel.hotelId;
4058
4059		})[0];
4060
4061		returnValue.hotelId = params.hotelId;
4062
4063		returnValue.apiKey = params.apiKey;
4064
4065	}
4066
4067
4068
4069	return returnValue;
4070
4071};
4072
4073
4074hp.getHotelInfoFromRoomId = function (id, properties) {
4075
4076	var returnValue = {};
4077
4078
4079
4080	var hotelId = 0;
4081
4082
4083
4084	ko.utils.arrayForEach(properties, function (item) {
4085
4086		if (hotelId > 0) {
4087
4088			return true;
4089
4090		}
4091
4092		var room = ko.utils.arrayFilter(item.vm.step1.rooms(), function (room) {
4093
4094			return room.id === id;
4095
4096		});
4097
4098		if (room.length) {
4099
4100			returnValue = item.vm.step1.hotel().info;
4101
4102		}
4103
4104	});
4105
4106
4107
4108	return returnValue;
4109
4110};
4111
4112
4113hp.hash = function (value) {
4114
4115
4116
4117	if (typeof value === 'undefined' || value == false) {
4118
4119		if (history.pushState) {
4120
4121			history.pushState(null, null, '#');
4122
4123		} else {
4124
4125			window.location.hash = '';
4126
4127		}
4128
4129		return;
4130
4131	}
4132
4133
4134
4135	if (history.pushState) {
4136
4137		history.pushState(null, null, '#' + value);
4138
4139	} else {
4140
4141		window.location.hash = '#' + value;
4142
4143	}
4144
4145};
4146
4147
4148hp.isBound = function (id) {
4149
4150	return !!ko.dataFor(document.getElementById(id));
4151
4152};
4153
4154
4155hp.isCheckRatesPage = function () {
4156
4157	var page = BookwizeLiquid.getPostByBookwizeLiquidPageType('checkrates');
4158
4159	return page.ID === IBE.Config.post.ID;
4160
4161};
4162
4163
4164
4165
4166hp.isMobile = function () {
4167
4168	var md = new MobileDetect(window.navigator.userAgent);
4169
4170	// alert('md.phone(): ' + md.phone() + ', md.tablet(): ' + md.tablet());
4171
4172
4173
4174	return (md.phone() !== null && md.tablet() === null);
4175
4176};
4177
4178
4179hp.isOfferPage = function (id) {
4180
4181	if (typeof id === 'undefined') {
4182
4183		id = IBE.Config.post.ID;
4184
4185	}
4186
4187	var page = BookwizeLiquid.getOfferByPostId(id);
4188
4189	return page.ID === IBE.Config.post.ID;
4190
4191};
4192
4193
4194hp.isRoomPage = function (id) {
4195
4196	if (typeof id === 'undefined') {
4197
4198		id = IBE.Config.post.ID;
4199
4200	}
4201
4202	var page = BookwizeLiquid.getRoomByPostId(id);
4203
4204	return page.ID === IBE.Config.post.ID;
4205
4206};
4207
4208
4209
4210
4211hp.legacy = function (mobile) {
4212
4213	var $hpLegacy = $('.hp-legacy');
4214
4215
4216
4217	// remove all booking engine elements
4218
4219	$('[id^="hp-"], [class^="hp-"]').remove();
4220
4221
4222
4223	// show legacy booking link
4224
4225	$('.header__book').append($hpLegacy);
4226
4227	if (typeof mobile !== 'undefined') {
4228
4229		$('.hp-legacy-booking-link--mobile').addClass('active');
4230
4231
4232
4233	} else {
4234
4235		$('.hp-legacy-booking-link').addClass('active');
4236
4237	}
4238
4239
4240
4241	$('.hp-legacy-booking-link[data-toggle]').on('click', function () {
4242
4243		var $sidebarHotel = $('.sidebar__hotel');
4244
4245		if ($sidebarHotel.length && $sidebarHotel.data('hotelId')) {
4246
4247			var hotelId = $sidebarHotel.data('hotelId');
4248
4249			window.open(
4250
4251				$('.hp-legacy-booking-link[data-hotel-id="' + hotelId + '"]').attr('href'),
4252
4253				'_blank'
4254
4255			);
4256
4257
4258
4259			setTimeout(function () {
4260
4261				$('.dropdown__list--book').removeClass('opened');
4262
4263			}, 200);
4264
4265		}
4266
4267	});
4268
4269};
4270
4271
4272hp.removeParam = function (key, sourceURL) {
4273
4274	var rtn = sourceURL.split("?")[0],
4275
4276		param,
4277
4278		params_arr = [],
4279
4280		queryString = (sourceURL.indexOf("?") !== -1) ? sourceURL.split("?")[1] : "";
4281
4282	if (queryString !== "") {
4283
4284		params_arr = queryString.split("&");
4285
4286		for (var i = params_arr.length - 1; i >= 0; i -= 1) {
4287
4288			param = params_arr[i].split("=")[0];
4289
4290			if (param === key) {
4291
4292				params_arr.splice(i, 1);
4293
4294			}
4295
4296		}
4297
4298		rtn = rtn + "?" + params_arr.join("&");
4299
4300	}
4301
4302	return rtn;
4303
4304};
4305
4306
4307hp.scrollTo = function (options) {
4308
4309	options = $.extend({
4310
4311		offset: 0,
4312
4313		duration: 777,
4314
4315		callback: function () {
4316
4317		}
4318
4319	}, options);
4320
4321
4322
4323	$('html:not(:animated),body:not(:animated)').animate(
4324
4325		{scrollTop: options.offset},
4326
4327		{
4328
4329			duration: options.duration,
4330
4331			complete: function () {
4332
4333				options.callback();
4334
4335			}
4336
4337		}
4338
4339	);
4340
4341};
4342
4343
4344hp.setHotelApiConfig = function(params) {
4345
4346	if (typeof params !== 'undefined') {
4347
4348		if (params.hotelId) {
4349
4350			IBE.Config.hotelId = params.hotelId;
4351
4352		}
4353
4354		if (params.apiKey) {
4355
4356			IBE.Config.apiKey = params.apiKey;
4357
4358		}
4359
4360	}
4361
4362};
4363
4364
4365hp.setOverlayDefaults = function (viewModel, session) {
4366
4367	if (session === false) {
4368
4369		return;
4370
4371	}
4372
4373	var currentValues = ko.toJS({
4374
4375		checkIn: ko.unwrap(viewModel.checkIn).format('YYYY-MM-DD'),
4376
4377		checkOut: ko.unwrap(viewModel.checkOut).format('YYYY-MM-DD'),
4378
4379		guests: ko.unwrap(viewModel.guests),
4380
4381		boardType: ko.unwrap(viewModel.boardType),
4382
4383		currency: ko.unwrap(viewModel.currency),
4384
4385		couponCode: ko.unwrap(viewModel.couponCode),
4386
4387		lang: ko.unwrap(viewModel.lang)
4388
4389	});
4390
4391
4392
4393	if (currentValues.checkIn !== session.checkIn) {
4394
4395		viewModel.checkIn(session.checkIn);
4396
4397	}
4398
4399
4400
4401	if (currentValues.checkOut !== session.checkOut) {
4402
4403		viewModel.checkOut(session.checkOut);
4404
4405	}
4406
4407
4408
4409	if (currentValues.boardType !== session.board) {
4410
4411		viewModel.boardType(session.board);
4412
4413	}
4414
4415
4416
4417	if (currentValues.currency !== session.currency) {
4418
4419		viewModel.currency(session.currency);
4420
4421	}
4422
4423
4424
4425	if (currentValues.lang !== session.lang) {
4426
4427		viewModel.lang(session.lang);
4428
4429	}
4430
4431
4432
4433	if (currentValues.couponCode !== session.couponCode) {
4434
4435		viewModel.couponCode(session.couponCode);
4436
4437	}
4438
4439
4440
4441	ko.utils.arrayForEach(currentValues.guests, function (guest) {
4442
4443		ko.utils.arrayForEach(session.rooms[0], function (sessionGuest) {
4444
4445			if (guest.pax !== sessionGuest.pax) {
4446
4447				var viewModelGuestType = ko.utils.arrayFilter(viewModel.guests, function (item) {
4448
4449					return item.ageCategory() === sessionGuest.ageCategory && item.fromAge() == sessionGuest.fromAge
4450
4451				});
4452
4453				if (viewModelGuestType.length) {
4454
4455					viewModelGuestType[0].pax(sessionGuest.pax);
4456
4457				}
4458
4459			}
4460
4461		});
4462
4463	});
4464
4465};
4466
4467
4468hp.setOverlayHash = function () {
4469
4470	var self = this;
4471
4472	var $window = $(window);
4473
4474	var overlayHash = 'booking-preferences';
4475
4476
4477
4478	self.hash = window.location.hash.replace('#!', '');
4479
4480
4481
4482	self.setHash = function (newHash) {
4483
4484		var hash = overlayHash;
4485
4486		if (typeof newHash !== 'undefined') {
4487
4488			hash = newHash;
4489
4490		}
4491
4492		window.location.hash = '#!' + hash;
4493
4494	};
4495
4496
4497
4498	self.init = function () {
4499
4500			
4501		$window.on('load', function () {
4502			setTimeout(function() {
4503				var $el = $('[data-hp-toggle=".hp-step1"]');
4504				if (self.hash === overlayHash && $el.length) {
4505					$el[0].click();
4506				}
4507			}, 1000);
4508		});
4509
4510		$(document).on('hp/overlayClosed', function (e, data) {
4511
4512			self.setHash('');
4513
4514		});
4515
4516	};
4517
4518
4519
4520	return self;
4521
4522};
4523
4524
4525hp.timestampToDate = function (UNIX_timestamp) {
4526
4527	var a = new Date(UNIX_timestamp * 1000);
4528
4529	var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
4530
4531	var year = a.getFullYear();
4532
4533	var month = months[a.getMonth()];
4534
4535	var date = a.getDate();
4536
4537	var hour = a.getHours();
4538
4539	var min = a.getMinutes();
4540
4541	var sec = a.getSeconds();
4542
4543	return  date + ' ' + month + ' ' + year + ' ' + hour + ':' + min + ':' + sec;
4544
4545};
4546
4547
4548hp.toDash = function (str, replacement) {
4549
4550	replacement = replacement || '-';
4551
4552
4553
4554	return str.replace(/([a-z])([A-Z])/g, '$1' + replacement + '$2').toLowerCase();
4555
4556};
4557
4558
4559hp.toggle = function () {
4560
4561
4562
4563	var self = this;
4564
4565
4566
4567	self.toggleOverlays = function ($el, e) {
4568
4569		var $target = $($el.data('hpToggle'));
4570
4571		var margin = $el.data('hpMarginTop') || undefined;
4572
4573		var useBackDrop = ($el.data('hpBackdrop') === undefined) ? true : $el.data('hpBackdrop');
4574
4575
4576
4577		if ($(e.target).parents('.hp-toggle').length && $target.hasClass("hp-toggle--open")) {
4578
4579			// if current clicked element is child of hp-toggle, don't close the overlay
4580
4581			return;
4582
4583		}
4584
4585        $('.hp-wait').hide();
4586
4587		$('.hp-toggle-open').slideUp();
4588
4589		$target.removeClass('hp-toggle--open');
4590
4591        $('body').removeClass('step1-open');
4592
4593		if ($target.is(':hidden')) {
4594
4595            if ($target.hasClass('hp-step1')){
4596
4597                $('body').addClass('step1-open');
4598
4599            }
4600
4601			if (typeof margin !== 'undefined') {
4602
4603				$target.css({top: hp.calculateTop($target, margin)});
4604
4605			}
4606
4607			$target.stop(true, true).fadeIn(100);
4608
4609			$target.addClass('hp-toggle--open');
4610
4611			if (useBackDrop) {
4612
4613				$('body').append('<div class="hp-backdrop"></div>');
4614
4615			}
4616
4617
4618
4619			$target.trigger('hp/overlayOpened', {
4620
4621				source: $el
4622
4623			});
4624
4625
4626
4627		} else {
4628
4629			$target.stop(true, true).fadeOut(100);
4630
4631			$target.removeClass('hp-toggle--open');
4632
4633            $('body').removeClass('step1-open');
4634
4635            $('.hp-wait').hide();
4636
4637			if (useBackDrop) {
4638
4639				$('.hp-backdrop').remove();
4640
4641			}
4642
4643
4644
4645			$target.trigger('hp/overlayClosed', {
4646
4647				source: $el
4648
4649			});
4650
4651		}
4652
4653	};
4654
4655
4656
4657	$(document).on('click', '[data-hp-toggle]', function (e) {
4658
4659		self.toggleOverlays($(this), e);
4660
4661	});
4662
4663
4664
4665	return self;
4666
4667};
4668ko.bindingHandlers.affiliate = {
4669	init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
4670		if (bindingContext.$root.cache.read('AllowAnalyticsTracking') === false) {
4671			return true;
4672		}
4673
4674		var val = ko.unwrap(valueAccessor());
4675
4676		if (typeof val.vendor === 'undefined' || typeof val.affiliateId === 'undefined') {
4677			return true;
4678		}
4679
4680		// find price and reservation id
4681		var affiliateId = val.affiliateId || 0;
4682		var reservationId = val.reservation.id;
4683		var totalCost = val.reservation.totalCost();
4684		var charatsee = 1 + val.taxPercentage;
4685		var totalCostWithoutTax = (totalCost / charatsee).toFixed(2);
4686
4687		switch (val.vendor.toLowerCase()) {
4688			case 'linkwise':
4689				var $body = $('body');
4690				// first load any external libraries
4691				var html = [
4692					'<img src="https://affiliate.linkwise.gr/i_track_sale/{AFFILIATE_ID}/:prod:{PRICE_WITHOUT_TAX}:qty:1/{RESERVATION_ID}/&sale_status=P">',
4693					'<script type="text/javascript">Linkwise.load_action("{AFFILIATE_ID}", "{RESERVATION_ID}", "1::{PRICE_WITHOUT_TAX}", "", "pending"); </script>',
4694					'<noscript>',
4695					'<img src="https://gr.linkwi.se/delivery/acl.php?cam_id={AFFILIATE_ID}&amp;trans_id={RESERVATION_ID}&amp;sale_amount=1::{PRICE_WITHOUT_TAX}&amp;adv_subid=&amp;status=pending" style="width:0px;height:0px;\"/>',
4696					'</noscript>'
4697				].join('');
4698
4699				// replace
4700				html = html.replace(/{PRICE_WITHOUT_TAX}/g, totalCostWithoutTax);
4701				html = html.replace(/{RESERVATION_ID}/g, reservationId);
4702				html = html.replace(/{AFFILIATE_ID}/g, affiliateId);
4703
4704				// save old document.write
4705				// http://www.jakobbeyer.de/asynchronous-google-adwords-conversion-tracking
4706				var oldDocumentWrite = document.write;
4707				document.write = function (node) {
4708					$body.append(node);
4709				};
4710
4711				$.getScript('https://gr.linkwi.se/delivery/lwc/lwc.js', function (data, textStatus, jqxhr) {
4712					setTimeout(function () {
4713						$body.append(html);
4714						bindingContext.$root.cache.clear('AllowAnalyticsTracking');
4715						document.write = oldDocumentWrite;
4716					}, 100);
4717				});
4718
4719				break;
4720			default:
4721				break;
4722		}
4723	}
4724};
4725ko.bindingHandlers.ga = {
4726	init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
4727		if ('function' !== typeof ga) {
4728			return;
4729		}
4730		var options = ko.utils.extend({
4731			category: '',
4732			action: '',
4733			label: ''
4734		}, valueAccessor());
4735
4736		$(element).on('click', function () {
4737			ga('send', 'event', options.category, options.action, options.label);
4738		});
4739	}
4740};
4741
4742ko.bindingHandlers.hover = {
4743	update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
4744		var value = valueAccessor();
4745		if (typeof(value.element) === 'undefined') {
4746			value.element = element;
4747		}
4748		ko.applyBindingsToNode(element, {
4749			event: {
4750				mouseenter: function () {
4751					if (value.element && value.css) {
4752						// toggle class on element
4753						$(value.element).toggleClass(value.css);
4754					}
4755					// togggle observable value
4756					value.value(!value.value());
4757				},
4758				mouseleave: function () {
4759					if (value.element && value.css) {
4760						// toggle class on element
4761						$(value.element).toggleClass(value.css);
4762					}
4763					// togggle observable value
4764					value.value(!value.value());
4765				}
4766			}
4767		});
4768	}
4769};
4770ko.bindingHandlers.loadingWhen = {
4771    init: function (element) {
4772        //unchanged
4773    },
4774    update: function (element, valueAccessor) {
4775        var
4776            //unwrap the value of the flag using knockout utilities
4777            isLoading = ko.utils.unwrapObservable(valueAccessor()),
4778
4779            //get a reference to the parent element
4780            $element = $(element),
4781
4782            //get a reference to the loader
4783            $loader = $element.find("div.loader")
4784
4785        //get a reference to every *other* element
4786        $childrenToHide = $element.children(":not(div.loader)");
4787
4788        //if we are currently loading...
4789        if (isLoading) {
4790            //...hide and disable the children...
4791            $childrenToHide.css("visibility", "hidden").attr("disabled", "disabled");
4792            //...and show the loader
4793            $loader.show();
4794        }
4795        else {
4796            //otherwise, fade out the loader
4797            $loader.fadeOut("fast");
4798            //and re-display and enable the children
4799            $childrenToHide.css("visibility", "visible").removeAttr("disabled");
4800        }
4801    }
4802};
4803ko.bindingHandlers.log = {
4804	update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
4805
4806		// store a counter with this element
4807		var count = ko.utils.domData.get(element, "_ko_logger") || 0;
4808		var data = ko.toJS(ko.unwrap(valueAccessor()) || allBindings());
4809
4810		ko.utils.domData.set(element, "_ko_logger", ++count);
4811
4812		if (window.console && console.log) {
4813			console.log(count, element, data);
4814		}
4815	}
4816};
4817ko.bindingHandlers.scroll = {
4818	init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
4819		var $el = $(element);
4820		var val = ko.unwrap(valueAccessor());
4821		var _defaults = {
4822			advanced: {
4823				updateOnContentResize: true,
4824				updateOnSelectorChange: "ul li"
4825			}
4826		};
4827		var options = $.extend(true, _defaults, val);
4828		$el.mCustomScrollbar(options);
4829	}
4830};
4831ko.bindingHandlers.selectBoxIt = {
4832	listenBindings: ['value', 'disable', 'options', 'foreach'],
4833	init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
4834		setTimeout(function () {
4835			var options = ko.unwrap(valueAccessor());
4836			var $el = $(element);
4837
4838			$el.selectBoxIt($.extend(options, {
4839				isMobile: function () {
4840					// Adapted from http://www.detectmobilebrowsers.com
4841					var ua = navigator.userAgent || navigator.vendor || window.opera;
4842					// Checks for iOs mobile devices
4843					return (/iPhone|iPod|iPad/).test(ua);
4844				},
4845				downArrowIcon: 'fa fa-angle-down',
4846				defaultText: ('undefined' !== typeof $el.data('placeholder')) ? $el.data('placeholder') : '',
4847				'native': ('undefined' !== typeof $el.data('native')) ? $el.data('native') : false
4848			}));
4849
4850			ko.computed(function () {
4851
4852				$.each(ko.bindingHandlers.selectBoxIt.listenBindings, function (i, binding) {
4853					var b = allBindings.get(binding);
4854					b = $.isFunction(b) ? b() : b;
4855					ko.unwrap(b);
4856					$el.data('selectBox-selectBoxIt').refresh();
4857				});
4858
4859			}, null, {disposeWhenNodeIsRemoved: element});
4860
4861
4862			$el.change(function(e) {
4863				if(!$(this).hasClass('hp-guests-select')) {
4864					return;
4865				}
4866				
4867				existingValue = this.value;
4868
4869				var step1 = bindingContext.$parents[1];
4870				var requestedRoom =  bindingContext.$parent;
4871
4872				if(!requestedRoom.canAddGuest( step1.hotel().conditions.maxGuests + 1)){
4873					$(this).val($.data(this, 'val')); //set back; 
4874					return;
4875				}
4876
4877			});
4878
4879			$el.on('change', function (e) {				
4880				var value = allBindings.get('value');
4881				value(parseInt($(this).val()));
4882			});
4883			ko.utils.domNodeDisposal.addDisposeCallback(element, function (node) {
4884				$(node).data('selectBox-selectBoxIt').destroy();
4885			});
4886		}, 500);
4887	}
4888	
4889};
4890ko.bindingHandlers.slick = {
4891	init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
4892		var $el = $(element);
4893		$(document).on('click', '.hp-slick-prev', function () {
4894			$el.slick('slickPrev');
4895		});
4896
4897		$(document).on('click', '.hp-slick-next', function () {
4898			if ($(this).data('validate')) {
4899				if (bindingContext.$root.vm.step3.validateCustomer() === false) {
4900					return false;
4901				}
4902			}
4903			$el.slick('slickNext');
4904		});
4905
4906		$el.on('beforeChange', function (event, slick, currentSlide, nextSlide) {
4907			$(document).find('.hp-checkout-nav span').removeClass('active');
4908			$(document).find('.hp-checkout-nav span:eq(' + nextSlide + ')').addClass('active');
4909		});
4910
4911		if (bindingContext.$root.isMultiProperty){
4912			var selectedStep2ViewModel = ko.utils.arrayFilter(bindingContext.$root.vm.step2.properties(), function(item){
4913				return item.selectedRooms().length > 0
4914			});
4915			if (selectedStep2ViewModel.length) {
4916				selectedStep2ViewModel[0].selectedRooms.subscribe(function (value) {
4917					$el.slick('slickGoTo', 0);
4918				});
4919			}
4920		} else {
4921			bindingContext.$root.vm.step2.selectedRooms.subscribe(function (value) {
4922				$el.slick('slickGoTo', 0);
4923			});
4924		}
4925
4926		window.setTimeout(function(){
4927			$el.slick({
4928				infinite: false,
4929				dots: true,
4930				arrows: false,
4931				speed: 277,
4932				swipe: false,
4933				slidesToShow: 1,
4934				draggable: false,
4935				fade: true,
4936				cssEase: 'linear'
4937			});
4938		}, 50);
4939	}
4940};
4941ko.bindingHandlers.src = {
4942	update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
4943		ko.bindingHandlers.attr.update(element, function () {
4944			return {src: valueAccessor()}
4945		});
4946	}
4947};
4948ko.bindingHandlers.timeAgo = {
4949	toTimeAgo: function (dt) {
4950		var secs = (((new Date()).getTime() - dt.getTime()) / 1000),
4951			days = Math.floor(secs / 86400);
4952
4953		return days === 0 && (
4954			secs < 60 && "just now" ||
4955			secs < 120 && "a minute ago" ||
4956			secs < 3600 && Math.floor(secs / 60) + " minutes ago" ||
4957			secs < 7200 && "an hour ago" ||
4958			secs < 86400 && Math.floor(secs / 3600) + " hours ago") ||
4959			days === 1 && "yesterday" ||
4960			days < 31 && days + " days ago" ||
4961			days < 60 && "one month ago" ||
4962			days < 365 && Math.ceil(days / 30) + " months ago" ||
4963			days < 730 && "one year ago" ||
4964			Math.ceil(days / 365) + " years ago";
4965	},
4966	update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
4967		var val = ko.unwrap(valueAccessor());
4968		var date = new Date(val); // WARNING: this is not compatibile with IE8
4969		var timeAgo = ko.bindingHandlers.timeAgo.toTimeAgo(date);
4970
4971		return ko.bindingHandlers.html.update(element, function () {
4972			return '<time datetime="' + encodeURIComponent(val) + '">' + timeAgo + '</time>';
4973		});
4974	}
4975};
4976ko.bindingHandlers.toggleActive = {
4977	update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
4978		var $el = $(element);
4979
4980		$el.on('click', function(e){
4981			$el.attr('disabled', 'disabled');
4982
4983			var data = ko.toJS(ko.unwrap(valueAccessor()));
4984			if (data === true) {
4985				$el.removeAttr('disabled');
4986			}
4987		});
4988	}
4989};
4990ko.bindingHandlers.uppercase = {
4991	update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
4992		ko.bindingHandlers.text.update(element, function () {
4993			return valueAccessor().toLocaleUpperCase();
4994		});
4995	}
4996};
4997function BookwizeLiquidAppViewModel(options) {
4998    var self = this;
4999    self.lang = ko.observable();
5000    self.currency = ko.observable();
5001	self.phoneCountryCode = ko.observable();
5002    // the localstoreage cache key for storing Customer Selection
5003    self.customerPreferencesCacheKey = 'BookwizeLiquid_CustomerPreferences';
5004
5005    // used for localstorage cache
5006    self.cachePrefix = '';
5007
5008    // stores all hotel IDs/API Keys
5009    self.properties = IBE.Config.properties;
5010
5011    // keeps customer preferences and cached the in Cache engine
5012    self.session = ko.observable(false);
5013
5014    // each time a session variable changes, we update the session in cache
5015    self.session.subscribe(function (value) {
5016        var data = self.cache.read(self.customerPreferencesCacheKey);
5017        data = value;
5018        self.cache.write(self.customerPreferencesCacheKey, data);
5019    });
5020// sort rooms based on availability / price
5021    self.sort = function (rooms) {
5022        var available = [];
5023        var onrequest = [];
5024        var other = [];
5025
5026        ko.utils.arrayForEach(rooms, function (item) {
5027            if (item.availability === IBE.Config.roomAvailability.Available) {
5028                available.push(item);
5029            } else if ((item.availability === IBE.Config.roomAvailability.UponRequest || item.availability === IBE.Config.roomAvailability.NoAvailability )
5030                && item.minPrice() > 0) {
5031                onrequest.push(item);
5032            } else {
5033                other.push(item);
5034            }
5035        });
5036        available.sort(firstBy(function (v1, v2) {
5037            return v1.minPrice() - v2.minPrice();
5038        })).reverse().reverse();
5039
5040        onrequest.sort(firstBy(function (v1, v2) {
5041            return v1.minPrice() - v2.minPrice()
5042        })).reverse().reverse();
5043
5044        other.sort(firstBy(function (v1, v2) {
5045            return v2.minPrice() - v1.minPrice();
5046        }));
5047
5048        return available.concat(onrequest).concat(other);
5049    };
5050
5051    // used to render the HTML after VMs have data
5052    self.ready = ko.observable(false);
5053    self.ready.subscribe(function (value) {
5054        if (value) {
5055            $('.hp-legacy-booking-link--loading').removeClass('hp-legacy-booking-link--loading');
5056            self.acceptedCookies();            
5057        }    
5058    }, this);
5059
5060    self.acceptedCookies = function () {
5061    	if (!window.OnetrustActiveGroups) {
5062    		return;
5063    	}
5064    	if (window.OnetrustActiveGroups.includes('C0004')) { // targeting Cookies
5065    		IBE.Config.userCookieAcceptance = true;
5066    	}        
5067    }
5068
5069    // used to disable the submit buttons of step1
5070    self.submitting = ko.observable(false);
5071
5072    // used in rooms internal page
5073    self.currentRoom = ko.observable(false);
5074
5075    // used in offers internal page
5076    self.currentOffer = ko.observable(false);
5077
5078    // used inside room page to show hotel's room's rateplans
5079    self.currentHotel = ko.observable({});
5080
5081    // tha instance of Magnific popup
5082    self.popup = {};
5083
5084    // set and check the step1 overlay hash
5085    self.overlayHash = null;
5086
5087    // stores IBE view model instances for single/multi properties
5088    self.vm = {};
5089
5090    // true/false if website is multi-property
5091    self.isMultiProperty = IBE.Config.isMultiProperty;
5092
5093    if (self.isMultiProperty === true) {
5094        ko.utils.arrayForEach(self.properties, function (item) {
5095            self.cachePrefix += item.hotelId + '_';
5096        });
5097    } else {
5098        self.cachePrefix += IBE.Config.hotelId + '_';
5099    }
5100    // the cache key used to save to local storage
5101
5102    // Overload IBE Cache
5103    if (typeof Cache !== 'undefined') {
5104        self.cache = Cache;
5105        self.cache.prefix += self.cachePrefix;
5106    }
5107
5108    // used in step1 hotel select box and step2 filtering hotel results
5109    self.selectedHotel = ko.observable(self.cache.read(self.customerPreferencesCacheKey + '_selectedHotel') || parseInt(window.location.hash.replace(/[^\d.]/g, '')));
5110    self.selectedHotel.subscribe(function (value) {
5111        if (typeof value === 'undefined' || value === false) {
5112            return;
5113        }
5114        self.cache.write(self.customerPreferencesCacheKey + '_selectedHotel', value);
5115    });
5116    var $sidebarHotel = $('.sidebar__hotel');
5117    if ($sidebarHotel.length && $sidebarHotel.data('hotelId')) {
5118        self.selectedHotel($sidebarHotel.data('hotelId'));
5119    }
5120    // the current active booking step
5121    self.step = ko.observable(0);
5122    self.step.subscribe(function (value) {
5123        hp.addClass($('body'), 'hp-step-' + value);
5124        if( self.session() ) {
5125            $('body').addClass('hp-top-bar-enabled');
5126        }
5127
5128    });
5129    self.setStep = function (step, data) {
5130        self.step(step);
5131        switch (step) {
5132            case 1:
5133                try {
5134                    if (self.isMultiProperty === true) {
5135                        self.initMultipleStep('step1', data);
5136                    } else {    
5137                        
5138                        self.vm.step1.ready.subscribe(function(value){
5139                            if( IBEConfig.post.post_type == "cpt_offer" && IBEConfig.offers.length > 0 ) {
5140                                currentOfferId = IBEConfig.offers[0].bookwize_liquid_offer_id;
5141                                //search all offers to find the one specified in current offer ( wp cpt_offer post )
5142                                ko.utils.arrayForEach(self.vm.step1.hotel().offers, function(offer, index){    
5143                                    if( currentOfferId == offer.id){
5144                                        if( typeof offer.rules.checkInMin !== "undefined" ){
5145                                            checkInMinMoment = moment(offer.rules.checkInMin);
5146                                            self.vm.step1.minCheckInDate(checkInMinMoment.toDate());
5147                                            if( checkInMinMoment.diff(self.vm.step1.checkIn(), 'days') > 0 ){
5148                                                self.vm.step1.checkIn(checkInMinMoment);                            
5149                                            }
5150                                        }
5151                                        
5152                                        if( typeof offer.rules.checkInMax !== "undefined" )
5153                                            self.vm.step1.maxCheckInDate(moment(offer.rules.checkInMax).toDate());
5154                
5155                                        if( typeof offer.rules.checkOutMax !== "undefined" )
5156                                            self.vm.step1.maxCheckOutDate(moment(offer.rules.checkOutMax).toDate());
5157                                    }                                    
5158                                });
5159                            }
5160                        });
5161
5162                        self.vm.step1.init(data);
5163                        
5164                    }
5165                } catch (e) {
5166                    IBE.Utils.consoleLog(e);
5167                }
5168                break;
5169            case 2:
5170                try {
5171                    if (self.isMultiProperty === true) {
5172                        self.initMultipleStep('step2', data);
5173                    } else {
5174                        if (self.vm.step2) {
5175                            self.vm.step2.init(data);
5176                        }
5177                    }
5178                } catch (e) {
5179                    IBE.Utils.consoleLog(e);
5180                }
5181                break;
5182            case 3:
5183                try {
5184                    var params = {};
5185                    if (self.isMultiProperty === true) {
5186                        params = hp.getHotelCredentialsFromRoomId(data.bookingInfo.rooms[0].roomId(), self.properties);
5187                        // we need to re instantiate the step3 VM because user may select room from different hotel
5188                        self.ready(false);
5189                        self.vm.step3 = new BookwizeLiquidStep3Vie
5189wModel(self);
5190
5191                    }
5192                    self.ready(false);
5193                    BookwizeLiquid.vm.step3.dispose();
5194                    BookwizeLiquid.vm.step3 = undefined;
5195                    self.vm.step3 = new BookwizeLiquidStep3ViewModel(self);
5196                    self.vm.step3.init(data, params);
5197
5198                    self.ready(true);
5199                } catch (e) {
5200                    if (self.isMultiProperty === true) {
5201
5202                    } else {
5203                        self.vm.step2.message(e);
5204                    }
5205                    IBE.Utils.consoleLog(e);
5206                }
5207                break;
5208            case 4:
5209                //debugger;
5210                if (typeof data === 'undefined') {
5211                    self.ready(true);
5212                    break;
5213                }
5214                try {
5215                    var params = {};
5216                    if (self.isMultiProperty === true) {
5217                        params = hp.getHotelCredentialsFromRoomId(data.rooms[0].roomId(), self.properties);
5218                    }
5219                    params.trackAdwords = false;
5220                    params.appendAdwordsImage = false;
5221                    params.trackAdwordsAsync = true;
5222                    params.trackEcommerceScripts = false;
5223                    self.vm.step4.init(data, params);
5224                } catch (e) {
5225                    self.vm.step3.message(e);
5226                    IBE.Utils.consoleLog(e);
5227                }
5228
5229                break;
5230            case 'my-reservation':
5231
5232                try {
5233                    var user = self.cache.read('ReservationUser');
5234                    if (user) {
5235                        self.vm.myReservation.username(user.username);
5236                        self.vm.myReservation.password(user.password);
5237                    }
5238                    self.vm.myReservation.init(data);
5239                } catch (e) {
5240                    self.vm.myReservation.message(e);
5241                    IBE.Utils.consoleLog(e);
5242                }
5243
5244                break;
5245        }
5246    };
5247
5248    // Submit Functions for all view models
5249    self.submit = {
5250        step1: function (data) {
5251            // save to session the selected currency and language
5252            data.currency = self.currency;
5253            data.lang = self.lang;
5254
5255            self.cache.write(self.customerPreferencesCacheKey, data);
5256            self.session($.extend({
5257                nights: moment(data.checkOut).diff(moment(data.checkIn), 'days'),
5258                totalGuests: self.calculateTotalGuests(data)
5259            }, ko.toJS(data)));
5260            self.setStep(2, data);
5261        },
5262        step2: function (data) {
5263            self.setStep(3, data);
5264        },
5265        step3: function (data) {
5266            self.setStep(4, data);
5267        }
5268    };
5269
5270    // when .hp-checkout is opened this will be true
5271    // and self.redirectToCheckRates will do nothing
5272    self.checkoutProcessStart = false;
5273
5274    // When we click on top book now btn is set to TRUE in order to redirect after step1.submits AND step2.ready
5275    self.forceCheckRatesRedirect = false;
5276
5277    // used to toggle elements through data-toggle="{selector}"
5278    self.toggle = null;
5279
5280    // this function is called when we click on the top book now button
5281    self.setCheckRatesRedirect = function () {
5282        // if current page is NOT
5283        // 1. check rates
5284        // 2. room page
5285        // set the redirect flag to TRUE
5286        if (hp.isCheckRatesPage() === false
5287            && hp.isRoomPage(IBE.Config.post.ID) === false
5288            && hp.isOfferPage(IBE.Config.post.ID) === false) {
5289            self.forceCheckRatesRedirect = true;
5290        }
5291
5292    };
5293    self.redirectToCheckRates = function (force, hash) {
5294        // if user has started the checkout process
5295        if (self.checkoutProcessStart === true) {
5296            return;
5297        }
5298        var checkRatesPage = self.getPostByBookwizeLiquidPageType('checkrates');
5299
5300        // if there is not set a checkpage page
5301        if (checkRatesPage.hasOwnProperty('bookwize_liquid_page_url') === false) {
5302            console.log('Bookwize Liquid: Please create a check-rates page.')
5303            return false;
5304        }
5305        // if we ARE in check rates page DO NOTHING
5306        if (hp.isCheckRatesPage() && force !== true) {
5307            return true;
5308        }
5309        // if we ARE in a room page DO NOTHING
5310        if (hp.isRoomPage(IBE.Config.post.ID) && force !== true) {
5311            return true;
5312        }
5313        var url = checkRatesPage.bookwize_liquid_page_url;
5314
5315
5316        // eg. in header template we pass force=true
5317        if (force) {
5318            window.location.href = url;
5319            return true;
5320        }
5321
5322        // check the force redirect flag
5323        if (self.forceCheckRatesRedirect == true) {
5324            window.location.href = url;
5325            return true;
5326        }
5327        window.location.href = url;
5328    };
5329
5330    self.calculateTotalGuests = function (data) {
5331        var total = 0;
5332        ko.utils.arrayForEach(data.rooms[0], function (guest) {
5333            if(!guest.pax){
5334                guest.pax = 0;
5335            }
5336            total += guest.pax;
5337        });
5338        return total;
5339    };
5340
5341    // when we click on the cancel button
5342    self.clearPreferences = function () {
5343        hp.clearCache();
5344    };
5345
5346    // loop through all step2 requestedRooms and find associated room based on post Id
5347    self.setCurrentRoom = function () {
5348        var found = false;
5349
5350        if (self.isMultiProperty === false) {
5351            if (typeof self.vm.step2.request === 'undefined') {
5352                return false;
5353            }
5354            ko.utils.arrayForEach(self.vm.step2.request().hotelResults()[0].requestedRooms()[0].roomResults(), function (room) {
5355                if (found) {
5356                    return;
5357                }
5358                ko.utils.arrayForEach(IBE.Config.rooms, function (item) {
5359                    if (found) {
5360                        return;
5361                    }
5362                    if (room.roomId() == item.bookwize_liquid_room_id && IBE.C
5362onfig.post.ID == item.ID) {
5363                        self.currentRoom(room);
5364                        found = true;
5365                    }
5366                });
5367            });
5368
5369        } else {
5370            ko.utils.arrayForEach(self.vm.step2.properties(), function (viewModel) {
5371                if (found) {
5372                    return;
5373                }
5374                ko.utils.arrayForEach(viewModel.request().hotelResults()[0].requestedRooms()[0].roomResults(), function (room) {
5375                    if (found) {
5376                        return;
5377                    }
5378                    if (typeof IBE.Config.hotels_rooms[viewModel.hotelId()].rooms !== 'undefined'
5379                        && IBE.Config.hotels_rooms[viewModel.hotelId()].rooms != null) {
5380                        ko.utils.arrayForEach(IBE.Config.hotels_rooms[viewModel.hotelId()].rooms, function (item) {
5381                            if (found) {
5382                                return;
5383                            }
5384                            if (room.roomId() == item.bookwize_liquid_room_id && IBE.C
5384onfig.post.ID == item.ID) {
5385                                self.currentRoom(room);
5386                                self.currentHotel = viewModel;
5387                                found = true;
5388                            }
5389                        });
5390                    }
5391                });
5392            });
5393
5394        }
5395        if (!found) {
5396            return false;
5397        }
5398    };
5399
5400    // loop through all step2 requestedRooms and find associated room based on post Id
5401    self.setCurrentOffer = function () {
5402        var found = false;
5403        ko.utils.arrayForEach(self.vm.step1.hotel().offers, function (offer) {
5404            if (found) {
5405                return;
5406            }
5407            ko.utils.arrayForEach(IBEConfig.offers, function (item) {
5408                if (found) {
5409                    return;
5410                }
5411                if (offer.id == item.bookwize_liquid_offer_id && IBEConfig.post.ID == item.ID) {
5412                    offer.rooms = [];
5413                    self.currentOffer(offer);
5414                    found = true;
5415                }
5416            });
5417        });
5418        if (!found) {
5419            return false;
5420        }
5421
5422        // offer found, search for rooms that it is applied
5423        ko.utils.arrayForEach(self.vm.step2.request().hotelResults()[0].requestedRooms()[0].roomResults(), function (room) {
5424            if (self.currentOffer().rules.rooms.indexOf(room.roomId()) > -1) {
5425                ko.utils.arrayForEach(room.rateplans(), function(a) {
5426                    if(self.currentOffer().id === a.ratePlanId()){
5427                        self.currentOffer().rooms.push(room);
5428                    }
5429
5430                });
5431
5432            }
5433        });
5434    };
5435
5436    // used to toggle the popup preferences or redirect to check rates page
5437    self.bookButtons = function (selector) {
5438        if (typeof selector === 'undefined') {
5439            selector = '.btn--check-rates';
5440        }
5441        var $buttons = $(selector);
5442        if ($buttons.length === 0) {
5443            return;
5444        }
5445
5446        $buttons.removeAttr('target');
5447        $buttons.on('click', function (e) {
5448            e.preventDefault();
5449
5450            if (self.session() !== false) {
5451                self.redirectToCheckRates(false, $(this).data('roomType'));
5452            } else {
5453                self.toggle.toggleOverlays($('[data-hp-toggle=".hp-step1"]'), e);
5454            }
5455            return false;
5456        });
5457    };
5458    // General HTML / CSS / JS events
5459    self.design = function () {
5460
5461        self.popup = $.magnificPopup.instance;
5462
5463        // used to set / check the overlay hash;
5464        self.overlayHash = hp.setOverlayHash();
5465
5466        // because step2 might be multi/single property
5467        // we need to bind here the overlayOpened/Closed event
5468        $(document).on('hp/overlayOpened', function (e, data) {
5469            if ($(e.target).is('.hp-checkout')) {
5470                if (window.location.hash.length) {
5471                    window.location.hash = '';
5472                }
5473                self.checkoutProcessStart = true;
5474                hp.scrollTo({duration: 333});
5475            }
5476        });
5477        $(document).on('hp/overlayClosed', function (e, data) {
5478            if ($(e.target).is('.hp-checkout')) {
5479                self.checkoutProcessStart = false;
5480                self.step(2);
5481            }
5482        });
5483
5484        $(document).on('ready',function() {
5485            if (hp.isCheckRatesPage() ) {                
5486                setTimeout(function(){                    
5487                    self.vm.step2.showTriptease();
5488                }, 2000);
5489            }
5490        });
5491
5492        // bind ESC key to close open overlays
5493        hp.escapeKeyUp();
5494
5495        // for currencies dropdown
5496        hp.dropdown();
5497
5498        // used to toggle elements through data-toggle="{selector}"
5499        self.toggle = hp.toggle();
5500
5501        self.overlayHash.init();
5502
5503        self.bookButtons();
5504
5505        // when overlay opened and click on transparent div, close all overlays
5506        $(document).on('click', '.hp-backdrop', function (e) {
5507            hp.closeOverlays();
5508        });
5509    };
5510
5511    self.createDeepLink = function (roomId, hotelId) {
5512        var room_url = self.getRoomByBookwizeLiquidRoomId(roomId, hotelId).bookwize_liquid_room_url;
5513
5514        if (typeof room_url === 'undefined') {
5515            return '#';
5516        }
5517        if (IBE.Config.isUnderSameDomain === true) {
5518            return room_url;
5519        } else {
5520            return room_url + '?' + hp.deepLink(self.session());
5521        }
5522    };
5523
5524    self.createIntegratedDeepLinking = function (hotelId) {
5525        var url = self.getPostByBookwizeLiquidPageType('integrated').bookwize_liquid_page_url;
5526        if (self.session() === false) {
5527            return url + '?hotel_id=' + hotelId;
5528        }
5529        var session = ko.toJS(self.session());
5530        session.rooms.push(session.rooms[0]);
5531        var query = $.param(
5532            $.extend({hotel_id: hotelId}, hp.deepLink(session, 'object'))
5533        );
5534        return url + '?' + decodeURIComponent(query);
5535    };
5536
5537    self.getRoomByBookwizeLiquidRoomId = function (roomId, hotelId) {
5538        var rooms = [];
5539
5540        if (self.isMultiProperty === true && typeof hotelId !== 'undefined') {
5541            if (typeof IBE.Config.hotels_rooms[hotelId] !== 'undefined' && IBE.Config.hotels_rooms[hotelId] !== null) {
5542                rooms = (typeof IBE.Config.hotels_rooms[hotelId].rooms !== 'undefined') ? IBE.Config.hotels_rooms[hotelId].rooms : [];
5543            }
5544        } else {
5545            rooms = IBE.Config.rooms;
5546        }
5547
5548        var room = ko.utils.arrayFirst(rooms, function (item) {
5549                return item.bookwize_liquid_room_id == roomId;
5550            }) || {};
5551
5552        if (room.bookwize_liquid_room_image === '' && self.isMultiProperty) {
5553            ko.utils.arrayForEach(self.vm.step1.step1ViewModels, function (viewModel) {
5554                ko.utils.arrayForEach(viewModel.rooms(), function (hpRoom) {
5555                    if (hpRoom.id === roomId) {
5556                        room.bookwize_liquid_room_image = hpRoom.photos[0].url();
5557                    }
5558                });
5559            });
5560        }
5561
5562        return room;
5563    };
5564    self.getRoomByPostId = function (id) {
5565        if (self.isMultiProperty === false) {
5566            return ko.utils.arrayFirst(IBE.Config.rooms, function (item) {
5567                    return item.ID == id;
5568                }) || {};
5569        }
5570        // multi-property
5571        for (var prop in IBE.Config.hotels_rooms) {
5572            var room = ko.utils.arrayFirst(IBE.Config.hotels_rooms[prop].rooms, function (item) {
5573                    return item.ID == id;
5574                }) || {};
5575            if (room.ID) {
5576                return room;
5577            }
5578        }
5579        return room;
5580    };
5581
5582    self.getOfferByBookwizeLiquidOfferId = function (id) {
5583        return ko.utils.arrayFirst(IBE.Config.offers, function (item) {
5584                return item.bookwize_liquid_offer_id == id;
5585            }) || {};
5586    };
5587    self.getOfferByPostId = function (id) {
5588        return ko.utils.arrayFirst(IBE.Config.offers, function (item) {
5589                return item.ID == id;
5590            }) || {};
5591    };
5592
5593    self.getPostByBookwizeLiquidPageType = function (type) {
5594        return ko.utils.arrayFirst(IBE.Config.pages, function (item) {
5595                return item.bookwize_liquid_page_type == type;
5596            }) || {};
5597    };
5598
5599    self.getHotelId = function () {
5600        var $sidebarHotel = $('.sidebar__hotel');
5601        if ($sidebarHotel.length && $sidebarHotel.data('hotelId')) {
5602            return $sidebarHotel.data('hotelId');
5603        }
5604        return false;
5605    };
5606
5607    self.getBookwizeLiquidRoomData = function (roomId) {
5608        var room = {
5609            maxGuests: 0
5610        };
5611        if (self.isMultiProperty && self.properties.length) {
5612            ko.utils.arrayForEach(self.vm.step1.step1ViewModels, function (viewModel) {
5613                ko.utils.arrayForEach(viewModel.rooms(), function (hpRoom) {
5614                    if (hpRoom.id === roomId) {
5615                        room = hpRoom;
5616                    }
5617                });
5618            });
5619        } else {
5620            ko.utils.arrayForEach(self.vm.step1.rooms(), function (hpRoom) {
5621                if (hpRoom.id === roomId) {
5622                    room = hpRoom;
5623                }
5624            });
5625        }
5626
5627        return room;
5628    };
5629
5630    self.promises = {
5631        step1: [],
5632        step2: []
5633    };
5634    self.initMultipleStep = function (step, data) {
5635        if (typeof step === 'undefined') {
5636            step = 'step1';
5637        }
5638        if (self.promises[step].length) {
5639            return false;
5640        }
5641
5642        ko.utils.arrayForEach(self.properties, function (item) {
5643            for (var prop in item.vm) {
5644                if (prop === step) {
5645                    // store initmulti promise method of Step[ X ]ViewModel
5646                    self.promises[step].push(item.vm[prop].initmulti(data, item));
5647                }
5648            }
5649        });
5650
5651        // when all promises are completely initialize VMs
5652        $.when.apply($, self.promises[step]).then(function () {
5653            if (arguments.length !== self.promises[step].length) {
5654                return false;
5655            }
5656            try {
5657                // self.ready(false);
5658                switch (step) {
5659                    case 'step1':
5660                        self.vm[step] = new BookwizeLiquidMultipleStep1ViewModel(self, arguments);
5661                        break;
5662                    case 'step2':
5663                        if (hp.isCheckRatesPage() || hp.isOfferPage() || hp.isRoomPage()) {
5664                            self.ready(false);
5665                        }
5666
5667                        self.vm[step] = new BookwizeLiquidMultipleStep2ViewModel(self, arguments);
5668                        if (self.forceCheckRatesRedirect === true) {
5669                            return self.redirectToCheckRates();
5670                        }
5671                        break;
5672                }
5673
5674                if (self.vm.step1 && self.vm.step2 && self.vm.step1.ready() && self.vm.step2.ready()) {
5675                    // user has submitted step1 and step2 is initialized
5676
5677                    self.promises.step1 = [];
5678                    self.promises.step2 = [];
5679
5680                    // if we are in a room page-
5681                    self.setCurrentRoom();
5682
5683                    // if we are in a offer page
5684                    self.setCurrentOffer();
5685
5686                    self.ready(true);
5687                } else if (self.session() === false && self.vm.step1.ready()) {
5688                    // user's first time :P
5689                    self.promises[step] = [];
5690
5691                    self.ready(true);
5692                }
5693            } catch (e) {
5694                IBE.Utils.consoleLog(e);
5695            }
5696        });
5697    };
5698
5699    self.init = function (options) {
5700        var $hotelProxyApp = $('[data-bookwize-liquid-app]');
5701        if ($hotelProxyApp.length == 0) {
5702            return;
5703        }
5704
5705        try {
5706
5707            if (self.isMultiProperty && self.properties.length) {
5708                ko.utils.arrayForEach(self.properties, function (item, index) {
5709                    item.vm = {};
5710                    item.vm['step1'] = new BookwizeLiquidStep1ViewModel(self, item);
5711                    item.vm['step2'] = new BookwizeLiquidStep2ViewModel(self, item);
5712                });
5713                // assign bindings fro step1 because all other VM use them
5714                self.currency = self.properties[0].vm.step1.currency;
5715                self.lang = self.properties[0].vm.step1.lang;
5716            } else {
5717                self.vm['step1'] = new BookwizeLiquidStep1ViewModel(self);
5718                if (self.vm['step1'].requestedRooms()[0]) {
5719                    ko.utils.arrayForEach(self.vm['step1'].requestedRooms()[0].guests, function (item) {
5720                        if (item.ageCategory() != IBE.Config.guestType.Adult) {
5721                            item.childrenAges = ko.observableArray([]);
5722                            // set the childrenAges and selected
5723                        }
5724                    });
5725
5726                    self.guests = self.vm['step1'].requestedRooms()[0].guests;
5727                }
5728                // assign bindings fro step1 because all other VM use them
5729                self.currency = self.vm.step1.currency;
5730                self.lang = self.vm.step1.lang;
5731                self.vm['step2'] = new BookwizeLiquidStep2ViewModel(self);
5732            }
5733
5734            self.vm['step3'] = new BookwizeLiquidStep3ViewModel(self);
5735            self.vm['step4'] = new BookwizeLiquidStep4ViewModel(self);
5736            self.vm['myReservation'] = new BookwizeLiquidMyReservationViewModel(self);
5737
5738            // if we need to bind to self.lang, or self.currency changes
5739            // here we should add the subscribers
5740
5741            self.lang($('html').attr('lang'));//.substring(0, 2));
5742
5743            // apply bindings to <html>
5744            ko.applyBindings(self, $hotelProxyApp[0]);
5745            self.setStep(1);
5746
5747        } catch (e) {
5748            IBE.Utils.consoleLog(e);
5749        }
5750
5751        // if we completed a request/reservation
5752        var confirmedPage = self.getPostByBookwizeLiquidPageType('confirmed');
5753        if (confirmedPage.ID === IBE.Config.post.ID) {
5754
5755            // ready reservation user from cache
5756            var user = self.cache.read('ReservationUser');
5757            var flagTracking = self.cache.read('flagTracking');
5758            if (user !== false) {
5759                self.cache.write('flagTracking', true);
5760                self.vm.step4.init(user, {
5761                    reservationSubmitted: true,
5762                    trackEcommerceScripts: !flagTracking
5763                });
5764            }
5765        }
5766
5767        // if we are in my reservation page
5768        var myreservationPage = self.getPostByBookwizeLiquidPageType('myreservation');
5769        if (myreservationPage.ID === IBE.Config.post.ID) {
5770            // ready reservation user from cache
5771            self.setStep('my-reservation');
5772        }
5773
5774        // finally apply all HTML/JS functions
5775        self.design();
5776    };
5777}
5778function BookwizeLiquidMyReservationViewModel(parent) {
5779	var self = new IBE.ViewModels.MyReservation();
5780
5781	self.lang = parent.lang;
5782	self.currency = parent.currency;
5783	self.statusUpdate = ko.observable(false);
5784
5785	function _setSuccessMessage() {
5786		hp.scrollTo({
5787			offset: 0
5788		});
5789		self.message({
5790			id: 200,
5791			severity: 'success',
5792			text: IBE.Utils.translate('updateReservationSuccess')
5793		});
5794	}
5795
5796	self.beforeLogin = function ($data, e) {
5797		// in order not to click multiple times the SUBMIT button
5798		hp.disable($(e.currentTarget));
5799
5800		self.login();
5801	};
5802
5803	self.beforeConfirmAction = function ($data, viewModel, e) {
5804		if (typeof e === 'undefined') {
5805			e = viewModel;
5806		}
5807		// in order not to click multiple times the SUBMIT button
5808		hp.disable($(e.currentTarget));
5809
5810		self.confirmAction($data, e);
5811	};
5812
5813	self.events.subscribe(function (status) {
5814
5815		self.ready(status);
5816
5817	}, this, 'ready');
5818
5819	self.events.subscribe(function (data) {
5820		parent.cache.clear('ReservationUser');
5821		parent.cache.clear('flagTracking');
5822		self.errors.showAllMessages(false); // FORCE NEW validation
5823		self.ready(true);
5824	}, this, 'logout');
5825
5826	self.events.subscribe(function (data) {
5827
5828		// save reservation user to automatically login in my reservation page
5829		parent.cache.write('ReservationUser', {
5830			username: data.email,
5831			password: data.code
5832		});
5833
5834	}, this, 'login');
5835
5836	self.events.subscribe(function (error) {
5837		self.ready(true);
5838
5839		self.message(error);
5840
5841		// enable all available buttons in My Reservation section
5842		hp.enable($(document).find('.hp-my-reservation .hp-btn'));
5843
5844	}, this, 'error');
5845
5846	// when updating the reservation
5847	// the below events will trigger based on the
5848	// update action
5849
5850	self.events.subscribe(function (data) {
5851		if (data === true) {
5852			self.statusUpdate(data);
5853			_setSuccessMessage();
5854		}
5855	}, this, 'CancelledReservation');
5856
5857	self.events.subscribe(function (data) {
5858		if (data === true) {
5859			self.statusUpdate(data);
5860			_setSuccessMessage();
5861		}
5862	}, this, 'CancelledRequest');
5863
5864	self.events.subscribe(function (data) {
5865		if (data === true) {
5866			self.statusUpdate(data);
5867			_setSuccessMessage();
5868		}
5869	}, this, 'ConfirmedReservation');
5870
5871	return self;
5872}
5873function BookwizeLiquidStep1ViewModel(parent, params) {
5874
5875    // this is used to set the IBE.Config credentials
5876    // for current hotel
5877    hp.setHotelApiConfig(params);
5878
5879    var self = new IBE.ViewModels.Step1ViewModel();
5880
5881    self.params = params;
5882
5883    self.dfd = {};
5884
5885    self.guests = [];
5886
5887    self.maxCheckInDate = ko.observable();
5888    self.maxCheckOutDate = ko.observable();
5889
5890    self.events.subscribe(function (value) {
5891        if (value === false) {
5892            return;
5893        }
5894
5895        self.design();
5896
5897        parent.submitting(false);
5898
5899        if (parent.isMultiProperty) {
5900            self.dfd.resolve({
5901                viewModel: self,
5902                ready: value
5903            });
5904        } else {
5905
5906            // Check if user has already set the preferences
5907            if(self.requestedRooms()[0] !== 'undefined') {
5908                self.guests = self.requestedRooms()[0].guests;
5909            }
5910            ko.utils.arrayForEach(self.guests, function (item) {
5911                if (item.ageCategory() != IBE.Config.guestType.Adult) {
5912                    item.childrenAges = ko.observableArray([]);
5913                    // set the childrenAges and selected
5914                }
5915            });
5916            _parseSession();
5917            $(document).on('hp/overlayOpened', function (e) {
5918                hp.setOverlayDefaults(self, parent.session());
5919                var $el = $(e.target);
5920                // add hash and on load trigger it
5921                if ($el.is('.hp-guests-overlay') === false) {
5922                    // parent.overlayHash.setHash();
5923                } else {
5924                    $(document).on('click', function (e) {
5925                        if($(e.target).parents('.hp-toggle').length !== 0){
5926                            $('.hp-guests-overlay').addClass('hp-toggle--open').fadeIn(100);
5927                        }
5928                       else if (!$(e.target).is('.hp-toggle') && $(e.target).parents('.hp-guests-overlay').length === 0) {
5929
5930                            $el.removeClass('hp-toggle--open');
5931                            $el.stop(true, true).fadeOut(100);
5932                        }
5933
5934                    });
5935                }
5936            });
5937
5938            // set app view model ready
5939            parent.ready(value);
5940        }
5941
5942        //_parseSession();
5943    }, this, 'ready');
5944    self.events.subscribe(function (error) {
5945
5946        if (error.severity == 'Forbidden') {
5947            self.ready(false);
5948        }
5949        IBE.Utils.logEvent(error);
5950    }, this, 'error');
5951
5952    self.events.subscribe(function (result) {
5953
5954        if (result.success) {
5955            parent.setCheckRatesRedirect();
5956            // when binding the html to vm the popup template data was changed
5957            // this was trigger the afterRender callback
5958            if (parent.isMultiProperty) {
5959                ko.utils.arrayForEach(parent.properties, function (item) {
5960                    if (typeof item.vm.step2.popup() !== 'undefined') {
5961                        item.vm.step2.popup().data(false);
5962                    }
5963                });
5964            } else if (typeof parent.vm.step2.popup() !== 'undefined') {
5965                parent.vm.step2.popup().data(false);
5966            }
5967
5968            parent.submit.step1(result.success);
5969        }
5970        if (result.failure) {
5971            self.message(result.failure);
5972            parent.submitting(false);
5973        }
5974    }, this, 'submit');
5975
5976    self.lang.subscribe(function (value) {
5977        if (typeof value === 'undefined' || value === false) {//} || parent.session() === false) {
5978            return;// false;
5979        }
5980        parent.lang(value);
5981    });
5982
5983    self.currency.subscribe(function (value) {
5984        if (typeof value === 'undefined' || value === false || parent.session() === false) {
5985            return;// false;
5986        }
5987        parent.session().currency = value;
5988        parent.session.valueHasMutated();
5989    });
5990
5991	self.showRoomBoardRateWithSurcharge = function (t, i, r) {
5992        if (!r) return false;
5993        return t + self.surchargeTotalCost(i, r);
5994    };
5995
5996    self.showRoomBoardRateDailyBreakdownWithSurcharge = function (t, i, r) {
5997        if (!r) return false;
5998        return self.request().nights() > 0 ? t + self.surchargeTotalCost(i, r) / self.request().nights() : t;
5999    };
6000
6001    function _parseSession() {
6002        var result = parent.cache.read(parent.customerPreferencesCacheKey);
6003        var todaysDate = new Date();
6004        if (result === false) {
6005            if(IBEConfig.post_offer_checkin !== '') {
6006                var checkInOffer = new Date(IBEConfig.post_offer_checkin);
6007                
6008                if(todaysDate <= checkInOffer ) {
6009                    self.checkIn(checkInOffer);
6010                }
6011            }
6012            if(IBEConfig.post_offer_checkout !== '') {
6013                var checkOutOffer = new Date(IBEConfig.post_offer_checkout);
6014                if(todaysDate <= checkOutOffer || ( checkInOffer !== undefined && checkInOffer <= checkOutOffer)) {
6015                    self.checkOut(checkOutOffer);
6016                }
6017            }
6018            return false;
6019        }
6020
6021        parent.session(ko.toJS(result));
6022
6023        self.currency(parent.session().currency);
6024        self.checkIn(new Date(parent.session().checkIn));
6025        self.checkOut(new Date(parent.session().checkOut));
6026        self.couponCode(parent.session().couponCode);
6027
6028        // assign rooms based on session
6029        try {
6030            ko.utils.arrayForEach(self.requestedRooms()[0].guests, function (selfGuestType, idx) {
6031                var guestType = selfGuestType;
6032                ko.utils.arrayForEach(parent.session().rooms[0], function (sessionGuestType) {
6033                    if (guestType.ageCategory() == sessionGuestType.ageCategory && guestType.fromAge() == sessionGuestType.fromAge) {
6034                        self.requestedRooms()[0].guests[idx].pax(sessionGuestType.pax);
6035                    }
6036                });
6037            });
6038
6039            ko.utils.arrayForEach(self.guests, function (guest) {
6040                var session_guest = ko.utils.arrayFilter(parent.session().rooms[0], function (item) {
6041                    return item.ageCategory === guest.ageCategory() && item.fromAge === guest.fromAge()
6042                });
6043                if (session_guest.length) {
6044                    guest.pax(session_guest[0].pax);
6045                    if (session_guest[0].ages.length) {
6046                        var options = [];
6047                        for (var i = guest.fromAge(); i <= guest.toAge(); i++) {
6048                            options.push(i);
6049                        }
6050                        for (var j = 0; j < session_guest[0].ages.length; j++) {
6051                            guest.childrenAges.push({
6052                                selected: ko.observable(session_guest[0].ages[j]),
6053                                options: options
6054                            });
6055                        }
6056                    }
6057                }
6058            });
6059            parent.submit.step1(parent.session());
6060        } catch (e) {
6061            IBE.Utils.consoleLog(e);
6062        }
6063    }
6064
6065    self.design = function () {
6066        $(document).on('click', '.hp-close--checkout', function (e) {
6067            hp.closeOverlays();
6068        });
6069        $(document).on('click', '.hp-close', function (e) {
6070            hp.closeOverlays();
6071
6072        });
6073    };
6074
6075    self.beforeSubmit = function ($data, e) {
6076        parent.submitting(true);
6077        $('.hp-wait.loader').show();
6078        self.validate();
6079    };
6080
6081    self.initmulti = function (data, params) {
6082        self.dfd = $.Deferred();
6083
6084        hp.setHotelApiConfig(params);
6085        self.init(params);
6086
6087        return self.dfd.promise();
6088    };
6089
6090    self.addGuest = function (parent, data) {
6091        if (data.pax() > data.options().length - 1) {
6092            return false;
6093        }
6094        if (self.hotel()) {
6095            if (data.ageCategory() !== IBE.Config.guestType.Infant) {
6096                if (!parent.canAddGuest(self.hotel().conditions.maxGuests)) {
6097                    return false;
6098                }
6099            }
6100
6101
6102            data.pax(data.pax() + 1);
6103        }
6104    };
6105
6106    self.removeGuest = function (data) {
6107        if (data.pax() === 0) {
6108            return false;
6109        }
6110        if (data.pax() === 1 && data.ageCategory() === 'Adult') {
6111            return false;
6112        }
6113        data.pax(data.pax() - 1);
6114    };
6115
6116    return self;
6117}
6118function BookwizeLiquidStep2ViewModel(parent, params) {
6119
6120    hp.setHotelApiConfig(params);
6121
6122    var self = new IBE.ViewModels.Step2ViewModel();
6123
6124    self.params = params;
6125
6126    self.dfd = {};
6127    self.soldPopup = function (data) {
6128        parent.popup.open({
6129            items: {
6130                src: '#hp-popup-sold' + self.hotelId()
6131            },
6132            type: 'inline'
6133        }, 0);
6134    };
6135    self.lang = parent.lang;
6136    self.currency = parent.currency;
6137    self.group = function () {
6138        var groupedRooms = [];
6139        for (var prop in IBE.Config.room_types) {
6140            if (IBE.Config.room_types.hasOwnProperty(prop)) {
6141                var rooms = ko.utils.arrayFilter(IBE.Config.rooms, function (post) {
6142                    return post.bookwize_liquid_room_type === prop;
6143                });
6144                ko.utils.arrayForEach(rooms, function (room, index) {
6145                    var found = ko.utils.arrayFilter(self.request().hotelResults()[0].requestedRooms()[0].roomResults(), function (item) {
6146                        return item.roomId() === room.bookwize_liquid_room_id;
6147                    });
6148                    if (found.length) {
6149                        rooms[index] = found[0];
6150                    }
6151                });
6152
6153                groupedRooms.push({
6154                    group: prop,
6155                    type: IBE.Config.room_types[prop],
6156                    rooms: parent.sort(rooms)
6157                });
6158            }
6159        }
6160        return groupedRooms;
6161    };
6162    self.design = function () {
6163        hp.closeOverlays();
6164
6165        $(document).on('click', '#hp-popup-rateplan .hp-btn', function () {
6166            parent.popup.close();
6167        });
6168
6169    };
6170    
6171    // self.sortRooms = function () {
6172    //     if (typeof self.request() === 'undefined') {
6173    //         return false;
6174    //     }
6175    //     var rooms = self.request().hotelResults()[0].requestedRooms()[0].roomResults();
6176
6177    //     rooms.sort(
6178    //         firstBy(function (v) {
6179    //             if (typeof v.availability === 'undefined' || v.minPrice() === null) {
6180    //                 return 1;
6181    //             }
6182    //             return v.minPrice() > 0 && (v.availability === IBE.Config.roomAvailability.Available) ? -1 : 1;
6183    //         }).thenBy(function (v1, v2) {
6184    //             if (v1.minPrice() === null || v2.minPrice() === null) {
6185    //                 return 1;
6186    //             }
6187    //             return v1.minPrice() < v2.minPrice() ? -1 : 1;
6188    //         }).thenBy(function (v) {
6189    //             if (typeof v.availability === 'undefined') {
6190    //                 return 1;
6191    //             }
6192    //             return v.availability === IBE.Config.roomAvailability.Available ? -1 : 1;
6193    //         })
6194    //     );
6195    // };
6196
6197    self.isRoomRatesVisible = function (roomResult) {
6198        for(var i=0; i < roomResult.rateplans().length; i++) {
6199            var rateplan = roomResult.rateplans()[i];
6200            if(rateplan.isVisible() && rateplan.price() !== 0) {
6201                return true;
6202            }
6203        }
6204        return false;
6205    };
6206
6207	self.sortRooms = function () {
6208		if (typeof self.request() === 'undefined') {
6209			return false;
6210		}
6211		var rooms = self.request().hotelResults()[0].requestedRooms()[0].roomResults();
6212		rooms.sort(function (v1, v2) {
6213			if (typeof v1.availability === 'undefined' || typeof v2.availability === 'undefined'
6214				|| v1.availability === null || v2.availability === null) {
6215				return 0;
6216			}
6217
6218			var toSortPriority = function (availability) {
6219				if (availability === IBE.Config.roomAvailability.Available || availability === 1) {
6220					return 3;
6221				}
6222				if (availability === IBE.Config.roomAvailability.AvailabilityRestriction || availability === -3) {
6223					return 2;
6224				}
6225				if (availability === IBE.Config.roomAvailability.CannotHost || availability === -4) {
6226					return 1;
6227				}
6228				if (availability === IBE.Config.roomAvailability.NoAvailability || availability === 0) {
6229					return 0;
6230				}
6231				return -10 + availability;
6232			};
6233
6234			var s1 = toSortPriority(v1.availability);
6235			var s2 = toSortPriority(v2.availability);
6236
6237			if (s1 !== s2) {
6238				return s2 >
6238 s1 ? 1 : -1;
6239			}
6240
6241			var roomRatesVisible1 = self.isRoomRatesVisible(v1);
6242			var roomRatesVisible2 = self.isRoomRatesVisible(v2);
6243			if (roomRatesVisible1 && !roomRatesVisible2) { return -1; }
6244			if (!roomRatesVisible1 && roomRatesVisible2) { return 1; }
6245
6246			var p1 = v1.price();
6247			var p2 = v2.price();
6248			if (p1 !== null && p2 !== null) {
6249				return p1 < p2 ? -1 : 1;
6250			}
6251
6252			return 0;
6253		});
6254
6255		self.request().hotelResults()[0].requestedRooms()[0].roomResults.valueHasMutated();
6256
6257		self.sortRatePlans();
6258	};
6259
6260    self.sortRatePlans = function () {
6261        if (typeof self.request() === 'undefined') {
6262            return false;
6263        }
6264        var rooms = self.request().hotelResults()[0].requestedRooms()[0].roomResults();
6265        for(var i=0; i < rooms.length; i++) {
6266            // console.log("step2 sortRatePlans "+rooms[i].info().name);
6267            rooms[i].rateplans().sort(
6268                function (v1, v2) {
6269                // debugger;
6270                    if ( v1.roomBoards().length == 0 || v2.roomBoards().length == 0) {
6271                        return 0;
6272                    }
6273
6274                    var v1MinRoomBoard = null;
6275                    for(var i = 0; i < v1.roomBoards().length; i++) {
6276                        var v1RoomBoardTmp = v1.roomBoards()[i];
6277                        if(!v1RoomBoardTmp.showPrices() || v1RoomBoardTmp.price() === null) {
6278                            continue;
6279                        }
6280                        if(v1MinRoomBoard == null || v1RoomBoardTmp.price() < v1MinRoomBoard.price()) {
6281                            v1MinRoomBoard = v1RoomBoardTmp;
6282                        }
6283                    }
6284                    var v2MinRoomBoard = null;
6285                    for(var i = 0; i < v2.roomBoards().length; i++) {
6286                        var v2RoomBoardTmp = v2.roomBoards()[i];
6287                        if(!v2RoomBoardTmp.showPrices() || v2RoomBoardTmp.price() === null) {
6288                            continue;
6289                        }
6290                        if(v2MinRoomBoard == null || v2RoomBoardTmp.price() < v2MinRoomBoard.price()) {
6291                            v2MinRoomBoard = v2RoomBoardTmp;
6292                        }
6293                    }
6294
6295                    if(v1MinRoomBoard === null || v2MinRoomBoard === null || v1MinRoomBoard.price() === v2MinRoomBoard.price()) {
6296                        return -1;
6297                    }
6298                    var v1MinPrice = self.showRoomBoardRateWithSurcharge(v1MinRoomBoard.price(), v1.ratePlanId(), rooms[i].roomIndex());
6299					var v2MinPrice = self.showRoomBoardRateWithSurcharge(v2MinRoomBoard.price(), v2.ratePlanId(), rooms[i].roomIndex());
6300
6301					return v1MinPrice < v2MinPrice ? -1 : 1;
6302                }
6303            );
6304        }
6305    };
6306
6307    self.showPopup = function (room, activeMenu, otherData) {
6308        if(activeMenu === 'rateplan' && otherData instanceof IBE.Models.RoomBoard) {
6309            self.selectedRoomBoardForPopup = otherData.boardType();
6310            self.selectedRatePlanForPopup = otherData.ratePlanId;
6311        }
6312        if (room instanceof BookwizeLiquidMultipleStep2ViewModel && typeof otherData !== 'undefined' && otherData.room) {
6313            room = otherData.room;
6314        }
6315        if (activeMenu !== 'callus') {
6316            self.popup().toggle(room);
6317        }
6318        self.popup().activeMenu(activeMenu);
6319
6320
6321        parent.popup.open({
6322            items: {
6323                src: '#hp-popup' + self.hotelId()
6324            },
6325            type: 'inline',
6326            callbacks: {
6327                open: function () {
6328                    $('#hp-popup' + self.hotelId()).addClass('hp-popup--' + self.popup().activeMenu());
6329                },
6330                close: function () {
6331                    $('#hp-popup' + self.hotelId()).removeClass('hp-popup--' + self.popup().activeMenu());
6332                }
6333            }
6334        }, 0);
6335    };
6336
6337    self.showCrossSales = function (roomId) {
6338        var room = ko.utils.arrayFilter(self.request().hotelResults()[0].requestedRooms()[0].roomResults(), function (room) {
6339            return room.roomId() == roomId;
6340        });
6341        if (room.length > 0) {
6342            return (room[0].availability == IBE.Config.roomAvailability['Available']);
6343        }
6344        return false;
6345    };
6346    self.offerExist = function (offer) {
6347        var found = false;
6348        ko.utils.arrayFilter(self.request().hotelResults()[0].requestedRooms()[0].roomResults(), function (room) {
6349            if (room.ratePlanId() == offer) {
6350                found = true;
6351            }
6352        });
6353        return found;
6354    };
6355    self.ready.subscribe(function (value) {
6356        if (value === false) {
6357            return;
6358        }
6359        // sort rooms based on availability, booking status
6360        //self.sortRooms();
6361        self.sortRatePlans();
6362        if (parent.isMultiProperty === false) {
6363            if(typeof self.bookingProcess() !== 'undefined') {
6364                self.bookingProcess().boardType = parent.vm.step1.boardType;
6365                self.bookingProcess().totalGuests = parent.vm.step1.totalGuests;
6366                var groupedRooms = self.group();
6367                self.request().hotelResults()[0].requestedRooms()[0].groupedRooms = ko.observableArray(groupedRooms);
6368            }
6369            
6370        } else {
6371
6372            var currentStep2ViewModel = ko.utils.arrayFilter(parent.properties, function (item) {
6373                return self.hotelId() == item.hotelId;
6374            });
6375            if (currentStep2ViewModel.length && typeof self.bookingProcess() !== 'undefined') {
6376                self.bookingProcess().boardType = currentStep2ViewModel[0].vm.step1.boardType;
6377                self.bookingProcess().totalGuests = currentStep2ViewModel[0].vm.step1.totalGuests;
6378            }
6379        }
6380
6381        if (parent.forceCheckRatesRedirect === true && parent.isMultiProperty === false) {
6382            return parent.redirectToCheckRates();
6383        }
6384
6385        self.removeLastRoom();
6386
6387        parent.submitting(false);
6388
6389        if (parent.isMultiProperty === true) {
6390            self.dfd.resolve({
6391                viewModel: self,
6392                ready: value
6393            });
6394        } else {
6395            // if we are in a room page-
6396            parent.setCurrentRoom();
6397
6398            // if we are in a offer page
6399            parent.setCurrentOffer();
6400
6401            // set app view model ready
6402            parent.ready(value);
6403        }
6404
6405        self.design();
6406    });
6407
6408    self.events.subscribe(function (data) {
6409        parent.submit.step2(data);
6410    }, this, 'submit');
6411
6412    self.events.subscribe(function (error) {
6413        if (error.severity == "Forbidden") {
6414            self.ready(false);
6415        }
6416        IBE.Utils.logEvent(error);
6417        parent.submitting(false);
6418    }, this, 'error');
6419
6420    self.roomUpgrade = function (roomId) {
6421        var upgrades = [];
6422
6423        // get current room
6424        var room = ko.utils.arrayFirst(self.request().hotelResults()[0].requestedRooms()[0].roomResults(), function (room) {
6425            return room.roomId() == roomId;
6426        });
6427
6428        if (room === null) {
6429            return upgrades;
6430        }
6431
6432        // get current ADR
6433        var currenetAverageDailyRate = room.averageDailyRate();
6434
6435        if(room.averageDailyRate() < 0.1) {
6436            for(var i=0; i < room.rateplans().length; i++) {
6437                if(room.rateplans()[i].averageDailyRate() > 0.1) {
6438                    room.ratePlanId(room.rateplans()[i].ratePlanId());
6439                }
6440
6441            }
6442        }
6443        var allPrices = [];
6444        for (var i = 0; i < room.rateplans().length; i++) {
6445        	var roomRates = [];
6446        	if (room.rateplans()[i].isVisible()) {
6447        		for (let x = 0; x < room.rateplans()[i].roomBoards().length; x++) {
6448        			if (room.rateplans()[i].roomBoards()[x].showPrices()) {
6449        				roomRates.push(room.rateplans()[i].roomBoards()[x].price());
6450        			}
6451        		}
6452        		
6453        	}
6454        	var roomMinPrice = Math.min(...roomRates);
6455        	if (roomRates.length > 0 && roomMinPrice > 0) {
6456				allPrices.push(roomMinPrice );
6457        	}
6458        }
6459        var minRate = Math.min(...allPrices);
6460
6461        var availablesRooms = [];
6462        var roomResults = self.request().hotelResults()[0].requestedRooms()[0].roomResults();
6463        for (var y = 0; y < roomResults.length; y++) {
6464        	var item = roomResults[y];
6465			var mappedRoom = IBEConfig.rooms.filter(row => row.bookwize_liquid_room_id == item.roomId());
6466			if (item.roomId() == roomId || mappedRoom.length === 0 || item.availableRooms <= 0 || item.alternativeBoard()) {
6467                continue;
6468            }
6469			availablesRooms.push(item.roomId());
6470		}
6471
6472		ko.utils.arrayForEach(self.roomsPrices(), function (item, index) {
6473            if (!availablesRooms.includes(item.roomId) || item.averageDailyRate <= minRate || item.availability < 1) {
6474            	return;
6475            }
6476
6477            upgrades.push(item);
6478        });
6479
6480        upgrades.sort(function (a, b) {
6481            return a.price > b.price ? 1 : -1;
6482        });
6483        return upgrades;
6484    };
6485
6486    self.roomDowngrade = function (roomId) {
6487        var downgrades = [];
6488        var mostExpensiveRoom = self.roomsPrices().sort(function (a, b) {
6489            return a.price < b.price ? 1 : -1;
6490        })[0];
6491        // if the current room is not the most expensive one
6492        // then return empty array
6493        if (mostExpensiveRoom) {
6494            if (roomId !== mostExpensiveRoom.roomId) {
6495
6496                return downgrades;
6497            }
6498        }
6499
6500        // get current room
6501        var room = ko.utils.arrayFirst(self.request().hotelResults()[0].requestedRooms()[0].roomResults(), function (room) {
6502            return room.roomId() == roomId;
6503        });
6504
6505        if (room === null) {
6506            return downgrades;
6507        }
6508
6509        // get current ADR
6510        var currenetAverageDailyRate = room.averageDailyRate();
6511
6512        ko.utils.arrayForEach(self.roomsPrices(), function (item, index) {
6513            if (item.roomId === roomId) {
6514                return;
6515            }
6516
6517            if (item.averageDailyRate >= currenetAverageDailyRate) {
6518                return;
6519            }
6520
6521            // push rooms with greater ADR
6522            downgrades.push(item);
6523        });
6524
6525        return downgrades.sort(function (a, b) {
6526            a.price < b.price ? 1 : -1;
6527        });
6528    };
6529    /**
6530     * Give hotels/info call response and return the
6531     * correct guests for step2 init method
6532     *
6533     * @param hotelInfo
6534     * @param guests
6535     * @returns {*}
6536     *
6537     */
6538    self.prepareMultiPropertyGuestTypes = function (hotelInfo, guests) {
6539        var _tmp_guests = ko.toJS(guests); //in order to remove references
6540
6541        // add all hotel's supported guests
6542        $.extend(guests, hotelInfo.info.guestTypes);
6543
6544        // loop through guests and add the selected pax
6545        ko.utils.arrayForEach(guests, function (dataGuest) {
6546            var guest = ko.utils.arrayFilter(_tmp_guests, function (item) {
6547                return dataGuest.ageCategory === item.ageCategory;
6548            });
6549            if (guest.length) {
6550                dataGuest.pax = guest[0].pax;
6551            }
6552        });
6553
6554        return guests;
6555    };
6556
6557    self.initmulti = function (data, params) {
6558        self.dfd = $.Deferred();
6559
6560        hp.setHotelApiConfig(params);
6561
6562        $.ajaxSetup({
6563            dataType: 'json',
6564            contentType: 'application/json'
6565        });
6566        self.init(data, params);
6567
6568        return self.dfd.promise();
6569    };
6570	
6571    
6572    return self;
6573}
6574function BookwizeLiquidStep3ViewModel(parent) {
6575	var self = new IBE.ViewModels.Step3ViewModel();
6576
6577	self.lang = parent.lang;
6578	self.currency = parent.currency;
6579
6580	self.design = function () {
6581		parent.popup.close();
6582		$('.hp-supplements').mCustomScrollbar({theme: 'minimal'});
6583	};
6584
6585	self.validateExternalPaymentButton = function () {
6586		if (!self.terms.value() && self.terms.id !== -1) {
6587			self.customer().errors().customer.showAllMessages();
6588			self.terms.value.notifySubscribers();
6589			return;
6590		}
6591		window.scrollTo(0, 0);
6592		self.externalPaymentGateway().payWithSixPaymentPage();
6593	};
6594
6595	self.validateCardPaymentButton = function () {
6596		if (!self.terms.value() && self.terms.id !== -1) {
6597			self.customer().errors().customer.showAllMessages();
6598			self.terms.value.notifySubscribers();
6599			return;
6600		}
6601		window.scrollTo(0, 0);
6602		self.externalPaymentGateway().showSixCreditCardForm();
6603	};
6604
6605	self.validateCustomer = function () {
6606		var returnValue = true;
6607
6608		// we set by default Terms & Conditions to true
6609		// self.customer().acceptTerms(true);
6610
6611		// and an empty value for arrivalTime
6612		//self.customer().arrivalTime(' - ');
6613
6614		if (self.customer().errors().customer().length > 0 && self.customer().errors().customer()[0] !== null) {
6615			self.customer().errors().customer.showAllMessages();
6616			returnValue = false;
6617		}
6618
6619		if (returnValue && typeof ga !== 'undefined') {
6620			ga('send', 'event', 'Liquid', 'Booking Flow', 'Payment Details');
6621		}
6622
6623		return returnValue;
6624	};
6625
6626	self.beforeSubmit = function () {
6627        $('.hp-wait.loader').show();
6628        hp.scrollTo({duration: 333});
6629		var hasErrors = false;
6630		if (self.bookingProcess().isOnline()) {
6631			if (self.requiredCreditCard() && self.customer().errors().creditCard().length > 0 && self.customer().errors().creditCard()[0] !== null) {
6632				self.customer().errors().creditCard.showAllMessages();
6633				hasErrors = true;
6634			}
6635		}
6636        if (!self.terms.value() && self.terms.id !== -1) {
6637            self.customer().errors().customer.showAllMessages();
6638            self.terms.value.notifySubscribers();
6639
6640            hasErrors = true;
6641		}
6642		
6643		if (hasErrors == true) {
6644            $('.hp-wait.loader').hide();
6645			return;
6646		}
6647
6648		// keep only numbers from customer phone
6649		var customerPhone = self.customer().phone().match(/\d+/g);
6650		if (customerPhone !== null && customerPhone[0].length > 0) {
6651			if(parent.phoneCountryCode() !== '' && parent.phoneCountryCode() !== undefined) {
6652				self.customer().phone(parent.phoneCountryCode() + ' ' + customerPhone.join(''));
6653			} else {
6654				self.customer().phone(customerPhone.join(''));
6655			}
6656		}
6657
6658		var country = ko.utils.arrayFirst(self.countries(), function(item){
6659				return item.code() === $('#CustomerCountry').val();
6660			}
6661		);
6662		
6663		if (country !== null){
6664			self.customer().country(ko.toJS(country));
6665		}
6666
6667		var customerPreferences = parent.cache.read(parent.customerPreferencesCacheKey);
6668        if (customerPreferences.travelAgencyID !== '') {
6669            var notes = self.customer().notes() + '<ul><li>' + IBE.Utils.translate('travelAgencyID') + ': ' + customerPreferences.travelAgencyID + '</li></ul>';
6670            self.customer().notes(notes);
6671        }
6672
6673        if (self.bookingProcess().paymentMethodType() === 'ExternalPaymentGateway') {
6674			self.externalPaymentGateway().beforeSubmit();
6675			if (!self.externalPaymentGateway().externalPaymentGatewayErrors()) {
6676				parent.submitting(true);
6677			}
6678			return;
6679		}
6680		
6681		parent.submitting(true);
6682
6683		self.makeReservation();
6684	};
6685
6686	self.splitSupplements = function() {
6687		var result = {
6688			mandatory: [],
6689			optional: []
6690		}
6691		ko.utils.arrayForEach(self.supplements(), function (item) {
6692			ko.utils.arrayForEach(item.supplementResults(), function (supplement) {
6693				if (supplement) {
6694					if (supplement.isMandatory) {
6695						result.mandatory.push(supplement);
6696					} else {
6697						result.optional.push(supplement);
6698					}
6699				}
6700			});
6701		});
6702		return result;
6703	};
6704
6705	self.events.subscribe(function (value) {
6706		if (value === false) {
6707			return;
6708		}
6709
6710		// override customer telephone
6711		self.customer().phone = ko.observable('').extend({
6712			phone: {message: IBE.Utils.translate("validation.required")}
6713		});
6714
6715		self.design();
6716        if (IBE.Config.ibeFlightInfo == 'True') {
6717            self.flightInformation(true);
6718        }
6719
6720        self.externalPaymentGateway().init();
6721        
6722		parent.ready(value);
6723	}, this, 'ready');
6724
6725	self.titleOptions = [
6726		{ key: 'Mr', text: IBE.Utils.translate('title-mr') },
6727		{ key: 'Mrs', text: IBE.Utils.translate('title-mrs') },
6728		{ key: 'Dr', text: IBE.Utils.translate('title-dr') }
6729	];
6730	
6731	self.events.subscribe(function (data) {
6732        if (self.flightInformation()) {
6733            data.notes += '<ul>';
6734            data.notes += '<li><b>' + IBE.Utils.translate('flightInfo') + '</b></li>';
6735            if (self.flightInformationDetails.flightNumber()) {
6736                data.notes += '<li>' + IBE.Utils.translate('label_flightNumber') + ': ' + ko.unwrap(self.flightInformationDetails.flightNumber) + "</li>";
6737            }
6738            if (self.flightInformationDetails.airportOfArrival()) {
6739                data.notes += '<li>' + IBE.Utils.translate('airportOfArrival') + ': ' + ko.unwrap(self.flightInformationDetails.airportOfArrival) + "</li>";
6740            }
6741            if (self.flightInformationDetails.estimatedTime()) {
6742                data.notes += '<li>' + IBE.Utils.translate('estimatedTime') + ': ' + ko.unwrap(self.flightInformationDetails.estimatedTime().value) + "</li>";
6743            }
6744
6745            data.notes += '</ul>';
6746		}
6747		
6748		parent.submit.step3(data);
6749		
6750        IBE.Utils.deleteAuthenticationCookie("Authorization=");
6751        parent.cache.clear('Reservation');
6752        parent.cache.clear('ReservationUser');
6753        parent.cache.clear('flagTracking');
6754
6755	}, this, 'submit');
6756
6757	self.events.subscribe(function (error) {
6758		IBE.Utils.logEvent(error);
6759		parent.submitting(false);
6760	}, this, 'error');
6761
6762	self.moveToTab3 = function() {
6763		// console.log("tab3");
6764		setTimeout(function () {
6765			$('.slick-slider').slick('slickGoTo', 2);
6766		}, 20);
6767	};
6768	
6769	return self;
6770}
6771function BookwizeLiquidStep4ViewModel(parent) {
6772    var self = new IBE.ViewModels.Step4ViewModel();
6773
6774    self.lang = parent.lang;
6775    self.currency = parent.currency;
6776
6777    self.events.subscribe(function (value) {
6778        // debugger;
6779        // we came here from redirection
6780        if (self.options.reservationSubmitted === true) {
6781            return;
6782        }
6783
6784        if (value === false) {
6785            return;
6786        }
6787
6788        parent.vm.step2.ready(false);
6789
6790        // clear all cached data
6791        parent.cache.clear();
6792
6793        // save reservation user to automatically login in my reservation page
6794        parent.cache.write('ReservationUser', {
6795            username: self.reservations()[0].customer.email(),
6796            password: self.reservations()[0].code
6797        });
6798
6799        // save reservation for thank you page
6800        parent.cache.write('Reservation', {
6801            lang: ko.toJS(self.lang()),
6802            hotel: ko.toJS(self.hotel()),
6803            reservation: ko.toJS(self.reservations()[0])
6804        });
6805
6806        if (typeof ga !== 'undefined') {
6807            ga('send', 'event', 'Liquid', 'Booking Flow', 'Complete ' + self.reservations()[0].status());
6808        }
6809
6810        // redirect user to thank-you page
6811        var redirect = parent.getPostByBookwizeLiquidPageType('confirmed').bookwize_liquid_page_url;
6812
6813        if (typeof redirect === 'undefined' || redirect.indexOf('http') == -1) {
6814            parent.popup.open({
6815                items: {
6816                    src: '#hp-popup-error'
6817                },
6818                type: 'inline'
6819            });
6820            return;
6821        }
6822
6823            parent.cache.write('AllowAnalyticsTracking', true);
6824            redirect += '?code=' + self.reservations()[0].code;
6825        if (window.location.href !== redirect) {
6826            window.location.href = redirect;
6827        }
6828        hp.closeOverlays();
6829    }, this, 'ready');
6830
6831    self.events.subscribe(function (error) {
6832
6833        parent.ready(true);
6834        parent.vm.step3.ready(true);
6835        parent.vm.step3.clearMessage();
6836        parent.vm.step3.message(error);
6837
6838        // enable button previously disabled on click event
6839        parent.submitting(false);
6840
6841        if (error.severity == 'critical') {
6842            IBE.Utils.logEvent(error);
6843        }
6844        parent.vm.step3.moveToTab3();
6845    }, this, 'error');
6846
6847    return self;
6848}
6849function BookwizeLiquidMultipleStep1ViewModel(parent, multViewModelData) {
6850	if (typeof multViewModelData === 'undefined' || multViewModelData.length === 0) {
6851		return {};
6852	}
6853	var self = this;
6854
6855	self.design = function () {
6856		$(document).on('hp/overlayOpened', function (e) {
6857			hp.setOverlayDefaults(self, parent.session());
6858			var $el = $(e.target);
6859			// add hash and on load trigger it
6860			if ($el.is('.hp-guests-overlay') === false) {
6861				parent.overlayHash.setHash();
6862			} else {
6863				$(document).on('click', function (e) {
6864					if (!$(e.target).is('.hp-toggle') && $(e.target).parents('.hp-guests-overlay').length === 0) {
6865						$el.removeClass('hp-toggle--open');
6866						$el.stop(true, true).fadeOut(100);
6867					}
6868				});
6869			}
6870		});
6871	};
6872
6873	self.ready = ko.observable(false);
6874
6875	self.events = new ko.subscribable();
6876
6877	self.currencies = multViewModelData[0].viewModel.currencies;
6878
6879	self.lang = parent.lang;
6880	self.currency = parent.currency;
6881	self.step1ViewModels = [];
6882	self.boardType = ko.observable('');
6883	self.couponCode = ko.observable('');
6884	self.couponValid = ko.observable(true);
6885	self.couponExists = ko.observable(false);
6886	self.guests = [];
6887
6888	self.addGuest = function (data) {
6889		if (data.pax() > data.options[data.options.length - 1]) {
6890			return false;
6891		}
6892		data.pax(data.pax() + 1);
6893
6894		if (data.ageCategory() !== IBE.Config.guestType.Adult) {
6895			var options = [];
6896			for (var i = data.fromAge(); i <= data.toAge(); i++) {
6897				options.push(i);
6898			}
6899			data.childrenAges.push({
6900				selected: ko.observable(options[0]),
6901				options: options
6902			});
6903		}
6904	};
6905	self.removeGuest = function (data) {
6906		if (data.pax() === 1 && data.ageCategory() === IBE.Config.guestType.Adult) {
6907			return false;
6908		}
6909		if (data.pax() <= 0) {
6910			return;
6911		}
6912		data.pax(data.pax() - 1);
6913
6914		if (data.ageCategory() !== IBE.Config.guestType.Adult) {
6915			data.childrenAges.pop();
6916		}
6917	};
6918
6919	self.minStay = ko.observable();
6920	self.maxStay = ko.observable();
6921	self.minCheckInDate = ko.observable();
6922	self.currentViewModel = ko.observable();
6923	self._checkIn = ko.observable(moment());
6924	self.checkIn = ko.computed({
6925		write: function (date) {
6926			self._checkIn(moment(date));
6927		},
6928		read: function () {
6929			return self._checkIn();
6930		}
6931	});
6932	self.checkOut = ko.computed({
6933		write: function (date) {
6934
6935			if (typeof date === 'undefined') {
6936				self._checkOut(self.minCheckOutDate());
6937				return;
6938			}
6939			var co = moment(date);
6940			if (co > self.minCheckOutDate()) {
6941				self._checkOut(co);
6942			} else {
6943				self._checkOut(self.minCheckOutDate());
6944			}
6945		},
6946		read: function () {
6947			return self._checkOut();
6948		},
6949		deferEvaluation: true
6950	});
6951
6952	self.selectViewModel = function (hotelId) {
6953		return ko.utils.arrayFilter(self.step1ViewModels, function (item) {
6954			return hotelId == item.hotel().info.hotelId;
6955		});
6956	};
6957
6958	// stores the hotelId -> hotel name for the dropdown
6959	self.hotelOptions = [];
6960
6961	self.beforeSubmit = function () {
6962		parent.submitting(true);
6963		_prepare_multi_view_models_data();
6964		self.validate();
6965	};
6966
6967	self.validate = function () {
6968		var promises = [];
6969		var mutiValid = 0;
6970		if (self.couponExists()) {
6971			ko.utils.arrayForEach(self.step1ViewModels, function (item) {
6972				promises.push(item.isValidCoupon({
6973					hotelId: item.hotel().info.hotelId,
6974					apiKey: ko.utils.arrayFilter(parent.properties, function (prop) {
6975						return prop.hotelId == item.hotel().info.hotelId;
6976					})[0].apiKey
6977				}));
6978			});
6979
6980			$.when.apply($, promises).then(function () {
6981				ko.utils.arrayForEach(arguments, function (item) {
6982					if (item) {
6983						mutiValid++;
6984					} else {
6985						mutiValid--;
6986					}
6987				});
6988				ko.utils.arrayForEach(self.step1ViewModels, function (item) {
6989					if (mutiValid > 0) {
6990						self.couponValid(true);
6991						item.validate();
6992					} else {
6993						self.couponValid(false);
6994						item.message({
6995							id: 3001,
6996							severity: 'error',
6997							text: 'invalidPromoCode'
6998						});
6999						item.events.notifySubscribers({
7000							failure: item.message()
7001						}, 'submit');
7002					}
7003				});
7004			});
7005		} else {
7006			ko.utils.arrayForEach(self.step1ViewModels, function (item) {
7007				item.validate();
7008			});
7009		}
7010	};
7011
7012	// set in all step1 viewmodels key = value
7013	function _set_property_to_multi_view_models(key, value) {
7014		if (typeof key === 'undefined' || typeof value === 'undefined') {
7015			return false;
7016		}
7017		ko.utils.arrayForEach(self.step1ViewModels, function (item) {
7018			if (ko.isObservable(item[key])) {
7019				item[key](value);
7020			} else {
7021				item[key] = value;
7022			}
7023		});
7024	}
7025
7026	// prepare data before submit each step1 View Model
7027	function _prepare_multi_view_models_data() {
7028		_set_property_to_multi_view_models('_checkIn', self._checkIn());
7029		_set_property_to_multi_view_models('_checkOut', self._checkOut());
7030		_set_property_to_multi_view_models('couponCode', self.couponCode());
7031
7032		ko.utils.arrayForEach(self.guests, function (guest) {
7033			if (guest.ageCategory() !== IBE.Config.guestType.Adult) {
7034				for (var i = 0; i < guest.pax(); i++) {
7035					guest.ages.push(guest.childrenAges()[i].selected());
7036				}
7037			}
7038		});
7039
7040		ko.utils.arrayForEach(self.step1ViewModels, function (vm) {
7041			vm.requestedRooms()[0].guests = self.guests;
7042		});
7043	}
7044
7045	function _populate_multi_view_models_from_session() {
7046		if (parent.session() === false) {
7047			return false;
7048		}
7049
7050		self.checkIn(parent.session().checkIn);
7051		self.checkOut(parent.session().checkOut);
7052		self.couponCode(parent.session().couponCode);
7053		self.boardType(parent.session().board);
7054
7055		ko.utils.arrayForEach(self.guests, function (guest) {
7056			var session_guest = ko.utils.arrayFilter(parent.session().rooms[0], function (item) {
7057				return item.ageCategory === guest.ageCategory()
7058			});
7059			if (session_guest.length) {
7060				guest.pax(session_guest[0].pax);
7061				if (session_guest[0].ages.length) {
7062					var options = [];
7063					for (var i = guest.fromAge(); i <= guest.toAge(); i++) {
7064						options.push(i);
7065					}
7066					for (var j = 0; j < session_guest[0].ages.length; j++) {
7067						guest.childrenAges.push({
7068							selected: ko.observable(session_guest[0].ages[j]),
7069							options: options
7070						});
7071					}
7072				}
7073			}
7074		});
7075	}
7076
7077	function _populate_data(multViewModelData) {
7078		var minCheckInDates = [];
7079		var maxStays = [];
7080		var minStays = [];
7081		var checkIns = [];
7082		var checkOuts = [];
7083		var couponExists = [];
7084
7085		ko.utils.arrayForEach(multViewModelData, function (item) {
7086			self.step1ViewModels.push(item.viewModel);
7087
7088			self.hotelOptions.push({
7089				id: item.viewModel.hotel().info.hotelId,
7090				name: item.viewModel.hotel().info.name
7091			});
7092
7093			minCheckInDates.push(item.viewModel.minCheckInDate());
7094			checkIns.push(item.viewModel.checkIn());
7095
7096			checkOuts.push(item.viewModel.checkOut());
7097
7098			minStays.push(item.viewModel.minStay());
7099			maxStays.push(item.viewModel.maxStay());
7100			couponExists.push(item.viewModel.couponExists());
7101		});
7102
7103		// todo: hotels may not have the same default BoardType
7104		self.boardType(self.step1ViewModels[0].boardType());
7105		self.guests = self.step1ViewModels[0].requestedRooms()[0].guests;
7106		ko.utils.arrayForEach(self.guests, function (item) {
7107			if (item.ageCategory() != IBE.Config.guestType.Adult) {
7108				item.childrenAges = ko.observableArray([]);
7109				// set the childrenAges and selected
7110			}
7111		});
7112		self.minCheckInDate(moment(minCheckInDates.reduce(function (a, b) {
7113			return a < b ? a : b;
7114		})));
7115		self.minCheckOutDate = ko.computed(function () {
7116			var co = moment(self.checkIn());
7117			co.add(self.minStay(), 'days');
7118			return co;
7119		});
7120
7121		self._checkOut = ko.observable(self.minCheckOutDate());
7122
7123		self.checkIn.subscribe(function (newDate) {
7124			var checkOut = moment(newDate).add(self.minStay(), 'days');
7125
7126			if (self.checkOut() - checkOut <= 0) {
7127				self.checkOut(checkOut);
7128			}
7129		});
7130		self.checkIn(moment(checkIns.reduce(function (a, b) {
7131			return a < b ? a : b;
7132		})));
7133
7134		self.minStay(minStays.reduce(function (a, b) {
7135			return a > b ? a : b;
7136		}));
7137		self.maxStay(maxStays.reduce(function (a, b) {
7138			return a > b ? a : b;
7139		}));
7140
7141		self.isValidStay = ko.computed({
7142			read: function () {
7143				return (self.nights() > 0 && self.nights() <= self.maxStay());
7144			},
7145			deferEvaluation: true
7146		});
7147
7148		self.nights = ko.computed({
7149			write: function (nights) {
7150				self.checkOut(self.checkIn().add(nights, 'days'));
7151			},
7152			read: function () {
7153				return self.checkOut().diff(self.checkIn(), 'days');
7154			},
7155			deferEvaluation: true
7156		});
7157
7158		self.couponExists(( couponExists.reduce(function (a, b) {
7159				return a + b;
7160			}, 0) ) === couponExists.length);
7161
7162		self.bookingProcess = ko.observable(new IBE.Models.BookingProcess({
7163			checkIn: self.checkIn,
7164			checkOut: self.checkOut,
7165			couponCode: self.couponCode,
7166			reservationStatus: "Unknown",
7167			lang: self.lang(),
7168			currency: self.currency()
7169		}));
7170	}
7171
7172	// populate multiple response data
7173	_populate_data(multViewModelData);
7174
7175	// set to multi step1 view models the preferences found on parent.session
7176	_populate_multi_view_models_from_session();
7177
7178	self.ready(true);
7179
7180	self.design();
7181
7182	return self;
7183}
7184function BookwizeLiquidMultipleStep2ViewModel(parent, multViewModelData) {
7185	if (typeof multViewModelData === 'undefined' || multViewModelData.length === 0) {
7186		return {};
7187	}
7188	var self = this;
7189
7190	self.ready = ko.observable(false);
7191
7192	self.events = new ko.subscribable();
7193
7194	self.lang = parent.lang;
7195	self.currency = parent.currency;
7196
7197	self.properties = ko.observableArray();
7198
7199	ko.utils.arrayForEach(multViewModelData, function (item) {
7200		item.viewModel.lang = self.lang;
7201		item.viewModel.currency = self.currency;
7202		// set the default boardType for the dropdown select mealplan
7203		item.viewModel.request().preferredBoard(item.viewModel.hotelInfo().defaultBoardType());
7204		self.properties.push(item.viewModel);
7205	});
7206
7207	self.currencies = self.properties()[0].currencies;
7208	self.calculateRate = self.properties()[0].calculateRate;
7209	self.popup = self.properties()[0].popup;
7210
7211	self.bookingProcess = ko.observable(new IBE.Models.BookingProcess({
7212		checkIn: ko.observable(moment(parent.session().checkIn)),
7213		checkOut: ko.observable(moment(parent.session().checkOut)),
7214		rooms: parent.session().rooms,
7215		couponCode: parent.session().couponCode,
7216		reservationStatus: "Unknown",
7217		lang: self.lang,
7218		currency: self.currency
7219	}));
7220
7221	// assign references
7222	self.bookingProcess().boardType = self.properties()[0].bookingProcess().boardType;
7223	self.bookingProcess().totalGuests = ko.observable(parent.session().totalGuests);
7224	self.bookingProcess().boardType(parent.session().board);
7225
7226	// we need to reference the self.currency and self.lang to bookingProcess objects
7227	// for each property
7228	ko.utils.arrayForEach(self.properties(), function (item) {
7229		item.bookingProcess().currency = self.bookingProcess().currency;
7230		item.bookingProcess().lang = self.bookingProcess().lang;
7231	});
7232
7233	self.scrollToSelectedHotel = function () {
7234		$(document).on('hp/overlayClosed', function (e) {
7235			if (isNaN(parent.selectedHotel())) {
7236				return;
7237			}
7238			hp.scrollTo({
7239				offset: $('#hp-hotel-' + parent.selectedHotel()).offset().top - $('.site-head ').height(),
7240				callback: function () {
7241					window.location.hash = '#!hotel-' + parent.selectedHotel();
7242				}
7243			});
7244		});
7245	};
7246
7247	self.design = function () {
7248		hp.anchorLink({
7249			header: $('.site-head')
7250		});
7251	};
7252
7253	self.filterHotel = function (hotelId) {
7254		if (isNaN(parent.selectedHotel())) {
7255			// users didn't select any hotel
7256			return true;
7257		}
7258		if (typeof hotelId !== 'undefined') {
7259			return (parent.selectedHotel() === hotelId);
7260		}
7261		return false;
7262	};
7263
7264	self.design();
7265
7266	self.ready(true);
7267
7268	return self;
7269}
7270
7271(function ($) {
7272	'use strict';
7273
7274	if (typeof IBE === 'undefined') {
7275		console.log('IBE is not loaded');
7276		return false;
7277	}
7278
7279	var browser = hp.browserVersion();
7280	if (browser.name.indexOf('MSIE') !== -1 && parseInt(browser.version) < 10) {
7281		// browser is Internet Explorer 9 or older
7282		// Booking engine does not support this browser
7283		return hp.legacy();
7284	}
7285
7286	if (IBE.Config.validApiKeys === false) {
7287		console.log('IBE cannot initialize because API KEYS are not the same');
7288		return false;
7289	}
7290
7291	IBE.Config.deviceType = 'Desktop';
7292
7293	window.BookwizeLiquid = new BookwizeLiquidAppViewModel();
7294
7295	if (hp.isMobile()) {
7296		IBE.Config.deviceType = 'Mobile';
7297		if (window.location.hash === '#!booking-preferences') {
7298			window.location.href = window.BookwizeLiquid.getPostByBookwizeLiquidPageType('integrated').bookwize_liquid_page_url;
7299		}
7300		window.BookwizeLiquid.bookButtons();
7301		return hp.legacy(true);
7302	}
7303
7304	// initialize liquid booking engine
7305	window.BookwizeLiquid.init();
7306
7307	// if is set the ?clearCache
7308	if (window.location.search.indexOf('clearCache') !== -1) {
7309		return hp.clearCache();
7310	}
7311})(jQuery);

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