/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */ !function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0 49 ? function(){ requestIdleCallback(run, {timeout: rICTimeout}); if(rICTimeout !== lazySizesConfig.ricTimeout){ rICTimeout = lazySizesConfig.ricTimeout; } } : rAFIt(function(){ setTimeout(run); }, true) ; return function(isPriority){ var delay; if((isPriority = isPriority === true)){ rICTimeout = 33; } if(running){ return; } running = true; delay = gDelay - (Date.now() - lastTime); if(delay < 0){ delay = 0; } if(isPriority || delay < 9){ idleCallback(); } else { setTimeout(idleCallback, delay); } }; }; //based on http://modernjavascript.blogspot.de/2013/08/building-better-debounce.html var debounce = function(func) { var timeout, timestamp; var wait = 99; var run = function(){ timeout = null; func(); }; var later = function() { var last = Date.now() - timestamp; if (last < wait) { setTimeout(later, wait - last); } else { (requestIdleCallback || run)(run); } }; return function() { timestamp = Date.now(); if (!timeout) { timeout = setTimeout(later, wait); } }; }; (function(){ var prop; var lazySizesDefaults = { lazyClass: 'lazyload', loadedClass: 'lazyloaded', loadingClass: 'lazyloading', preloadClass: 'lazypreload', errorClass: 'lazyerror', //strictClass: 'lazystrict', autosizesClass: 'lazyautosizes', srcAttr: 'data-src', srcsetAttr: 'data-srcset', sizesAttr: 'data-sizes', //preloadAfterLoad: false, minSize: 40, customMedia: {}, init: true, expFactor: 1.5, hFac: 0.8, loadMode: 2, loadHidden: true, ricTimeout: 0, throttleDelay: 125, }; lazySizesConfig = window.lazySizesConfig || window.lazysizesConfig || {}; for(prop in lazySizesDefaults){ if(!(prop in lazySizesConfig)){ lazySizesConfig[prop] = lazySizesDefaults[prop]; } } window.lazySizesConfig = lazySizesConfig; setTimeout(function(){ if(lazySizesConfig.init){ init(); } }); })(); var loader = (function(){ var preloadElems, isCompleted, resetPreloadingTimer, loadMode, started; var eLvW, elvH, eLtop, eLleft, eLright, eLbottom, isBodyHidden; var regImg = /^img$/i; var regIframe = /^iframe$/i; var supportScroll = ('onscroll' in window) && !(/(gle|ing)bot/.test(navigator.userAgent)); var shrinkExpand = 0; var currentExpand = 0; var isLoading = 0; var lowRuns = -1; var resetPreloading = function(e){ isLoading--; if(!e || isLoading < 0 || !e.target){ isLoading = 0; } }; var isVisible = function (elem) { if (isBodyHidden == null) { isBodyHidden = getCSS(document.body, 'visibility') == 'hidden'; } return isBodyHidden || (getCSS(elem.parentNode, 'visibility') != 'hidden' && getCSS(elem, 'visibility') != 'hidden'); }; var isNestedVisible = function(elem, elemExpand){ var outerRect; var parent = elem; var visible = isVisible(elem); eLtop -= elemExpand; eLbottom += elemExpand; eLleft -= elemExpand; eLright += elemExpand; while(visible && (parent = parent.offsetParent) && parent != document.body && parent != docElem){ visible = ((getCSS(parent, 'opacity') || 1) > 0); if(visible && getCSS(parent, 'overflow') != 'visible'){ outerRect = parent.getBoundingClientRect(); visible = eLright > outerRect.left && eLleft < outerRect.right && eLbottom > outerRect.top - 1 && eLtop < outerRect.bottom + 1 ; } } return visible; }; var checkElements = function() { var eLlen, i, rect, autoLoadElem, loadedSomething, elemExpand, elemNegativeExpand, elemExpandVal, beforeExpandVal, defaultExpand, preloadExpand, hFac; var lazyloadElems = lazysizes.elements; if((loadMode = lazySizesConfig.loadMode) && isLoading < 8 && (eLlen = lazyloadElems.length)){ i = 0; lowRuns++; defaultExpand = (!lazySizesConfig.expand || lazySizesConfig.expand < 1) ? docElem.clientHeight > 500 && docElem.clientWidth > 500 ? 500 : 370 : lazySizesConfig.expand; lazysizes._defEx = defaultExpand; preloadExpand = defaultExpand * lazySizesConfig.expFactor; hFac = lazySizesConfig.hFac; isBodyHidden = null; if(currentExpand < preloadExpand && isLoading < 1 && lowRuns > 2 && loadMode > 2 && !document.hidden){ currentExpand = preloadExpand; lowRuns = 0; } else if(loadMode > 1 && lowRuns > 1 && isLoading < 6){ currentExpand = defaultExpand; } else { currentExpand = shrinkExpand; } for(; i < eLlen; i++){ if(!lazyloadElems[i] || lazyloadElems[i]._lazyRace){continue;} if(!supportScroll){unveilElement(lazyloadElems[i]);continue;} if(!(elemExpandVal = lazyloadElems[i][_getAttribute]('data-expand')) || !(elemExpand = elemExpandVal * 1)){ elemExpand = currentExpand; } if(beforeExpandVal !== elemExpand){ eLvW = innerWidth + (elemExpand * hFac); elvH = innerHeight + elemExpand; elemNegativeExpand = elemExpand * -1; beforeExpandVal = elemExpand; } rect = lazyloadElems[i].getBoundingClientRect(); if ((eLbottom = rect.bottom) >= elemNegativeExpand && (eLtop = rect.top) <= elvH && (eLright = rect.right) >= elemNegativeExpand * hFac && (eLleft = rect.left) <= eLvW && (eLbottom || eLright || eLleft || eLtop) && (lazySizesConfig.loadHidden || isVisible(lazyloadElems[i])) && ((isCompleted && isLoading < 3 && !elemExpandVal && (loadMode < 3 || lowRuns < 4)) || isNestedVisible(lazyloadElems[i], elemExpand))){ unveilElement(lazyloadElems[i]); loadedSomething = true; if(isLoading > 9){break;} } else if(!loadedSomething && isCompleted && !autoLoadElem && isLoading < 4 && lowRuns < 4 && loadMode > 2 && (preloadElems[0] || lazySizesConfig.preloadAfterLoad) && (preloadElems[0] || (!elemExpandVal && ((eLbottom || eLright || eLleft || eLtop) || lazyloadElems[i][_getAttribute](lazySizesConfig.sizesAttr) != 'auto')))){ autoLoadElem = preloadElems[0] || lazyloadElems[i]; } } if(autoLoadElem && !loadedSomething){ unveilElement(autoLoadElem); } } }; var throttledCheckElements = throttle(checkElements); var switchLoadingClass = function(e){ var elem = e.target; if (elem._lazyCache) { delete elem._lazyCache; return; } resetPreloading(e); addClass(elem, lazySizesConfig.loadedClass); removeClass(elem, lazySizesConfig.loadingClass); addRemoveLoadEvents(elem, rafSwitchLoadingClass); triggerEvent(elem, 'lazyloaded'); }; var rafedSwitchLoadingClass = rAFIt(switchLoadingClass); var rafSwitchLoadingClass = function(e){ rafedSwitchLoadingClass({target: e.target}); }; var changeIframeSrc = function(elem, src){ try { elem.contentWindow.location.replace(src); } catch(e){ elem.src = src; } }; var handleSources = function(source){ var customMedia; var sourceSrcset = source[_getAttribute](lazySizesConfig.srcsetAttr); if( (customMedia = lazySizesConfig.customMedia[source[_getAttribute]('data-media') || source[_getAttribute]('media')]) ){ source.setAttribute('media', customMedia); } if(sourceSrcset){ source.setAttribute('srcset', sourceSrcset); } }; var lazyUnveil = rAFIt(function (elem, detail, isAuto, sizes, isImg){ var src, srcset, parent, isPicture, event, firesLoad; if(!(event = triggerEvent(elem, 'lazybeforeunveil', detail)).defaultPrevented){ if(sizes){ if(isAuto){ addClass(elem, lazySizesConfig.autosizesClass); } else { elem.setAttribute('sizes', sizes); } } srcset = elem[_getAttribute](lazySizesConfig.srcsetAttr); src = elem[_getAttribute](lazySizesConfig.srcAttr); if(isImg) { parent = elem.parentNode; isPicture = parent && regPicture.test(parent.nodeName || ''); } firesLoad = detail.firesLoad || (('src' in elem) && (srcset || src || isPicture)); event = {target: elem}; addClass(elem, lazySizesConfig.loadingClass); if(firesLoad){ clearTimeout(resetPreloadingTimer); resetPreloadingTimer = setTimeout(resetPreloading, 2500); addRemoveLoadEvents(elem, rafSwitchLoadingClass, true); } if(isPicture){ forEach.call(parent.getElementsByTagName('source'), handleSources); } if(srcset){ elem.setAttribute('srcset', srcset); } else if(src && !isPicture){ if(regIframe.test(elem.nodeName)){ changeIframeSrc(elem, src); } else { elem.src = src; } } if(isImg && (srcset || isPicture)){ updatePolyfill(elem, {src: src}); } } if(elem._lazyRace){ delete elem._lazyRace; } removeClass(elem, lazySizesConfig.lazyClass); rAF(function(){ // Part of this can be removed as soon as this fix is older: https://bugs.chromium.org/p/chromium/issues/detail?id=7731 (2015) var isLoaded = elem.complete && elem.naturalWidth > 1; if( !firesLoad || isLoaded){ if (isLoaded) { addClass(elem, 'ls-is-cached'); } switchLoadingClass(event); elem._lazyCache = true; setTimeout(function(){ if ('_lazyCache' in elem) { delete elem._lazyCache; } }, 9); } }, true); }); var unveilElement = function (elem){ var detail; var isImg = regImg.test(elem.nodeName); //allow using sizes="auto", but don't use. it's invalid. Use data-sizes="auto" or a valid value for sizes instead (i.e.: sizes="80vw") var sizes = isImg && (elem[_getAttribute](lazySizesConfig.sizesAttr) || elem[_getAttribute]('sizes')); var isAuto = sizes == 'auto'; if( (isAuto || !isCompleted) && isImg && (elem[_getAttribute]('src') || elem.srcset) && !elem.complete && !hasClass(elem, lazySizesConfig.errorClass) && hasClass(elem, lazySizesConfig.lazyClass)){return;} detail = triggerEvent(elem, 'lazyunveilread').detail; if(isAuto){ autoSizer.updateElem(elem, true, elem.offsetWidth); } elem._lazyRace = true; isLoading++; lazyUnveil(elem, detail, isAuto, sizes, isImg); }; var onload = function(){ if(isCompleted){return;} if(Date.now() - started < 999){ setTimeout(onload, 999); return; } var afterScroll = debounce(function(){ lazySizesConfig.loadMode = 3; throttledCheckElements(); }); isCompleted = true; lazySizesConfig.loadMode = 3; throttledCheckElements(); addEventListener('scroll', function(){ if(lazySizesConfig.loadMode == 3){ lazySizesConfig.loadMode = 2; } afterScroll(); }, true); }; return { _: function(){ started = Date.now(); lazysizes.elements = document.getElementsByClassName(lazySizesConfig.lazyClass); preloadElems = document.getElementsByClassName(lazySizesConfig.lazyClass + ' ' + lazySizesConfig.preloadClass); addEventListener('scroll', throttledCheckElements, true); addEventListener('resize', throttledCheckElements, true); if(window.MutationObserver){ new MutationObserver( throttledCheckElements ).observe( docElem, {childList: true, subtree: true, attributes: true} ); } else { docElem[_addEventListener]('DOMNodeInserted', throttledCheckElements, true); docElem[_addEventListener]('DOMAttrModified', throttledCheckElements, true); setInterval(throttledCheckElements, 999); } addEventListener('hashchange', throttledCheckElements, true); //, 'fullscreenchange' ['focus', 'mouseover', 'click', 'load', 'transitionend', 'animationend', 'webkitAnimationEnd'].forEach(function(name){ document[_addEventListener](name, throttledCheckElements, true); }); if((/d$|^c/.test(document.readyState))){ onload(); } else { addEventListener('load', onload); document[_addEventListener]('DOMContentLoaded', throttledCheckElements); setTimeout(onload, 20000); } if(lazysizes.elements.length){ checkElements(); rAF._lsFlush(); } else { throttledCheckElements(); } }, checkElems: throttledCheckElements, unveil: unveilElement }; })(); var autoSizer = (function(){ var autosizesElems; var sizeElement = rAFIt(function(elem, parent, event, width){ var sources, i, len; elem._lazysizesWidth = width; width += 'px'; elem.setAttribute('sizes', width); if(regPicture.test(parent.nodeName || '')){ sources = parent.getElementsByTagName('source'); for(i = 0, len = sources.length; i < len; i++){ sources[i].setAttribute('sizes', width); } } if(!event.detail.dataAttr){ updatePolyfill(elem, event.detail); } }); var getSizeElement = function (elem, dataAttr, width){ var event; var parent = elem.parentNode; if(parent){ width = getWidth(elem, parent, width); event = triggerEvent(elem, 'lazybeforesizes', {width: width, dataAttr: !!dataAttr}); if(!event.defaultPrevented){ width = event.detail.width; if(width && width !== elem._lazysizesWidth){ sizeElement(elem, parent, event, width); } } } }; var updateElementsSizes = function(){ var i; var len = autosizesElems.length; if(len){ i = 0; for(; i < len; i++){ getSizeElement(autosizesElems[i]); } } }; var debouncedUpdateElementsSizes = debounce(updateElementsSizes); return { _: function(){ autosizesElems = document.getElementsByClassName(lazySizesConfig.autosizesClass); addEventListener('resize', debouncedUpdateElementsSizes); }, checkElems: debouncedUpdateElementsSizes, updateElem: getSizeElement }; })(); var init = function(){ if(!init.i){ init.i = true; autoSizer._(); loader._(); } }; lazysizes = { cfg: lazySizesConfig, autoSizer: autoSizer, loader: loader, init: init, uP: updatePolyfill, aC: addClass, rC: removeClass, hC: hasClass, fire: triggerEvent, gW: getWidth, rAF: rAF, }; return lazysizes; } )); (function(window, factory) { var globalInstall = function(){ factory(window.lazySizes); window.removeEventListener('lazyunveilread', globalInstall, true); }; factory = factory.bind(null, window, window.document); if(typeof module == 'object' && module.exports){ factory(require('lazysizes')); } else if(window.lazySizes) { globalInstall(); } else { window.addEventListener('lazyunveilread', globalInstall, true); } }(window, function(window, document, lazySizes) { 'use strict'; if(!window.addEventListener){return;} var regWhite = /\s+/g; var regSplitSet = /\s*\|\s+|\s+\|\s*/g; var regSource = /^(.+?)(?:\s+\[\s*(.+?)\s*\])(?:\s+\[\s*(.+?)\s*\])?$/; var regType = /^\s*\(*\s*type\s*:\s*(.+?)\s*\)*\s*$/; var regBgUrlEscape = /\(|\)|'/; var allowedBackgroundSize = {contain: 1, cover: 1}; var proxyWidth = function(elem){ var width = lazySizes.gW(elem, elem.parentNode); if(!elem._lazysizesWidth || width > elem._lazysizesWidth){ elem._lazysizesWidth = width; } return elem._lazysizesWidth; }; var getBgSize = function(elem){ var bgSize; bgSize = (getComputedStyle(elem) || {getPropertyValue: function(){}}).getPropertyValue('background-size'); if(!allowedBackgroundSize[bgSize] && allowedBackgroundSize[elem.style.backgroundSize]){ bgSize = elem.style.backgroundSize; } return bgSize; }; var setTypeOrMedia = function(source, match){ if(match){ var typeMatch = match.match(regType); if(typeMatch && typeMatch[1]){ source.setAttribute('type', typeMatch[1]); } else { source.setAttribute('media', lazySizesConfig.customMedia[match] || match); } } }; var createPicture = function(sets, elem, img){ var picture = document.createElement('picture'); var sizes = elem.getAttribute(lazySizesConfig.sizesAttr); var ratio = elem.getAttribute('data-ratio'); var optimumx = elem.getAttribute('data-optimumx'); if(elem._lazybgset && elem._lazybgset.parentNode == elem){ elem.removeChild(elem._lazybgset); } Object.defineProperty(img, '_lazybgset', { value: elem, writable: true }); Object.defineProperty(elem, '_lazybgset', { value: picture, writable: true }); sets = sets.replace(regWhite, ' ').split(regSplitSet); picture.style.display = 'none'; img.className = lazySizesConfig.lazyClass; if(sets.length == 1 && !sizes){ sizes = 'auto'; } sets.forEach(function(set){ var match; var source = document.createElement('source'); if(sizes && sizes != 'auto'){ source.setAttribute('sizes', sizes); } if((match = set.match(regSource))){ source.setAttribute(lazySizesConfig.srcsetAttr, match[1]); setTypeOrMedia(source, match[2]); setTypeOrMedia(source, match[3]); } else { source.setAttribute(lazySizesConfig.srcsetAttr, set); } picture.appendChild(source); }); if(sizes){ img.setAttribute(lazySizesConfig.sizesAttr, sizes); elem.removeAttribute(lazySizesConfig.sizesAttr); elem.removeAttribute('sizes'); } if(optimumx){ img.setAttribute('data-optimumx', optimumx); } if(ratio) { img.setAttribute('data-ratio', ratio); } picture.appendChild(img); elem.appendChild(picture); }; var proxyLoad = function(e){ if(!e.target._lazybgset){return;} var image = e.target; var elem = image._lazybgset; var bg = image.currentSrc || image.src; if(bg){ var event = lazySizes.fire(elem, 'bgsetproxy', { src: bg, useSrc: regBgUrlEscape.test(bg) ? JSON.stringify(bg) : bg, }); if(!event.defaultPrevented){ elem.style.backgroundImage = 'url(' + event.detail.useSrc + ')'; } } if(image._lazybgsetLoading){ lazySizes.fire(elem, '_lazyloaded', {}, false, true); delete image._lazybgsetLoading; } }; addEventListener('lazybeforeunveil', function(e){ var set, image, elem; if(e.defaultPrevented || !(set = e.target.getAttribute('data-bgset'))){return;} elem = e.target; image = document.createElement('img'); image.alt = ''; image._lazybgsetLoading = true; e.detail.firesLoad = true; createPicture(set, elem, image); setTimeout(function(){ lazySizes.loader.unveil(image); lazySizes.rAF(function(){ lazySizes.fire(image, '_lazyloaded', {}, true, true); if(image.complete) { proxyLoad({target: image}); } }); }); }); document.addEventListener('load', proxyLoad, true); window.addEventListener('lazybeforesizes', function(e){ if(e.detail.instance != lazySizes){return;} if(e.target._lazybgset && e.detail.dataAttr){ var elem = e.target._lazybgset; var bgSize = getBgSize(elem); if(allowedBackgroundSize[bgSize]){ e.target._lazysizesParentFit = bgSize; lazySizes.rAF(function(){ e.target.setAttribute('data-parent-fit', bgSize); if(e.target._lazysizesParentFit){ delete e.target._lazysizesParentFit; } }); } } }, true); document.documentElement.addEventListener('lazybeforesizes', function(e){ if(e.defaultPrevented || !e.target._lazybgset || e.detail.instance != lazySizes){return;} e.detail.width = proxyWidth(e.target._lazybgset); }); })); // Adapted from https://github.com/udacity/ud891/tree/gh-pages/lesson2-focus/07-modals-and-keyboard-traps/solution // Will hold previously focused element let focusedElementBeforeMenu; // Find the modal and its overlay const mobileMenu = document.querySelector( '[data-mobile-menu]' ); const menuOverlay = document.querySelector( '.modal-overlay' ); // Attach listeners to all the modal toggles const menuToggle = document.querySelector( '[data-open-menu]' ); // for(let i = 0; i < menuToggles.length; i++) { menuToggle.addEventListener( 'click', openMenu ); // } /** * Opens the modal and traps focus. */ function openMenu() { // Save current focus focusedElementBeforeMenu = document.activeElement; // Listen for and trap the keyboard mobileMenu.addEventListener( 'keydown', trapTabKey ); // Listen for indicators to close the modal menuOverlay.addEventListener( 'click', closeMenu ); // Modal close buttons const closeButtons = mobileMenu.querySelectorAll( '[data-close-menu]' ); // Attach listeners to all the close modal buttons for( let i = 0; i < closeButtons.length; i++ ) { closeButtons[i].addEventListener( 'click', closeMenu ); } // Find all focusable children const focusableElementsString = 'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex="0"], [contenteditable]'; let focusableElements = mobileMenu.querySelectorAll( focusableElementsString ); // Convert NodeList to Array focusableElements = Array.prototype.slice.call( focusableElements ); const firstTabStop = focusableElements[0]; const lastTabStop = focusableElements[focusableElements.length - 1]; // Show the modal and overlay // mobileMenu.style.display = 'block'; // menuOverlay.style.display = 'block'; menuToggle.setAttribute( 'aria-expanded', 'true' ); mobileMenu.setAttribute( 'data-menu-closed', 'false' ); mobileMenu.setAttribute( 'aria-hidden', 'false' ); // This just allows for the animation, not required. setTimeout( function() { mobileMenu.classList.remove( 'is-closed' ); }, 10 ); // Focus first child firstTabStop.focus(); function trapTabKey( e ) { // Check for TAB key press if ( e.keyCode === 9 ) { // SHIFT + TAB if ( e.shiftKey ) { if ( document.activeElement === firstTabStop ) { e.preventDefault(); lastTabStop.focus(); } // TAB } else if ( document.activeElement === lastTabStop ) { e.preventDefault(); firstTabStop.focus(); } } // ESCAPE if ( e.keyCode === 27 ) { closeMenu(); } } } /** * Closes the modal. */ function closeMenu() { // Animate the close mobileMenu.classList.add( 'is-closed' ); menuToggle.setAttribute( 'aria-expanded', 'false' ); mobileMenu.setAttribute( 'aria-hidden', 'true' ); // This setTimeout just allows for the animation, not required. setTimeout( () => { // Hide the modal and overlay // mobileMenu.style.display = 'none'; // menuOverlay.style.display = 'none'; mobileMenu.setAttribute( 'data-menu-closed', 'true' ); }, 300 ); // Set focus back to element that had it before the modal was opened focusedElementBeforeMenu.focus(); } const showUpdates = document.querySelector( '[data-show-updates]' ); const updates = document.querySelector( '[data-menu-updates]' ); const postsToRead = []; function getFormData( object ) { const formData = new FormData(); Object.keys( object ).forEach( key => formData.append( key, object[key] ) ); return formData; } const readPosts = ( parent ) => { const posts = parent.querySelectorAll( '[data-id]' ); const postIds = []; const object = {}; posts.forEach( post => postIds.push( post.dataset.id ) ); object.postIds = postIds; object.userId = document.querySelector( '[data-user-id]' ).dataset.userId; console.log( 'object', object ); fetch( '/wp-content/themes/Volum8WP/assets/functions/inc/Read_Notifications/read-notifications.php', { method: 'POST', body: getFormData( object ) } ) .then( res => res.json() ) .then( data => { data.posts_to_read.forEach( postId => postsToRead.push( postId ) ); console.log( postsToRead ); } ); console.log( postIds ); } const dimBell = () => { const circle = document.querySelector( '[cx="24.528"]' ); const bell = document.querySelector( '.notification-bell-path' ); circle.classList.add( 'dn' ); bell.style.fill = '#eeeeee'; } const updateDOMPosts = () => { postsToRead.forEach( post => { const postElement = document.querySelector( `[data-id="${post}"]` ); postElement.classList.add( 'read', 'o-50' ); dimBell(); } ); } if ( showUpdates ) { showUpdates.addEventListener( 'click', () => { const parent = document.querySelector( '[data-menu-updates]' ); if ( updates.classList.contains( 'dn' ) ) { readPosts( parent ); updates.classList.remove( 'dn' ); updates.classList.add( 'scale-up' ); showUpdates.setAttribute( 'aria-expanded', 'true' ); updates.setAttribute( 'aria-hidden', 'false' ); } else { updates.classList.add( 'dn' ); updates.classList.remove( 'scale-up' ); showUpdates.setAttribute( 'aria-expanded', 'false' ); updates.setAttribute( 'aria-hidden', 'true' ); updateDOMPosts(); } } ); document.addEventListener( 'click', ( e ) => { if ( e.target !== showUpdates ) { updates.classList.add( 'dn' ); updates.classList.remove( 'scale-up' ); showUpdates.setAttribute( 'aria-expanded', 'false' ); updates.setAttribute( 'aria-hidden', 'true' ); updateDOMPosts(); } } ); } ( function ( root, factory ) { if ( typeof define === 'function' && define.amd ) { define( [], function () { return factory( root ); } ); } else if ( typeof exports === 'object' ) { module.exports = factory( root ); } else { root.Bouncer = factory( root ); } } )( typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : this, function ( window ) { 'use strict'; // // Variables // const defaults = { // Classes & IDs fieldClass: 'error', errorClass: 'error-message', fieldPrefix: 'bouncer-field_', errorPrefix: 'bouncer-error_', // Patterns patterns: { email: /^([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22))*\x40([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d))*(\.\w{2,})+$/, url: /^(?:(?:https?|HTTPS?|ftp|FTP):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-zA-Z\u00a1-\uffff0-9]-*)*[a-zA-Z\u00a1-\uffff0-9]+)(?:\.(?:[a-zA-Z\u00a1-\uffff0-9]-*)*[a-zA-Z\u00a1-\uffff0-9]+)*(?:\.(?:[a-zA-Z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/, number: /^(?:[-+]?[0-9]*[.,]?[0-9]+)$/, color: /^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/, date: /(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:30))|(?:(?:0[13578]|1[02])-31))/, time: /^(?:(0[0-9]|1[0-9]|2[0-3])(:[0-5][0-9]))$/, month: /^(?:(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])))$/ }, // Custom Validations customValidations: {}, // Messages messageAfterField: true, messageCustom: 'data-bouncer-message', messageTarget: 'data-bouncer-target', messages: { missingValue: { checkbox: 'This field is required.', radio: 'Please select a value.', select: 'Please select a value.', 'select-multiple': 'Please select at least one value.', default: 'Please fill out this field.' }, patternMismatch: { email: 'Please enter a valid email address.', url: 'Please enter a URL.', number: 'Please enter a number', color: 'Please match the following format: #rrggbb', date: 'Please use the YYYY-MM-DD format', time: 'Please use the 24-hour time format. Ex. 23:00', month: 'Please use the YYYY-MM format', default: 'Please match the requested format.' }, outOfRange: { over: 'Please select a value that is no more than {max}.', under: 'Please select a value that is no less than {min}.' }, wrongLength: { over: 'Please shorten this text to no more than {maxLength} characters. You are currently using {length} characters.', under: 'Please enter a password that is {minLength} characters or more. You are currently using {length} characters.' }, fallback: 'There was an error with this field.' }, // Form Submission disableSubmit: true, // Custom Events emitEvents: true }; // // Methods // /** * A wrapper for Array.prototype.forEach() for non-arrays * @param {Array-like} arr The array-like object * @param {Function} callback The callback to run */ const forEach = function ( arr, callback ) { Array.prototype.forEach.call( arr, callback ); }; /** * Merge two or more objects together. * @param {Object} objects The objects to merge together * @returns {Object} Merged values of defaults and options */ var extend = function () { const merged = {}; forEach( arguments, function ( obj ) { for ( const key in obj ) { if ( !obj.hasOwnProperty( key ) ) return; if ( Object.prototype.toString.call( obj[key] ) === '[object Object]' ) { merged[key] = extend( merged[key], obj[key] ); } else { merged[key] = obj[key]; } // merged[key] = obj[key]; } } ); return merged; }; /** * Emit a custom event * @param {String} type The event type * @param {Object} options The settings object * @param {Node} anchor The anchor element * @param {Node} toggle The toggle element */ const emitEvent = function ( elem, type, details ) { if ( typeof window.CustomEvent !== 'function' ) return; const event = new CustomEvent( type, { bubbles: true, detail: details || {} } ); elem.dispatchEvent( event ); }; /** * Add the `novalidate` attribute to all forms * @param {Boolean} remove If true, remove the `novalidate` attribute */ const addNoValidate = function ( selector ) { forEach( document.querySelectorAll( selector ), function ( form ) { form.setAttribute( 'novalidate', true ); } ); }; /** * Remove the `novalidate` attribute to all forms */ const removeNoValidate = function ( selector ) { forEach( document.querySelectorAll( selector ), function ( form ) { form.removeAttribute( 'novalidate' ); } ); }; /** * Check if a required field is missing its value * @param {Node} field The field to check * @return {Boolean} It true, field is missing it's value */ const missingValue = function ( field ) { // If not required, bail if ( !field.hasAttribute( 'required' ) ) return false; // Handle checkboxes if ( field.type === 'checkbox' ) { return !field.checked; } // Get the field value length let {length} = field.value; // Handle radio buttons if ( field.type === 'radio' ) { length = Array.prototype.filter.call( field.form.querySelectorAll( `[name="${ escapeCharacters( field.name ) }"]` ), function ( btn ) { return btn.checked; } ).length; } // Check for value return length < 1; }; /** * Check if field value doesn't match a patter. * @param {Node} field The field to check * @param {Object} settings The plugin settings * @see https://www.w3.org/TR/html51/sec-forms.html#the-pattern-attribute * @return {Boolean} If true, there's a pattern mismatch */ const patternMismatch = function ( field, settings ) { // Check if there's a pattern to match let pattern = field.getAttribute( 'pattern' ); pattern = pattern ? new RegExp( `^(?:${ pattern })$` ) : settings.patterns[field.type]; if ( !pattern || !field.value || field.value.length < 1 ) return false; // Validate the pattern return !field.value.match( pattern ); }; /** * Check if field value is out-of-range * @param {Node} field The field to check * @return {String} Returns 'over', 'under', or false */ const outOfRange = function ( field ) { // Make sure field has value if ( !field.value || field.value.length < 1 ) return false; // Check for range const max = field.getAttribute( 'max' ); const min = field.getAttribute( 'min' ); // Check validity const num = parseFloat( field.value ); if ( max && num > max ) return 'over'; if ( min && num < min ) return 'under'; return false; }; /** * Check if the field value is too long or too short * @param {Node} field The field to check * @return {String} Returns 'over', 'under', or false */ const wrongLength = function ( field ) { // Make sure field has value if ( !field.value || field.value.length < 1 ) return false; // Check for min/max length const max = field.getAttribute( 'maxlength' ); const min = field.getAttribute( 'minlength' ); // Check validity const {length} = field.value; if ( max && length > max ) return 'over'; if ( min && length < min ) return 'under'; return false; }; /** * Test for standard field validations * @param {Node} field The field to test * @param {Object} settings The plugin settings * @return {Object} The tests and their results */ const runValidations = function ( field, settings ) { return { missingValue: missingValue( field ), patternMismatch: patternMismatch( field, settings ), outOfRange: outOfRange( field ), wrongLength: wrongLength( field ) }; }; /** * Run any provided custom validations * @param {Node} field The field to test * @param {Object} errors The existing errors * @param {Object} validations The custom validations to run * @param {Object} settings The plugin settings * @return {Object} The tests and their results */ const customValidations = function ( field, errors, validations, settings ) { for ( const test in validations ) { if ( validations.hasOwnProperty( test ) ) { errors[test] = validations[test]( field, settings ); } } return errors; }; /** * Check if a field has any errors * @param {Object} errors The validation test results * @return {Boolean} Returns true if there are errors */ const hasErrors = function ( errors ) { for ( const type in errors ) { if ( errors[type] ) return true; } return false; }; /** * Check a field for errors * @param {Node} field The field to test * @param {Object} settings The plugin settings * @return {Object} The field validity and errors */ const getErrors = function ( field, settings ) { // Get standard validation errors let errors = runValidations( field,settings ); // Check for custom validations errors = customValidations( field, errors, settings.customValidations, settings ); return { valid: !hasErrors( errors ), errors }; }; /** * Escape special characters for use with querySelector * @author Mathias Bynens * @link https://github.com/mathiasbynens/CSS.escape * @param {String} id The anchor ID to escape */ var escapeCharacters = function ( id ) { const string = String( id ); const {length} = string; let index = -1; let codeUnit; let result = ''; const firstCodeUnit = string.charCodeAt( 0 ); while ( ++index < length ) { codeUnit = string.charCodeAt( index ); // Note: there’s no need to special-case astral symbols, surrogate // pairs, or lone surrogates. // If the character is NULL (U+0000), then throw an // `InvalidCharacterError` exception and terminate these steps. if ( codeUnit === 0x0000 ) { throw new InvalidCharacterError( 'Invalid character: the input contains U+0000.' ); } if ( // If the character is in the range [\1-\1F] (U+0001 to U+001F) or is // U+007F, […] ( codeUnit >= 0x0001 && codeUnit <= 0x001F ) || codeUnit == 0x007F || // If the character is the first character and is in the range [0-9] // (U+0030 to U+0039), […] ( index === 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039 ) || // If the character is the second character and is in the range [0-9] // (U+0030 to U+0039) and the first character is a `-` (U+002D), […] ( index === 1 && codeUnit >= 0x0030 && codeUnit <= 0x0039 && firstCodeUnit === 0x002D ) ) { // http://dev.w3.org/csswg/cssom/#escape-a-character-as-code-point result += `\\${ codeUnit.toString( 16 ) } `; continue; } // If the character is not handled by one of the above rules and is // greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or // is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to // U+005A), or [a-z] (U+0061 to U+007A), […] if ( codeUnit >= 0x0080 || codeUnit === 0x002D || codeUnit === 0x005F || codeUnit >= 0x0030 && codeUnit <= 0x0039 || codeUnit >= 0x0041 && codeUnit <= 0x005A || codeUnit >= 0x0061 && codeUnit <= 0x007A ) { // the character itself result += string.charAt( index ); continue; } // Otherwise, the escaped character. // http://dev.w3.org/csswg/cssom/#escape-a-character result += `\\${ string.charAt( index )}`; } // Return sanitized hash return result; }; /** * Get or create an ID for a field * @param {Node} field The field * @param {Object} settings The plugin settings * @param {Boolean} create If true, create an ID if there isn't one * @return {String} The field ID */ const getFieldID = function ( field, settings, create ) { let id = field.name ? field.name : field.id; if ( !id && create ) { id = settings.fieldPrefix + Math.floor( Math.random() * 999 ); field.id = id; } if ( field.type === 'checkbox' ) { id += `_${ field.value || field.id}`; } return id; }; /** * Special handling for radio buttons and checkboxes wrapped in labels. * @param {Node} field The field with the error * @return {Node} The field to show the error on */ const getErrorField = function ( field ) { // If the field is a radio button, get the last item in the radio group // @todo if location is before, get first item if ( field.type === 'radio' && field.name ) { const group = field.form.querySelectorAll( `[name="${ escapeCharacters( field.name ) }"]` ); field = group[group.length - 1]; } // Get the associated label for radio button or checkbox if ( field.type === 'radio' || field.type === 'checkbox' ) { const label = field.closest( 'label' ) || field.form.querySelector( `[for="${ field.id }"]` ); field = label || field; } return field; }; /** * Get the location for a field's error message * @param {Node} field The field * @param {Node} target The target for error message * @param {Object} settings The plugin settings * @return {Node} The error location */ const getErrorLocation = function ( field, target, settings ) { // Check for a custom error message const selector = field.getAttribute( settings.messageTarget ); if ( selector ) { const location = field.form.querySelector( selector ); if ( location ) { // @bugfix by @HaroldPutman // https://github.com/cferdinandi/bouncer/pull/28 return location.firstChild || location.appendChild( document.createTextNode( '' ) ); } } // If the message should come after the field if ( settings.messageAfterField ) { // If there's no next sibling, create one if ( !target.nextSibling ) { target.parentNode.appendChild( document.createTextNode( '' ) ); } return target.nextSibling; } // If it should come before return target; }; /** * Create a validation error message node * @param {Node} field The field * @param {Object} settings The plugin settings * @return {Node} The error message node */ const createError = function ( field, settings ) { // Create the error message const error = document.createElement( 'div' ); error.className = settings.errorClass; error.id = settings.errorPrefix + getFieldID( field, settings, true ); // If the field is a radio button or checkbox, grab the last field label const fieldTarget = getErrorField( field ); // Inject the error message into the DOM const location = getErrorLocation( field, fieldTarget, settings ); location.parentNode.insertBefore( error, location ); return error; }; /** * Get the error message test * @param {Node} field The field to get an error message for * @param {Object} errors The errors on the field * @param {Object} settings The plugin settings * @return {String|Function} The error message */ const getErrorMessage = function ( field, errors, settings ) { // Variables const {messages} = settings; // Missing value error if ( errors.missingValue ) { return messages.missingValue[field.type] || messages.missingValue.default; } // Numbers that are out of range if ( errors.outOfRange ) { return messages.outOfRange[errors.outOfRange].replace( '{max}', field.getAttribute( 'max' ) ).replace( '{min}', field.getAttribute( 'min' ) ).replace( '{length}', field.value.length ); } // Values that are too long or short if ( errors.wrongLength ) { return messages.wrongLength[errors.wrongLength].replace( '{maxLength}', field.getAttribute( 'maxlength' ) ).replace( '{minLength}', field.getAttribute( 'minlength' ) ).replace( '{length}', field.value.length ); } // Pattern mismatch error if ( errors.patternMismatch ) { const custom = field.getAttribute( settings.messageCustom ); if ( custom ) return custom; return messages.patternMismatch[field.type] || messages.patternMismatch.default; } // Custom validations for ( const test in settings.customValidations ) { if ( settings.customValidations.hasOwnProperty( test ) ) { if ( errors[test] && messages[test] ) return messages[test]; } } // Fallback error message return messages.fallback; }; /** * Add error attributes to a field * @param {Node} field The field with the error message * @param {Node} error The error message * @param {Object} settings The plugin settings */ const addErrorAttributes = function ( field, error, settings ) { field.classList.add( settings.fieldClass ); field.setAttribute( 'aria-describedby', error.id ); field.setAttribute( 'aria-invalid', true ); }; /** * Show error attributes on a field or radio/checkbox group * @param {Node} field The field with the error message * @param {Node} error The error message * @param {Object} settings The plugin settings */ const showErrorAttributes = function ( field, error, settings ) { // If field is a radio button, add attributes to every button in the group if ( field.type === 'radio' && field.name ) { Array.prototype.forEach.call( document.querySelectorAll( `[name="${ field.name }"]` ), function ( button ) { addErrorAttributes( button, error, settings ); } ); } // Otherwise, add an error class and aria attribute to the field addErrorAttributes( field, error, settings ); }; /** * Show an error message in the DOM * @param {Node} field The field to show an error message for * @param {Object} errors The errors on the field * @param {Object} settings The plugin settings */ const showError = function ( field, errors, settings ) { // Get/create an error message const error = field.form.querySelector( `#${ escapeCharacters( settings.errorPrefix + getFieldID( field, settings ) )}` ) || createError( field, settings ); const msg = getErrorMessage( field, errors, settings ); error.textContent = typeof msg === 'function' ? msg( field, settings ) : msg; // Add error attributes showErrorAttributes( field, error, settings ); // Emit custom event if ( settings.emitEvents ) { emitEvent( field, 'bouncerShowError', { errors } ); } }; /** * Remove error attributes from a field * @param {Node} field The field with the error message * @param {Node} error The error message * @param {Object} settings The plugin settings */ const removeAttributes = function ( field, settings ) { field.classList.remove( settings.fieldClass ); field.removeAttribute( 'aria-describedby' ); field.removeAttribute( 'aria-invalid' ); }; /** * Remove error attributes from the field or radio group * @param {Node} field The field with the error message * @param {Node} error The error message * @param {Object} settings The plugin settings */ const removeErrorAttributes = function ( field, settings ) { // If field is a radio button, remove attributes from every button in the group if ( field.type === 'radio' && field.name ) { Array.prototype.forEach.call( document.querySelectorAll( `[name="${ field.name }"]` ), function ( button ) { removeAttributes( button, settings ); } ); return; } // Otherwise, add an error class and aria attribute to the field removeAttributes( field, settings ); }; /** * Remove an error message from the DOM * @param {Node} field The field with the error message * @param {Object} settings The plugin settings */ const removeError = function ( field, settings ) { // Get the error message for this field const error = field.form.querySelector( `#${ escapeCharacters( settings.errorPrefix + getFieldID( field, settings ) )}` ); if ( !error ) return; // Remove the error error.parentNode.removeChild( error ); // Remove error and a11y from the field removeErrorAttributes( field, settings ); // Emit custom event if ( settings.emitEvents ) { emitEvent( field, 'bouncerRemoveError' ); } }; /** * Remove errors from all fields * @param {String} selector The selector for the form * @param {Object} settings The plugin settings */ const removeAllErrors = function ( selector, settings ) { forEach( document.querySelectorAll( selector ), function ( form ) { forEach( form.querySelectorAll( 'input, select, textarea' ), function ( field ) { removeError( field, settings ); } ); } ); }; /** * The plugin constructor * @param {String} selector The selector to use for forms to be validated * @param {Object} options User settings [optional] */ const Constructor = function ( selector, options ) { // // Variables // const publicAPIs = {}; let settings; // // Methods // /** * Validate a field * @param {Node} field The field to validate * @param {Object} options Validation options * @return {Object} The validity state and errors */ publicAPIs.validate = function ( field, options ) { // Don't validate submits, buttons, file and reset inputs, and disabled and readonly fields if ( field.disabled || field.readOnly || field.type === 'reset' || field.type === 'submit' || field.type === 'button' ) return; // Local settings const _settings = extend( settings, options || {} ); // Check for errors const isValid = getErrors( field, _settings ); // If valid, remove any error messages if ( isValid.valid ) { removeError( field, _settings ); return; } // Otherwise, show an error message showError( field, isValid.errors, _settings ); return isValid; }; /** * Validate all fields in a form or section * @param {Node} target The form or section to validate fields in * @return {Array} An array of fields with errors */ publicAPIs.validateAll = function ( target ) { return Array.prototype.filter.call( target.querySelectorAll( 'input, select, textarea' ), function ( field ) { const validate = publicAPIs.validate( field ); return validate && !validate.valid; } ); }; /** * Run a validation on field blur */ const blurHandler = function ( event ) { // Only run if the field is in a form to be validated if ( !event.target.form || !event.target.form.matches( selector ) ) return; // Validate the field publicAPIs.validate( event.target ); }; /** * Run a validation on a fields with errors when the value changes */ const inputHandler = function ( event ) { // Only run if the field is in a form to be validated if ( !event.target.form || !event.target.form.matches( selector ) ) return; // Only run on fields with errors if ( !event.target.classList.contains( settings.fieldClass ) ) return; // Validate the field publicAPIs.validate( event.target ); }; /** * Validate an entire form when it's submitted */ const submitHandler = function ( event ) { // Only run on matching elements if ( !event.target.matches( selector ) ) return; // Prevent form submission event.preventDefault(); // Validate each field const errors = publicAPIs.validateAll( event.target ); // If there are errors, focus on the first one if ( errors.length > 0 ) { errors[0].focus(); emitEvent( event.target, 'bouncerFormInvalid', {errors} ); return; } // Otherwise, submit if not disabled if ( !settings.disableSubmit ) { event.target.submit(); } // Emit custom event if ( settings.emitEvents ) { emitEvent( event.target, 'bouncerFormValid' ); } }; /** * Destroy the current plugin instantiation */ publicAPIs.destroy = function () { // Remove event listeners document.removeEventListener( 'blur', blurHandler, true ); document.removeEventListener( 'input', inputHandler, false ); document.removeEventListener( 'click', inputHandler, false ); document.removeEventListener( 'submit', submitHandler, false ); // Remove all errors removeAllErrors( selector, settings ); // Remove novalidate attribute removeNoValidate( selector ); // Emit custom event if ( settings.emitEvents ) { emitEvent( document, 'bouncerDestroyed', { settings } ); } // Reset settings settings = null; }; /** * Instantiate a new instance of the plugin */ const init = function () { // Create settings settings = extend( defaults, options || {} ); // Add novalidate attribute addNoValidate( selector ); // Event Listeners document.addEventListener( 'blur', blurHandler, true ); document.addEventListener( 'input', inputHandler, false ); document.addEventListener( 'click', inputHandler, false ); document.addEventListener( 'submit', submitHandler, false ); // Emit custom event if ( settings.emitEvents ) { emitEvent( document, 'bouncerInitialized', { settings } ); } }; // // Inits & Event Listeners // init(); return publicAPIs; }; // // Return the constructor // return Constructor; } ); !function( e,t ){typeof exports==='object'&&typeof module!=='undefined'?module.exports=t():typeof define==='function'&&define.amd?define( t ):( e=e||self ).MicroModal=t()}( this,( function(){'use strict'; function e( e,t ){for( let o=0;oe.length )&&( t=e.length );for( var o=0,n=new Array( t );o0&&this.registerTriggers.apply( this,t( a ) ),this.onClick=this.onClick.bind( this ),this.onKeydown=this.onKeydown.bind( this )}let i; let a; let r;return i=o,( a=[{key:'registerTriggers',value(){for( var e=this,t=arguments.length,o=new Array( t ),n=0;n0&&void 0!==arguments[0]?arguments[0]:null;if( this.activeElement=document.activeElement,this.modal.setAttribute( 'aria-hidden','false' ),this.modal.classList.add( this.config.openClass ),this.scrollBehaviour( 'disable' ),this.addEventListeners(),this.config.awaitOpenAnimation ){const o=function t(){e.modal.removeEventListener( 'animationend',t,!1 ),e.setFocusToFirstNode()};this.modal.addEventListener( 'animationend',o,!1 )}else this.setFocusToFirstNode();this.config.onShow( this.modal,this.activeElement,t )}},{key:'closeModal',value(){const e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null; const t=this.modal;if( this.modal.setAttribute( 'aria-hidden','true' ),this.removeEventListeners(),this.scrollBehaviour( 'enable' ),this.activeElement&&this.activeElement.focus&&this.activeElement.focus(),this.config.onClose( this.modal,this.activeElement,e ),this.config.awaitCloseAnimation ){const o=this.config.openClass;this.modal.addEventListener( 'animationend',( function e(){t.classList.remove( o ),t.removeEventListener( 'animationend',e,!1 )} ),!1 )}else t.classList.remove( this.config.openClass )}},{key:'closeModalById',value( e ){this.modal=document.getElementById( e ),this.modal&&this.closeModal()}},{key:'scrollBehaviour',value( e ){if( this.config.disableScroll ){const t=document.querySelector( 'body' );switch( e ){case'enable':Object.assign( t.style,{overflow:''} );break;case'disable':Object.assign( t.style,{overflow:'hidden'} )}}}},{key:'addEventListeners',value(){this.modal.addEventListener( 'touchstart',this.onClick ),this.modal.addEventListener( 'click',this.onClick ),document.addEventListener( 'keydown',this.onKeydown )}},{key:'removeEventListeners',value(){this.modal.removeEventListener( 'touchstart',this.onClick ),this.modal.removeEventListener( 'click',this.onClick ),document.removeEventListener( 'keydown',this.onKeydown )}},{key:'onClick',value( e ){e.target.hasAttribute( this.config.closeTrigger )&&this.closeModal( e )}},{key:'onKeydown',value( e ){e.keyCode===27&&this.closeModal( e ),e.keyCode===9&&this.retainFocus( e )}},{key:'getFocusableNodes',value(){const e=this.modal.querySelectorAll( n );return Array.apply( void 0,t( e ) )}},{key:'setFocusToFirstNode',value(){const e=this;if( !this.config.disableFocus ){const t=this.getFocusableNodes();if( t.length!==0 ){const o=t.filter( ( function( t ){return!t.hasAttribute( e.config.closeTrigger )} ) );o.length>0&&o[0].focus(),o.length===0&&t[0].focus()}}}},{key:'retainFocus',value( e ){let t=this.getFocusableNodes();if( t.length!==0 )if( t=t.filter( ( function( e ){return e.offsetParent!==null} ) ),this.modal.contains( document.activeElement ) ){const o=t.indexOf( document.activeElement );e.shiftKey&&o===0&&( t[t.length-1].focus(),e.preventDefault() ),!e.shiftKey&&t.length>0&&o===t.length-1&&( t[0].focus(),e.preventDefault() )}else t[0].focus()}}] )&&e( i.prototype,a ),r&&e( i,r ),o}(),a=null,r=function( e ){if( !document.getElementById( e ) )return console.warn( 'MicroModal: ❗Seems like you have missed %c\''.concat( e,'\'' ),'background-color: #f8f9fa;color: #50596c;font-weight: bold;','ID somewhere in your code. Refer example below to resolve it.' ),console.warn( '%cExample:','background-color: #f8f9fa;color: #50596c;font-weight: bold;','' ) ),!1},s=function( e,t ){if( function( e ){e.length<=0&&( console.warn( 'MicroModal: ❗Please specify at least one %c\'micromodal-trigger\'','background-color: #f8f9fa;color: #50596c;font-weight: bold;','data attribute.' ),console.warn( '%cExample:','background-color: #f8f9fa;color: #50596c;font-weight: bold;','' ) )}( e ),!t )return!0;for( const o in t )r( o );return!0},{init( e ){const o={openTrigger:'data-micromodal-trigger',...e}; const n=t( document.querySelectorAll( '['.concat( o.openTrigger,']' ) ) ); const r=function( e,t ){const o=[];return e.forEach( ( function( e ){const n=e.attributes[t].value;void 0===o[n]&&( o[n]=[] ),o[n].push( e )} ) ),o}( n,o.openTrigger );if( !0!==o.debugMode||!1!==s( n,r ) )for( const l in r ){const c=r[l];o.targetModal=l,o.triggers=t( c ),a=new i( o )}},show( e,t ){const o=t||{};o.targetModal=e,!0===o.debugMode&&!1===r( e )||( a&&a.removeEventListeners(),( a=new i( o ) ).showModal() )},close( e ){e?a.closeModalById( e ):a.closeModal()}} );return window.MicroModal=l,l} ) ); function refreshModals() { const modalTrigger = 'data-micromodal-trigger'; const modalTriggers = document.querySelectorAll( `[${modalTrigger}]` ); modalTriggers.forEach( trigger => { const triggerElement = trigger.getAttribute( modalTrigger ); trigger.removeAttribute( modalTrigger ); trigger.setAttribute( modalTrigger, triggerElement ); MicroModal.init(); } ); } document.addEventListener( 'DOMContentLoaded', () => { const modalExists = document.querySelector( '.modal' ); console.log( 'modals' ); if ( modalExists ) { MicroModal.init( { onShow: modal => console.info( `${modal.id} is shown` ), // [1] onClose: modal => console.info( `${modal.id} is hidden` ), openClass: 'is-open', // [5] disableScroll: true, // [6] disableFocus: false, // [7] awaitOpenAnimation: true, // [8] awaitCloseAnimation: true, // [9] debugMode: true // [10] } ); } } ); ;( function ( w, doc, undefined ) { 'use strict'; /** * Local object for method references * and define script meta-data */ const ARIAaccordion = {}; w.ARIAaccordion = ARIAaccordion; ARIAaccordion.NS = 'ARIAaccordion'; ARIAaccordion.AUTHOR = 'Scott O\'Hara'; ARIAaccordion.VERSION = '3.2.1'; ARIAaccordion.LICENSE = 'https://github.com/scottaohara/accessible_accordions/blob/master/LICENSE'; const widgetClass = 'accordion'; const widgetTriggerClass = `${widgetClass }__trigger`; const widgetHeadingClass = `${widgetClass }__heading`; const widgetPanelClass = `${widgetClass }__panel`; const widgetHeading = '[data-aria-accordion-heading]'; const widgetPanel = '[data-aria-accordion-panel]' let idCounter = 0; /** * Global Create * * This function validates that the minimum required markup * is present to create the ARIA widget(s). * Any additional markup elements or attributes that * do not exist in the found required markup patterns * will be generated via this function. */ ARIAaccordion.create = function () { let self; let panels; let defaultPanel = 'none'; let headings; let triggers; let constantPanel; let multiPanel; let i; const widget = doc.querySelectorAll( '[data-aria-accordion]' ); idCounter += 1; for ( i = 0; i < widget.length; i++ ) { self = widget[i]; var t; /** * Check for IDs and create arrays of necessary * panels & headings for further setup functions. */ if ( !self.hasAttribute( 'id' ) ) { self.id = `acc_${ idCounter }-${ i}`; } /** * Setup accordion classes */ self.classList.add( widgetClass ); /** * Get all panels & headings of an accordion pattern based * on a specific ID > direct child selector (this will ensure * that nested accordions don't get properties meant for * the parent accordion, or vice-versa). * * If accordions are contained within an ol/ul, the selector * needs to be different. */ if ( doc.querySelectorAll( `#${ self.id }> li` ).length ) { panels = doc.querySelectorAll( `#${ self.id } li > ${ widgetPanel}` ); headings = doc.querySelectorAll( `#${ self.id } li > ${ widgetHeading}` ); } else { panels = doc.querySelectorAll( `#${ self.id } > ${ widgetPanel}` ); headings = doc.querySelectorAll( `#${ self.id } > ${ widgetHeading}` ); } /** * Check for options: * data-default - is there a default opened panel? * data-constant - should the accordion always have A panel open? */ if ( self.hasAttribute( 'data-default' ) ) { defaultPanel = self.getAttribute( 'data-default' ); } /** * Accordions with a constantly open panel are not a default * but if a data-constant attribute is used, then we need this * to be true. */ constantPanel = self.hasAttribute( 'data-constant' ); /** * Accordions can have multiple panels open at a time, * if they have a data-multi attribute. */ multiPanel = self.hasAttribute( 'data-multi' ); /** * If accordion panels are meant to transition, apply this inline style. * This is to help mitigate a quick flash of CSS being applied to the * no-js styling, and having an unwanted transition on initial page load. */ if ( self.hasAttribute( 'data-transition' ) ) { const thesePanels = self.querySelectorAll( widgetPanel ); for ( t = 0; t < thesePanels.length; t++ ) { thesePanels[t].classList.add( `${widgetPanelClass }--transition` ); } } /** * Setup Panels, Headings & Buttons */ ARIAaccordion.setupPanels( self.id, panels, defaultPanel, constantPanel ); ARIAaccordion.setupHeadingButton( headings, constantPanel ); if ( doc.querySelectorAll( `#${ self.id }> li` ).length ) { triggers = doc.querySelectorAll( `#${ self.id } li > ${ widgetHeading } .${ widgetTriggerClass}` ); } else { triggers = doc.querySelectorAll( `#${ self.id } > ${ widgetHeading } .${ widgetTriggerClass}` ); } /** * Now that the headings/triggers and panels are setup * we can grab all the triggers and setup their functionality. */ for ( t = 0; t < triggers.length; t++ ) { triggers[t].addEventListener( 'click', ARIAaccordion.actions ); triggers[t].addEventListener( 'keydown', ARIAaccordion.keytrolls ); } } // for(widget.length) }; // ARIAaccordion.create() ARIAaccordion.setupPanels = function ( id, panels, defaultPanel, constantPanel ) { let i; let panel; let panelID; let setPanel; let constant; for ( i = 0; i < panels.length; i++ ) { panel = panels[i]; panelID = `${id }_panel_${ i + 1}`; setPanel = defaultPanel; constant = constantPanel; panel.setAttribute( 'id', panelID ); ariaHidden( panels[0], true ); panel.classList.add( widgetPanelClass ); /** * Set the accordion to have the appropriately * opened panel if a data-default value is set. * If no value set, then no panels are open. */ if ( setPanel !== 'none' && parseInt( setPanel ) !== NaN ) { // if value is 1 or less if ( setPanel <= 1 ) { ariaHidden( panels[0], false ); } // if value is more than the number of panels, then open // the last panel by default else if ( ( setPanel - 1 ) >= panels.length ) { ariaHidden( panels[panels.length - 1], false ); } // for any other value between 2 - the last panel #, open that one else { ariaHidden( panels[( setPanel - 1 )], false ); } } /** * If an accordion is meant to have a consistently open panel, * and a default open panel was not set (or was not set correctly), * then run one more check. */ if ( constant && setPanel === 'none' || parseInt( setPanel ) === NaN ) { ariaHidden( panels[0], false ); } } }; // ARIAaccordion.setupPanels ARIAaccordion.setupHeadingButton = function ( headings, constantPanel ) { let heading; let targetID; let targetState; let newButton; let buttonText; let i; for ( i = 0; i < headings.length; i++ ) { heading = headings[i]; targetID = heading.nextElementSibling.id; targetState = doc.getElementById( targetID ).getAttribute( 'aria-hidden' ); // setup new heading buttons newButton = doc.createElement( 'button' ); buttonText = heading.textContent; // clear out the heading's content heading.innerHTML = ''; // provide the heading with a class for styling heading.classList.add( widgetHeadingClass ); newButton.setAttribute( 'type', 'button' ); newButton.setAttribute( 'aria-controls', targetID ); newButton.setAttribute( 'id', `${targetID }_trigger` ); newButton.classList.add( widgetTriggerClass ); /** * Check the corresponding panel to see if it was set up * to be hidden or shown by default. Add an aria-expanded * attribute value that is appropriate. */ if ( targetState === 'false' ) { ariaExpanded( newButton, true ); isCurrent( newButton, true ); /** * Check to see if this an accordion that needs a constantly * opened panel, and if the button's target is not hidden. */ if ( constantPanel ) { newButton.setAttribute( 'aria-disabled', 'true' ); } } else { ariaExpanded( newButton, false ); isCurrent( newButton, false ); } // Add the Button & previous heading text heading.appendChild( newButton ); newButton.appendChild( doc.createTextNode( buttonText ) ); } }; // ARIAaccordion.createButton ARIAaccordion.actions = function ( e ) { // Need to pass in if this is a multi accordion or not. // Also need to pass in existing trigger arrays. const thisAccordion = this.id.replace( /_panel.*$/g, '' ); const thisTarget = doc.getElementById( this.getAttribute( 'aria-controls' ) ); let thisTriggers; if ( doc.querySelectorAll( `#${ thisAccordion }> li` ).length ) { thisTriggers = doc.querySelectorAll( `#${ thisAccordion } li > ${ widgetHeading } .${ widgetTriggerClass}` ); } else { thisTriggers = doc.querySelectorAll( `#${ thisAccordion } > ${ widgetHeading } .${ widgetTriggerClass}` ); } e.preventDefault(); ARIAaccordion.togglePanel( e, thisAccordion, thisTarget, thisTriggers ); }; // ARIAaccordion.actions() ARIAaccordion.togglePanel = function ( e, thisAccordion, targetPanel, triggers ) { let getID; let i; const thisTrigger = e.target; // check to see if a trigger is disabled if ( thisTrigger.getAttribute( 'aria-disabled' ) !== 'true' ) { getID = thisTrigger.getAttribute( 'aria-controls' ); isCurrent( thisTrigger, 'true' ); if ( thisTrigger.getAttribute( 'aria-expanded' ) === 'true' ) { ariaExpanded( thisTrigger, 'false' ); ariaHidden( targetPanel, 'true' ); } else { ariaExpanded( thisTrigger, 'true' ); ariaHidden( targetPanel, 'false' ); if ( doc.getElementById( thisAccordion ).hasAttribute( 'data-constant' ) ) { ariaDisabled( thisTrigger, 'true' ); } } if ( doc.getElementById( thisAccordion ).hasAttribute( 'data-constant' ) || !doc.getElementById( thisAccordion ).hasAttribute( 'data-multi' ) ) { for ( i = 0; i < triggers.length; i++ ) { if ( thisTrigger !== triggers[i] ) { isCurrent( triggers[i], 'false' ); getID = triggers[i].getAttribute( 'aria-controls' ); ariaDisabled( triggers[i], 'false' ); ariaExpanded( triggers[i], 'false' ); ariaHidden( doc.getElementById( getID ), 'true' ); } } } } }; ARIAaccordion.keytrolls = function ( e ) { if ( e.target.classList.contains( widgetTriggerClass ) ) { const keyCode = e.keyCode || e.which; // var keyUp = 38; // var keyDown = 40; const keyHome = 36; const keyEnd = 35; const thisAccordion = this.id.replace( /_panel.*$/g, '' ); let thisTriggers; if ( doc.querySelectorAll( `#${ thisAccordion }> li` ).length ) { thisTriggers = doc.querySelectorAll( `#${ thisAccordion } li > ${ widgetHeading } .${ widgetTriggerClass}` ); } else { thisTriggers = doc.querySelectorAll( `#${ thisAccordion } > ${ widgetHeading } .${ widgetTriggerClass}` ); } switch ( keyCode ) { /** * keyUp & keyDown are optional controls * for accordion components. */ // case keyUp: // if ( doc.getElementById(thisAccordion).hasAttribute('data-up-down') ) { // e.preventDefault(); // // optional up arrow controls // } // break; // case keyDown: // if ( doc.getElementById(thisAccordion).hasAttribute('data-up-down') ) { // e.preventDefault(); // // optional down arrow control // } // break; /** * keyEnd/Home are optional functions that may not be inherently known * to most users and, in the case of END, conflict with expected * usage of that key with NVDA. */ case keyEnd: e.preventDefault(); thisTriggers[thisTriggers.length - 1].focus(); break; case keyHome: e.preventDefault(); thisTriggers[0].focus(); break; default: break; } } }; // ARIAaccordion.keytrolls() /** * Initialize Accordion Functions * if expanding this script, place any other * initialize functions within here. */ ARIAaccordion.init = function () { ARIAaccordion.create(); }; /** * Helper Functions * Just to cut down on the verboseness of some declarations */ var ariaHidden = function ( el, state ) { el.setAttribute( 'aria-hidden', state ); }; var ariaExpanded = function ( el, state ) { el.setAttribute( 'aria-expanded', state ); }; var ariaDisabled = function ( el, state ) { el.setAttribute( 'aria-disabled', state ); }; var isCurrent = function ( el, state ) { el.setAttribute( 'data-current', state ); }; // go go JavaScript ARIAaccordion.init(); } )( window, document ); 'use strict'; if ( typeof Object.assign !== 'function' ) { // Must be writable: true, enumerable: false, configurable: true Object.defineProperty( Object, 'assign', { value: function assign( target, varArgs ) { // .length of function is 2 'use strict'; if ( target == null ) { // TypeError if undefined or null throw new TypeError( 'Cannot convert undefined or null to object' ); } const to = Object( target ); for ( let index = 1; index < arguments.length; index++ ) { const nextSource = arguments[index]; if ( nextSource != null ) { // Skip over if undefined or null for ( const nextKey in nextSource ) { // Avoid bugs when hasOwnProperty is shadowed if ( Object.prototype.hasOwnProperty.call( nextSource, nextKey ) ) { to[nextKey] = nextSource[nextKey]; } } } } return to; }, writable: true, configurable: true } ); } const util = { keyCodes: { ESC: 27 }, generateID ( base ) { return base + Math.floor( Math.random() * 999 ); } }; ( function ( w, doc, undefined ) { /** * ARIA Tooltips * Widget to reveal a short description, or * the element's accessible name on hover/focus. * * Author: Scott O'Hara * Version: 1.0.0 * License: MIT */ const tipConfig = { baseID: 'tt_', ariaHiddenTip: true, tipWrapperClass: 'tooltip', tipContentClass: 'tooltip__content', tipTypeAttr: 'data-tooltip', tipContentAttr: 'data-tooltip-content', tipSelector: '[data-tooltip-tip]', triggerSelector: '[data-tooltip-trigger]' }; const ARIAtip = function ( inst, options ) { const el = inst; let elID; let elTip; let elTipID; let elTrigger; let tipContent; var tipType; const _options = Object.assign( tipConfig, options ); const init = function () { // if an element has an ID, use that as the // basis for the tooltip's ID. Or, generate one. elID = el.id || util.generateID( _options.baseID ); // the element that will trigger the tooltip on hover or focus. elTrigger = el.querySelector( _options.triggerSelector ); // base the tip's ID off from the element's tip elTipID = `${elID }_tip`; // determine the type of tip tipType = tipType(); // retrieve the content for the tip (flatted text string) tipContent = getTipContent(); // create the tip createTip(); // add/modify the necessary attributes of the trigger. setupTrigger(); // get the generated tooltip elTip = el.querySelector( `.${_options.tipContentClass}` ); // Attach the various events to the triggers attachEvents(); }; /** * A tooltip can either provide a description to * the element that it is associated with, or it * can provide a means to visually display the element's * accessible name (making it not actually a tooltip then, * but rather the accessible name that is revealed on * hover/focus). */ var tipType = function () { if ( el.getAttribute( _options.tipTypeAttr ) === 'label' ) { return 'label'; } return 'description'; }; /** * The content of a tooltip can come from different sources * so as to allow for fallback content in case this script * cannot run. * * A tip could be sourced from an element in the DOM via * the attribute data-tooltip-tip (a child of the widget), * or an ID referenced from data-tooltip-source (does not * have to be a child of the widget), * the trigger's title or aria-label attribute */ var getTipContent = function () { let returnTextString; // text string to return const tipAttrContent = el.getAttribute( _options.tipContentAttr ); const tipAriaLabel = elTrigger.getAttribute( 'aria-label' ); const widgetChild = el.querySelector( _options.tipSelector ); if ( tipAttrContent ) { returnTextString = tipAttrContent; } else if ( widgetChild ) { returnTextString = widgetChild.textContent; widgetChild.parentNode.removeChild( widgetChild ); } else if ( tipAriaLabel && tipType === 'label' ) { returnTextString = tipAriaLabel; elTrigger.removeAttribute( 'aria-label' ); } else if ( elTrigger.title ) { returnTextString = elTrigger.title; } // an element cannot have both a custom tooltip // and a tooltip from a title attribute. So no // matter what, remove the title attribute. elTrigger.removeAttribute( 'title' ); return returnTextString; }; /** * Create the necessary tooltip components for each * instance of the widget. */ var createTip = function () { const tipOuter = doc.createElement( 'span' ); const tipInner = doc.createElement( 'span' ); tipOuter.classList.add( _options.tipWrapperClass ); tipInner.classList.add( _options.tipContentClass ); tipInner.textContent = tipContent; tipInner.id = elTipID; if ( tipType !== 'label' ) { tipInner.setAttribute( 'role', 'tooltip' ); } // this is a bit silly, as it shows how unnecessary the // tooltip role is, but it ensures that a screen reader's // virtual cursor cannot interact with the tooltip by itself, // and helps reduce Chrome on PC w/JAWS and NVDA announcing the tooltip // multiple times, when it's shown/hidden. // If you want to reveal the tips anyway, // set the ariaHiddenTip config to false. if ( _options.ariaHiddenTip ) { tipInner.setAttribute( 'aria-hidden', 'true' ); } tipOuter.appendChild( tipInner ); el.appendChild( tipOuter ); }; /** * Ensure the tooltip trigger has the appropriate * attributes on it, and that they point to the * correct IDs. */ var setupTrigger = function () { if ( tipType === 'label' ) { elTrigger.setAttribute( 'aria-labelledby', elTipID ); } else { elTrigger.setAttribute( 'aria-describedby', elTipID ); } }; /** * Check the current viewport to determine if the tooltip * will be revealed outside of the current viewport's bounds. * If so, try to reposition to ensure it's within. */ const checkPositioning = function () { const bounding = elTip.getBoundingClientRect(); if ( bounding.bottom > w.innerHeight ) { el.classList.add( 'push-up' ); } if ( bounding.right > w.innerWidth ) { el.classList.add( 'push-right' ); } }; /** * Remove the positioning classes, assuming the next time * the element is interacted with, it will require the * default positioning. */ const resetPositioning = function () { el.classList.remove( 'push-up', 'push-right' ); }; /** * Add class to show tooltip. * Checks positioning to help ensure within viewport. * Adds global event to escape tip. */ const showTip = function () { el.classList.add( `${_options.tipWrapperClass }--show` ); checkPositioning(); doc.addEventListener( 'keydown', globalEscape, false ); doc.addEventListener( 'touchend', hideTip, false ); }; /** * Removes classes for show and/or suppressed tip. * Removes classes for positioning. * Removes global event to escape tip. */ var hideTip = function () { el.classList.remove( `${_options.tipWrapperClass }--show` ); resetPositioning(); doc.removeEventListener( 'keydown', globalEscape ); doc.addEventListener( 'touchend', hideTip ); }; /** * Global event to allow the ESC key to close * an invoked tooltip, regardless of where focus/hover * is in the DOM. * * Calls both hideTip and suppressTip functions to help * better replicate native tooltip suppression behavior. */ var globalEscape = function ( e ) { const keyCode = e.keyCode || e.which; switch ( keyCode ) { case util.keyCodes.ESC: e.preventDefault(); hideTip(); break; default: break; } }; var attachEvents = function () { elTrigger.addEventListener( 'mouseenter', showTip, false ); elTrigger.addEventListener( 'focus', showTip, false ); el.addEventListener( 'mouseleave', hideTip, false ); elTrigger.addEventListener( 'blur', hideTip, false ); }; init.call( this ); return this; }; // ARIAtip w.ARIAtip = ARIAtip; } )( window, document ); const selector = '[data-tooltip]'; const tooltips = document.querySelectorAll( selector ); tooltips.forEach( tooltip => { const dm = new ARIAtip( tooltip ); } ); /** * @fileOverview * @author Zoltan Toth * @version 3.1.2 */ /** * @description * 1Kb (gzipped) pure JavaScript carousel with all the basic features. * * @class * @param {object} options - User defined settings for the carousel. * @param {string} options.elem [options.elem=carousel] - The HTML id of the carousel container. * @param {(boolean)} [options.infinite=false] - Enables infinite mode for the carousel. * @param {(boolean)} [options.autoplay=false] - Enables auto play for slides. * @param {number} [options.interval=3000] - The interval between slide change. * @param {number} [options.show=0] - Index of the slide to start on. Numeration begins at 0. * * @param {(boolean)} [options.dots=true] - Display navigation dots. * @param {(boolean)} [options.arrows=true] - Display navigation arrows (PREV/NEXT). * @param {(boolean)} [options.buttons=true] - Display navigation buttons (STOP/PLAY). * * @param {(string)} [options.btnPlayText=Play] - Text for _PLAY_ button. * @param {(string)} [options.btnStopText=Stop] - Text for _STOP_ button. * @param {(string)} [options.arrPrevText=«] - Text for _PREV_ arrow. * @param {(string)} [options.arrNextText=»] - Text for _NEXT_ arrow. */ function Carousel( options ) { const element = document.querySelector( options.elem || 'carousel' ); const interval = options.interval || 3000; const btnPlayText = options.btnPlayText || 'Play'; const btnStopText = options.btnStopText || 'Stop'; const arrNextText = options.arrNextText || '›'; const arrPrevText = options.arrPrevText || '‹'; const crslClass = '[data-carousel]'; const crslArrowPrevClass = 'js-Carousel-arrowPrev'; const crslArrowNextClass = 'js-Carousel-arrowNext'; const crslDotsClass = 'js-Carousel-dots'; const crslButtonStopClass = 'js-Carousel-btnStop'; const crslButtonPlayClass = 'js-Carousel-btnPlay'; const count = element.querySelectorAll( 'li' ).length; let current = 0; let cycle = null; /** * Render the carousel if more than one slide. * Otherwise just show the single item. */ if ( count > 1 ) { render(); } /** * Render the carousel and all the navigation elements (arrows, dots, * play/stop buttons) if needed. Start with a particular slide, if set. * If infinite - move the last item to the very beginning and off the display area. */ function render() { const actions = { dots() { return showDots(); }, arrows() { return showArrows(); }, buttons() { return showButtons(); }, autoplay() { return play(); }, infinite() { return moveItem( count - 1, `${-element.offsetWidth }px`, 'afterBegin' ); }, initial() { const initial = 0 || ( options.initial >= count ) ? count : options.initial; return show( initial ); } }; for ( const key in actions ) { if ( options.hasOwnProperty( key ) && options[key] ) { actions[key](); } } } /** * Helper for moving items - last to be first or first to be the last. Needed * for infinite rotation of the carousel. * * @param {number} i - Position of the list item to move (either first or last). * @param {number} marginLeft - Left margin to position the item off-screen * at the beginning or no margin at the end. * @param {string} position - Where to insert the item. One of the following - * 'afterBegin' or 'beforeEnd'. */ function moveItem( i, marginLeft, position ) { const itemToMove = element.querySelectorAll( `${ crslClass } > ul li` )[i]; itemToMove.style.marginLeft = marginLeft; element.querySelector( `${ crslClass } > ul` ) .removeChild( itemToMove ); element.querySelector( `${ crslClass } > ul` ) .insertAdjacentHTML( position, itemToMove.outerHTML ); } /** * Create the navigation dots and attach to carousel. */ function showDots() { const dotContainer = document.createElement( 'ul' ); dotContainer.classList.add( crslDotsClass ); dotContainer.addEventListener( 'click', scrollToImage.bind( this ) ); for ( let i = 0; i < count; i++ ) { const dotElement = document.createElement( 'li' ); dotElement.setAttribute( 'data-position', i ); dotContainer.appendChild( dotElement ); } element.appendChild( dotContainer ); currentDot(); } /** * Highlight the corresponding dot of the currently visible carousel item. */ function currentDot() { [].forEach.call( element.querySelectorAll( `.${ crslDotsClass } li` ), function( item ) { item.classList.remove( 'is-active' ); } ); element.querySelectorAll( `.${ crslDotsClass } li` )[current].classList.add( 'is-active' ); } /** * Moves the carousel to the desired slide on a navigation dot click. * * @param {object} e - The clicked dot element. */ function scrollToImage( e ) { if ( e.target.tagName === 'LI' ) { show( e.target.getAttribute( 'data-position' ) ); resetInterval(); } } /** * Create the navigation arrows (prev/next) and attach to carousel. */ function showArrows() { const buttonPrev = document.createElement( 'button' ); buttonPrev.innerHTML = arrPrevText; buttonPrev.classList.add( crslArrowPrevClass ); buttonPrev.setAttribute( 'aria-label', 'Previous slide' ); const buttonNext = document.createElement( 'button' ); buttonNext.innerHTML = arrNextText; buttonNext.classList.add( crslArrowNextClass ); buttonNext.setAttribute( 'aria-label', 'Next slide' ); buttonPrev.addEventListener( 'click', showPrev ); buttonNext.addEventListener( 'click', showNext ); element.appendChild( buttonPrev ); element.appendChild( buttonNext ); } /** * Create the navigation buttons (play/stop) and attach to carousel. */ function showButtons() { const buttonPlay = document.createElement( 'button' ); buttonPlay.innerHTML = btnPlayText; buttonPlay.classList.add( crslButtonPlayClass ); buttonPlay.addEventListener( 'click', play ); const buttonStop = document.createElement( 'button' ); buttonStop.innerHTML = btnStopText; buttonStop.classList.add( crslButtonStopClass ); buttonStop.addEventListener( 'click', stop ); element.appendChild( buttonPlay ); element.appendChild( buttonStop ); } /** * Animate the carousel to go back 1 slide. Moves the very first (off-screen) * item to the visible area. * * @param {object} item - The element to move into view. */ function animatePrev( item ) { item.style.marginLeft = ''; } /** * Animate the carousel to go forward 1 slide. * * @param {object} item - The element to move into view. */ function animateNext( item ) { item.style.marginLeft = `${-element.offsetWidth }px`; } /** * Move the carousel to the desired slide. * * @param {number} slide - The index of the item. * @public */ function show( slide ) { const delta = current - slide; if ( delta < 0 ) { moveByDelta( -delta, showNext ); } else { moveByDelta( delta, showPrev ); } } /** * Helper to move the slides by index. * * @param {number} delta - how many slides to move. * @param {function} direction - function to move forward or back. */ function moveByDelta( delta, direction ) { for ( let i = 0; i < delta; i++ ) { direction(); } } /** * Move the carousel back. * * @public */ function showPrev() { if ( options.infinite ) { showPrevInfinite(); } else { showPrevLinear(); } resetInterval(); } /** * Helper function to show the previous slide for INFINITE carousel. * Do the sliding, move the last item to the very beginning. */ function showPrevInfinite() { animatePrev( element.querySelectorAll( `${ crslClass } > ul li` )[0] ); moveItem( count - 1, `${-element.offsetWidth }px`, 'afterBegin' ); adjustCurrent( -1 ); } /** * Helper function to show the previous slide for LINEAR carousel. * Stop the autoplay if user goes back. If on the first slide - do nothing. */ function showPrevLinear() { stop(); if ( current === 0 ) { return; } animatePrev( element.querySelectorAll( `${ crslClass } > ul li` )[current - 1] ); adjustCurrent( -1 ); } /** * Move the carousel forward. * * @public */ function showNext() { if ( options.infinite ) { showNextInfinite(); } else { showNextLinear(); } resetInterval(); } /** * Helper function to show the next slide for INFINITE carousel. * Do the sliding, move the second item to the very end. */ function showNextInfinite() { animateNext( element.querySelectorAll( `${ crslClass } > ul li` )[1] ); moveItem( 0, '', 'beforeEnd' ); adjustCurrent( 1 ); } /** * Helper function to show the next slide for LINEAR carousel. * If on the last slide - stop the play and do nothing else. */ function showNextLinear() { if ( current === count - 1 ) { stop(); return; } animateNext( element.querySelectorAll( `${ crslClass } > ul li` )[current] ); adjustCurrent( 1 ); } /** * Adjust _current_ and highlight the respective dot. * * @param {number} val - defines which way current should be corrected. */ function adjustCurrent( val ) { current += val; switch ( current ) { case -1: current = count - 1; break; case count: current = 0; break; default: current = current; } if ( options.dots ) { currentDot(); } } /** * Reset the autoplay interval. */ function resetInterval() { if ( cycle ) { stop(); play(); } } /** * Start the auto play. * If already playing do nothing. * * @public */ function play() { if ( cycle ) { return; } cycle = setInterval( showNext.bind( this ), interval ); } /** * Stop the auto play. * * @public */ function stop() { clearInterval( cycle ); cycle = null; } /** * Returns the current slide index. * * @public */ function live() { return current; } return { 'live': live, 'show': show, 'prev': showPrev, 'next': showNext, 'play': play, 'stop': stop }; } const prev = ''; const next = ''; document.addEventListener( 'DOMContentLoaded', () => { const testimonials = document.getElementById( 'testimonials-carousel' ); if ( testimonials ) { const testimonialsCarousel = new Carousel( { elem: '#testimonials-carousel', autoplay: true, infinite: true, interval: 12000, initial: 0, dots: true, arrows: false, buttons: false, btnStopText: 'Pause', arrPrevText: prev, arrNextText: next } ); } } ); /** * Makes the slider responsive. * Recalculates slide widths based on the dynamic container. * * @param {boolean} resize Whether or not the calculation is triggered from a resize event. */ function calculateSlideWidth( resize = false ) { const carousels = document.querySelectorAll( '[data-carousel]' ); carousels.forEach( carousel => { const width = carousel.clientWidth; const firstSlide = carousel.querySelector( '[data-carousel-slide]' ); const slides = carousel.querySelectorAll( '[data-carousel-slide]' ); slides.forEach( slide => { slide.style.width = `${width}px`; } ); if ( resize ) { firstSlide.style.marginLeft = `-${width}px`; } } ); } calculateSlideWidth(); window.addEventListener( 'resize', () => { calculateSlideWidth( true ); } ); if ( window.navigator ) { const widthHeightInput = document.querySelector( '#wpforms-12381-field_5' ); const browserInput = document.querySelector( '#wpforms-12381-field_4' ); const osInput = document.querySelector( '#wpforms-12381-field_2' ); const userIdInput = document.querySelector( '#wpforms-12381-field_6' ); const userEmailInput = document.querySelector( '#wpforms-12381-field_7' ); const { userId } = document.querySelector( '[data-user-id]' ).dataset; const { userEmail } = document.querySelector( '[data-user-email]' ).dataset; const { userAgent } = window.navigator; const operatingSystem = window.navigator.appVersion; if ( window.innerWidth && window.innerHeight && widthHeightInput ) { widthHeightInput.value = `${window.innerWidth}, ${window.innerHeight}`; } if ( userAgent && browserInput ) { browserInput.value = userAgent; userIdInput.value = userId; userEmailInput.value = userEmail; } if ( operatingSystem && osInput ) { osInput.value = operatingSystem; } }