PageSourceSearch

https://histology-and-embryology.com/_static/??-eJydkU0SwiAMRi9kxM…wsmrzYK5Dut6F28t0LvRZ/GY3Vo6mZXNfv9C8y69xw=

js histology-and-embryology.com collected 2026-10-05 03:38:03 UTC 267,989 bytes, 5,052 lines download raw bytes

1!function(){"use strict";var e,t={noop:function(){},texturize:function(e){return(e=(e=(e=(e+="").replace(/'/g,"&#8217;").replace(/&#039;/g,"&#8217;")).replace(/"/g,"&#8221;").replace(/&#034;/g,"&#8221;").replace(/&quot;/g,"&#8221;").replace(/[\u201D]/g,"&#8221;")).replace(/([\w]+)=&#[\d]+;(.+?)&#[\d]+;/g,'$1="$2"')).trim()},applyReplacements:function(e,t){if(e)return t?e.replace(/{(\d+)}/g,function(e,r){return void 0!==t[r]?t[r]:e}):e},getBackgroundImage:function(e){var t=document.createElement("canvas"),r=t.getContext&&t.getContext("2d");if(e){r.filter="blur(20px) ",r.drawImage(e,0,0);var o=t.toDataURL("image/png");return t=null,o}}},r=function(){function e(e,t){return Element.prototype.matches?e.matches(t):Element.prototype.msMatchesSelector?e.msMatchesSelector(t):void 0}function r(e,t,r,o){if(!e)return o();e.jpCarouselCancelFade&&e.jpCarouselCancelFade(),e.classList.remove("jp-carousel-fade"),e.style.removeProperty("display"),e.style.opacity=t,e.style.pointerEvents="none",e.offsetWidth,e.classList.add("jp-carousel-fade"),e.style.opacity=r;var a=1e3*parseFloat(getComputedStyle(e).transitionDuration)||0,n=setTimeout(function(){e.jpCarouselCancelFade=null,e.style.removeProperty("pointer-events"),o()},a+50);e.jpCarouselCancelFade=function(){clearTimeout(n),e.jpCarouselCancelFade=null}}return{closest:function(t,r){if(t.closest)return t.closest(r);var o=t;do{if(e(o,r))return o;o=o.parentElement||o.parentNode}while(null!==o&&1===o.nodeType);return null},matches:e,hide:function(e){e&&(e.style.display="none")},show:function(e){e&&(e.style.display="block")},fadeIn:function(e,o){r(e,0,1,o=o||t.noop)},fadeOut:function(e,o){o=o||t.noop,r(e,1,0,function(){e&&(e.style.display="none"),o()})},scrollToElement:function(e,t,r){if(!e||!t)return r?r():void 0;var o=t.querySelector(".jp-carousel-info-extra");o&&(o.style.minHeight=window.innerHeight-64+"px");var a=!0,n=Date.now(),i=t.scrollTop,l=Math.max(0,e.offsetTop-Math.max(0,window.innerHeight-function(e){var t=e.querySelector(".jp-carousel-info-footer"),r=e.querySelector(".jp-carousel-info-extra"),o=e.querySelector(".jp-carousel-info-content-wrapper");if(t&&r&&o){var a=window.getComputedStyle(r),n=parseInt(a.paddingTop,10)+parseInt(a.paddingBottom,10);return n=isNaN(n)?0:n,o.offsetHeight+t.offsetHeight+n}return 0}(t)))-t.scrollTop;function s(){a=!1}l=Math.min(l,t.scrollHeight-window.innerHeight),t.addEventListener("wheel",s),function e(){var c,u=Date.now(),d=(c=(u-n)/300)<.5?2*c*c:1-Math.pow(-2*c+2,2)/2,m=(d=d>1?1:d)*l;if(t.scrollTop=i+m,u<=n+300&&a)return requestAnimationFrame(e);r&&r(),o&&(o.style.minHeight=""),a=!1,t.removeEventListener("wheel",s)}()},getJSONAttribute:function(e,t){if(e&&e.hasAttribute(t))try{return JSON.parse(e.getAttribute(t))}catch{return}},convertToPlainText:function(e){var t=document.createElement("div");return t.textContent=e,t.innerHTML},stripHTML:function(e){var t=document.createElement("div");return t.innerHTML=e.replace(/<[^>]*>?/gm,""),t.textContent},emitEvent:function(e,t,r){var o;try{o=new CustomEvent(t,{bubbles:!0,cancelable:!0,detail:r||null})}catch{(o=document.createEvent("CustomEvent")).initCustomEvent(t,!0,!0,r||null)}e.dispatchEvent(o)},isTouch:function(){return"ontouchstart"in window||window.DocumentTouch&&document instanceof DocumentTouch},prefersReducedMotion:function(){return!!window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches}}}();function o(){var o,a,n,i,l={},s={},c="",u=!1,d="div.gallery, div.tiled-gallery, ul.wp-block-gallery, ul.blocks-gallery-grid, figure.wp-block-gallery.has-nested-images, div.wp-block-jetpack-tiled-gallery, a.single-image-gallery",m=".gallery-item, .tiled-gallery-item, .blocks-gallery-item,  .tiled-gallery__item",p=m+", .wp-block-image",g={},h="undefined"!=typeof wpcom&&wpcom.carousel&&wpcom.carousel.stat?wpcom.carousel.stat:t.noop,f="undefined"!=typeof wpcom&&wpcom.carousel&&wpcom.carousel.pageview?wpcom.carousel.pageview:t.noop;function v(t){if(!u)switch(t.which){case 38:t.preventDefault(),g.overlay.scrollTop-=100;break;case 40:t.preventDefault(),g.overlay.scrollTop+=100;break;case 39:t.preventDefault(),e.slideNext();break;case 37:case 8:t.preventDefault(),e.slidePrev();break;case 27:t.preventDefault(),k()}}function y(){u=!0}function w(){u=!1}function S(e){e.role="button",e.tabIndex=0,e.ariaLabel=jetpackCarouselStrings.image_label}function j(){g.overlay||(g.overlay=document.querySelector(".jp-carousel-overlay"),g.container=g.overlay.querySelector(".jp-carousel-wrap"),g.gallery=g.container.querySelector(".jp-carousel"),g.info=g.overlay.querySelector(".jp-carousel-info"),g.caption=g.
1info.querySelector(".jp-carousel-caption"),g.commentField=g.overlay.querySelector("#jp-carousel-comment-form-comment-field"),g.emailField=g.overlay.querySelector("#jp-carousel-comment-form-email-field"),g.authorField=g.overlay.querySelector("#jp-carousel-comment-form-author-field"),g.urlField=g.overlay.querySelector("#jp-carousel-comment-form-url-field"),window.innerWidth<=760&&Math.round(window.innerWidth/760*110)<40&&r.isTouch(),[g.commentField,g.emailField,g.authorField,g.urlField].forEach(function(e){e&&(e.addEventListener("focus",y),e.addEventListener("blur",w))}),g.overlay.addEventListener("click",function(e){var t,o,a=e.target,n=!!r.closest(a,".jp-carousel-close-hint"),i=!!window.matchMedia("(max-device-width: 760px)").matches;a===g.overlay?i||k():n?k():a.classList.contains("jp-carousel-image-download")?h("download_original_click"):a.classList.contains("jp-carousel-comment-login")?(t=g.currentSlide,o=t?t.attrs.attachmentId:"0",window.location.href=jetpackCarouselStrings.login_url+"%23jp-carousel-"+o):r.closest(a,"#jp-carousel-comment-form-container")?function(e){var t=e.target,o=r.getJSONAttribute(g.container,"data-carousel-extra")||{},a=g.currentSlide.attrs.attachmentId,n=document.querySelector("#jp-carousel-comment-form-submit-and-info-wrapper"),i=document.querySelector("#jp-carousel-comment-form-spinner"),s=document.querySelector("#jp-carousel-comment-form-button-submit"),c=document.querySelector("#jp-carousel-comment-form");if(g.commentField&&g.commentField.getAttribute("id")===t.getAttribute("id"))y(),r.show(n);else if(r.matches(t,'input[type="submit"]')){e.preventDefault(),e.stopPropagation(),r.show(i),c.classList.add("jp-carousel-is-disabled");var u={action:"post_attachment_comment",nonce:jetpackCarouselStrings.nonce,blog_id:o.blog_id,id:a,comment:g.commentField.value};if(!u.comment.length)return void b(jetpackCarouselStrings.no_comment_text,!1);if(1!==Number(jetpackCarouselStrings.is_logged_in)&&(u.email=g.emailField.value,u.author=g.authorField.value,u.url=g.urlField.value,1===Number(jetpackCarouselStrings.require_name_email))){if(!u.email.length||!u.email.match("@"))return void b(jetpackCarouselStrings.no_comment_email,!1);if(!u.author.length)return void b(jetpackCarouselStrings.no_comment_author,!1)}var d=new XMLHttpRequest;d.open("POST",jetpackCarouselStrings.ajaxurl,!0),d.setRequestHeader("X-Requested-With","XMLHttpRequest"),d.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8"),d.onreadystatechange=function(){if(this.readyState===XMLHttpRequest.DONE&&this.status>=200&&this.status<300){var e;try{e=JSON.parse(this.response)}catch{return void b(jetpackCarouselStrings.comment_post_error,!1)}if("approved"===e.comment_status){b(jetpackCarouselStrings.comment_approved,!0);var t=function(e){for(var t=0;t<g.slides.length;t++)if(g.slides[t].attrs.attachmentId===e)return g.slides[t];return null}(a);t&&(t.attrs.commentsCount=(parseInt(t.attrs.commentsCount,10)||0)+1)}else"unapproved"===e.comment_status?b(jetpackCarouselStrings.comment_unapproved,!0):b(jetpackCarouselStrings.comment_post_error,!1);W(),delete l[a],_(a,void 0,!0),s.value=jetpackCarouselStrings.post_comment,r.hide(i),c.classList.remove("jp-carousel-is-disabled")}else b(jetpackCarouselStrings.comment_post_error,!1)};var m=[];for(var p in u)if(p){var h=encodeURIComponent(p)+"="+encodeURIComponent(u[p]);m.push(h.replace(/%20/g,"+"))}var f=m.join("&");d.send(f)}}(e):(r.closest(a,".jp-carousel-photo-icons-container")||a.classList.contains("jp-carousel-photo-title"))&&function(e){e.preventDefault();var t=e.target,o=g.info.querySelector(".jp-carousel-info-extra"),a=g.info.querySelector(".jp-carousel-image-meta"),n=g.info.querySelector(".jp-carousel-comments-wrapper"),i=g.info.querySelector(".jp-carousel-icon-info"),s=g.info.querySelector(".jp-carousel-icon-comments");function c(){s&&s.classList.remove("jp-carousel-selected"),i.classList.toggle("jp-carousel-selected"),n&&n.classList.remove("jp-carousel-show"),a&&(a.classList.toggle("jp-carousel-show"),a.classList.contains("jp-carousel-show")?o.classList.add("jp-carousel-show"):o.classList.remove("jp-carousel-show"))}function u(){if(i&&i.classList.remove("jp-carousel-selected"),s.classList.toggle("jp-carousel-selected"),a&&a.classList.remove("jp-carousel-show"),n)if(n.classList.toggle("jp-carousel-show"),n.classList.contains("jp-carousel-show")){o.classList.add("jp-carousel-show");var e=g.currentSlide;if(e){var t=l[e.attrs.attachmentId];t?t.hasMore&&T(e.attrs.attachmentId,t.nextOffset):_(e.attrs.attachmentId)}}else o.classList.remove("jp-carousel-show")}(r.closest(t,".jp-carousel-icon-info")||t.classList.contains("jp-carousel-photo-title"))&&(a&&a.classList.contains("jp-carousel-show")?r.scrollToElement(g.overlay,g.overlay,c):(c(),r.scrollToElement(g.info,g.overlay))),r.closest(t,".jp-carousel-icon-comments")&&(n&&n.classList.contains("jp-carousel-show")?r.scrollToElement(g.overlay,g.overlay,u):(u(),r.scrollToElement(g.info,g.overlay)))}(e)}),window.addEventListener("keydown",v),g.overlay.addEventListener("jp_carousel.afterOpen",function(){w(),g.slides.length<=1||(g.slides.length<=5?r.show(g.info.querySelector(".jp-swiper-pagination")):r.show(g.info.querySelector(".jp-carousel-pagination")))}),g.overlay.addEventListener("jp_carousel.beforeClose",function(){y(),document.documentElement.style.removeProperty("height"),e&&e.enable(),r.hide(g.info.querySelector(".jp-swiper-pagination")),r.hide(g.info.querySelector(".jp-carousel-pagination"))}),g.overlay.addEventListener("jp_carousel.afterClose",function(){window.history.pushState?history.pushState("",document.title,window.location.pathname+window.location.search):window.location.href="",c="",g.isOpen=!1}),g.overlay.addEventListener("touchstart",function(e){e.touches.length>1&&e.preventDefault()}))}function b(e,t){var o=g.overlay.querySelector("#jp-carousel-comment-post-results"),a="jp-carousel-comment-post-"+(t?"success":"error");o.innerHTML='<span class="'+a+'">'+e+"</span>",r.hide(g.overlay.querySelector("#jp-carousel-comment-form-spinner")),g.overlay.querySelector("#jp-carousel-comment-form").classList.remove("jp-carousel-is-disabled"),r.show(o)}function L(){var e=document.querySelectorAll("a img[data-attachment-id]");Array.prototype.forEach.call(e,function(e){var t=e.parentElement,o=t.parentElement;if(!o.classList.contains("gallery-icon")&&!r.closest(o,m)&&t.hasAttribute("href")){var a=!1;t.getAttribute("href").split("?")[0]===e.getAttribute("data-orig-file").split("?")[0]&&1===Number(jetpackCarouselStrings.single_image_gallery_media_file)&&(a=!0),t.getAttribute("href")===e.getAttribute("data-permalink")&&(a=!0),a&&(S(e),t.classList.add("single-image-gallery"),t.setAttribute("data-carousel-extra",JSON.stringify({blog_id:Number(jetpackCarouselStrings.blog_id)})))}})}function x(t,r){g.isOpen?(C(r),e.slideTo(r+1)):z(t,{startIndex:r})}function C(e){(!e||e<0||e>g.slides.length)&&(e=0),g.currentSlide=g.slides[e];var a,n,i=g.currentSlide,s=i.attrs.attachmentId;M(g.slides[e]),function(e){var t=[],r=g.slides.length;if(r>1){var o=e>0?e-1:r-1;t.push(o);var a=e<r-1?e+1:0;t.push(a)}t.forEach(function(e){var t=g.slides[e];t&&(M(t),1!==Number(jetpackCarouselStrings.display_background_image)||t.backgroundImage||F(t))})}(e),1!==Number(jetpackCarouselStrings.display_background_image)||g.slides[e].backgroundImage||F(g.slides[e]),r.hide(g.caption),function(e){var t,o,a,n,i,l,s;if(n=g.overlay.querySelector(".jp-carousel-photo-caption"),i=g.overlay.querySelector(".jp-carousel-caption"),l=g.overlay.querySelector(".jp-carousel-photo-title"),s=g.overlay.querySelector(".jp-carousel-photo-description"),r.hide(n),r.hide(i),r.hide(l),r.hide(s),t=A(e.caption)||"",o=A(e.title)||"",a=A(e.desc)||"",(t||o||a)&&(t&&(n.innerHTML=t,i.innerHTML=t,r.show(n),r.show(i)),r.stripHTML(t)===r.stripHTML(o)&&(o=""),r.stripHTML(t)===r.stripHTML(a)&&(a=""),r.stripHTML(o)===r.stripHTML(a)&&(a=""),a&&(s.innerHTML=a,r.show(s),o||t||(n.textContent=r.stripHTML(a),r.show(n))),o)){var c=r.stripHTML(o);l.textContent=c,t||(n.textContent=c,i.textContent=c,r.show(n)),r.show(l)}}
1({caption:i.attrs.caption,title:i.attrs.title,desc:i.attrs.desc}),function(e){if(!e||1!==Number(jetpackCarouselStrings.display_exif))return!1;var t=g.info.querySelector(".jp-carousel-image-meta");if(!t)return!1;var r="";for(var o in e){var a=e[o],n=jetpackCarouselStrings.meta_data||[];if(0!==parseFloat(a)&&a.length&&-1!==n.indexOf(o)){switch(o){case"focal_length":a+="mm";break;case"shutter_speed":a=E(a);break;case"aperture":a="f/"+a}r+="<li><h5>"+jetpackCarouselStrings[o]+"</h5>"+a+"</li>"}}var i=t.querySelector("ul.jp-carousel-image-exif");if(""!==r){if(!i){(i=document.createElement("ul")).className="jp-carousel-image-exif";var l=t.querySelector(".jp-carousel-title-and-caption");l&&l.nextSibling?t.insertBefore(i,l.nextSibling):t.insertBefore(i,t.firstChild)}i.innerHTML=r,i.style.removeProperty("display")}else i&&i.parentNode.removeChild(i)}(g.slides[e].attrs.imageMeta),function(e){if(!e)return!1;var r,o=[e.attrs.origWidth,e.attrs.origHeight],a=document.createElement("a");a.href=e.attrs.src.replace(/\?.+$/,""),r=null!==a.hostname.match(/^i[\d]{1}\.wp\.com$/i)?a.href:e.attrs.origFile.replace(/\?.+$/,"");var n=g.info.querySelector(".jp-carousel-download-text"),i=g.info.querySelector(".jp-carousel-image-download");n.innerHTML=t.applyReplacements(jetpackCarouselStrings.download_original,o),i.setAttribute("href",r),i.style.removeProperty("display")}(i),1===Number(jetpackCarouselStrings.display_comments)&&(a=g.slides[e].attrs.commentsOpened,n=g.info.querySelector("#jp-carousel-comment-form-container"),1===parseInt(a,10)?r.fadeIn(n):r.fadeOut(n),function(e){var t=e.attrs.attachmentId,a=g.info.querySelector(".jp-carousel-comments"),n=l[t];if(clearInterval(o),r.hide(g.info.querySelector("#jp-carousel-comments-loading")),n)return a.innerHTML=n.html,H(n.count),r.show(a),void(n.hasMore&&I()&&T(t,n.nextOffset));a.innerHTML="",r.hide(a),H(parseInt(e.attrs.commentsCount,10)||0),I()&&_(t)}(i),r.hide(g.info.querySelector("#jp-carousel-comment-post-results")));var u=g.info.querySelector(".jp-carousel-pagination");if(u&&g.slides.length>5){var d=e+1;u.innerHTML="<span>"+d+" / "+g.slides.length+"</span>"}jetpackCarouselStrings.stats&&g.isOpen&&((new Image).src=document.location.protocol+"//pixel.wp.com/g.gif?"+jetpackCarouselStrings.stats+"&post="+encodeURIComponent(s)+"&rand="+Math.random()),g.isOpen&&f(s),c="#jp-carousel-"+s,window.location.hash=c}function k(){document.body.style.overflow=a,document.documentElement.style.overflow=n,W(),y(),r.emitEvent(g.overlay,"jp_carousel.beforeClose"),window.scrollTo(window.scrollX||window.pageXOffset||0,i||0),g.isOpen=!1,e.destroy(),g.slides=[],l={},s={},g.currentSlide=void 0,g.gallery.innerHTML="",r.fadeOut(g.overlay,function(){r.emitEvent(g.overlay,"jp_carousel.afterClose")})}function q(e){if("object"!=typeof e&&(e={}),void 0===e.origFile)return"";if(void 0===e.origWidth||void 0===e.maxWidth)return e.origFile;if(!e.largeFile)return e.origFile;var t=document.createElement("a");t.href=e.largeFile;var r=/^i[0-2]\.wp\.com$/i.test(t.hostname),o=function(e,t,r){var o,a=r?e.replace(/.*=([\d]+%2C[\d]+).*$/,"$1"):e.replace(/.*-([\d]+x[\d]+)\..+$/,"$1");return"9999"===(o=a!==e?r?a.split("%2C"):a.split("x"):[t,0])[0]&&(o[0]="0"),"9999"===o[1]&&(o[1]="0"),o}(e.largeFile,e.origWidth,r),a=parseInt(o[0],10),n=parseInt(o[1],10);e.origMaxWidth=e.maxWidth,e.origMaxHeight=e.maxHeight,void 0!==window.devicePixelRatio&&window.devicePixelRatio>1&&(e.maxWidth=e.maxWidth*window.devicePixelRatio,e.maxHeight=e.maxHeight*window.devicePixelRatio);var i=e.zoomHeadroom||1;if(i>1&&(e.maxWidth=e.maxWidth*i,e.maxHeight=e.maxHeight*i),a>=e.maxWidth||n>=e.maxHeight)return e.largeFile;if(r){if(-1===e.largeFile.lastIndexOf("?"))return e.largeFile;var l=function(e){var t;try{t=new URL(e)}catch(t){return e}var r=["quality","ssl","filter","brightness","contrast","colorize","smooth"],o=Array.from(t.searchParams.entries());return t.search="",o.forEach(([e,o])=>{r.includes(e)&&t.searchParams.append(e,o)}),t}(e.largeFile);return(e.origWidth>e.maxWidth||e.origHeight>e.maxHeight)&&(e.origMaxWidth=e.maxWidth,e.origMaxHeight=e.maxHeight,l.searchParams.set("fit",e.origMaxWidth+","+e.origMaxHeight)),l.toString()}return e.origFile}function E(e){return e>=1?Math.round(10*e)/10+"s":"1/"+Math.round(1/e)+"s"}
1function A(e){return!e.match(" ")&&e.match("_")?"":e}function I(){var e=g.info.querySelector(".jp-carousel-comments-wrapper");return!!e&&e.classList.contains("jp-carousel-show")}function H(e){var t=g.info.querySelector(".jp-carousel-icon-comments .jp-carousel-has-comments-indicator");t&&(e>0?(t.innerText=e,t.classList.add("jp-carousel-show")):t.classList.remove("jp-carousel-show"))}function T(e,t){clearInterval(o),o=setInterval(function(){g.container.scrollTop+150>window.innerHeight&&(clearInterval(o),_(e,t))},300)}function _(e,t,a){var n=void 0===t;if(clearInterval(o),e&&g.currentSlide&&g.currentSlide.attrs.attachmentId===e&&(!s[e]||a)){(!t||t<1)&&(t=0);var i=g.info.querySelector(".jp-carousel-comments"),c=g.info.querySelector("#jp-carousel-comments-loading");r.show(c),n&&(r.hide(i),i.innerHTML="");var u=new XMLHttpRequest;s[e]=u;var d=function(){return s[e]===u},m=jetpackCarouselStrings.ajaxurl+"?action=get_attachment_comments&nonce="+jetpackCarouselStrings.nonce+"&id="+e+"&offset="+t;u.open("GET",m),u.setRequestHeader("X-Requested-With","XMLHttpRequest");var p=function(){r.fadeIn(i),r.fadeOut(c)};u.onload=function(){if(d()&&(delete s[e],g.currentSlide&&g.currentSlide.attrs.attachmentId===e)){var o,a=u.status>=200&&u.status<300;try{o=JSON.parse(u.responseText)}catch{}if(a&&o&&Array.isArray(o)){n&&(i.innerHTML="");for(var m=0;m<o.length;m++){var h=o[m];if(!i.querySelector("#jp-carousel-comment-"+h.id)){var f=document.createElement("div");f.classList.add("jp-carousel-comment"),f.setAttribute("id","jp-carousel-comment-"+h.id),f.innerHTML='<div class="comment-gravatar">'+h.gravatar_markup+'</div><div class="comment-content"><div class="comment-author">'+h.author_markup+'</div><div class="comment-date">'+h.date_gmt+"</div>"+h.content+"</div>",i.appendChild(f)}}var v=o.length>=10,y=t+10;v&&T(e,y);var w=i.querySelectorAll(".jp-carousel-comment").length,S=Math.max(parseInt(g.currentSlide.attrs.commentsCount,10)||0,w);o.length>0&&(r.show(i),H(S)),l[e]={html:i.innerHTML,count:S,hasMore:v,nextOffset:y},r.hide(c)}else p()}},u.onerror=function(){d()&&(delete s[e],g.currentSlide&&g.currentSlide.attrs.attachmentId===e&&p())},u.send()}}function M(e){var t=e.attrs,r=e.el.querySelector("img");if(!r.hasAttribute("data-loaded"))if(r.setAttribute("itemprop","image"),r.setAttribute("data-loaded",1),t.previewImage&&t.previewImage!==t.src){r.src=t.previewImage,r.style.filter="blur(8px)";var o=new window.Image;o.addEventListener("load",function(){r.hasAttribute("data-zoom-loaded")||(r.src=t.src),r.style.filter=""},{once:!0}),o.addEventListener("error",function(){r.style.filter=""},{once:!0}),o.src=t.src}else r.src=t.src}function F(e){var t=e.attrs.originalElement;if(t)if(t.complete&&0!==t.naturalHeight)O(e,t);else{var r=function(){t.removeEventListener("error",o),O(e,t)},o=function(){t.removeEventListener("load",r)};t.addEventListener("load",r,{once:!0}),t.addEventListener("error",o,{once:!0})}}function O(e,r){var o=t.getBackgroundImage(r);o&&(e.backgroundImage=o,e.el.style.backgroundImage="url("+o+")",e.el.style.backgroundSize="cover",e.el.style.backgroundPosition="center")}function W(){g.commentField&&(g.commentField.value="")}function N(e,o){g.slides=[];var a={width:window.innerWidth,height:window.innerHeight-64};0!==o&&null!==e[o].getAttribute("data-gallery-src")&&((new Image).src=e[o].getAttribute("data-gallery-src")),Array.prototype.forEach.call(e,function(e,o){var n=r.closest(e,"a"),i=e.getAttribute("data-orig-file")||e.getAttribute("src-orig"),l=e.getAttribute("data-attachment-id")||e.getAttribute("data-id")||"0",s=document.querySelector('img[data-attachment-id="'+l+'"] + figcaption');s=s?s.innerHTML:e.getAttribute("data-image-caption");var c={originalElement:e,attachmentId:l,commentsOpened:e.getAttribute("data-comments-opened")||"0",commentsCount:e.getAttribute("data-comments-count")||"0",imageMeta:r.getJSONAttribute(e,"data-image-meta")||{},title:e.getAttribute("data-image-title")||"",desc:e.getAttribute("data-image-description")||"",largeFile:e.getAttribute("data-large-file")||"",origFile:i||"",thumbSize:{width:e.naturalWidth,height:e.naturalHeight},caption:s||"",permalink:n&&n.getAttribute("href"),src:i||e.getAttribute("src")||""},u=r.closest(e,".tiled-gallery-item"),d=u&&u.querySelector(".tiled-gallery-caption"),m=d&&d.innerHTML;m&&(c.caption=m);var p=function(e){var t=e.getAttribute("data-orig-size")||"";if(t){var r=t.split(",");return{width:parseInt(r[0],10),height:parseInt(r[1],10)}}return{width:e.getAttribute("data-original-width")||e.getAttribute("width")||void 0,height:e.getAttribute("data-original-height")||e.getAttribute("height")||void 0}}(e);if(c.origWidth=p.width||c.thumbSize.width,c.origHeight=p.height||c.thumbSize.height,"undefined"!=typeof wpcom&&wpcom.carousel&&wpcom.carousel.generateImgSrc)c.src=wpcom.carousel.generateImgSrc(e,a);else{var h=function(e){return{origFile:c.src,origWidth:c.origWidth,origHeight:c.origHeight,maxWidth:a.width,maxHeight:a.height,largeFile:c.largeFile,zoomHeadroom:e}};c.zoomSrc=q(h(2)),c.src=q(h(1))}if(e.setAttribute("data-gallery-src",c.src),"0"!==c.attachmentId){c.title=t.texturize(c.title),c.desc=t.texturize(c.desc),c.caption=t.texturize(c.caption);var f=new Image,v=document.createElement("div");v.classList.add("swiper-slide"),v.setAttribute("itemprop","associatedMedia"),v.setAttribute("itemscope",""),v.setAttribute("itemtype","https://schema.org/ImageObject");var y=document.createElement("div");y.classList.add("swiper-zoom-container"),g.gallery.appendChild(v),v.appendChild(y),y.appendChild(f),v.setAttribute("data-attachment-id",c.attachmentId),v.setAttribute("data-permalink",c.permalink),v.setAttribute("data-orig-file",c.origFile),c.previewImage=e.currentSrc||e.getAttribute("src")||"";var w={el:v,attrs:c,index:o};g.slides.push(w)}})}function z(e,t){if(!window.JetpackSwiper){var o=document.querySelector("#jp-carousel-loading-overlay");r.show(o);var a=document.createElement("script");return a.id="jetpack-carousel-swiper-js",a.src=window.jetpackSwiperLibraryPath.url,a.async=!0,a.onload=function(){r.hide(o),R(e,t)},a.onerror=function(){r.hide(o)},void document.head.appendChild(a)}R(e,t)}function R(t,o){var c,u={imgSelector:".gallery-item [data-attachment-id], .tiled-gallery-item [data-attachment-id], img[data-attachment-id], img[data-id]",startIndex:0},d=r.getJSONAttribute(t,"data-carousel-extra");if(!d)return;const m=t.querySelectorAll(u.imgSelector);if(m.length&&(j(),!g.isOpen)){for(var p in g.isOpen=!0,a=getComputedStyle(document.body).overflow,document.body.style.overflow="hidden",n=getComputedStyle(document.documentElement).overflow,document.documentElement.style.overflow="hidden",i=window.scrollY||window.pageYOffset||0,g.container.setAttribute("data-carousel-extra",JSON.stringify(d)),h(["open","view_image"]),o||{})u[p]=o[p];-1===u.startIndex&&(u.startIndex=0),r.emitEvent(g.overlay,"jp_carousel.beforeOpen"),g.gallery.innerHTML="",l={},s={},g.overlay.style.opacity=1,g.overlay.style.display="block",N(m,u.startIndex),(e=new window.JetpackSwiper(".jp-carousel-swiper-container",{centeredSlides:!0,speed:r.prefersReducedMotion()?0:250,zoom:!0,loop:g.slides.length>1,enabled:g.slides.length>1,pagination:{el:".jp-swiper-pagination",clickable:!0},navigation:{nextEl:".jp-swiper-button-next",prevEl:".jp-swiper-button-prev"},initialSlide:u.startIndex,on:{init:function(){C(u.startIndex)}},preventClicks:!1,preventClicksPropagation:!1,preventInteractionOnTransition:!r.isTouch(),threshold:5})).on("slideChange",function(e){g.isOpen&&(C(e.realIndex),g.overlay.classList.remove("jp-carousel-hide-control
1s"))}),e.on("zoomChange",function(e,t){t>1&&(function(e){if(e&&e.attrs.zoomSrc&&e.attrs.zoomSrc!==e.attrs.src){var t=e.el.querySelector("img");t&&!t.hasAttribute("data-zoom-loaded")&&(t.setAttribute("data-zoom-loaded",1),t.src=e.attrs.zoomSrc)}}(g.currentSlide),g.overlay.classList.add("jp-carousel-hide-controls")),1===t&&g.overlay.classList.remove("jp-carousel-hide-controls")}),e.on("doubleTap",function(e){if(clearTimeout(c),1===e.zoom.scale)var t=setTimeout(function(){g.overlay.classList.remove("jp-carousel-hide-controls"),clearTimeout(t)},150)}),e.on("tap",function(){e.zoom.scale>1&&(c=setTimeout(function(){g.overlay.classList.toggle("jp-carousel-hide-controls")},150))}),r.fadeIn(g.overlay,function(){r.emitEvent(g.overlay,"jp_carousel.afterOpen")})}}function P(e){if("click"!==e.type){if("keydown"===e.type){const t=document.activeElement.parentElement,r=t&&t.classList.contains("tiled-gallery__item");" "!==e.key&&"Enter"!==e.key||!r||$(e)}}else $(e)}function D(e){return(e||"").split("?")[0].replace(/\/$/,"")}function J(e){"A"===e.tagName&&(e=e.querySelector("img")||e);var t=e.parentElement,o=t?t.parentElement:null,a=null;if(o&&o.classList.contains("wp-block-image")?a=t.getAttribute("href"):t&&t.classList.contains("wp-block-image")&&t.querySelector(":scope > a")&&(a=t.querySelector(":scope > a").getAttribute("href")),a){var n=D(a),i=D(e.getAttribute("data-orig-file")),l=D(e.getAttribute("data-permalink"));if(n!==i&&n!==l)return!1}return!(t&&t.classList.contains("gallery-caption")||r.matches(t,"figcaption"))}function $(e){if(window.CSS&&window.CSS.supports&&window.CSS.supports("display","grid")){var t,o=e.target,a=r.closest(o,d);if(a){if(!(t=a)||!t.getAttribute("data-carousel-extra"))return;if(!J(o))return;document.documentElement.style.height="auto",e.preventDefault(),e.stopPropagation();var n=r.closest(o,p),i=Array.prototype.indexOf.call(a.querySelectorAll(p),n);z(a,{startIndex:i})}}}document.body.addEventListener("click",P),document.body.addEventListener("keydown",P),document.querySelectorAll(m+"img").forEach(function(e){J(e)&&S(e)}),1===Number(jetpackCarouselStrings.single_image_gallery)&&(L(),document.body.addEventListener("is.post-load",function(){L()})),window.addEventListener("hashchange",function(){var e=/jp-carousel-(\d+)/;if(window.location.hash&&e.test(window.location.hash)){if(window.location.hash!==c||!g.isOpen)if(window.location.hash&&g.gallery&&!g.isOpen&&history.back)history.back();else{c=window.location.hash;for(var t=window.location.hash.match(e),r=parseInt(t[1],10),o=document.querySelectorAll(d),a=0;a<o.length;a++){for(var n,i=o[a],l=i.querySelectorAll("img"),s=0;s<l.length;s++)if(parseInt(l[s].getAttribute("data-attachment-id"),10)===r||parseInt(l[s].getAttribute("data-id"),10)===r){n=s;break}if(void 0!==n){x(i,n);break}}}}else g.isOpen&&k()}),window.location.hash&&r.emitEvent(window,"hashchange")}"loading"!==document.readyState?o():document.addEventListener("DOMContentLoaded",o)}();;
vendor: 15,088 bytes, lines 2-3
2/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */
3!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<t&&t-1 in e)}S.fn=S.prototype={jquery:f,constructor:S,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=S.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return S.each(this,e)},map:function(n){return this.pushStack(S.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(S.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(S.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},S.extend=S.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(S.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||S.isPlainObject(n)?n:{},i=!1,a[t]=S.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},S.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==o.call(e))&&(!(t=r(e))||"function"==typeof(n=v.call(t,"constructor")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){b(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(p(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},makeArray:function(e,t){var n=t||[];return null!=e&&(p(Object(e))?S.merge(n,"string"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(p(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:y}),"function"==typeof Symbol&&(S.fn[Symbol.iterator]=t[Symbol.iterator]),S.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var d=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,S="sizzle"+1*new Date,p=n.document,k=0,r=0,m=ue(),x=ue(),A=ue(),N=ue(),D=function(e,t){return e===t&&(l=!0),0},j={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",I="(?:\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",W="\\["+M+"*("+I+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+I+"))|)"+M+"*\\]",F=":("+I+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+W+")*)|.*)\\)|)",B=new RegExp(M+"+","g"),$=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=new RegExp("^"+M+"*,"+M+"*"),z=new RegExp("^"+M+"*([>+~]|"+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="<a id='"+S+"'></a><select id='"+S+"-\r\\' msallowcapture=''><option selected=''></option></select>",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="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";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<se(t,C,null,[e]).length},se.contains=function(e,t){return(e.ownerDocument||e)!=C&&T(e),y(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!=C&&T(e);var n=b.attrHandle[t.toLowerCase()],r=n&&j.call(b.attrHandle,t.toLowerCase())?n(e,t,!E):void 0;return void 0!==r?r:d.attributes||!E?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},se.escape=function(e){return(e+"").replace(re,ie)},se.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},se.uniqueSort=function(e){var t,n=[],r=0,i=0;if(l=!d.detectDuplicates,u=!d.sortStable&&e.slice(0),e.sort(D),l){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return u=null,e},o=se.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else while(t=e[r++])n+=o(t);return n},(b=se.selectors={cacheLength:50,createPseudo:le,match:G,attrHandle:{},find:{},relative:{">":{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}
vendor: 4,356 bytes, line 3
3: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<t.indexOf(i):"$="===r?i&&t.slice(-i.length)===i:"~="===r?-1<(" "+t.replace(B," ")+" ").indexOf(i):"|="===r&&(t===i||t.slice(0,i.length+1)===i+"-"))}},CHILD:function(h,e,t,g,v){var y="nth"!==h.slice(0,3),m="last"!==h.slice(-4),x="of-type"===e;return 1===g&&0===v?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u,l=y!==m?"nextSibling":"previousSibling",c=e.parentNode,f=x&&e.nodeName.toLowerCase(),p=!n&&!x,d=!1;if(c){if(y){while(l){a=e;while(a=a[l])if(x?a.nodeName.toLowerCase()===f:1===a.nodeType)return!1;u=l="only"===h&&!u&&"nextSibling"}return!0}if(u=[m?c.firstChild:c.lastChild],m&&p){d=(s=(r=(i=(o=(a=c)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1])&&r[2],a=s&&c.childNodes[s];while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if(1===a.nodeType&&++d&&a===e){i[h]=[k,s,d];break}}else if(p&&(d=s=(r=(i=(o=(a=e)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1]),!1===d)while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if((x?a.nodeName.toLowerCase()===f:1===a.nodeType)&&++d&&(p&&((i=(o=a[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]=[k,d]),a===e))break;return(d-=v)===g||d%g==0&&0<=d/g}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||se.error("unsupported pseudo: "+e);return a[S]?a(o):1<a.length?(t=[e,e,"",o],b.setFilters.hasOwnProperty(e.toLowerCase())?le(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=P(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:le(function(e){var r=[],i=[],s=f(e.replace($,"$1"));return s[S]?le(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:le(function(t){return function(e){return 0<se(t,e).length}}),contains:le(function(t){return t=t.replace(te,ne),function(e){return-1<(e.textContent||o(e)).indexOf(t)}}),lang:le(function(n){return V.test(n||"")||se.error("unsupported lang: "+n),n=n.replace(te,ne).toLowerCase(),function(e){var t;do{if(t=E?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(t=t.toLowerCase())===n||0===t.indexOf(n+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=n.location&&n.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===a},focus:function(e){return e===C.activeElement&&(!C.hasFocus||C.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!b.pseudos.empty(e)},header:function(e){return J.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:ve(function(){return[0]}),last:ve(function(e,t){return[t-1]}),eq:ve(function(e,t,n){return[n<0?n+t:n]}),even:ve(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:ve(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:ve(function(e,t,n){for(var r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:ve(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=b.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})b.pseudos[e]=de(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=he(e);function me(){}function xe(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function be(s,e,t){var u=e.dir,l=e.next,c=l||u,f=t&&"parentNode"===c,p=r++;return e.first?function(e,t,n){while(e=e[u])if(1===e.nodeType||f)return s(e,t,n);return!1}:function(e,t,n){var r,i,o,a=[k,p];
vendor: 10,068 bytes, line 3
3if(n){while(e=e[u])if((1===e.nodeType||f)&&s(e,t,n))return!0}else while(e=e[u])if(1===e.nodeType||f)if(i=(o=e[S]||(e[S]={}))[e.uniqueID]||(o[e.uniqueID]={}),l&&l===e.nodeName.toLowerCase())e=e[u]||e;else{if((r=i[c])&&r[0]===k&&r[1]===p)return a[2]=r[2];if((i[c]=a)[2]=s(e,t,n))return!0}return!1}}function we(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Te(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Ce(d,h,g,v,y,e){return v&&!v[S]&&(v=Ce(v)),y&&!y[S]&&(y=Ce(y,e)),le(function(e,t,n,r){var i,o,a,s=[],u=[],l=t.length,c=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)se(e,t[r],n);return n}(h||"*",n.nodeType?[n]:n,[]),f=!d||!e&&h?c:Te(c,s,d,n,r),p=g?y||(e?d:l||v)?[]:t:f;if(g&&g(f,p,n,r),v){i=Te(p,u),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(p[u[o]]=!(f[u[o]]=a))}if(e){if(y||d){if(y){i=[],o=p.length;while(o--)(a=p[o])&&i.push(f[o]=a);y(null,p=[],i,r)}o=p.length;while(o--)(a=p[o])&&-1<(i=y?P(e,a):s[o])&&(e[i]=!(t[i]=a))}}else p=Te(p===t?p.splice(l,p.length):p),y?y(null,t,p,r):H.apply(t,p)})}function Ee(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[" "],s=o?1:0,u=be(function(e){return e===i},a,!0),l=be(function(e){return-1<P(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!==w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=b.relative[e[s].type])c=[be(we(c),t)];else{if((t=b.filter[e[s].type].apply(null,e[s].matches))[S]){for(n=++s;n<r;n++)if(b.relative[e[n].type])break;return Ce(1<s&&we(c),1<s&&xe(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace($,"$1"),t,s<n&&Ee(e.slice(s,n)),n<r&&Ee(e=e.slice(n)),n<r&&xe(e))}c.push(t)}return we(c)}return me.prototype=b.filters=b.pseudos,b.setFilters=new me,h=se.tokenize=function(e,t){var n,r,i,o,a,s,u,l=x[e+" "];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=_.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=z.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace($," ")}),a=a.slice(n.length)),b.filter)!(r=G[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?se.error(e):x(e,s).slice(0)},f=se.compile=function(e,t){var n,v,y,m,x,r,i=[],o=[],a=A[e+" "];if(!a){t||(t=h(e)),n=t.length;while(n--)(a=Ee(t[n]))[S]?i.push(a):o.push(a);(a=A(e,(v=o,m=0<(y=i).length,x=0<v.length,r=function(e,t,n,r,i){var o,a,s,u=0,l="0",c=e&&[],f=[],p=w,d=e||x&&b.find.TAG("*",i),h=k+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t==C||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument==C||(T(o),n=!E);while(s=v[a++])if(s(o,t||C,n)){r.push(o);break}i&&(k=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=y[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=q.call(r));f=Te(f)}H.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&se.uniqueSort(r)}return i&&(k=h,w=p),c},m?le(r):r))).selector=e}return a},g=se.select=function(e,t,n,r){var i,o,a,s,u,l="function"==typeof e&&e,c=!r&&h(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&"ID"===(a=o[0]).type&&9===t.nodeType&&E&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(te,ne),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=G.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],b.relative[s=a.type])break;if((u=b.find[s])&&(r=u(a.matches[0].replace(te,ne),ee.test(o[0].type)&&ye(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&xe(o)))return H.apply(n,r),n;break}}}return(l||f(e,c))(r,t,!E,n,!t||ee.test(e)&&ye(t.parentNode)||t),n},d.sortStable=S.split("").sort(D).join("")===S,d.detectDuplicates=!!l,T(),d.sortDetached=ce(function(e){return 1&e.compareDocumentPosition(C.createElement("fieldset"))}),ce(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||fe("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),d.attributes&&ce(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||fe("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute("disabled")})||fe(R,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),se}(C);S.find=d,S.expr=d.selectors,S.expr[":"]=S.expr.pseudos,S.uniqueSort=S.unique=d.uniqueSort,S.text=d.getText,S.isXMLDoc=d.isXML,S.contains=d.contains,S.escapeSelector=d.escape;var h=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&S(e).is(n))break;r.push(e)}return r},T=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},k=S.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var N=/^<([a-z][^\/\0>:\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<i.call(n,e)!==r}):S.filter(n,e,r)}S.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?S.find.matchesSelector(r,e)?[r]:[]:S.find.matches(e,S.grep(t,function(e){return 1===e.nodeType}))},S.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(S(e).filter(function(){for(t=0;t<r;t++)if(S.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)S.find(e,i[t],n);return 1<r?S.uniqueSort(n):n},filter:function(e){return this.pushStack(D(this,e||[],!1))},not:function(e){return this.pushStack(D(this,e||[],!0))},is:function(e){return!!D(this,"string"==typeof e&&k.test(e)?S(e):e||[],!1).length}});var j,q=/^(?:\s*(<[\w\W]+>)[^>]*|#([\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<n;e++)if(S.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&S(e);if(!k.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&S.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?S.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?i.call(S(e),this[0]):i.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(S.uniqueSort(S.merge(this.get(),S(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),S.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return h(e,"parentNode")},parentsUntil:function(e,t,n){return h(e,"parentNode",n)},next:function(e){return O(e,"nextSibling")},prev:function(e){return O(e,"previousSibling")},nextAll:function(e){return h(e,"nextSibling")},prevAll:function(e){return h(e,"previousSibling")},nextUntil:function(e,t,n){return h(e,"nextSibling",n)},prevUntil:function(e,t,n){return h(e,"previousSibling",n)},siblings:function(e){return T((e.parentNode||{}).firstChild,e)},children:function(e){return T(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(A(e,"template")&&(e=e.content||e),S.merge([],e.childNodes))}},function(r,i){S.fn[r]=function(e,t){var n=S.map(this,i,e);return"Until"!==r.slice(-5)&&(t=e),t&&"string"==typeof t&&(n=S.filter(t,n)),1<this.length&&(H[r]||S.uniqueSort(n),L.test(r)&&n.reverse()),this.pushStack(n)}});var P=/[^\x20\t\r\n\f]+/g;function R(e){return e}function M(e){throw e}function I(e,t,n,r){var i;try{e&&m(i=e.promise)?i.call(e).done(t).fail(n):e&&m(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}S.Callbacks=function(r){var e,n;r="string"==typeof r?(e=r,n={},S.each(e.match(P)||[],function(e,t){n[t]=!0}),n):S.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:"")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){S.each(e,function(e,t){m(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&"string"!==w(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return S.each(arguments,function(e,t){var n;while(-1<(n=S.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<S.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t="",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=""),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},S.extend({Deferred:function(e){var o=[["notify","progress",S.Callbacks("memory"),S.Callbacks("memory"),2],["resolve","done",S.Callbacks("once memory"),S.Callbacks("once memory"),0,"resolved"],["reject","fail",S.Callbacks("once memory"),S.Callbacks("once memory"),1,"rejected"]],i="pe
vendor: 8,547 bytes, line 3
3nding",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},"catch":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return S.Deferred(function(r){S.each(o,function(e,t){var n=m(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&m(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+"With"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError("Thenable self-resolution");t=e&&("object"==typeof e||"function"==typeof e)&&e.then,m(t)?s?t.call(e,l(u,o,R,s),l(u,o,M,s)):(u++,t.call(e,l(u,o,R,s),l(u,o,M,s),l(u,o,R,o.notifyWith))):(a!==R&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){S.Deferred.exceptionHook&&S.Deferred.exceptionHook(e,t.stackTrace),u<=i+1&&(a!==M&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(S.Deferred.getStackHook&&(t.stackTrace=S.Deferred.getStackHook()),C.setTimeout(t))}}return S.Deferred(function(e){o[0][3].add(l(0,e,m(r)?r:R,e.notifyWith)),o[1][3].add(l(0,e,m(t)?t:R)),o[2][3].add(l(0,e,m(n)?n:M))}).promise()},promise:function(e){return null!=e?S.extend(e,a):a}},s={};return S.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+"With"](this===s?void 0:this,arguments),this},s[t[0]+"With"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=s.call(arguments),o=S.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?s.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(I(e,o.done(a(t)).resolve,o.reject,!n),"pending"===o.state()||m(i[t]&&i[t].then)))return o.then();while(t--)I(i[t],a(t),o.reject);return o.promise()}});var W=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;S.Deferred.exceptionHook=function(e,t){C.console&&C.console.warn&&e&&W.test(e.name)&&C.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},S.readyException=function(e){C.setTimeout(function(){throw e})};var F=S.Deferred();function B(){E.removeEventListener("DOMContentLoaded",B),C.removeEventListener("load",B),S.ready()}S.fn.ready=function(e){return F.then(e)["catch"](function(e){S.readyException(e)}),this},S.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--S.readyWait:S.isReady)||(S.isReady=!0)!==e&&0<--S.readyWait||F.resolveWith(E,[S])}}),S.ready.then=F.then,"complete"===E.readyState||"loading"!==E.readyState&&!E.documentElement.doScroll?C.setTimeout(S.ready):(E.addEventListener("DOMContentLoaded",B),C.addEventListener("load",B));var $=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===w(n))for(s in i=!0,n)$(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(S(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},_=/^-ms-/,z=/-([a-z])/g;function U(e,t){return t.toUpperCase()}function X(e){return e.replace(_,"ms-").replace(z,U)}var V=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function G(){this.expando=S.expando+G.uid++}G.uid=1,G.prototype={cache:function(e){var t=e[this.expando];return t||(t={},V(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[X(t)]=n;else for(r in t)i[X(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][X(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(X):(t=X(t))in r?[t]:t.match(P)||[]).length;while(n--)delete r[t[n]]}(void 0===t||S.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!S.isEmptyObject(t)}};var Y=new G,Q=new G,J=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,K=/[A-Z]/g;function Z(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(K,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n="true"===(i=n)||"false"!==i&&("null"===i?null:i===+i+""?+i:J.test(i)?JSON.parse(i):i)}catch(e){}Q.set(e,t,n)}else n=void 0;return n}S.extend({hasData:function(e){return Q.hasData(e)||Y.hasData(e)},data:function(e,t,n){return Q.access(e,t,n)},removeData:function(e,t){Q.remove(e,t)},_data:function(e,t,n){return Y.access(e,t,n)},_removeData:function(e,t){Y.remove(e,t)}}),S.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=Q.get(o),1===o.nodeType&&!Y.get(o,"hasDataAttrs"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf("data-")&&(r=X(r.slice(5)),Z(o,r,i[r]));Y.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof n?this.each(function(){Q.set(this,n)}):$(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=Q.get(o,n))?t:void 0!==(t=Z(o,n))?t:void 0;this.each(function(){Q.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){Q.remove(this,e)})}}),S.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=Y.get(e,t),n&&(!r||Array.isArray(n)?r=Y.access(e,t,S.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=S.queue(e,t),r=n.length,i=n.shift(),o=S._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,function(){S.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Y.get(e,n)||Y.access(e,n,{empty:S.Callbacks("once memory").add(function(){Y.remove(e,[t+"queue",n])})})}}),S.fn.extend({queue:function(t,n){var e=2;return"string"!=typeof t&&(n=t,t="fx",e--),arguments.length<e?S.queue(this[0],t):void 0===n?this:this.each(function(){var e=S.queue(this,t,n);S._queueHooks(this,t),"fx"===t&&"inprogress"!==e[0]&&S.dequeue(this,t)})},dequeue:function(e){return this.each(function(){S.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=S.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=void 0),e=e||"fx";while(a--)(n=Y.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var ee=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,te=new RegExp("^(?:([+-])=|)("+ee+")([a-z%]*)$","i"),ne=["Top","Right","Bottom","Left"],re=E.documentElement,ie=function(e){return S.contains(e.ownerDocument,e)},oe={composed:!0};re.getRootNode&&(ie=function(e){return S.contains(e.ownerDocument,e)||e.getRootNode(oe)===e.ownerDocument});var ae=function(e,t){return"none"===(e=t||e).style.display||""===e.style.display&&ie(e)&&"none"===S.css(e,"display")};function se(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return S.css(e,t,"")},u=s(),l=n&&n[3]||(S.cssNumber[t]?"":"px"),c=e.nodeType&&(S.cssNumber[t]||"px"!==l&&+u)&&te.exec(S.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)S.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,S.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var ue={};function le(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?("none"===n&&(l[c]=Y.get(r,"display")||null,l[c]||(r.style.display="")),""===r.style.display&&ae(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ue[s])||(o=a.body.appendChild(a.createElement(s)),u=S.css(o,"display"),o.parentNode.removeChild(o),"none"===u&&(u="block"),ue[s]=u)))):"none"!==n&&(l[c]="none",Y.set(r,"display",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}S.fn.extend({show:function(){return le(this,!0)},hide:function(){return le(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){ae(this)?S(this).show():S(this).hide()})}});var ce,fe,pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\/\0>\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
vendor: 51,417 bytes, line 3
3=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="<textarea>x</textarea>",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="<option></option>",y.option=!!ce.lastChild;var ge={thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_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<r;n++)Y.set(e[n],"globalEval",!t||Y.get(t[n],"globalEval"))}ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td,y.option||(ge.optgroup=ge.option=[1,"<select multiple='multiple'>","</select>"]);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<h;d++)if((o=e[d])||0===o)if("object"===w(o))S.merge(p,o.nodeType?[o]:o);else if(me.test(o)){a=a||f.appendChild(t.createElement("div")),s=(de.exec(o)||["",""])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+S.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;S.merge(p,a.childNodes),(a=f.firstChild).textContent=""}else p.push(t.createTextNode(o));f.textContent="",d=0;while(o=p[d++])if(r&&-1<S.inArray(o,r))i&&i.push(o);else if(l=ie(o),a=ve(f.appendChild(o),"script"),l&&ye(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}var be=/^key/,we=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Te=/^([^.]*)(?:\.(.+)|)/;function Ce(){return!0}function Ee(){return!1}function Se(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function ke(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)ke(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Ee;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return S().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=S.guid++)),e.each(function(){S.event.add(this,t,i,r,n)})}function Ae(e,i,o){o?(Y.set(e,i,!1),S.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Y.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(S.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Y.set(this,i,r),t=o(this,i),this[i](),r!==(n=Y.get(this,i))||t?Y.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Y.set(this,i,{value:S.event.trigger(S.extend(r[0],S.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Y.get(e,i)&&S.event.add(e,i,Ce)}S.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.get(t);if(V(t)){n.handler&&(n=(o=n).handler,i=o.selector),i&&S.find.matchesSelector(re,i),n.guid||(n.guid=S.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof S&&S.event.triggered!==e.type?S.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(P)||[""]).length;while(l--)d=g=(s=Te.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=S.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=S.event.special[d]||{},c=S.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&S.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),S.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.hasData(e)&&Y.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(P)||[""]).length;while(l--)if(d=g=(s=Te.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=S.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||S.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)S.event.remove(e,d+t[l],n,r,!0);S.isEmptyObject(u)&&Y.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=S.event.fix(e),l=(Y.get(this,"events")||Object.create(null))[u.type]||[],c=S.event.special[u.type]||{};for(s[0]=u,t=1;t<arguments.length;t++)s[t]=arguments[t];if(u.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,u)){a=S.event.handlers.call(this,u,l),t=0;while((i=a[t++])&&!u.isPropagationStopped()){u.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!u.isImmediatePropagationStopped())u.rnamespace&&!1!==o.namespace&&!u.rnamespace.test(o.namespace)||(u.handleObj=o,u.data=o.data,void 0!==(r=((S.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s))&&!1===(u.result=r)&&(u.preventDefault(),u.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,u),u.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+" "]&&(a[i]=r.needsContext?-1<S(i,this).index(l):S.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(S.Event.prototype,t,{enumerable:!0,configurable:!0,get:m(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[S.expando]?e:new S.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Ae(t,"click",Ce),!1},trigger:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Ae(t,"click"),!0},_default:function(e){var t=e.target;return pe.test(t.type)&&t.click&&A(t,"input")&&Y.get(t,"click")||A(t,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},S.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},S.Event=function(e,t){if(!(this instanceof S.Event))return new S.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?Ce:Ee,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&S.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[S.expando]=!0},S.Event.prototype={constructor:S.Event,isDefaultPrevented:Ee,isPropagationStopped:Ee,isImmediatePropagationStopped:Ee,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=Ce,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=Ce,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=Ce,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},S.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(e){var t=e.button;return null==e.which&&be.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&we.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},S.event.addProp),S.each({focus:"focusin",blur:"focusout"},function(e,t){S.event.special[e]={setup:function(){return Ae(this,e,Se),!1},trigger:function(){return Ae(this,e),!0},delegateType:t}}),S.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,i){S.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||S.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),S.fn.extend({on:function(e,t,n,r){return ke(this,e,t,n,r)},one:function(e,t,n,r){return ke(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,S(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=Ee),this.each(function(){S.event.remove(this,e,n,t)})}});var Ne=/<script|<style|<link/i,De=/checked\s*(?:[^=]|=\s*.checked.)/i,je=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\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<r;n++)S.event.add(t,i,s[i][n]);Q.hasData(e)&&(o=Q.access(e),a=S.extend({},o),Q.set(t,a))}}function Pe(n,r,i,o){r=g(r);var e,t,a,s,u,l,c=0,f=n.length,p=f-1,d=r[0],h=m(d);if(h||1<f&&"string"==typeof d&&!y.checkClone&&De.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),Pe(t,r,i,o)});if(f&&(t=(e=xe(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=S.map(ve(e,"script"),Le)).length;c<f;c++)u=e,c!==p&&(u=S.clone(u,!0,!0),s&&S.merge(a,ve(u,"script"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,S.map(a,He),c=0;c<s;c++)u=a[c],he.test(u.type||"")&&!Y.access(u,"globalEval")&&S.contains(l,u)&&(u.src&&"module"!==(u.type||"").toLowerCase()?S._evalUrl&&!u.noModule&&S._evalUrl(u.src,{nonce:u.nonce||u.getAttribute("nonce")},l):b(u.textContent.replace(je,""),u,l))}return n}function Re(e,t,n){for(var r,i=t?S.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||S.cleanData(ve(r)),r.parentNode&&(n&&ie(r)&&ye(ve(r,"script")),r.parentNode.removeChild(r));return e}S.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=ie(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||S.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r<i;r++)s=o[r],u=a[r],void 0,"input"===(l=u.nodeName.toLowerCase())&&pe.test(s.type)?u.checked=s.checked:"input"!==l&&"textarea"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||ve(e),a=a||ve(c),r=0,i=o.length;r<i;r++)Oe(o[r],a[r]);else Oe(e,c);return 0<(a=ve(c,"script")).length&&ye(a,!f&&ve(e,"script")),c},cleanData:function(e){for(var t,n,r,i=S.event.special,o=0;void 0!==(n=e[o]);o++)if(V(n)){if(t=n[Y.expando]){if(t.events)for(r in t.events)i[r]?S.event.remove(n,r):S.removeEvent(n,r,t.handle);n[Y.expando]=void 0}n[Q.expando]&&(n[Q.expando]=void 0)}}}),S.fn.extend({detach:function(e){return Re(this,e,!0)},remove:function(e){return Re(this,e)},text:function(e){return $(this,function(e){return void 0===e?S.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Pe(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||qe(this,e).appendChild(e)})},prepend:function(){return Pe(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=qe(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(S.cleanData(ve(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return S.clone(this,e,t)})},html:function(e){return $(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ne.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=S.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(S.cleanData(ve(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return Pe(this,arguments,function(e){var t=this.parentNode;S.inArray(this,n)<0&&(S.cleanData(ve(this)),t&&t.replaceChild(e,this))},n)}}),S.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,a){S.fn[e]=function(e){for(var t,n=[],r=S(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),S(r[o])[a](t),u.apply(n,t.get());return this.pushStack(n)}});var Me=new RegExp("^("+ee+")(?!px)[a-z%]+$","i"),Ie=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=C),t.getComputedStyle(e)},We=function(e,t,n){var r,i,o={};for(i in t)o[i]=e.style[i],e.style[i]=t[i];for(i in r=n.call(e),t)e.style[i]=o[i];return r},Fe=new RegExp(ne.join("|"),"i");function Be(e,t,n){var r,i,o,a,s=e.style;return(n=n||Ie(e))&&(""!==(a=n.getPropertyValue(t)||n[t])||ie(e)||(a=S.style(e,t)),!y.pixelBoxStyles()&&Me.test(a)&&Fe.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function $e(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(l){u.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",l.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",re.appendChild(u).appendChild(l);var e=C.getComputedStyle(l);n="1%"!==e.top,s=12===t(e.marginLeft),l.style.right="60%",o=36===t(e.right),r=36===t(e.width),l.style.position="absolute",i=12===t(l.offsetWidth/3),re.removeChild(u),l=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s,u=E.createElement("div"),l=E.createElement("div");l.style&&(l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",y.clearCloneStyle="content-box"===l.style.backgroundClip,S.extend(y,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),s},scrollboxSize:function(){return e(),i},reliableTrDimensions:function(){var e,t,n,r;return null==a&&(e=E.createElement("table"),t=E.createElement("tr"),n=E.createElement("div"),e.style.cssText="position:absolute;left:-11111px",t.style.height="1px",n.style.height="9px",re.appendChild(e).appendChild(t).appendChild(n),r=C.getComputedStyle(t),a=3<parseInt(r.height),re.removeChild(e)),a}}))}();var _e=["Webkit","Moz","ms"],ze=E.createElement("div").style,Ue={};function Xe(e){var t=S.cssProps[e]||Ue[e];return t||(e in ze?e:Ue[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=_e.length;while(n--)if((e=_e[n]+t)in ze)return e}(e)||e)}var Ve=/^(none|table(?!-c[ea]).+)/,Ge=/^--/,Ye={position:"absolute",visibility:"hidden",display:"block"},Qe={letterSpacing:"0",fontWeight:"400"};function Je(e,t,n){var r=te.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function Ke(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(u+=S.css(e,n+ne[a],!0,i)),r?("content"===n&&(u-=S.css(e,"padding"+ne[a],!0,i)),"margin"!==n&&(u-=S.css(e,"border"+ne[a]+"Width",!0,i))):(u+=S.css(e,"padding"+ne[a],!0,i),"padding"!==n?u+=S.css(e,"border"+ne[a]+"Width",!0,i):s+=S.css(e,"border"+ne[a]+"Width",!0,i));return!r&&0<=o&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function Ze(e,t,n){var r=Ie(e),i=(!y.boxSizingReliable()||n)&&"border-box"===S.css(e,"boxSizing",!1,r),o=i,a=Be(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if(Me.test(a)){if(!n)return a;a="auto"}return(!y.boxSizingReliable()&&i||!y.reliableTrDimensions()&&A(e,"tr")||"auto"===a||!parseFloat(a)&&"inline"===S.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===S.css(e,"boxSizing",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+Ke(e,t,n||(i?"border":"content"),o,r,a)+"px"}function et(e,t,n,r,i){return new et.prototype.init(e,t,n,r,i)}S.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Be(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=X(t),u=Ge.test(t),l=e.style;if(u||(t=Xe(s)),a=S.cssHooks[t]||S.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"===(o=typeof n)&&(i=te.exec(n))&&i[1]&&(n=se(e,t,i),o="number"),null!=n&&n==n&&("number"!==o||u||(n+=i&&i[3]||(S.cssNumber[s]?"":"px")),y.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=X(t);return Ge.test(t)||(t=Xe(s)),(a=S.cssHooks[t]||S.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Be(e,t,r)),"normal"===i&&t in Qe&&(i=Qe[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),S.each(["height","width"],function(e,u){S.cssHooks[u]={get:function(e,t,n){if(t)return!Ve.test(S.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?Ze(e,u,n):We(e,Ye,function(){return Ze(e,u,n)})},set:function(e,t,n){var r,i=Ie(e),o=!y.scrollboxSize()&&"absolute"===i.position,a=(o||n)&&"border-box"===S.css(e,"boxSizing",!1,i),s=n?Ke(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e["offset"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-Ke(e,u,"border",!1,i)-.5)),s&&(r=te.exec(t))&&"px"!==(r[3]||"px")&&(e.style[u]=t,t=S.css(e,u)),Je(0,t,s)}}}),S.cssHooks.marginLeft=$e(y.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Be(e,"marginLeft"))||e.getBoundingClientRect().left-We(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),S.each({margin:"",padding:"",border:"Width"},function(i,o){S.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r="string"==typeof e?e.split(" "):[e];t<4;t++)n[i+ne[t]+o]=r[t]||r[t-2]||r[0];return n}},"margin"!==i&&(S.cssHooks[i+o].set=Je)}),S.fn.extend({css:function(e,t){return $(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Ie(e),i=t.length;a<i;a++)o[t[a]]=S.css(e,t[a],!1,r);return o}return void 0!==n?S.style(e,t,n):S.css(e,t)},e,t,1<arguments.length)}}),((S.Tween=et).prototype={constructor:et,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||S.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(S.cssNumber[n]?"":"px")},cur:function(){var e=et.propHooks[this.prop];return e&&e.get?e.get(this):et.propHooks._default.get(this)},run:function(e){var t,n=et.propHooks[this.prop];return this.options.duration?this.pos=t=S.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):et.propHooks._default.set(this),this}}).init.prototype=et.prototype,(et.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=S.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){S.fx.step[e.prop]?S.fx.step[e.prop](e):1!==e.elem.nodeType||!S.cssHooks[e.prop]&&null==e.elem.style[Xe(e.prop)]?e.elem[e.prop]=e.now:S.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=et.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},S.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},S.fx=et.prototype.init,S.fx.step={};var tt,nt,rt,it,ot=/^(?:toggle|show|hide)$/,at=/queueHooks$/;function st(){nt&&(!1===E.hidden&&C.requestAnimationFrame?C.requestAnimationFrame(st):C.setTimeout(st,S.fx.interval),S.fx.tick())}function ut(){return C.setTimeout(function(){tt=void 0}),tt=Date.now()}function lt(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=ne[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function ct(e,t,n){for(var r,i=(ft.tweeners[t]||[]).concat(ft.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function ft(o,e,t){var n,a,r=0,i=ft.prefilters.length,s=S.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=tt||ut(),t=Math.max(0,l.startTime+l.duration-e),n=1-(t/l.duration||0),r=0,i=l.tweens.length;r<i;r++)l.tweens[r].run(n);return s.notifyWith(o,[l,n,t]),n<1&&i?t:(i||s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l]),!1)},l=s.promise({elem:o,props:S.extend({},e),opts:S.extend(!0,{specialEasing:{},easing:S.easing._default},t),originalProperties:e,originalOptions:t,startTime:tt||ut(),duration:t.duration,tweens:[],createTween:function(e,t){var n=S.Tween(o,l.opts,e,t,l.opts.specialEasing[e]||l.opts.easing);return l.tweens.push(n),n},stop:function(e){var t=0,n=e?l.tweens.length:0;if(a)return this;for(a=!0;t<n;t++)l.tweens[t].run(1);return e?(s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l,e])):s.rejectWith(o,[l,e]),this}}),c=l.props;for(!function(e,t){var n,r,i,o,a;for(n in e)if(i=t[r=X(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=S.cssHooks[r])&&"expand"in a)for(n in o=a.expand(o),delete e[r],o)n in e||(e[n]=o[n],t[n]=i);else t[r]=i}(c,l.opts.specialEasing);r<i;r++)if(n=ft.prefilters[r].call(l,o,c,l.opts))return m(n.stop)&&(S._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return S.map(c,ct,l),m(l.opts.start)&&l.opts.start.call(o,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),S.fx.timer(S.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}S.Animation=S.extend(ft,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return se(n.elem,e,te.exec(t),n),n}]},tweener:function(e,t){m(e)?(t=e,e=["*"]):e=e.match(P);for(var n,r=0,i=e.length;r<i;r++)n=e[r],ft.tweeners[n]=ft.tweeners[n]||[],ft.tweeners[n].unshift(t)},prefilters:[function(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&ae(e),v=Y.get(e,"fxshow");for(r in n.queue||(null==(a=S._queueHooks(e,"fx")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,S.queue(e,"fx").length||a.empty.fire()})})),t)if(i=t[r],ot.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||S.style(e,r)}if((u=!S.isEmptyObject(t))||!S.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=v&&v.display)&&(l=Y.get(e,"display")),"none"===(c=S.css(e,"display"))&&(l?c=l:(le([e],!0),l=e.style.display||l,c=S.css(e,"display"),le([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===S.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1,d)u||(v?"hidden"in v&&(g=v.hidden):v=Y.access(e,"fxshow",{display:l}),o&&(v.hidden=!g),g&&le([e],!0),p.done(function(){for(r in g||le([e]),Y.remove(e,"fxshow"),d)S.style(e,r,d[r])})),u=ct(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}],prefilter:function(e,t){t?ft.prefilters.unshift(e):ft.prefilters.push(e)}}),S.speed=function(e,t,n){var r=e&&"object"==typeof e?S.extend({},e):{complete:n||!n&&t||m(e)&&e,duration:e,easing:n&&t||t&&!m(t)&&t};return S.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in S.fx.speeds?r.duration=S.fx.speeds[r.duration]:r.duration=S.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){m(r.old)&&r.old.call(this),r.queue&&S.dequeue(this,r.queue)},r},S.fn.extend({fadeTo:function(e,t,n,r){return this.filter(ae).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=S.isEmptyObject(t),o=S.speed(e,n,r),a=function(){var e=ft(this,S.extend({},t),o);(i||Y.get(this,"finish"))&&e.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(i,e,o){var a=function(e){var t=e.stop;delete e.stop,t(o)};return"string"!=typeof i&&(o=e,e=i,i=void 0),e&&this.queue(i||"fx",[]),this.each(function(){var e=!0,t=null!=i&&i+"queueHooks",n=S.timers,r=Y.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&at.test(t)&&a(r[t]);for(t=n.length;t--;)n[t].elem!==this||null!=i&&n[t].queue!==i||(n[t].anim.stop(o),e=!1,n.splice(t,1));!e&&o||S.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||"fx"),this.each(function(){var e,t=Y.get(this),n=t[a+"queue"],r=t[a+"queueHooks"],i=S.timers,o=n?n.length:0;for(t.finish=!0,S.queue(this,a,[]),r&&r.stop&&r.stop.call(this,!0),e=i.length;e--;)i[e].elem===this&&i[e].queue===a&&(i[e].anim.stop(!0),i.splice(e,1));for(e=0;e<o;e++)n[e]&&n[e].finish&&n[e].finish.call(this);delete t.finish})}}),S.each(["toggle","show","hide"],function(e,r){var i=S.fn[r];S.fn[r]=function(e,t,n){return null==e||"boolean"==typeof e?i.apply(this,arguments):this.animate(lt(r,!0),e,t,n)}}),S.each({slideDown:lt("show"),slideUp:lt("hide"),slideToggle:lt("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,r){S.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),S.timers=[],S.fx.tick=function(){var e,t=0,n=S.timers;for(tt=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||S.fx.stop(),tt=void 0},S.fx.timer=function(e){S.timers.push(e),S.fx.start()},S.fx.interval=13,S.fx.start=function(){nt||(nt=!0,st())},S.fx.stop=function(){nt=null},S.fx.speeds={slow:600,fast:200,_default:400},S.fn.delay=function(r,e){return r=S.fx&&S.fx.speeds[r]||r,e=e||"fx",this.queue(e,function(e,t){var n=C.setTimeout(e,r);t.stop=function(){C.clearTimeout(n)}})},rt=E.createElement("input"),it=E.createElement("select").appendChild(E.createElement("option")),rt.type="checkbox",y.checkOn=""!==rt.value,y.optSelected=it.selected,(rt=E.createElement("input")).value="t",rt.type="radio",y.radioValue="t"===rt.value;var pt,dt=S.expr.attrHandle;S.fn.extend({attr:function(e,t){return $(this,S.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){S.removeAttr(this,e)})}}),S.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?S.prop(e,t,n):(1===o&&S.isXMLDoc(e)||(i=S.attrHooks[t.toLowerCase()]||(S.expr.match.bool.test(t)?pt:void 0)),void 0!==n?null===n?void S.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=S.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!y.radioValue&&"radio"===t&&A(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(P);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),pt={set:function(e,t,n){return!1===t?S.removeAttr(e,n):e.setAttribute(n,n),n}},S.each(S.expr.match.bool.source.match(/\w+/g),function(e,t){var a=dt[t]||S.find.attr;dt[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=dt[o],dt[o]=r,r=null!=a(e,t,n)?o:null,dt[o]=i),r}});var ht=/^(?:input|select|textarea|button)$/i,gt=/^(?:a|area)$/i;function vt(e){return(e.match(P)||[]).join(" ")}function yt(e){return e.getAttribute&&e.getAttribute("class")||""}function mt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(P)||[]}S.fn.extend({prop:function(e,t){return $(this,S.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[S.propFix[e]||e]})}}),S.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&S.isXMLDoc(e)||(t=S.propFix[t]||t,i=S.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=S.find.attr(e,"tabindex");return t?parseInt(t,10):ht.test(e.nodeName)||gt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),y.optSelected||(S.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),S.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){S.propFix[this.toLowerCase()]=this}),S.fn.extend({addClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).addClass(t.call(this,e,yt(this)))});if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=e[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).removeClass(t.call(this,e,yt(this)))});if(!arguments.length)return this.attr("class","");if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=e[a++])while(-1<r.indexOf(" "+o+" "))r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(i,t){var o=typeof i,a="string"===o||Array.isArray(i);return"boolean"==typeof t&&a?t?this.addClass(i):this.removeClass(i):m(i)?this.each(function(e){S(this).toggleClass(i.call(this,e,yt(this),t),t)}):this.each(function(){var e,t,n,r;if(a){t=0,n=S(this),r=mt(i);while(e=r[t++])n.hasClass(e)?n.removeClass(e):n.addClass(e)}else void 0!==i&&"boolean"!==o||((e=yt(this))&&Y.set(this,"__className__",e),this.setAttribute&&this.setAttribute("class",e||!1===i?"":Y.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&-1<(" "+vt(yt(n))+" ").indexOf(t))return!0;return!1}});var xt=/\r/g;S.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=m(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,S(this).val()):n)?t="":"number"==typeof t?t+="":Array.isArray(t)&&(t=S.map(t,function(e){return null==e?"":e+""})),(r=S.valHooks[this.type]||S.valHooks[this.nodeName.toLowerCase()])&&"set"in r&&void 0!==r.set(this,t,"value")||(this.value=t))})):t?(r=S.valHooks[t.type]||S.valHooks[t.nodeName.toLowerCase()])&&"get"in r&&void 0!==(e=r.get(t,"value"))?e:"string"==typeof(e=t.value)?e.replace(xt,""):null==e?"":e:void 0}}),S.extend({valHooks:{option:{get:function(e){var t=S.find.attr(e,"value");return null!=t?t:vt(S.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!A(n.parentNode,"optgroup"))){if(t=S(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=S.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<S.inArray(S.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),S.each(["radio","checkbox"],function(){S.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<S.inArray(S(e).val(),t)}},y.checkOn||(S.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),y.focusin="onfocusin"in C;var bt=/^(?:focusinfocus|focusoutblur)$/,wt=function(e){e.stopPropagation()};S.extend(S.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,p=[n||E],d=v.call(e,"type")?e.type:e,h=v.call(e,"namespace")?e.namespace.split("."):[];if(o=f=a=n=n||E,3!==n.nodeType&&8!==n.nodeType&&!bt.test(d+S.event.triggered)&&(-1<d.indexOf(".")&&(d=(h=d.split(".")).shift(),h.sort()),u=d.indexOf(":")<0&&"on"+d,(e=e[S.expando]?e:new S.Event(d,"object"==typeof e&&e)).isTrigger=r?2:3,e.namespace=h.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:S.makeArray(t,[e]),c=S.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!x(n)){for(s=c.delegateType||d,bt.test(s+d)||(o=o.parentNode);o;o=o.parentNode)p.push(o),a=o;a===(n.ownerDocument||E)&&p.push(a.defaultView||a.parentWindow||C)}i=0;while((o=p[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||d,(l=(Y.get(o,"events")||Object.create(null))[e.type]&&Y.get(o,"handle"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&V(o)&&(e.result=l.apply(o,t),!1===e.result&&e.preventDefault());return e.type=d,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(p.pop(),t)||!V(n)||u&&m(n[d])&&!x(n)&&((a=n[u])&&(n[u]=null),S.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,wt),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,wt),S.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=S.extend(new S.Event,n,{type:e,isSimulated:!0});S.event.trigger(r,null,t)}}),S.fn.extend({trigger:function(e,t){return this.each(function(){S.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return S.event.trigger(e,t,n,!0)}}),y.focusin||S.each({focus:"focusin",blur:"focusout"},function(n,r){var i=function(e){S.event.simulate(r,e.target,S.event.fix(e))};S.event.special[r]={setup:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r);t||e.addEventListener(n,i,!0),Y.access(e,r,(t||0)+1)},teardown:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r)-1;t?Y.access(e,r,t):(e.removeEventListener(n,i,!0),Y.remove(e,r))}}});var Tt=C.location,Ct={guid:Date.now()},Et=/\?/;S.parseXML=function(e){var t;if(!e||"string"!=typeof e)return null;try{t=(new C.DOMParser).parseFromString(e,"text/xml")}catch(e){t=void 0}return t&&!t.getElementsByTagName("parsererror").length||S.error("Invalid XML: "+e),t};var St=/\[\]$/,kt=/\r?\n/g,At=/^(?:submit|button|image|reset|file)$/i,Nt=/^(?:input|select|textarea|keygen)/i;function Dt(n,e,r,i){var t;if(Array.isArray(e))S.each(e,function(e,t){r||St.test(n)?i(n,t):Dt(n+"["+("object"==typeof t&&null!=t?e:"")+"]",t,r,i)});else if(r||"object"!==w(e))i(n,e);else for(t in e)Dt(n+"["+t+"]",e[t],r,i)}S.param=function(e,t){var n,r=[],i=function(e,t){var n=m(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!S.isPlainObject(e))S.each(e,function(){i(this.name,this.value)});else for(n in e)Dt(n,e[n],t,i);return r.join("&")},S.fn.extend({serialize:function(){return S.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=S.prop(this,"elements");return e?S.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!S(this).is(":disabled")&&Nt.test(this.nodeName)&&!At.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=S(this).val();return null==n?null:Array.isArray(n)?S.map(n,function(e){return{name:t.name,value:e.replace(kt,"\r\n")}}):{name:t.name,value:n.replace(kt,"\r\n")}}).get()}});var jt=/%20/g,qt=/#.*$/,Lt=/([?&])_=[^&]*/,Ht=/^(.*?):[ \t]*([^\r\n]*)$/gm,Ot=/^(?:GET|HEAD)$/,Pt=/^\/\//,Rt={},Mt={},It="*/".concat("*"),Wt=E.createElement("a");function Ft(o){return function(e,t){"string"!=typeof e&&(t=e,e="*");var n,r=0,i=e.toLowerCase().match(P)||[];if(m(t))while(n=i[r++])"+"===n[0]?(n=n.slice(1)||"*",(o[n]=o[n]||[]).unshift(t)):(o[n]=o[n]||[]).push(t)}}function Bt(t,i,o,a){var s={},u=t===Mt;function l(e){var r;return s[e]=!0,S.each(t[e]||[],function(e,t){var n=t(i,o,a);return"string"!=typeof n||u||s[n]?u?!(r=n):void 0:(i.dataTypes.unshift(n),l(n),!1)}),r}return l(i.dataTypes[0])||!s["*"]&&l("*")}function $t(e,t){var n,r,i=S.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&S.extend(!0,e,r),e}Wt.href=Tt.href,S.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Tt.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Tt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":It,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":S.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?$t($t(e,S.ajaxSettings),t):$t(S.ajaxSettings,e)},ajaxPrefilter:Ft(Rt),ajaxTransport:Ft(Mt),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var c,f,p,n,d,r,h,g,i,o,v=S.ajaxSetup({},t),y=v.context||v,m=v.context&&(y.nodeType||y.jquery)?S(y):S.event,x=S.Deferred(),b=S.Callbacks("once memory"),w=v.statusCode||{},a={},s={},u="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(h){if(!n){n={};while(t=Ht.exec(p))n[t[1].toLowerCase()+" "]=(n[t[1].toLowerCase()+" "]||[]).concat(t[2])}t=n[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return h?p:null},setRequestHeader:function(e,t){return null==h&&(e=s[e.toLowerCase()]=s[e.toLowerCase()]||e,a[e]=t),this},overrideMimeType:function(e){return null==h&&(v.mimeType=e),this},statusCode:function(e){var t;if(e)if(h)T.always(e[T.status]);else for(t in e)w[t]=[w[t],e[t]];return this},abort:function(e){var t=e||u;return c&&c.abort(t),l(0,t),this}};if(x.promise(T),v.url=((e||v.url||Tt.href)+"").replace(Pt,Tt.protocol+"//"),v.type=t.method||t.type||v.method||v.type,v.dataTypes=(v.dataType||"*").toLowerCase().match(P)||[""],null==v.crossDomain){r=E.createElement("a");try{r.href=v.url,r.href=r.href,v.crossDomain=Wt.protocol+"//"+Wt.host!=r.protocol+"//"+r.host}catch(e){v.crossDomain=!0}}if(v.data&&v.processData&&"string"!=typeof v.data&&(v.data=S.param(v.data,v.traditional)),Bt(Rt,v,t,T),h)return T;for(i in(g=S.event&&v.global)&&0==S.active++&&S.event.trigger("ajaxStart"),v.type=v.type.toUpperCase(),v.hasContent=!Ot.test(v.type),f=v.url.replace(qt,""),v.hasContent?v.data&&v.processData&&0===(v.contentType||"").indexOf("application/x-www-form-urlencoded")&&(v.data=v.data.replace(jt,"+")):(o=v.url.slice(f.length),v.data&&(v.processData||"string"==typeof v.data)&&(f+=(Et.test(f)?"&":"?")+v.data,delete v.data),!1===v.cache&&(f=f.replace(Lt,"$1"),o=(Et.test(f)?"&":"?")+"_="+Ct.guid+++o),v.url=f+o),v.ifModified&&(S.lastModified[f]&&T.setRequestHeader("If-Modified-Since",S.lastModified[f]),S.etag[f]&&T.setRequestHeader("If-None-Match",S.etag[f])),(v.data&&v.hasContent&&!1!==v.contentType||t.contentType)&&T.setRequestHeader("Content-Type",v.contentType),T.setRequestHeader("Accept",v.dataTypes[0]&&v.accepts[v.dataTypes[0]]?v.accepts[v.dataTypes[0]]+("*"!==v.dataTypes[0]?", "+It+"; q=0.01":""):v.accepts["*"]),v.headers)T.setRequestHeader(i,v.headers[i]);if(v.beforeSend&&(!1===v.beforeSend.call(y,T,v)||h))return T.abort();if(u="abort",b.add(v.complete),T.done(v.success),T.fail(v.error),c=Bt(Mt,v,t,T)){if(T.readyState=1,g&&m.trigger("ajaxSend",[T,v]),h)return T;v.async&&0<v.timeout&&(d=C.setTimeout(function(){T.abort("timeout")},v.timeout));try{h=!1,c.send(a,l)}catch(e){if(h)throw e;l(-1,e)}}else l(-1,"No Transport");function l(e,t,n,r){var i,o,a,s,u,l=t;h||(h=!0,d&&C.clearTimeout(d),c=void 0,p=r||"",T.readyState=0<e?4:0,i=200<=e&&e<300||304===e,n&&(s=function(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(v,T,n)),!i&&-1<S.inArray("script",v.dataTypes)&&(v.converters["text script"]=function(){}),s=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}(v,s,T,i),i?(v.ifModified&&((u=T.getResponseHeader("Last-Modified"))&&(S.lastModified[f]=u),(u=T.getResponseHeader("etag"))&&(S.etag[f]=u)),204===e||"HEAD"===v.type?l="nocontent":304===e?l="notmodified":(l=s.state,o=s.data,i=!(a=s.error))):(a=l,!e&&l||(l="error",e<0&&(e=0))),T.status=e,T.statusText=(t||l)+"",i?x.resolveWith(y,[o,l,T]):x.rejectWith(y,[T,l,a]),T.statusCode(w),w=void 0,g&&m.trigger(i?"ajaxSuccess":"ajaxError",[T,v,i?o:a]),b.fireWith(y,[T,l]),g&&(m.trigger("ajaxComplete",[T,v]),--S.active||S.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return S.get(e,t,n,"json")},getScript:function(e,t){return S.get(e,void 0,t,"script")}}),S.each(["get","post"],function(e,i){S[i]=function(e,t,n,r){return m(t)&&(r=r||n,n=t,t=void 0),S.ajax(S.extend({url:e,type:i,dataType:r,data:t,success:n},S.isPlainObject(e)&&e))}}),S.ajaxPrefilter(function(e){var t;for(t in e.headers)"content-type"===t.toLowerCase()&&(e.contentType=e.headers[t]||"")}),S._evalUrl=function(e,t,n){return S.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){S.globalEval(e,t,n)}})},S.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=S(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(n){return m(n)?this.each(function(e){S(this).wrapInner(n.call(this,e))}):this.each(function(){var e=S(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=m(t);return this.each(function(e){S(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not("body").each(function(){S(this).replaceWith(this.childNodes)}),this}}),S.expr.pseudos.hidden=function(e){return!S.expr.pseudos.visible(e)},S.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},S.ajaxSettings.xhr=function(){try{return new C.XMLHttpRequest}catch(e){}};var _t={0:200,1223:204},zt=S.ajaxSettings.xhr();y.cors=!!zt&&"withCredentials"in zt,y.ajax=zt=!!zt,S.ajaxTransport(function(i){var o,a;if(y.cors||zt&&!i.crossDomain)return{send:function(e,t){var n,r=i.xhr();if(r.open(i.type,i.url,i.async,i.username,i.password),i.xhrFields)for(n in i.xhrFields)r[n]=i.xhrFields[n];for(n in i.mimeType&&r.overrideMimeType&&r.overrideMimeType(i.mimeType),i.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest"),e)r.setRequestHeader(n,e[n]);o=function(e){return function(){o&&(o=a=r.onload=r.onerror=r.onabort=r.ontimeout=r.onreadystatechange=null,"abort"===e?r.abort():"error"===e?"number"!=typeof r.status?t(0,"error"):t(r.status,r.statusText):t(_t[r.status]||r.status,r.statusText,"text"!==(r.responseType||"text")||"string"!=typeof r.responseText?{binary:r.response}:{text:r.responseText},r.getAllResponseHeaders()))}},r.onload=o(),a=r.onerror=r.ontimeout=o("error"),void 0!==r.onabort?r.onabort=a:r.onreadystatechange=function(){4===r.readyState&&C.setTimeout(function(){o&&a()})},o=o("abort");try{r.send(i.hasContent&&i.data||null)}catch(e){if(o)throw e}},abort:function(){o&&o()}}}),S.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),S.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return S.globalEval(e),e}}}),S.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),S.ajaxTransport("script",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=S("<script>").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="<form></form><form></form>",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<s&&(r=vt(e.slice(s)),e=e.slice(0,s)),m(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),0<a.length&&S.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?S("<div>").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<arguments.length?this.on(n,null,e,t):this.trigger(n)}});var Gt=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;S.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),m(e))return r=s.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(s.call(arguments)))}).guid=e.guid=e.guid||S.guid++,i},S.holdReady=function(e){e?S.readyWait++:S.ready(!0)},S.isArray=Array.isArray,S.parseJSON=JSON.parse,S.nodeName=A,S.isFunction=m,S.isWindow=x,S.camelCase=X,S.type=w,S.now=Date.now,S.isNumeric=function(e){var t=S.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},S.trim=function(e){return null==e?"":(e+"").replace(Gt,"")},"function"==typeof define&&define.amd&&define("jquery",[],function(){return S});var Yt=C.jQuery,Qt=C.$;return S.noConflict=function(e){return C.$===S&&(C.$=Qt),e&&C.jQuery===S&&(C.jQuery=Yt),S},"undefined"==typeof e&&(C.jQuery=C.$=S),S});
4var H5P = window.H5P = window.H5P || {};
5H5P.jQuery = jQuery.noConflict(true);
6H5P.jQuery.fn.__originalLoad = H5P.jQuery.load;
7
8H5P.jQuery.fn.load = function (url, params, callback) {
9  /**
10   * NOTE:
11   * This is needed in order to support old libraries that uses the .load() function
12   * for elements in the deprecated jQuery way (elem.load(fn)), the correct way to do this
13   * now is elem.on('load', fn)
14   */
15  if (typeof url === "function") {
16    console.warn('You are using a deprecated H5P library. Please upgrade!');
17    let args = Array.prototype.slice.call(arguments);
18    args.unshift('load');
19    return H5P.jQuery.fn.on.apply(this, args);
20  }
21
22  return H5P.jQuery.fn.__originalLoad.apply(this, arguments);
23};
24/*jshint multistr: true */
25// TODO: Should we split up the generic parts needed by the editor(and others), and the parts needed to "run" H5Ps?
26
27/** @namespace */
28var H5P = window.H5P = window.H5P || {};
29
30/**
31 * Tells us if we're inside of an iframe.
32 * @member {boolean}
33 */
34H5P.isFramed = (window.self !== window.parent);
35
36/**
37 * jQuery instance of current window.
38 * @member {H5P.jQuery}
39 */
40H5P.$window = H5P.jQuery(window);
41
42/**
43 * List over H5P instances on the current page.
44 * @member {Array}
45 */
46H5P.instances = [];
47
48// Detect if we support fullscreen, and what prefix to use.
49if (document.documentElement.requestFullscreen) {
50  /**
51   * Browser prefix to use when entering fullscreen mode.
52   * undefined means no fullscreen support.
53   * @member {string}
54   */
55  H5P.fullScreenBrowserPrefix = '';
56}
57else if (document.documentElement.webkitRequestFullScreen) {
58  H5P.safariBrowser = navigator.userAgent.match(/version\/([.\d]+)/i);
59  H5P.safariBrowser = (H5P.safariBrowser === null ? 0 : parseInt(H5P.safariBrowser[1]));
60
61  // Do not allow fullscreen for safari < 7.
62  if (H5P.safariBrowser === 0 || H5P.safariBrowser > 6) {
63    H5P.fullScreenBrowserPrefix = 'webkit';
64  }
65}
66else if (document.documentElement.mozRequestFullScreen) {
67  H5P.fullScreenBrowserPrefix = 'moz';
68}
69else if (document.documentElement.msRequestFullscreen) {
70  H5P.fullScreenBrowserPrefix = 'ms';
71}
72
73/**
74 * Keep track of when the H5Ps where started.
75 *
76 * @type {Object[]}
77 */
78H5P.opened = {};
79
80/**
81 * Initialize H5P content.
82 * Scans for ".h5p-content" in the document and initializes H5P instances where found.
83 *
84 * @param {Object} target DOM Element
85 */
86H5P.init = function (target) {
87  // Useful jQuery object.
88  if (H5P.$body === undefined) {
89    H5P.$body = H5P.jQuery(document.body);
90  }
91
92  // Determine if we can use full screen
93  if (H5P.fullscreenSupported === undefined) {
94    /**
95     * Use this variable to check if fullscreen is supported. Fullscreen can be
96     * restricted when embedding since not all browsers support the native
97     * fullscreen, and the semi-fullscreen solution doesn't work when embedded.
98     * @type {boolean}
99     */
100    H5P.fullscreenSupported = !H5PIntegration.fullscreenDisabled && !H5P.fullscreenDisabled && (!(H5P.isFramed && H5P.externalEmbed !== false) || !!(document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled));
101    // -We should consider document.msFullscreenEnabled when they get their
102    // -element sizing corrected. Ref. https://connect.microsoft.com/IE/feedback/details/838286/ie-11-incorrectly-reports-dom-element-sizes-in-fullscreen-mode-when-fullscreened-element-is-within-an-iframe
103    // Update: Seems to be no need as they've moved on to Webkit
104  }
105
106  // Deprecated variable, kept to maintain backwards compatability
107  if (H5P.canHasFullScreen === undefined) {
108    /**
109     * @deprecated since version 1.11
110     * @type {boolean}
111     */
112    H5P.canHasFullScreen = H5P.fullscreenSupported;
113  }
114
115  // H5Ps added in normal DIV.
116  H5P.jQuery('.h5p-content:not(.h5p-initialized)', target).each(function () {
117    var $element = H5P.jQuery(this).addClass('h5p-initialized');
118    var $container = H5P.jQuery('<div class="h5p-container"></div>').appendTo($element);
119    var contentId = $element.data('content-id');
120    var contentData = H5PIntegration.contents['cid-' + contentId];
121    if (contentData === undefined) {
122      return H5P.error('No data for content id ' + contentId + '. Perhaps the library is gone?');
123    }
124    var library = {
125      library: contentData.library,
126      params: JSON.parse(contentData.jsonContent),
127      metadata: contentData.metadata
128    };
129
130    // Apply theme density
131    let density = H5PIntegration.theme?.density ?? 'large';
132    $element.addClass('h5p-' + density);
133
134    H5P.getUserData(contentId, 'state', function (err, previousState) {
135      if (previousState) {
136        library.userDatas = {
137          state: previousState
138        };
139      }
140      else if (previousState === null && H5PIntegration.saveFreq) {
141        // Content has been reset. Display dialog.
142        delete contentData.contentUserData;
143        var dialog = new H5P.Dialog('content-user-data-reset', 'Data Reset', '<p>' + H5P.t('contentChanged') + '</p><p>' + H5P.t('startingOver') + '</p><div class="h5p-dialog-ok-button" tabIndex="0" role="button">OK</div>', $container);
144        H5P.jQuery(dialog).on('dialog-opened', function (event, $dialog) {
145
146          var closeDialog = function (event) {
147            if (event.type === 'click' || event.which === 32) {
148              dialog.close();
149              H5P.deleteUserData(contentId, 'state', 0);
150            }
151          };
152
153          $dialog.find('.h5p-dialog-ok-button').click(closeDialog).keypress(closeDialog);
154          H5P.trigger(instance, 'resize');
155        }).on('dialog-closed', function () {
156          H5P.trigger(instance, 'resize');
157        });
158        dialog.open();
159      }
160      // If previousState is false we don't have a previous state
161    });
162
163    // Create new instance.
164    var instance = H5P.newRunnable(library, contentId, $container, true, {standalone: true});
165
166    H5P.offlineRequestQueue = new H5P.OfflineRequestQueue({instance: instance});
167
168    // Check if we should add and display a fullscreen button for this H5P.
169    if (contentData.fullScreen == 1 && H5P.fullscreenSupported) {
170      H5P.jQuery(
171        '<div class="h5p-content-controls">' +
172          '<div role="button" ' +
173                'tabindex="0" ' +
174                'class="h5p-enable-fullscreen" ' +
175                'aria-label="' + H5P.t('fullscreen') + '" ' +
176                'title="' + H5P.t('fullscreen') + '">' +
177          '</div>' +
178        '</div>')
179        .prependTo($container)
180          .children()
181          .click(function () {
182            H5P.fullScreen($container, instance);
183          })
184        .keydown(function (e) {
185          if (e.which === 32 || e.which === 13) {
186            H5P.fullScreen($container, instance);
187            return false;
188          }
189        })
190      ;
191    }
192
193    /**
194     * Create action bar
195     */
196    var displayOptions = contentData.displayOptions;
197    var displayFrame = false;
198    if (displayOptions.frame) {
199      // Special handling of copyrights
200      if (displayOptions.copyright) {
201        var copyrights = H5P.getCopyrights(instance, library.params, contentId, library.metadata);
202        if (!copyrights) {
203          displayOptions.copyright = false;
204        }
205      }
206
207      // Create action bar
208      var actionBar = new H5P.ActionBar(displayOptions);
209      var $actions = actionBar.getDOMElement();
210
211      actionBar.on('reuse', function () {
212        H5P.openReuseDialog($actions, contentData, {
213          library: contentData.library,
214          params: JSON.parse(contentData.jsonContent),
215          metadata: contentData.metadata
216        }, instance, contentId);
217        instance.triggerXAPI('accessed-reuse');
218      });
219      actionBar.on('copyrights', function () {
220        var dialog = new H5P.Dialog('copyrights', H5P.t('copyrightInformation'), copyrights, $container, $actions.find('.h5p-copyrights')[0]);
221        dialog.open(true);
222        instance.triggerXAPI('accessed-copyright');
223      });
224      actionBar.on('embed', function () {
225        H5P.openEmbedDialog($actions, contentData.embedCode, contentData.resizeCode, {
226          width: $element.width(),
227          height: $element.height()
228        }, instance);
229        instance.triggerXAPI('accessed-embed');
230      });
231
232      if (actionBar.hasActions()) {
233        displayFrame = true;
234        $actions.insertAfter($container);
235      }
236    }
237
238    $element.addClass(displayFrame ? 'h5p-frame' : 'h5p-no-frame');
239
240    // Keep track of when we started
241    H5P.opened[contentId] = new Date();
242
243    // Handle events when the user finishes the content. Useful for logging exercise results.
244    H5P.on(instance, 'finish', function (event) {
245      if (event.data !== undefined) {
246        H5P.setFinished(contentId, event.data.score, event.data.maxScore, event.data.time);
247      }
248    });
249
250    // Listen for xAPI events.
251    H5P.on(instance, 'xAPI', H5P.xAPICompletedListener);
252
253    // Auto save current state if supported
254    if (H5PIntegration.saveFreq !== false && (
255        instance.getCurrentState instanceof Function ||
256        typeof instance.getCurrentState === 'function')) {
257
258      var saveTimer, save = function () {
259        var state = instance.getCurrentState();
260        if (state !== undefined) {
261          H5P.setUserData(contentId, 'state', state, {deleteOnChange: true});
262        }
263        if (H5PIntegration.saveFreq) {
264          // Continue autosave
265          saveTimer = setTimeout(save, H5PIntegration.saveFreq * 1000);
266        }
267      };
268
269      if (H5PIntegration.saveFreq) {
270        // Start autosave
271        saveTimer = setTimeout(save, H5PIntegration.saveFreq * 1000);
272      }
273
274      // xAPI events will schedule a save in three seconds.
275      H5P.on(instance, 'xAPI', function (event) {
276        var verb = event.getVerb();
277        if (verb === 'completed' || verb === 'progressed') {
278          clearTimeout(saveTimer);
279          saveTimer = setTimeout(save, 3000);
280        }
281      });
282    }
283
284    if (H5P.isFramed) {
285      var resizeDelay;
286      if (H5P.externalEmbed === false) {
287        // Internal embed
288        // Make it possible to resize the iframe when the content changes size. This way we get no scrollbars.
289        var iframe = window.frameElement;
290        var resizeIframe = function () {
291          if (window.parent.H5P.isFullscreen) {
292            return; // Skip if full screen.
293          }
294
295          // Retain parent size to avoid jumping/scrolling
296          var parentHeight = iframe.parentElement.style.height;
297          iframe.parentElement.style.height = iframe.parentElement.clientHeight + 'px';
298
299          // Note:  Force layout reflow
300          //        This fixes a flickering bug for embedded content on iPads
301          //        @see https://github.com/h5p/h5p-moodle-plugin/issues/237
302          iframe.getBoundingClientRect();
303
304          // Reset iframe height, in case content has shrinked.
305          iframe.style.height = '1px';
306
307          // Resize iframe so all content is visible.
308          iframe.style.height = (iframe.contentDocument.body.scrollHeight) + 'px';
309
310          // Free parent
311          iframe.parentElement.style.height = parentHeight;
312        };
313
314        H5P.on(instance, 'resize', function () {
315          // Use a delay to make sure iframe is resized to the correct size.
316          clearTimeout(resizeDelay);
317          resizeDelay = setTimeout(function () {
318            resizeIframe();
319          }, 1);
320        });
321      }
322      else if (H5P.communicator) {
323        // External embed
324        var parentIsFriendly = false;
325
326        // Handle that the resizer is loaded after the iframe
327        H5P.communicator.on('ready', function () {
328          H5P.communicator.send('hello');
329        });
330
331        // Handle hello message from our parent window
332        H5P.communicator.on('hello', function () {
333          // Initial setup/handshake is done
334          parentIsFriendly = true;
335
336          // Make iframe responsive
337          document.body.style.height = 'auto';
338
339          // Hide scrollbars for correct size
340          document.body.style.overflow = 'hidden';
341
342          // Content need to be resized to fit the new iframe size
343          H5P.trigger(instance, 'resize');
344        });
345
346        // When resize has been prepared tell parent window to resize
347        H5P.communicator.on('resizePrepared', function () {
348          H5P.communicator.send('resize', {
349            scrollHeight: document.body.scrollHeight
350          });
351        });
352
353        H5P.communicator.on('resize', function () {
354          H5P.trigger(instance, 'resize');
355        });
356
357        H5P.on(instance, 'resize', function () {
358          if (H5P.isFullscreen) {
359            return; // Skip iframe resize
360          }
361
362          // Use a delay to make sure iframe is resized to the correct size.
363          clearTimeout(resizeDelay);
364          resizeDelay = setTimeout(function () {
365            // Only resize if the iframe can be resized
366            if (parentIsFriendly) {
367              H5P.communicator.send('prepareResize', {
368                scrollHeight: document.body.scrollHeight,
369                clientHeight: document.body.clientHeight
370              });
371            }
372            else {
373              H5P.communicator.send('hello');
374            }
375          }, 0);
376        });
377      }
378    }
379
380    if (!H5P.isFramed || H5P.externalEmbed === false) {
381      // Resize everything when window is resized.
382      H5P.jQuery(window.parent).resize(function () {
383        H5P.trigger(instance, 'resize');
384      });
385    }
386
387    H5P.instances.push(instance);
388
389    // Resize content.
390    H5P.trigger(instance, 'resize');
391
392    // Logic for hiding focus effects when using mouse
393    $element.addClass('using-mouse');
394    $element.on('mousedown keydown keyup', function (event) {
395      $element.toggleClass('using-mouse', event.type === 'mousedown');
396    });
397
398    if (H5P.externalDispatcher) {
399      H5P.externalDispatcher.trigger('initialized');
400    }
401  });
402
403  // Insert H5Ps that should be in iframes.
404  H5P.jQuery('iframe.h5p-iframe:not(.h5p-initialized)', target).each(function () {
405    const iframe = this;
406    const $iframe = H5P.jQuery(iframe);
407
408    const contentId = $iframe.data('content-id');
409    const contentData = H5PIntegration.contents['cid-' + contentId];
410    const contentLanguage = contentData && contentData.metadata && contentData.metadata.defaultLanguage
411      ? contentData.metadata.defaultLanguage : 'en';
412
413    const writeDocument = function () {
414      iframe.contentDocument.open();
415      iframe.contentDocument.write('<!doctype html><html class="h5p-iframe" lang="' + contentLanguage + '"><head>' + H5P.getHeadTags(contentId) + '</head><body><div class="h5p-content" data-content-id="' + contentId + '"/></body></html>');
416      iframe.contentDocument.close();
417    };
418
419    $iframe.addClass('h5p-initialized')
420    if (iframe.contentDocument === null) {
421      // In some Edge cases the iframe isn't always loaded when the page is ready.
422      $iframe.on('load', writeDocument);
423      $iframe.attr('src', 'about:blank');
424    }
425    else {
426      writeDocument();
427    }
428  });
429};
430
431/**
432 * Loop through assets for iframe content and create a set of tags for head.
433 *
434 * @private
435 * @param {number} contentId
436 * @returns {string} HTML
437 */
438H5P.getHeadTags = function (contentId) {
439  var createStyleTags = function (styles) {
440    var tags = '';
441    for (var i = 0; i < styles.length; i++) {
442      tags += '<link rel="stylesheet" href="' + styles[i] + '">';
443    }
444    return tags;
445  };
446
447  var createScriptTags = function (scripts) {
448    var tags = '';
449    for (var i = 0; i < scripts.length; i++) {
450      tags += '<script src="' + scripts[i] + '"></script>';
451    }
452    return tags;
453  };
454
455  return '<base target="_parent">' +
456         createStyleTags(H5PIntegration.core.styles) +
457         createStyleTags(H5PIntegration.contents['cid-' + contentId].styles) +
458         createScriptTags(H5PIntegration.core.scripts) +
459         createScriptTags(H5PIntegration.contents['cid-' + contentId].scripts) +
460         '<script>H5PIntegration = window.parent.H5PIntegration; var H5P = H5P || {}; H5P.externalEmbed = false;</script>';
461};
462
463/**
464 * When embedded the communicator helps talk to the parent page.
465 *
466 * @type {Communicator}
467 */
468H5P.communicator = (function () {
469  /**
470   * @class
471   * @private
472   */
473  function Communicator() {
474    var self = this;
475
476    // Maps actions to functions
477    var actionHandlers = {};
478
479    // Register message listener
480    window.addEventListener('message', function receiveMessage(event) {
481      if (window.parent !== event.source || event.data.context !== 'h5p') {
482        return; // Only handle messages from parent and in the correct context
483      }
484
485      if (actionHandlers[event.data.action] !== undefined) {
486        actionHandlers[event.data.action](event.data);
487      }
488    } , false);
489
490
491    /**
492     * Register action listener.
493     *
494     * @param {string} action What you are waiting for
495     * @param {function} handler What you want done
496     */
497    self.on = function (action, handler) {
498      actionHandlers[action] = handler;
499    };
500
501    /**
502     * Send a message to the all mighty father.
503     *
504     * @param {string} action
505     * @param {Object} [data] payload
506     */
507    self.send = function (action, data) {
508      if (data === undefined) {
509        data = {};
510      }
511      data.context = 'h5p';
512      data.action = action;
513
514      // Parent origin can be anything
515      window.parent.postMessage(data, '*');
516    };
517  }
518
519  return (window.postMessage && window.addEventListener ? new Communicator() : undefined);
520})();
521
522/**
523 * Enter semi fullscreen for the given H5P instance
524 *
525 * @param {H5P.jQuery} $element Content container.
526 * @param {Object} instance
527 * @param {function} exitCallback Callback function called when user exits fullscreen.
528 * @param {H5P.jQuery} $body For internal use. Gives the body of the iframe.
529 */
530H5P.semiFullScreen = function ($element, instance, exitCallback, body) {
531  H5P.fullScreen($element, instance, exitCallback, body, true);
532};
533
534/**
535 * Enter fullscreen for the given H5P instance.
536 *
537 * @param {H5P.jQuery} $element Content container.
538 * @param {Object} instance
539 * @param {function} exitCallback Callback function called when user exits fullscreen.
540 * @param {H5P.jQuery} $body For internal use. Gives the body of the iframe.
541 * @param {Boolean} forceSemiFullScreen
542 */
543H5P.fullScreen = function ($element, instance, exitCallback, body, forceSemiFullScreen) {
544  if (H5P.exitFullScreen !== undefined) {
545    return; // Cannot enter new fullscreen until previous is over
546  }
547
548  if (H5P.isFramed && H5P.externalEmbed === false) {
549    // Trigger resize on wrapper in parent window.
550    window.parent.H5P.fullScreen($element, instance, exitCallback, H5P.$body.get(), forceSemiFullScreen);
551    H5P.isFullscreen = true;
552    H5P.exitFullScreen = function () {
553      window.parent.H5P.exitFullScreen();
554    };
555    H5P.on(instance, 'exitFullScreen', function () {
556      H5P.isFullscreen = false;
557      H5P.exitFullScreen = undefined;
558    });
559    return;
560  }
561
562  var $container = $element;
563  var $classes, $iframe, $body;
564  if (body === undefined)  {
565    $body = H5P.$body;
566  }
567  else {
568    // We're called from an iframe.
569    $body = H5P.jQuery(body);
570    $classes = $body.add($element.get());
571    var iframeSelector = '#h5p-iframe-' + $element.parent().data('content-id');
572    $iframe = H5P.jQuery(iframeSelector);
573    $element = $iframe.parent(); // Put iframe wrapper in fullscreen, not container.
574  }
575
576  $classes = $element.add(H5P.$body).add($classes);
577
578  /**
579   * Prepare for resize by setting the correct styles.
580   *
581   * @private
582   * @param {string} classes CSS
583   */
584  var before = function (classes) {
585    $classes.addClass(classes);
586
587    if ($iframe !== undefined) {
588      // Set iframe to its default size(100%).
589      $iframe.css('height', '');
590    }
591  };
592
593  /**
594   * Gets called when fullscreen mode has been entered.
595   * Resizes and sets focus on content.
596   *
597   * @private
598   */
599  var entered = function () {
600    // Do not rely on window resize events.
601    H5P.trigger(instance, 'resize');
602    H5P.trigger(instance, 'focus');
603    H5P.trigger(instance, 'enterFullScreen');
604  };
605
606  /**
607   * Gets called when fullscreen mode has been exited.
608   * Resizes and sets focus on content.
609   *
610   * @private
611   * @param {string} classes CSS
612   */
613  var done = function (classes) {
614    H5P.isFullscreen = false;
615    $classes.removeClass(classes);
616
617    // Do not rely on window resize events.
618    H5P.trigger(instance, 'resize');
619    H5P.trigger(instance, 'focus');
620
621    H5P.exitFullScreen = undefined;
622    if (exitCallback !== undefined) {
623      exitCallback();
624    }
625
626    H5P.trigger(instance, 'exitFullScreen');
627  };
628
629  H5P.isFullscreen = true;
630  if (H5P.fullScreenBrowserPrefix === undefined || forceSemiFullScreen === true) {
631    // Create semi fullscreen.
632
633    if (H5P.isFramed) {
634      return; // TODO: Should we support semi-fullscreen for IE9 & 10 ?
635    }
636
637    before('h5p-semi-fullscreen');
638    var $disable = H5P.jQuery('<div role="button" tabindex="0" class="h5p-disable-fullscreen" title="' + H5P.t('disableFullscreen') + '" aria-label="' + H5P.t('disableFullscreen') + '"></div>').appendTo($container.find('.h5p-content-controls'));
639    var keyup, disableSemiFullscreen = H5P.exitFullScreen = function () {
640      if (prevViewportContent) {
641        // Use content from the previous viewport tag
642        h5pViewport.content = prevViewportContent;
643      }
644      else {
645        // Remove viewport tag
646        head.removeChild(h5pViewport);
647      }
648      $disable.remove();
649      $body.unbind('keyup', keyup);
650      done('h5p-semi-fullscreen');
651    };
652    keyup = function (event) {
653      if (event.keyCode === 27) {
654        disableSemiFullscreen();
655      }
656    };
657    $disable.click(disableSemiFullscreen);
658    $body.keyup(keyup);
659
660    // Disable zoom
661    var prevViewportContent, h5pViewport;
662    var metaTags = document.getElementsByTagName('meta');
663    for (var i = 0; i < metaTags.length; i++) {
664      if (metaTags[i].name === 'viewport') {
665        // Use the existing viewport tag
666        h5pViewport = metaTags[i];
667        prevViewportContent = h5pViewport.content;
668        break;
669      }
670    }
671    if (!prevViewportContent) {
672      // Create a new viewport tag
673      h5pViewport = document.createElement('meta');
674      h5pViewport.name = 'viewport';
675    }
676    h5pViewport.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0';
677    if (!prevViewportContent) {
678      // Insert the new viewport tag
679      var head = document.getElementsByTagName('head')[0];
680      head.appendChild(h5pViewport);
681    }
682
683    entered();
684  }
685  else {
686    // Create real fullscreen.
687
688    before('h5p-fullscreen');
689    var first, eventName = (H5P.fullScreenBrowserPrefix === 'ms' ? 'MSFullscreenChange' : H5P.fullScreenBrowserPrefix + 'fullscreenchange');
690    document.addEventListener(eventName, function fullscreenCallback() {
691      if (first === undefined) {
692        // We are entering fullscreen mode
693        first = false;
694        entered();
695        return;
696      }
697
698      // We are exiting fullscreen
699      done('h5p-fullscreen');
700      document.removeEventListener(eventName, fullscreenCallback, false);
701    });
702
703    if (H5P.fullScreenBrowserPrefix === '') {
704      $element[0].requestFullscreen();
705    }
706    else {
707      var method = (H5P.fullScreenBrowserPrefix === 'ms' ? 'msRequestFullscreen' : H5P.fullScreenBrowserPrefix + 'RequestFullScreen');
708      var params = (H5P.fullScreenBrowserPrefix === 'webkit' && H5P.safariBrowser === 0 ? Element.ALLOW_KEYBOARD_INPUT : undefined);
709      $element[0][method](params);
710    }
711
712    // Allows everone to exit
713    H5P.exitFullScreen = function () {
714      if (H5P.fullScreenBrowserPrefix === '') {
715        document.exitFullscreen();
716      }
717      else if (H5P.fullScreenBrowserPrefix === 'moz') {
718        document.mozCancelFullScreen();
719      }
720      else {
721        document[H5P.fullScreenBrowserPrefix + 'ExitFullscreen']();
722      }
723    };
724  }
725};
726
727(function () {
728  /**
729   * Helper for adding a query parameter to an existing path that may already
730   * contain one or a hash.
731   *
732   * @param {string} path
733   * @param {string} parameter
734   * @return {string}
735   */
736  H5P.addQueryParameter = function (path, parameter) {
737    let newPath, secondSplit;
738    const firstSplit = path.split('?');
739    if (firstSplit[1]) {
740      // There is already an existing query
741      secondSplit = firstSplit[1].split('#');
742      newPath = firstSplit[0] + '?' + secondSplit[0] + '&';
743    }
744    else {
745      // No existing query, just need to take care of the hash
746      secondSplit = firstSplit[0].split('#');
747      newPath = secondSplit[0] + '?';
748    }
749    newPath += parameter;
750    if (secondSplit[1]) {
751      // Add back the hash
752      newPath += '#' + secondSplit[1];
753    }
754    return newPath;
755  };
756
757  /**
758   * Helper for setting the crossOrigin attribute + the complete correct source.
759   * Note: This will start loading the resource.
760   *
761   * @param {Element} element DOM element, typically img, video or audio
762   * @param {Object} source File object from parameters/json_content (created by H5PEditor)
763   * @param {number} contentId Needed to determine the complete correct file path
764   */
765  H5P.setSource = function (element, source, contentId) {
766    let path = source.path;
767
768    const crossOrigin = H5P.getCrossOrigin(source);
769    if (crossOrigin) {
770      element.crossOrigin = crossOrigin;
771
772      if (H5PIntegration.crossoriginCacheBuster) {
773        // Some sites may want to add a cache buster in case the same resource
774        // is used elsewhere without the crossOrigin attribute
775        path = H5P.addQueryParameter(path, H5PIntegration.crossoriginCacheBuster);
776      }
777    }
778    else {
779      // In case this element has been used before.
780      element.removeAttribute('crossorigin');
781    }
782
783    element.src = H5P.getPath(path, contentId);
784  };
785
786  /**
787   * Check if the given path has a protocol.
788   *
789   * @private
790   * @param {string} path
791   * @return {string}
792   */
793  var hasProtocol = function (path) {
794    return path.match(/^[a-z0-9]+:\/\//i);
795  };
796
797  /**
798   * Get the crossOrigin policy to use for img, video and audio tags on the current site.
799   *
800   * @param {Object|string} source File object from parameters/json_content - Can also be URL(deprecated usage)
801   * @returns {string|null} crossOrigin attribute value required by the source
802   */
803  H5P.getCrossOrigin = function (source) {
804    if (typeof source !== 'object') {
805      // Deprecated usage.
806      return H5PIntegration.crossorigin && H5PIntegration.crossoriginRegex && source.match(H5PIntegration.crossoriginRegex) ? H5PIntegration.crossorigin : null;
807    }
808
809    if (H5PIntegration.crossorigin && !hasProtocol(source.path)) {
810      // This is a local file, use the local crossOrigin policy.
811      return H5PIntegration.crossorigin;
812      // Note: We cannot use this for all external sources since we do not know
813      // each server's individual policy. We could add support for a list of
814      // external sources and their policy later on.
815    }
816  };
817
818  /**
819   * Find the path to the content files based on the id of the content.
820   * Also identifies and returns absolute paths.
821   *
822   * @param {string} path
823   *   Relative to content folder or absolute.
824   * @param {number} contentId
825   *   ID of the content requesting the path.
826   * @returns {string}
827   *   Complete URL to path.
828   */
829  H5P.getPath = function (path, contentId) {
830    if (hasProtocol(path)) {
831      return path;
832    }
833
834    var prefix;
835    var isTmpFile = (path.substr(-4,4) === '#tmp');
836    if (contentId !== undefined && !isTmpFile) {
837      // Check for custom override URL
838      if (H5PIntegration.contents !== undefined &&
839          H5PIntegration.contents['cid-' + contentId]) {
840        prefix = H5PIntegration.contents['cid-' + contentId].contentUrl;
841      }
842      if (!prefix) {
843        prefix = H5PIntegration.url + '/content/' + contentId;
844      }
845    }
846    else if (window.H5PEditor !== undefined) {
847      prefix = H5PEditor.filesPath;
848    }
849    else {
850      return;
851    }
852
853    if (!hasProtocol(prefix)) {
854      // Use absolute urls
855      prefix = window.location.protocol + "//" + window.location.host + prefix;
856    }
857
858    return prefix + '/' + path;
859  };
860})();
861
862/**
863 * THIS FUNCTION IS DEPRECATED, USE getPath INSTEAD
864 * Will be remove march 2016.
865 *
866 * Find the path to the content files folder based on the id of the content
867 *
868 * @deprecated
869 *   Will be removed march 2016.
870 * @param contentId
871 *   Id of the content requesting the path
872 * @returns {string}
873 *   URL
874 */
875H5P.getContentPath = function (contentId) {
876  return H5PIntegration.url + '/content/' + contentId;
877};
878
879/**
880 * Get library class constructor from H5P by classname.
881 * Note that this class will only work for resolve "H5P.NameWithoutDot".
882 * Also check out {@link H5P.newRunnable}
883 *
884 * Used from libraries to construct instances of other libraries' objects by name.
885 *
886 * @param {string} name Name of library
887 * @returns {Object} Class constructor
888 */
889H5P.classFromName = function (name) {
890  var arr = name.split(".");
891  return this[arr[arr.length - 1]];
892};
893
894/**
895 * A safe way of creating a new instance of a runnable H5P.
896 *
897 * @param {Object} library
898 *   Library/action object form params.
899 * @param {number} contentId
900 *   Identifies the content.
901 * @param {H5P.jQuery} [$attachTo]
902 *   Element to attach the instance to.
903 * @param {boolean} [skipResize]
904 *   Skip triggering of the resize event after attaching.
905 * @param {Object} [extras]
906 *   Extra parameters for the H5P content constructor
907 * @returns {Object}
908 *   Instance.
909 */
910H5P.newRunnable = function (library, contentId, $attachTo, skipResize, extras) {
911  var nameSplit, versionSplit, machineName;
912  try {
913    nameSplit = library.library.split(' ', 2);
914    machineName = nameSplit[0];
915    versionSplit = nameSplit[1].split('.', 2);
916  }
917  catch (err) {
918    return H5P.error('Invalid library string: ' + library.library);
919  }
920
921  if ((library.params instanceof Object) !== true || (library.params instanceof Array) === true) {
922    H5P.error('Invalid library params for: ' + library.library);
923    return H5P.error(library.params);
924  }
925
926  // Find constructor function
927  var constructor;
928  try {
929    nameSplit = nameSplit[0].split('.');
930    constructor = window;
931    for (var i = 0; i < nameSplit.length; i++) {
932      constructor = constructor[nameSplit[i]];
933    }
934    if (typeof constructor !== 'function') {
935      throw null;
936    }
937  }
938  catch (err) {
939    return H5P.error('Unable to find constructor for: ' + library.library);
940  }
941
942  if (extras === undefined) {
943    extras = {};
944  }
945  if (library.subContentId) {
946    extras.subContentId = library.subContentId;
947  }
948
949  if (library.userDatas && library.userDatas.state && H5PIntegration.saveFreq) {
950    extras.previousState = library.userDatas.state;
951  }
952
953  if (library.metadata) {
954    extras.metadata = library.metadata;
955  }
956
957  // Makes all H5P libraries extend H5P.ContentType:
958  var standalone = extras.standalone || false;
959  // This order makes it possible for an H5P library to override H5P.ContentType functions!
960  constructor.prototype = H5P.jQuery.extend({}, H5P.ContentType(standalone).prototype, constructor.prototype);
961
962  var instance;
963  // Some old library versions have their own custom third parameter.
964  // Make sure we don't send them the extras.
965  // (they will interpret it as something else)
966  if (H5P.jQuery.inArray(library.library, ['H5P.CoursePresentation 1.0', 'H5P.CoursePresentation 1.1', 'H5P.CoursePresentation 1.2', 'H5P.CoursePresentation 1.3']) > -1) {
967    instance = new constructor(library.params, contentId);
968  }
969  else {
970    instance = new constructor(library.params, contentId, extras);
971  }
972
973  if (instance.$ === undefined) {
974    instance.$ = H5P.jQuery(instance);
975  }
976
977  if (instance.contentId === undefined) {
978    instance.contentId = contentId;
979  }
980  if (instance.subContentId === undefined && library.subContentId) {
981    instance.subContentId = library.subContentId;
982  }
983  if (instance.parent === undefined && extras && extras.parent) {
984    instance.parent = extras.parent;
985  }
986  if (instance.libraryInfo === undefined) {
987    instance.libraryInfo = {
988      versionedName: library.library,
989      versionedNameNoSpaces: machineName + '-' + versionSplit[0] + '.' + versionSplit[1],
990      machineName: machineName,
991      majorVersion: versionSplit[0],
992      minorVersion: versionSplit[1]
993    };
994  }
995
996  if ($attachTo !== undefined) {
997    $attachTo.toggleClass('h5p-standalone', standalone);
998    instance.attach($attachTo);
999    H5P.trigger(instance, 'domChanged', {
1000      '$target': $attachTo,
1001      'library': machineName,
1002      'key': 'newLibrary'
1003    }, {'bubbles': true, 'external': true});
1004
1005    if (skipResize === undefined || !skipResize) {
1006      // Resize content.
1007      H5P.trigger(instance, 'resize');
1008    }
1009  }
1010  return instance;
1011};
1012
1013/**
1014 * Used to print useful error messages. (to JavaScript error console)
1015 *
1016 * @param {*} err Error to print.
1017 */
1018H5P.error = function (err) {
1019  if (window.console !== undefined && console.error !== undefined) {
1020    console.error(err.stack ? err.stack : err);
1021  }
1022};
1023
1024/**
1025 * Translate text strings.
1026 *
1027 * @param {string} key
1028 *   Translation identifier, may only contain a-zA-Z0-9. No spaces or special chars.
1029 * @param {Object} [vars]
1030 *   Data for placeholders.
1031 * @param {string} [ns]
1032 *   Translation namespace. Defaults to H5P.
1033 * @returns {string}
1034 *   Translated text
1035 */
1036H5P.t = function (key, vars, ns) {
1037  if (ns === undefined) {
1038    ns = 'H5P';
1039  }
1040
1041  if (H5PIntegration.l10n[ns] === undefined) {
1042    return '[Missing translation namespace "' + ns + '"]';
1043  }
1044
1045  if (H5PIntegration.l10n[ns][key] === undefined) {
1046    return '[Missing translation "' + key + '" in "' + ns + '"]';
1047  }
1048
1049  var translation = H5PIntegration.l10n[ns][key];
1050
1051  if (vars !== undefined) {
1052    // Replace placeholder with variables.
1053    for (var placeholder in vars) {
1054      translation = translation.replace(placeholder, vars[placeholder]);
1055    }
1056  }
1057
1058  return translation;
1059};
1060
1061/**
1062 * Creates a new popup dialog over the H5P content.
1063 *
1064 * @class
1065 * @param {string} name
1066 *   Used for html class.
1067 * @param {string} title
1068 *   Used for header.
1069 * @param {string} content
1070 *   Displayed inside the dialog.
1071 * @param {H5P.jQuery} $element
1072 *   Which DOM element the dialog should be inserted after.
1073 * @param {H5P.jQuery} $returnElement
1074 *   Which DOM element the focus should be moved to on close
1075 */
1076H5P.Dialog = function (name, title, content, $element, $returnElement) {
1077  /** @alias H5P.Dialog# */
1078  var self = this;
1079  this.activeElement = document.activeElement;
1080  var $dialog = H5P.jQuery('<div class="h5p-popup-dialog h5p-' + name + '-dialog" aria-labelledby="' + name + '-dialog-header" aria-modal="true" role="dialog" tabindex="-1">\
1081                              <div class="h5p-inner">\
1082                                <h2 id="' + name + '-dialog-header">' + title + '</h2>\
1083                                <div class="h5p-scroll-content">' + content + '</div>\
1084                                <div class="h5p-close" role="button" tabindex="0" aria-label="' + H5P.t('close') + '" title="' + H5P.t('close') + '"></div>\
1085                              </div>\
1086                            </div>')
1087    .insertAfter($element)
1088    .click(function (e) {
1089      if (e && e.originalEvent && e.originalEvent.preventClosing) {
1090        return;
1091      }
1092
1093      self.close();
1094    })
1095    .children('.h5p-inner')
1096      .click(function (e) {
1097        e.originalEvent.preventClosing = true;
1098      })
1099      .find('.h5p-close')
1100        .click(function () {
1101          self.close();
1102        })
1103        .keypress(function (e) {
1104          if (e.which === 13 || e.which === 32) {
1105            self.close();
1106            return false;
1107          }
1108        })
1109        .end()
1110      .find('a')
1111        .click(function (e) {
1112          e.stopPropagation();
1113        })
1114      .end()
1115    .end();
1116
1117  /**
1118   * Opens the dialog.
1119   */
1120  self.open = function (scrollbar) {
1121    if (scrollbar) {
1122      $dialog.css('height', '100%');
1123    }
1124    setTimeout(function () {
1125      $dialog.addClass('h5p-open'); // Fade in
1126      // Triggering an event, in case something has to be done after dialog has been opened.
1127      H5P.jQuery(self).trigger('dialog-opened', [$dialog]);
1128      $dialog.focus();
1129    }, 1);
1130  };
1131
1132  /**
1133   * Closes the dialog.
1134   */
1135  self.close = function () {
1136    $dialog.removeClass('h5p-open'); // Fade out
1137    setTimeout(function () {
1138      $dialog.remove();
1139      H5P.jQuery(self).trigger('dialog-closed', [$dialog]);
1140      $element.attr('tabindex', '-1');
1141      if ($returnElement) {
1142        $returnElement.focus();
1143      }
1144      else if(self.activeElement) {
1145        self.activeElement.focus();
1146      }
1147      else {
1148        $element.focus();
1149      }
1150    }, 200);
1151  };
1152};
1153
1154/**
1155 * Gather copyright information for the given content.
1156 *
1157 * @param {Object} instance
1158 *   H5P instance to get copyright information for.
1159 * @param {Object} parameters
1160 *   Parameters of the content instance.
1161 * @param {number} contentId
1162 *   Identifies the H5P content
1163 * @param {Object} metadata
1164 *   Metadata of the content instance.
1165 * @returns {string} Copyright information.
1166 */
1167H5P.getCopyrights = function (instance, parameters, contentId, metadata) {
1168  var copyrights;
1169
1170  if (instance.getCopyrights !== undefined) {
1171    try {
1172      // Use the instance's own copyright generator
1173      copyrights = instance.getCopyrights();
1174    }
1175    catch (err) {
1176      // Failed, prevent crashing page.
1177    }
1178  }
1179
1180  if (copyrights === undefined) {
1181    // Create a generic flat copyright list
1182    copyrights = new H5P.ContentCopyrights();
1183    H5P.findCopyrights(copyrights, parameters, contentId);
1184  }
1185
1186  var metadataCopyrights = H5P.buildMetadataCopyrights(metadata, instance.libraryInfo.machineName);
1187  if (metadataCopyrights !== undefined) {
1188    copyrights.addMediaInFront(metadataCopyrights);
1189  }
1190
1191  if (copyrights !== undefined) {
1192    // Convert to string
1193    copyrights = copyrights.toString();
1194  }
1195  return copyrights;
1196};
1197
1198/**
1199 * Gather a flat list of copyright information from the given parameters.
1200 *
1201 * @param {H5P.ContentCopyrights} info
1202 *   Used to collect all information in.
1203 * @param {(Object|Array)} parameters
1204 *   To search for file objects in.
1205 * @param {number} contentId
1206 *   Used to insert thumbnails for images.
1207 * @param {Object} extras - Extras.
1208 * @param {object} extras.metadata - Metadata
1209 * @param {object} extras.machineName - Library name of some kind.
1210 *   Metadata of the content instance.
1211 */
1212H5P.findCopyrights = function (info, parameters, contentId, extras) {
1213  // If extras are
1214  if (extras) {
1215    extras.params = parameters;
1216    buildFromMetadata(extras, extras.machineName, contentId);
1217  }
1218
1219  var lastContentTypeName;
1220  // Cycle through parameters
1221  for (var field in parameters) {
1222    if (!parameters.hasOwnProperty(field)) {
1223      continue; // Do not check
1224    }
1225
1226    /**
1227     * @deprecated This hack should be removed after 2017-11-01
1228     * The code that was using this was removed by HFP-574
1229     * This note was seen on 2018-04-04, and consultation with
1230     * higher authorities lead to keeping the code for now ;-)
1231     */
1232    if (field === 'overrideSettings') {
1233      console.warn("The semantics field 'overrideSettings' is DEPRECATED and should not be used.");
1234      console.warn(parameters);
1235      continue;
1236    }
1237
1238    var value = parameters[field];
1239
1240    if (value && value.library && typeof value.library === 'string') {
1241      lastContentTypeName = value.library.split(' ')[0];
1242    }
1243    else if (value && value.library && typeof value.library === 'object') {
1244      lastContentTypeName = (value.library.library && typeof value.library.library === 'string') ? value.library.library.split(' ')[0] : lastContentTypeName;
1245    }
1246
1247    if (value instanceof Array) {
1248      // Cycle through array
1249      H5P.findCopyrights(info, value, contentId);
1250    }
1251    else if (value instanceof Object) {
1252      buildFromMetadata(value, lastContentTypeName, contentId);
1253
1254      // Check if object is a file with copyrights (old core)
1255      if (value.copyright === undefined ||
1256          value.copyright.license === undefined ||
1257          value.path === undefined ||
1258          value.mime === undefined) {
1259
1260        // Nope, cycle throught object
1261        H5P.findCopyrights(info, value, contentId);
1262      }
1263      else {
1264        // Found file, add copyrights
1265        var copyrights = new H5P.MediaCopyright(value.copyright);
1266        if (value.width !== undefined && value.height !== undefined) {
1267          copyrights.setThumbnail(new H5P.Thumbnail(H5P.getPath(value.path, contentId), value.width, value.height));
1268        }
1269        info.addMedia(copyrights);
1270      }
1271    }
1272  }
1273
1274  function buildFromMetadata(data, name, contentId) {
1275    if (data.metadata) {
1276      const metadataCopyrights = H5P.buildMetadataCopyrights(data.metadata, name);
1277      if (metadataCopyrights !== undefined) {
1278        if (data.params && data.params.contentName === 'Image' && data.params.file) {
1279          const path = data.params.file.path;
1280          const width = data.params.file.width;
1281          const height = data.params.file.height;
1282          metadataCopyrights.setThumbnail(new H5P.Thumbnail(H5P.getPath(path, contentId), width, height, data.params.alt));
1283        }
1284        info.addMedia(metadataCopyrights);
1285      }
1286    }
1287  }
1288};
1289
1290H5P.buildMetadataCopyrights = function (metadata) {
1291  if (metadata && metadata.license !== undefined && metadata.license !== 'U') {
1292    var dataset = {
1293      contentType: metadata.contentType,
1294      title: metadata.title,
1295      author: (metadata.authors && metadata.authors.length > 0) ? metadata.authors.map(function (author) {
1296        return (author.role) ? author.name + ' (' + author.role + ')' : author.name;
1297      }).join(', ') : undefined,
1298      source: metadata.source,
1299      year: (metadata.yearFrom) ? (metadata.yearFrom + ((metadata.yearTo) ? '-' + metadata.yearTo: '')) : undefined,
1300      license: metadata.license,
1301      version: metadata.licenseVersion,
1302      licenseExtras: metadata.licenseExtras,
1303      changes: (metadata.changes && metadata.changes.length > 0) ? metadata.changes.map(function (change) {
1304        return change.log + (change.author ? ', ' + change.author : '') + (change.date ? ', ' + change.date : '');
1305      }).join(' / ') : undefined
1306    };
1307
1308    return new H5P.MediaCopyright(dataset);
1309  }
1310};
1311
1312/**
1313 * Display a dialog containing the download button and copy button.
1314 *
1315 * @param {H5P.jQuery} $element
1316 * @param {Object} contentData
1317 * @param {Object} library
1318 * @param {Object} instance
1319 * @param {number} contentId
1320 */
1321H5P.openReuseDialog = function ($element, contentData, library, instance, contentId) {
1322  let html = '';
1323  let buttonCount = 0;
1324  if (contentData.displayOptions.export) {
1325    html += '<button type="button" class="h5p-big-button h5p-download-button"><div class="h5p-button-title">Download as an .h5p file</div><div class="h5p-button-description">.h5p files may be uploaded to any web-site where H5P content may be created.</div></button>';
1326    buttonCount += 1;
1327  }
1328  if (contentData.displayOptions.export && contentData.displayOptions.copy) {
1329    html += '<div class="h5p-horizontal-line-text"><span>or</span></div>';
1330  }
1331  if (contentData.displayOptions.copy) {
1332    html += '<button type="button" class="h5p-big-button h5p-copy-button"><div class="h5p-button-title">Copy content</div><div class="h5p-button-description">Copied content may be pasted anywhere this content type is supported on this website.</div></button>';
1333    buttonCount += 1;
1334  }
1335
1336  const dialog = new H5P.Dialog('reuse', H5P.t('reuseContent'), html, $element);
1337
1338  // Selecting embed code when dialog is opened
1339  H5P.jQuery(dialog).on('dialog-opened', function (e, $dialog) {
1340    const scrollContent = $dialog[0].querySelector('.h5p-scroll-content');
1341    scrollContent.style.setProperty('--button-count', buttonCount);
1342    H5P.jQuery('<a href="https://h5p.org/node/442225" target="_blank">More Info</a>').click(function (e) {
1343      e.stopPropagation();
1344    }).appendTo($dialog.find('h2'));
1345    $dialog.find('.h5p-download-button').click(function () {
1346      window.location.href = contentData.exportUrl;
1347      instance.triggerXAPI('downloaded');
1348      dialog.close();
1349    });
1350    $dialog.find('.h5p-copy-button').click(function () {
1351      const item = new H5P.ClipboardItem(library);
1352      item.contentId = contentId;
1353      H5P.setClipboard(item);
1354      instance.triggerXAPI('copied');
1355      dialog.close();
1356      H5P.attachToastTo(
1357        H5P.jQuery('.h5p-content:first')[0],
1358        H5P.t('contentCopied'),
1359        {
1360          position: {
1361            horizontal: 'centered',
1362            vertical: 'centered',
1363            noOverflowX: true
1364          }
1365        }
1366      );
1367    });
1368    H5P.trigger(instance, 'resize');
1369  }).on('dialog-closed', function () {
1370    H5P.trigger(instance, 'resize');
1371  });
1372
1373  dialog.open();
1374};
1375
1376/**
1377 * Display a dialog containing the embed code.
1378 *
1379 * @param {H5P.jQuery} $element
1380 *   Element to insert dialog after.
1381 * @param {string} embedCode
1382 *   The embed code.
1383 * @param {string} resizeCode
1384 *   The advanced resize code
1385 * @param {Object} size
1386 *   The content's size.
1387 * @param {number} size.width
1388 * @param {number} size.height
1389 */
1390H5P.openEmbedDialog = function ($element, embedCode, resizeCode, size, instance) {
1391  var fullEmbedCode = embedCode + resizeCode;
1392  var dialog = new H5P.Dialog('embed', H5P.t('embed'), '<textarea class="h5p-embed-code-container" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>' + H5P.t('size') + ': <input aria-label="'+ H5P.t('width') +'" type="text" value="' + Math.ceil(size.width) + '" class="h5p-embed-size"/> × <input aria-label="'+ H5P.t('width') +'" type="text" value="' + Math.ceil(size.height) + '" class="h5p-embed-size"/> px<br/><div role="button" tabindex="0" class="h5p-expander">' + H5P.t('showAdvanced') + '</div><div class="h5p-expander-content"><p>' + H5P.t('advancedHelp') + '</p><textarea class="h5p-embed-code-container" autocorrect="off" autocapitalize="off" spellcheck="false">' + resizeCode + '</textarea></div>', $element);
1393
1394  // Selecting embed code when dialog is opened
1395  H5P.jQuery(dialog).on('dialog-opened', function (event, $dialog) {
1396    var $inner = $dialog.find('.h5p-inner');
1397    var $scroll = $inner.find('.h5p-scroll-content');
1398    var diff = $scroll.outerHeight() - $scroll.innerHeight();
1399    var positionInner = function () {
1400      H5P.trigger(instance, 'resize');
1401    };
1402
1403    // Handle changing of width/height
1404    var $w = $dialog.find('.h5p-embed-size:eq(0)');
1405    var $h = $dialog.find('.h5p-embed-size:eq(1)');
1406    var getNum = function ($e, d) {
1407      var num = parseFloat($e.val());
1408      if (isNaN(num)) {
1409        return d;
1410      }
1411      return Math.ceil(num);
1412    };
1413    var updateEmbed = function () {
1414      $dialog.find('.h5p-embed-code-container:first').val(fullEmbedCode.replace(':w', getNum($w, size.width)).replace(':h', getNum($h, size.height)));
1415    };
1416
1417    $w.change(updateEmbed);
1418    $h.change(updateEmbed);
1419    updateEmbed();
1420
1421    // Select text and expand textareas
1422    $dialog.find('.h5p-embed-code-container').each(function () {
1423      H5P.jQuery(this).css('height', this.scrollHeight + 'px').focus(function () {
1424        H5P.jQuery(this).select();
1425      });
1426    });
1427    $dialog.find('.h5p-embed-code-container').eq(0).select();
1428    positionInner();
1429
1430    // Expand advanced embed
1431    var expand = function () {
1432      var $expander = H5P.jQuery(this);
1433      var $content = $expander.next();
1434      if ($content.is(':visible')) {
1435        $expander.removeClass('h5p-open').text(H5P.t('showAdvanced')).attr('aria-expanded', 'true');
1436        $content.hide();
1437      }
1438      else {
1439        $expander.addClass('h5p-open').text(H5P.t('hideAdvanced')).attr('aria-expanded', 'false');
1440        $content.show();
1441      }
1442      $dialog.find('.h5p-embed-code-container').each(function () {
1443        H5P.jQuery(this).css('height', this.scrollHeight + 'px');
1444      });
1445      positionInner();
1446    };
1447    $dialog.find('.h5p-expander').click(expand).keypress(function (event) {
1448      if (event.keyCode === 32) {
1449        expand.apply(this);
1450        return false;
1451      }
1452    });
1453  }).on('dialog-closed', function () {
1454    H5P.trigger(instance, 'resize');
1455  });
1456
1457  dialog.open();
1458};
1459
1460/**
1461 * Show a toast message.
1462 *
1463 * The reference element could be dom elements the toast should be attached to,
1464 * or e.g. the document body for general toast messages.
1465 *
1466 * @param {DOM} element Reference element to show toast message for.
1467 * @param {string} message Message to show.
1468 * @param {object} [config] Configuration.
1469 * @param {string} [config.style=h5p-toast] Style name for the tooltip.
1470 * @param {number} [config.duration=3000] Toast message length in ms.
1471 * @param {object} [config.position] Relative positioning of the toast.
1472 * @param {string} [config.position.horizontal=centered] [before|left|centered|right|after].
1473 * @param {string} [config.position.vertical=below] [above|top|centered|bottom|below].
1474 * @param {number} [config.position.offsetHorizontal=0] Extra horizontal offset.
1475 * @param {number} [config.position.offsetVertical=0] Extra vetical offset.
1476 * @param {boolean} [config.position.noOverflowLeft=false] True to prevent overflow left.
1477 * @param {boolean} [config.position.noOverflowRight=false] True to prevent overflow right.
1478 * @param {boolean} [config.position.noOverflowTop=false] True to prevent overflow top.
1479 * @param {boolean} [config.position.noOverflowBottom=false] True to prevent overflow bottom.
1480 * @param {boolean} [config.position.noOverflowX=false] True to prevent overflow left and right.
1481 * @param {boolean} [config.position.noOverflowY=false] True to prevent overflow top and bottom.
1482 * @param {object} [config.position.overflowReference=document.body] DOM reference for overflow.
1483 */
1484H5P.attachToastTo = function (element, message, config) {
1485  if (element === undefined || message === undefined) {
1486    return;
1487  }
1488
1489  const eventPath = function (evt) {
1490    var path = (evt.composedPath && evt.composedPath()) || evt.path;
1491    var target = evt.target;
1492
1493    if (path != null) {
1494      // Safari doesn't include Window, but it should.
1495      return (path.indexOf(window) < 0) ? path.concat(window) : path;
1496    }
1497
1498    if (target === window) {
1499      return [window];
1500    }
1501
1502    function getParents(node, memo) {
1503      memo = memo || [];
1504      var parentNode = node.parentNode;
1505
1506      if (!parentNode) {
1507        return memo;
1508      }
1509      else {
1510        return getParents(parentNode, memo.concat(parentNode));
1511      }
1512    }
1513
1514    return [target].concat(getParents(target), window);
1515  };
1516
1517  /**
1518   * Handle click while toast is showing.
1519   */
1520  const clickHandler = function (event) {
1521    /*
1522     * A common use case will be to attach toasts to buttons that are clicked.
1523     * The click would remove the toast message instantly without this check.
1524     * Children of the clicked element are also ignored.
1525     */
1526    var path = eventPath(event);
1527    if (path.indexOf(element) !== -1) {
1528      return;
1529    }
1530    clearTimeout(timer);
1531    removeToast();
1532  };
1533
1534
1535
1536  /**
1537   * Remove the toast message.
1538   */
1539  const removeToast = function () {
1540    document.removeEventListener('click', clickHandler);
1541    if (toast.parentNode) {
1542      toast.parentNode.removeChild(toast);
1543    }
1544  };
1545
1546  /**
1547   * Get absolute coordinates for the toast.
1548   *
1549   * @param {DOM} element Reference element to show toast message for.
1550   * @param {DOM} toast Toast element.
1551   * @param {object} [position={}] Relative positioning of the toast message.
1552   * @param {string} [position.horizontal=centered] [before|left|centered|right|after].
1553   * @param {string} [position.vertical=below] [above|top|centered|bottom|below].
1554   * @param {number} [position.offsetHorizontal=0] Extra horizontal offset.
1555   * @param {number} [position.offsetVertical=0] Extra vetical offset.
1556   * @param {boolean} [position.noOverflowLeft=false] True to prevent overflow left.
1557   * @param {boolean} [position.noOverflowRight=false] True to prevent overflow right.
1558   * @param {boolean}
1558 [position.noOverflowTop=false] True to prevent overflow top.
1559   * @param {boolean} [position.noOverflowBottom=false] True to prevent overflow bottom.
1560   * @param {boolean} [position.noOverflowX=false] True to prevent overflow left and right.
1561   * @param {boolean} [position.noOverflowY=false] True to prevent overflow top and bottom.
1562   * @return {object}
1563   */
1564  const getToastCoordinates = function (element, toast, position) {
1565    position = position || {};
1566    position.offsetHorizontal = position.offsetHorizontal || 0;
1567    position.offsetVertical = position.offsetVertical || 0;
1568
1569    const toastRect = toast.getBoundingClientRect();
1570    const elementRect = element.getBoundingClientRect();
1571
1572    let left = 0;
1573    let top = 0;
1574
1575    // Compute horizontal position
1576    switch (position.horizontal) {
1577      case 'before':
1578        left = elementRect.left - toastRect.width - position.offsetHorizontal;
1579        break;
1580      case 'after':
1581        left = elementRect.left + elementRect.width + position.offsetHorizontal;
1582        break;
1583      case 'left':
1584        left = elementRect.left + position.offsetHorizontal;
1585        break;
1586      case 'right':
1587        left = elementRect.left + elementRect.width - toastRect.width - position.offsetHorizontal;
1588        break;
1589      case 'centered':
1590        left = elementRect.left + elementRect.width / 2 - toastRect.width / 2 + position.offsetHorizontal;
1591        break;
1592      default:
1593        left = elementRect.left + elementRect.width / 2 - toastRect.width / 2 + position.offsetHorizontal;
1594    }
1595
1596    // Compute vertical position
1597    switch (position.vertical) {
1598      case 'above':
1599        top = elementRect.top - toastRect.height - position.offsetVertical;
1600        break;
1601      case 'below':
1602        top = elementRect.top + elementRect.height + position.offsetVertical;
1603        break;
1604      case 'top':
1605        top = elementRect.top + position.offsetVertical;
1606        break;
1607      case 'bottom':
1608        top = elementRect.top + elementRect.height - toastRect.height - position.offsetVertical;
1609        break;
1610      case 'centered':
1611        top = elementRect.top + elementRect.height / 2 - toastRect.height / 2 + position.offsetVertical;
1612        break;
1613      default:
1614        top = elementRect.top + elementRect.height + position.offsetVertical;
1615    }
1616
1617    // Prevent overflow
1618    const overflowElement = document.body;
1619    const bounds = overflowElement.getBoundingClientRect();
1620    if ((position.noOverflowLeft || position.noOverflowX) && (left < bounds.x)) {
1621      left = bounds.x;
1622    }
1623    if ((position.noOverflowRight || position.noOverflowX) && ((left + toastRect.width) > (bounds.x + bounds.width))) {
1624      left = bounds.x + bounds.width - toastRect.width;
1625    }
1626    if ((position.noOverflowTop || position.noOverflowY) && (top < bounds.y)) {
1627      top = bounds.y;
1628    }
1629    if ((position.noOverflowBottom || position.noOverflowY) && ((top + toastRect.height) > (bounds.y + bounds.height))) {
1630      left = bounds.y + bounds.height - toastRect.height;
1631    }
1632
1633    return {left: left, top: top};
1634  };
1635
1636  // Sanitization
1637  config = config || {};
1638  config.style = config.style || 'h5p-toast';
1639  config.duration = config.duration || 3000;
1640
1641  // Build toast
1642  const toast = document.createElement('div');
1643  toast.setAttribute('id', config.style);
1644  toast.classList.add('h5p-toast-disabled');
1645  toast.classList.add(config.style);
1646
1647  const msg = document.createElement('span');
1648  msg.innerHTML = message;
1649  toast.appendChild(msg);
1650
1651  document.body.appendChild(toast);
1652
1653  // The message has to be set before getting the coordinates
1654  const coordinates = getToastCoordinates(element, toast, config.position);
1655  toast.style.left = Math.round(coordinates.left) + 'px';
1656  toast.style.top = Math.round(coordinates.top) + 'px';
1657
1658  toast.classList.remove('h5p-toast-disabled');
1659  const timer = setTimeout(removeToast, config.duration);
1660
1661  // The toast can also be removed by clicking somewhere
1662  document.addEventListener('click', clickHandler);
1663};
1664
1665/**
1666 * Copyrights for a H5P Content Library.
1667 *
1668 * @class
1669 */
1670H5P.ContentCopyrights = function () {
1671  var label;
1672  var media = [];
1673  var content = [];
1674
1675  /**
1676   * Set label.
1677   *
1678   * @param {string} newLabel
1679   */
1680  this.setLabel = function (newLabel) {
1681    label = newLabel;
1682  };
1683
1684  /**
1685   * Add sub content.
1686   *
1687   * @param {H5P.MediaCopyright} newMedia
1688   */
1689  this.addMedia = function (newMedia) {
1690    if (newMedia !== undefined) {
1691      media.push(newMedia);
1692    }
1693  };
1694
1695  /**
1696   * Add sub content in front.
1697   *
1698   * @param {H5P.MediaCopyright} newMedia
1699   */
1700  this.addMediaInFront = function (newMedia) {
1701    if (newMedia !== undefined) {
1702      media.unshift(newMedia);
1703    }
1704  };
1705
1706  /**
1707   * Add sub content.
1708   *
1709   * @param {H5P.ContentCopyrights} newContent
1710   */
1711  this.addContent = function (newContent) {
1712    if (newContent !== undefined) {
1713      content.push(newContent);
1714    }
1715  };
1716
1717  /**
1718   * Print content copyright.
1719   *
1720   * @returns {string} HTML.
1721   */
1722  this.toString = function () {
1723    var html = '';
1724
1725    // Add media rights
1726    for (var i = 0; i < media.length; i++) {
1727      html += media[i];
1728    }
1729
1730    // Add sub content rights
1731    for (i = 0; i < content.length; i++) {
1732      html += content[i];
1733    }
1734
1735
1736    if (html !== '') {
1737      // Add a label to this info
1738      if (label !== undefined) {
1739        html = '<h3>' + label + '</h3>' + html;
1740      }
1741
1742      // Add wrapper
1743      html = '<div class="h5p-content-copyrights">' + html + '</div>';
1744    }
1745
1746    return html;
1747  };
1748};
1749
1750/**
1751 * A ordered list of copyright fields for media.
1752 *
1753 * @class
1754 * @param {Object} copyright
1755 *   Copyright information fields.
1756 * @param {Object} [labels]
1757 *   Translation of labels.
1758 * @param {Array} [order]
1759 *   Order of the fields.
1760 * @param {Object} [extraFields]
1761 *   Add extra copyright fields.
1762 */
1763H5P.MediaCopyright = function (copyright, labels, order, extraFields) {
1764  var thumbnail;
1765  var list = new H5P.DefinitionList();
1766
1767  /**
1768   * Get translated label for field.
1769   *
1770   * @private
1771   * @param {string} fieldName
1772   * @returns {string}
1773   */
1774  var getLabel = function (fieldName) {
1775    if (labels === undefined || labels[fieldName] === undefined) {
1776      return H5P.t(fieldName);
1777    }
1778
1779    return labels[fieldName];
1780  };
1781
1782  /**
1783   * Get humanized value for the license field.
1784   *
1785   * @private
1786   * @param {string} license
1787   * @param {string}
1787 [version]
1788   * @returns {string}
1789   */
1790  var humanizeLicense = function (license, version) {
1791    var copyrightLicense = H5P.copyrightLicenses[license];
1792
1793    // Build license string
1794    var value = '';
1795    if (!(license === 'PD' && version)) {
1796      // Add license label
1797      value += (copyrightLicense.hasOwnProperty('label') ? copyrightLicense.label : copyrightLicense);
1798    }
1799
1800    // Check for version info
1801    var versionInfo;
1802    if (copyrightLicense.versions) {
1803      if (copyrightLicense.versions.default && (!version || !copyrightLicense.versions[version])) {
1804        version = copyrightLicense.versions.default;
1805      }
1806      if (version && copyrightLicense.versions[version]) {
1807        versionInfo = copyrightLicense.versions[version];
1808      }
1809    }
1810
1811    if (versionInfo) {
1812      // Add license version
1813      if (value) {
1814        value += ' ';
1815      }
1816      value += (versionInfo.hasOwnProperty('label') ? versionInfo.label : versionInfo);
1817    }
1818
1819    // Add link if specified
1820    var link;
1821    if (copyrightLicense.hasOwnProperty('link')) {
1822      link = copyrightLicense.link.replace(':version', copyrightLicense.linkVersions ? copyrightLicense.linkVersions[version] : version);
1823    }
1824    else if (versionInfo && copyrightLicense.hasOwnProperty('link')) {
1825      link = versionInfo.link;
1826    }
1827    if (link) {
1828      value = '<a href="' + link + '" target="_blank">' + value + '</a>';
1829    }
1830
1831    // Generate parenthesis
1832    var parenthesis = '';
1833    if (license !== 'PD' && license !== 'C') {
1834      parenthesis += license;
1835    }
1836    if (version && version !== 'CC0 1.0') {
1837      if (parenthesis && license !== 'GNU GPL') {
1838        parenthesis += ' ';
1839      }
1840      parenthesis += version;
1841    }
1842    if (parenthesis) {
1843      value += ' (' + parenthesis + ')';
1844    }
1845    if (license === 'C') {
1846      value += ' &copy;';
1847    }
1848
1849    return value;
1850  };
1851
1852  if (copyright !== undefined) {
1853    // Add the extra fields
1854    for (var field in extraFields) {
1855      if (extraFields.hasOwnProperty(field)) {
1856        copyright[field] = extraFields[field];
1857      }
1858    }
1859
1860    if (order === undefined) {
1861      // Set default order
1862      order = ['contentType', 'title', 'license', 'author', 'year', 'source', 'licenseExtras', 'changes'];
1863    }
1864
1865    for (var i = 0; i < order.length; i++) {
1866      var fieldName = order[i];
1867      if (copyright[fieldName] !== undefined && copyright[fieldName] !== '') {
1868        var humanValue = copyright[fieldName];
1869        if (fieldName === 'license') {
1870          humanValue = humanizeLicense(copyright.license, copyright.version);
1871        }
1872        if (fieldName === 'source') {
1873          humanValue = (humanValue) ? '<a href="' + humanValue + '" target="_blank">' + humanValue + '</a>' : undefined;
1874        }
1875        list.add(new H5P.Field(getLabel(fieldName), humanValue));
1876      }
1877    }
1878  }
1879
1880  /**
1881   * Set thumbnail.
1882   *
1883   * @param {H5P.Thumbnail} newThumbnail
1884   */
1885  this.setThumbnail = function (newThumbnail) {
1886    thumbnail = newThumbnail;
1887  };
1888
1889  /**
1890   * Checks if this copyright is undisclosed.
1891   * I.e. only has the license attribute set, and it's undisclosed.
1892   *
1893   * @returns {boolean}
1894   */
1895  this.undisclosed = function () {
1896    if (list.size() === 1) {
1897      var field = list.get(0);
1898      if (field.getLabel() === getLabel('license') && field.getValue() === humanizeLicense('U')) {
1899        return true;
1900      }
1901    }
1902    return false;
1903  };
1904
1905  /**
1906   * Print media copyright.
1907   *
1908   * @returns {string} HTML.
1909   */
1910  this.toString = function () {
1911    var html = '';
1912
1913    if (this.undisclosed()) {
1914      return html; // No need to print a copyright with a single undisclosed license.
1915    }
1916
1917    if (thumbnail !== undefined) {
1918      html += thumbnail;
1919    }
1920    html += list;
1921
1922    if (html !== '') {
1923      html = '<div class="h5p-media-copyright">' + html + '</div>';
1924    }
1925
1926    return html;
1927  };
1928};
1929
1930/**
1931 * A simple and elegant class for creating thumbnails of images.
1932 *
1933 * @class
1934 * @param {string} source
1935 * @param {number} width
1936 * @param {number} height
1937 * @param {string} alt
1938 *  alternative text for the thumbnail
1939 */
1940H5P.Thumbnail = function (source, width, height, alt) {
1941  var thumbWidth, thumbHeight = 100;
1942  if (width !== undefined) {
1943    thumbWidth = Math.round(thumbHeight * (width / height));
1944  }
1945
1946  /**
1947   * Print thumbnail.
1948   *
1949   * @returns {string} HTML.
1950   */
1951  this.toString = function () {
1952    return '<img src="' + source + '" alt="' + (alt ? alt : '') + '" class="h5p-thumbnail" height="' + thumbHeight + '"' + (thumbWidth === undefined ? '' : ' width="' + thumbWidth + '"') + '/>';
1953  };
1954};
1955
1956/**
1957 * Simple data structure class for storing a single field.
1958 *
1959 * @class
1960 * @param {string} label
1961 * @param {string} value
1962 */
1963H5P.Field = function (label, value) {
1964  /**
1965   * Public. Get field label.
1966   *
1967   * @returns {String}
1968   */
1969  this.getLabel = function () {
1970    return label;
1971  };
1972
1973  /**
1974   * Public. Get field value.
1975   *
1976   * @returns {String}
1977   */
1978  this.getValue = function () {
1979    return value;
1980  };
1981};
1982
1983/**
1984 * Simple class for creating a definition list.
1985 *
1986 * @class
1987 */
1988H5P.DefinitionList = function () {
1989  var fields = [];
1990
1991  /**
1992   * Add field to list.
1993   *
1994   * @param {H5P.Field} field
1995   */
1996  this.add = function (field) {
1997    fields.push(field);
1998  };
1999
2000  /**
2001   * Get Number of fields.
2002   *
2003   * @returns {number}
2004   */
2005  this.size = function () {
2006    return fields.length;
2007  };
2008
2009  /**
2010   * Get field at given index.
2011   *
2012   * @param {number} index
2013   * @returns {H5P.Field}
2014   */
2015  this.get = function (index) {
2016    return fields[index];
2017  };
2018
2019  /**
2020   * Print definition list.
2021   *
2022   * @returns {string} HTML.
2023   */
2024  this.toString = function () {
2025    var html = '';
2026    for (var i = 0; i < fields.length; i++) {
2027      var field = fields[i];
2028      html += '<dt>' + field.getLabel() + '</dt><dd>' + field.getValue() + '</dd>';
2029    }
2030    return (html === '' ? html : '<dl class="h5p-definition-list">' + html + '</dl>');
2031  };
2032};
2033
2034/**
2035 * THIS FUNCTION/CLASS IS DEPRECATED AND WILL BE REMOVED.
2036 *
2037 * Helper object for keeping coordinates in the same format all over.
2038 *
2039 * @deprecated
2040 *   Will be removed march 2016.
2041 * @class
2042 * @param {number} x
2043 * @param {number} y
2044 * @param {number} w
2045 * @param {number} h
2046 */
2047H5P.Coords = function (x, y, w, h) {
2048  if ( !(this instanceof H5P.Coords) )
2049    return new H5P.Coords(x, y, w, h);
2050
2051  /** @member {number} */
2052  this.x = 0;
2053  /** @member {number} */
2054  this.y = 0;
2055  /** @member {number} */
2056  this.w = 1;
2057  /** @member {number} */
2058  this.h = 1;
2059
2060  if (typeof(x) === 'object') {
2061    this.x = x.x;
2062    this.y = x.y;
2063    this.w = x.w;
2064    this.h = x.h;
2065  }
2066  else {
2067    if (x !== undefined) {
2068      this.x = x;
2069    }
2070    if (y !== undefined) {
2071      this.y = y;
2072    }
2073    if (w !== undefined) {
2074      this.w = w;
2075    }
2076    if (h !== undefined) {
2077      this.h = h;
2078    }
2079  }
2080  return this;
2081};
2082
2083/**
2084 * Parse library string into values.
2085 *
2086 * @param {string} library
2087 *   library in the format "machineName majorVersion.minorVersion"
2088 * @returns {Object}
2089 *   library as an object with machineName, majorVersion and minorVersion properties
2090 *   return false if the library parameter is invalid
2091 */
2092H5P.libraryFromString = function (library) {
2093  var regExp = /(.+)\s(\d+)\.(\d+)$/g;
2094  var res = regExp.exec(library);
2095  if (res !== null) {
2096    return {
2097      'machineName': res[1],
2098      'majorVersion': parseInt(res[2]),
2099      'minorVersion': parseInt(res[3])
2100    };
2101  }
2102  else {
2103    return false;
2104  }
2105};
2106
2107/**
2108 * Get the path to the library
2109 *
2110 * @param {string} library
2111 *   The library identifier in the format "machineName-majorVersion.minorVersion".
2112 * @returns {string}
2113 *   The full path to the library.
2114 */
2115H5P.getLibraryPath = function (library) {
2116  if (H5PIntegration &&
2117      H5PIntegration.libraryDirectories &&
2118      library in H5PIntegration.libraryDirectories) {
2119    // Use H5PIntegration.libraryDirectories if it exists for this library
2120    library = H5PIntegration.libraryDirectories[library];
2121  }
2122
2123  if (H5PIntegration.urlLibraries !== undefined) {
2124    // This is an override for those implementations that has a different libraries URL, e.g. Moodle
2125    return H5PIntegration.urlLibraries + '/' + library;
2126  }
2127  else {
2128    return H5PIntegration.url + '/libraries/' + library;
2129  }
2130};
2131
2132/**
2133 * Recursivly clone the given object.
2134 *
2135 * @param {Object|Array} object
2136 *   Object to clone.
2137 * @param {boolean} [recursive]
2138 * @returns {Object|Array}
2139 *   A clone of object.
2140 */
2141H5P.cloneObject = function (object, recursive) {
2142  // TODO: Consider if this needs to be in core. Doesn't $.extend do the same?
2143  var clone = object instanceof Array ? [] : {};
2144
2145  for (var i in object) {
2146    if (object.hasOwnProperty(i)) {
2147      if (recursive !== undefined && recursive && typeof object[i] === 'object') {
2148        clone[i] = H5P.cloneObject(object[i], recursive);
2149      }
2150      else {
2151        clone[i] = object[i];
2152      }
2153    }
2154  }
2155
2156  return clone;
2157};
2158
2159/**
2160 * Remove all empty spaces before and after the value.
2161 *
2162 * @param {string} value
2163 * @returns {string}
2164 */
2165H5P.trim = function (value) {
2166  return value.replace(/^\s+|\s+$/g, '');
2167
2168  // TODO: Only include this or String.trim(). What is best?
2169  // I'm leaning towards implementing the missing ones: http://kangax.github.io/compat-table/es5/
2170  // So should we make this function deprecated?
2171};
2172
2173/**
2174 * Recursive function that detects deep empty structures.
2175 *
2176 * @param {*} value
2177 * @returns {bool}
2178 */
2179H5P.isEmpty = value => {
2180  if (!value && value !== 0 && value !== false) {
2181    return true; // undefined, null, NaN and empty strings.
2182  }
2183  else if (Array.isArray(value)) {
2184    for (let i = 0; i < value.length; i++) {
2185      if (!H5P.isEmpty(value[i])) {
2186        return false; // Array contains a non-empty value
2187      }
2188    }
2189    return true; // Empty array
2190  }
2191  else if (typeof value === 'object') {
2192    for (let prop in value) {
2193      if (value.hasOwnProperty(prop) && !H5P.isEmpty(value[prop])) {
2194        return false; // Object contains a non-empty value
2195      }
2196    }
2197    return true; // Empty object
2198  }
2199  return false;
2200};
2201
2202/**
2203 * Check if JavaScript path/key is loaded.
2204 *
2205 * @param {string} path
2206 * @returns {boolean}
2207 */
2208H5P.jsLoaded = function (path) {
2209  H5PIntegration.loadedJs = H5PIntegration.loadedJs || [];
2210  return H5P.jQuery.inArray(path, H5PIntegration.loadedJs) !== -1;
2211};
2212
2213/**
2214 * Check if styles path/key is loaded.
2215 *
2216 * @param {string} path
2217 * @returns {boolean}
2218 */
2219H5P.cssLoaded = function (path) {
2220  H5PIntegration.loadedCss = H5PIntegration.loadedCss || [];
2221  return H5P.jQuery.inArray(path, H5PIntegration.loadedCss) !== -1;
2222};
2223
2224/**
2225 * Shuffle an array in place.
2226 *
2227 * @param {Array} array
2228 *   Array to shuffle
2229 * @returns {Array}
2230 *   The passed array is returned for chaining.
2231 */
2232H5P.shuffleArray = function (array) {
2233  // TODO: Consider if this should be a part of core. I'm guessing very few libraries are going to use it.
2234  if (!(array instanceof Array)) {
2235    return;
2236  }
2237
2238  var i = array.length, j, tempi, tempj;
2239  if ( i === 0 ) return false;
2240  while ( --i ) {
2241    j       = Math.floor( Math.random() * ( i + 1 ) );
2242    tempi   = array[i];
2243    tempj   = array[j];
2244    array[i] = tempj;
2245    array[j] = tempi;
2246  }
2247  return array;
2248};
2249
2250/**
2251 * Post finished results for user.
2252 *
2253 * @deprecated
2254 *   Do not use this function directly, trigger the finish event instead.
2255 *   Will be removed march 2016
2256 * @param {number} contentId
2257 *   Identifies the content
2258 * @param {number} score
2259 *   Achieved score/points
2260 * @param {number} maxScore
2261 *   The maximum score/points that can be achieved
2262 * @param {number} [time]
2263 *   Reported time consumption/usage
2264 */
2265H5P.setFinished = function (contentId, score, maxScore, time) {
2266  var validScore = typeof score === 'number' || score instanceof Number;
2267  if (validScore && H5PIntegration.postUserStatistics === true) {
2268    /**
2269     * Return unix timestamp for the given JS Date.
2270     *
2271     * @private
2272     * @param {Date} date
2273     * @returns {Number}
2274     */
2275    var toUnix = function (date) {
2276      return Math.round(date.getTime() / 1000);
2277    };
2278
2279    // Post the results
2280    const data = {
2281      contentId: contentId,
2282      score: score,
2283      maxScore: maxScore,
2284      opened: toUnix(H5P.opened[contentId]),
2285      finished: toUnix(new Date()),
2286      time: time
2287    };
2288    H5P.jQuery.post(H5PIntegration.ajax.setFinished, data)
2289      .fail(function () {
2290        H5P.offlineRequestQueue.add(H5PIntegration.ajax.setFinished, data);
2291      });
2292  }
2293};
2294
2295// Add indexOf to browsers that lack them. (IEs)
2296if (!Array.prototype.indexOf) {
2297  Array.prototype.indexOf = function (needle) {
2298    for (var i = 0; i < this.length; i++) {
2299      if (this[i] === needle) {
2300        return i;
2301      }
2302    }
2303    return -1;
2304  };
2305}
2306
2307// Need to define trim() since this is not available on older IEs,
2308// and trim is used in several libs
2309if (String.prototype.trim === undefined) {
2310  String.prototype.trim = function () {
2311    return H5P.trim(this);
2312  };
2313}
2314
2315/**
2316 * Trigger an event on an instance
2317 *
2318 * Helper function that triggers an event if the instance supports event handling
2319 *
2320 * @param {Object} instance
2321 *   Instance of H5P content
2322 * @param {string} eventType
2323 *   Type of event to trigger
2324 * @param {*} data
2325 * @param {Object} extras
2326 */
2327H5P.trigger = function (instance, eventType, data, extras) {
2328  // Try new event system first
2329  if (instance.trigger !== undefined) {
2330    instance.trigger(eventType, data, extras);
2331  }
2332  // Try deprecated event system
2333  else if (instance.$ !== undefined && instance.$.trigger !== undefined) {
2334    instance.$.trigger(eventType);
2335  }
2336};
2337
2338/**
2339 * Register an event handler
2340 *
2341 * Helper function that registers an event handler for an event type if
2342 * the instance supports event handling
2343 *
2344 * @param {Object} instance
2345 *   Instance of H5P content
2346 * @param {string} eventType
2347 *   Type of event to listen for
2348 * @param {H5P.EventCallback} handler
2349 *   Callback that gets triggered for events of the specified type
2350 */
2351H5P.on = function (instance, eventType, handler) {
2352  // Try new event system first
2353  if (instance.on !== undefined) {
2354    instance.on(eventType, handler);
2355  }
2356  // Try deprecated event system
2357  else if (instance.$ !== undefined && instance.$.on !== undefined) {
2358    instance.$.on(eventType, handler);
2359  }
2360};
2361
2362/**
2363 * Generate random UUID
2364 *
2365 * @returns {string} UUID
2366 */
2367H5P.createUUID = function () {
2368  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (char) {
2369    var random = Math.random()*16|0, newChar = char === 'x' ? random : (random&0x3|0x8);
2370    return newChar.toString(16);
2371  });
2372};
2373
2374/**
2375 * Create title
2376 *
2377 * @param {string} rawTitle
2378 * @param {number} maxLength
2379 * @returns {string}
2380 */
2381H5P.createTitle = function (rawTitle, maxLength) {
2382  if (!rawTitle) {
2383    return '';
2384  }
2385  if (maxLength === undefined) {
2386    maxLength = 60;
2387  }
2388  var title = H5P.jQuery('<div></div>')
2389    .text(
2390      // Strip tags
2391      rawTitle.replace(/(<([^>]+)>)/ig,"")
2392    // Escape
2393    ).text();
2394  if (title.length > maxLength) {
2395    title = title.substr(0, maxLength - 3) + '...';
2396  }
2397  return title;
2398};
2399
2400// Wrap in privates
2401(function ($) {
2402
2403  /**
2404   * Creates ajax requests for inserting, updateing and deleteing
2405   * content user data.
2406   *
2407   * @private
2408   * @param {number} contentId What content to store the data for.
2409   * @param {string} dataType Identifies the set of data for this content.
2410   * @param {string} subContentId Identifies sub content
2411   * @param {function} [done] Callback when ajax is done.
2412   * @param {object} [data] To be stored for future use.
2413   * @param {boolean} [preload=false] Data is loaded when content is loaded.
2414   * @param {boolean}
2414 [invalidate=false] Data is invalidated when content changes.
2415   * @param {boolean} [async=true]
2416   */
2417  function contentUserDataAjax(contentId, dataType, subContentId, done, data, preload, invalidate, async) {
2418    if (H5PIntegration.user === undefined) {
2419      // Not logged in, no use in saving.
2420      done('Not signed in.');
2421      return;
2422    }
2423
2424    var options = {
2425      url: H5PIntegration.ajax.contentUserData.replace(':contentId', contentId).replace(':dataType', dataType).replace(':subContentId', subContentId ? subContentId : 0),
2426      dataType: 'json',
2427      async: async === undefined ? true : async
2428    };
2429    if (data !== undefined) {
2430      options.type = 'POST';
2431      options.data = {
2432        data: (data === null ? 0 : data),
2433        preload: (preload ? 1 : 0),
2434        invalidate: (invalidate ? 1 : 0)
2435      };
2436    }
2437    else {
2438      options.type = 'GET';
2439    }
2440    if (done !== undefined) {
2441      options.error = function (xhr, error) {
2442        done(error);
2443      };
2444      options.success = function (response) {
2445        if (!response.success) {
2446          done(response.message);
2447          return;
2448        }
2449
2450        if (response.data === false || response.data === undefined) {
2451          done();
2452          return;
2453        }
2454
2455        done(undefined, response.data);
2456      };
2457    }
2458
2459    $.ajax(options);
2460  }
2461
2462  /**
2463   * Get user data for given content.
2464   *
2465   * @param {number} contentId
2466   *   What content to get data for.
2467   * @param {string} dataId
2468   *   Identifies the set of data for this content.
2469   * @param {function} done
2470   *   Callback with error and data parameters.
2471   * @param {string} [subContentId]
2472   *   Identifies which data belongs to sub content.
2473   */
2474  H5P.getUserData = function (contentId, dataId, done, subContentId) {
2475    if (!subContentId) {
2476      subContentId = 0; // Default
2477    }
2478
2479    H5PIntegration.contents = H5PIntegration.contents || {};
2480    var content = H5PIntegration.contents['cid-' + contentId] || {};
2481    var preloadedData = content.contentUserData;
2482    if (preloadedData && preloadedData[subContentId] && preloadedData[subContentId][dataId] !== undefined) {
2483      if (preloadedData[subContentId][dataId] === 'RESET') {
2484        done(undefined, null);
2485        return;
2486      }
2487      try {
2488        done(undefined, JSON.parse(preloadedData[subContentId][dataId]));
2489      }
2490      catch (err) {
2491        done(err);
2492      }
2493    }
2494    else {
2495      contentUserDataAjax(contentId, dataId, subContentId, function (err, data) {
2496        if (err || data === undefined) {
2497          done(err, data);
2498          return; // Error or no data
2499        }
2500
2501        // Cache in preloaded
2502        if (content.contentUserData === undefined) {
2503          content.contentUserData = preloadedData = {};
2504        }
2505        if (preloadedData[subContentId] === undefined) {
2506          preloadedData[subContentId] = {};
2507        }
2508        preloadedData[subContentId][dataId] = data;
2509
2510        // Done. Try to decode JSON
2511        try {
2512          done(undefined, JSON.parse(data));
2513        }
2514        catch (e) {
2515          done(e);
2516        }
2517      });
2518    }
2519  };
2520
2521  /**
2522   * Async error handling.
2523   *
2524   * @callback H5P.ErrorCallback
2525   * @param {*} error
2526   */
2527
2528  /**
2529   * Set user data for given content.
2530   *
2531   * @param {number} contentId
2532   *   What content to get data for.
2533   * @param {string} dataId
2534   *   Identifies the set of data for this content.
2535   * @param {Object} data
2536   *   The data that is to be stored.
2537   * @param {Object} [extras]
2538   *   Extra properties
2539   * @param {string} [extras.subContentId]
2540   *   Identifies which data belongs to sub content.
2541   * @param {boolean} [extras.preloaded=true]
2542   *   If the data should be loaded when content is loaded.
2543   * @param {boolean} [extras.deleteOnChange=false]
2544   *   If the data should be invalidated when the content changes.
2545   * @param {H5P.ErrorCallback} [extras.errorCallback]
2546   *   Callback with error as parameters.
2547   * @param {boolean} [extras.async=true]
2548   */
2549  H5P.setUserData = function (contentId, dataId, data, extras) {
2550    var options = H5P.jQuery.extend(true, {}, {
2551      subContentId: 0,
2552      preloaded: true,
2553      deleteOnChange: false,
2554      async: true
2555    }, extras);
2556
2557    try {
2558      data = JSON.stringify(data);
2559    }
2560    catch (err) {
2561      if (options.errorCallback) {
2562        options.errorCallback(err);
2563      }
2564      return; // Failed to serialize.
2565    }
2566
2567    var content = H5PIntegration.contents['cid-' + contentId];
2568    if (content === undefined) {
2569      content = H5PIntegration.contents['cid-' + contentId] = {};
2570    }
2571    if (!content.contentUserData) {
2572      content.contentUserData = {};
2573    }
2574    var preloadedData = content.contentUserData;
2575    if (preloadedData[options.subContentId] === undefined) {
2576      preloadedData[options.subContentId] = {};
2577    }
2578    if (data === preloadedData[options.subContentId][dataId]) {
2579      return; // No need to save this twice.
2580    }
2581
2582    preloadedData[options.subContentId][dataId] = data;
2583    contentUserDataAjax(contentId, dataId, options.subContentId, function (error) {
2584      if (options.errorCallback && error) {
2585        options.errorCallback(error);
2586      }
2587    }, data, options.preloaded, options.deleteOnChange, options.async);
2588  };
2589
2590  /**
2591   * Delete user data for given content.
2592   *
2593   * @param {number} contentId
2594   *   What content to remove data for.
2595   * @param {string} dataId
2596   *   Identifies the set of data for this content.
2597   * @param {string} [subContentId]
2598   *   Identifies which data belongs to sub content.
2599   */
2600  H5P.deleteUserData = function (contentId, dataId, subContentId) {
2601    if (!subContentId) {
2602      subContentId = 0; // Default
2603    }
2604
2605    // Remove from preloaded/cache
2606    var preloadedData = H5PIntegration.contents['cid-' + contentId].contentUserData;
2607    if (preloadedData && preloadedData[subContentId] && preloadedData[subContentId][dataId]) {
2608      delete preloadedData[subContentId][dataId];
2609    }
2610
2611    contentUserDataAjax(contentId, dataId, subContentId, undefined, null);
2612  };
2613
2614  /**
2615   * Function for getting content for a certain ID
2616   *
2617   * @param {number} contentId
2618   * @return {Object}
2619   */
2620  H5P.getContentForInstance = function (contentId) {
2621    var key = 'cid-' + contentId;
2622    var exists = H5PIntegration && H5PIntegration.contents &&
2623                 H5PIntegration.contents[key];
2624
2625    return exists ? H5PIntegration.contents[key] : undefined;
2626  };
2627
2628  /**
2629   * Prepares the content parameters for storing in the clipboard.
2630   *
2631   * @class
2632   * @param {Object} parameters The parameters for the content to store
2633   * @param {string} [genericProperty] If only part of the parameters are generic, which part
2634   * @param {string} [specificKey] If the parameters are specific, what content type does it fit
2635   * @returns {Object} Ready for the clipboard
2636   */
2637  H5P.ClipboardItem = function (parameters, genericProperty, specificKey) {
2638    var self = this;
2639
2640    /**
2641     * Set relative dimensions when params contains a file with a width and a height.
2642     * Very useful to be compatible with wysiwyg editors.
2643     *
2644     * @private
2645     */
2646    var setDimensionsFromFile = function () {
2647      if (!self.generic) {
2648        return;
2649      }
2650      var params = self.specific[self.generic];
2651      if (!params.params.file || !params.params.file.width || !params.params.file.height) {
2652        return;
2653      }
2654
2655      self.width = 20; // %
2656      self.height = (params.params.file.height / params.params.file.width) * self.width;
2657    };
2658
2659    if (!genericProperty) {
2660      genericProperty = 'action';
2661      parameters = {
2662        action: parameters
2663      };
2664    }
2665
2666    self.specific = parameters;
2667
2668    if (genericProperty && parameters[genericProperty]) {
2669      self.generic = genericProperty;
2670    }
2671    if (specificKey) {
2672      self.from = specificKey;
2673    }
2674
2675    if (window.H5PEditor && H5PEditor.contentId) {
2676      self.contentId = H5PEditor.contentId;
2677    }
2678
2679    if (!self.specific.width && !self.specific.height) {
2680      setDimensionsFromFile();
2681    }
2682  };
2683
2684  /**
2685   * Store item in the H5P Clipboard.
2686   *
2687   * @param {H5P.ClipboardItem|*} clipboardItem
2688   */
2689  H5P.clipboardify = function (clipboardItem) {
2690    if (!(clipboardItem instanceof H5P.ClipboardItem)) {
2691      clipboardItem = new H5P.ClipboardItem(clipboardItem);
2692    }
2693    H5P.setClipboard(clipboardItem);
2694  };
2695
2696  /**
2697   * Retrieve parsed clipboard data.
2698   *
2699   * @return {Object}
2700   */
2701  H5P.getClipboard = function () {
2702    return parseClipboard();
2703  };
2704
2705  /**
2706   * Set item in the H5P Clipboard.
2707   *
2708   * @param {H5P.ClipboardItem|object} clipboardItem - Data to be set.
2709   */
2710  H5P.setClipboard = function (clipboardItem) {
2711    localStorage.setItem('h5pClipboard', JSON.stringify(clipboardItem));
2712
2713    // Trigger an event so all 'Paste' buttons may be enabled.
2714    H5P.externalDispatcher.trigger('datainclipboard', {reset: false});
2715  };
2716
2717  /**
2718   * Get config for a library
2719   *
2720   * @param string machineName
2721   * @return Object
2722   */
2723  H5P.getLibraryConfig = function (machineName) {
2724    var hasConfig = H5PIntegration.libraryConfig && H5PIntegration.libraryConfig[machineName];
2725    return hasConfig ? H5PIntegration.libraryConfig[machineName] : {};
2726  };
2727
2728  /**
2729   * Get item from the H5P Clipboard.
2730   *
2731   * @private
2732   * @return {Object}
2733   */
2734  var parseClipboard = function () {
2735    var clipboardData = localStorage.getItem('h5pClipboard');
2736    if (!clipboardData) {
2737      return;
2738    }
2739
2740    // Try to parse clipboard dat
2741    try {
2742      clipboardData = JSON.parse(clipboardData);
2743    }
2744    catch (err) {
2745      console.error('Unable to parse JSON from clipboard.', err);
2746      return;
2747    }
2748
2749    // Update file URLs and reset content Ids
2750    recursiveUpdate(clipboardData.specific, function (path) {
2751      var isTmpFile = (path.substr(-4, 4) === '#tmp');
2752      if (!isTmpFile && clipboardData.contentId && !path.match(/^https?:\/\//i)) {
2753        // Comes from existing content
2754
2755        let prefix;
2756        if (H5PEditor.contentId) {
2757          // .. to existing content
2758          prefix = '../' + clipboardData.contentId + '/';
2759        }
2760        else {
2761          // .. to new content
2762          prefix = (H5PEditor.contentRelUrl ? H5PEditor.contentRelUrl : '../content/') + clipboardData.contentId + '/';
2763        }
2764        return path.substr(0, prefix.length) === prefix ? path : prefix + path;
2765      }
2766
2767      return path; // Will automatically be looked for in tmp folder
2768    });
2769
2770
2771    if (clipboardData.generic) {
2772      // Use reference instead of key
2773      clipboardData.generic = clipboardData.specific[clipboardData.generic];
2774    }
2775
2776    return clipboardData;
2777  };
2778
2779  /**
2780   * Update file URLs and reset content IDs.
2781   * Useful when copying content.
2782   *
2783   * @private
2784   * @param {object} params Reference
2785   * @param {function} handler Modifies the path to work when pasted
2786   */
2787  var recursiveUpdate = function (params, handler) {
2788    for (var prop in params) {
2789      if (params.hasOwnProperty(prop) && params[prop] instanceof Object) {
2790        var obj = params[prop];
2791        if (obj.path !== undefined && obj.mime !== undefined) {
2792          obj.path = handler(obj.path);
2793        }
2794        else {
2795          if (obj.library !== undefined && obj.subContentId !== undefined) {
2796            // Avoid multiple content with same ID
2797            delete obj.subContentId;
2798          }
2799          recursiveUpdate(obj, handler);
2800        }
2801      }
2802    }
2803  };
2804
2805  // Init H5P when page is fully loadded
2806  $(document).ready(function () {
2807
2808    window.addEventListener('storage', function (event) {
2809      // Pick up clipboard changes from other tabs
2810      if (event.key === 'h5pClipboard') {
2811        // Trigger an event so all 'Paste' buttons may be enabled.
2812        H5P.externalDispatcher.trigger('datainclipboard', {reset: event.newValue === null});
2813      }
2814    });
2815
2816    var ccVersions = {
2817      'default': '4.0',
2818      '4.0': H5P.t('licenseCC40'),
2819      '3.0': H5P.t('licenseCC30'),
2820      '2.5': H5P.t('licenseCC25'),
2821      '2.0': H5P.t('licenseCC20'),
2822      '1.0': H5P.t('licenseCC10'),
2823    };
2824
2825    /**
2826     * Maps copyright license codes to their human readable counterpart.
2827     *
2828     * @type {Object}
2829     */
2830    H5P.copyrightLicenses = {
2831      'U': H5P.t('licenseU'),
2832      'CC BY': {
2833        label: H5P.t('licenseCCBY'),
2834        link: 'http://creativecommons.org/licenses/by/:version',
2835        versions: ccVersions
2836      },
2837      'CC BY-SA': {
2838        label: H5P.t('licenseCCBYSA'),
2839        link: 'http://creativecommons.org/licenses/by-sa/:version',
2840        versions: ccVersions
2841      },
2842      'CC BY-ND': {
2843        label: H5P.t('licenseCCBYND'),
2844        link: 'http://creativecommons.org/licenses/by-nd/:version',
2845        versions: ccVersions
2846      },
2847      'CC BY-NC': {
2848        label: H5P.t('licenseCCBYNC'),
2849        link: 'http://creativecommons.org/licenses/by-nc/:version',
2850        versions: ccVersions
2851      },
2852      'CC BY-NC-SA': {
2853        label: H5P.t('licenseCCBYNCSA'),
2854        link: 'http://creativecommons.org/licenses/by-nc-sa/:version',
2855        versions: ccVersions
2856      },
2857      'CC BY-NC-ND': {
2858        label: H5P.t('licenseCCBYNCND'),
2859        link: 'http://creativecommons.org/licenses/by-nc-nd/:version',
2860        versions: ccVersions
2861      },
2862      'CC0 1.0': {
2863        label: H5P.t('licenseCC010'),
2864        link: 'https://creativecommons.org/publicdomain/zero/1.0/'
2865      },
2866      'GNU GPL': {
2867        label: H5P.t('licenseGPL'),
2868        link: 'http://www.gnu.org/licenses/gpl-:version-standalone.html',
2869        linkVersions: {
2870          'v3': '3.0',
2871          'v2': '2.0',
2872          'v1': '1.0'
2873        },
2874        versions: {
2875          'default': 'v3',
2876          'v3': H5P.t('licenseV3'),
2877          'v2': H5P.t('licenseV2'),
2878          'v1': H5P.t('licenseV1')
2879        }
2880      },
2881      'PD': {
2882        label: H5P.t('licensePD'),
2883        versions: {
2884          'CC0 1.0': {
2885            label: H5P.t('licenseCC010'),
2886            link: 'https://creativecommons.org/publicdomain/zero/1.0/'
2887          },
2888          'CC PDM': {
2889            label: H5P.t('licensePDM'),
2890            link: 'https://creativecommons.org/publicdomain/mark/1.0/'
2891          }
2892        }
2893      },
2894      'ODC PDDL': '<a href="http://opendatacommons.org/licenses/pddl/1.0/" target="_blank">Public Domain Dedication and Licence</a>',
2895      'CC PDM': {
2896        label: H5P.t('licensePDM'),
2897        link: 'https://creativecommons.org/publicdomain/mark/1.0/'
2898      },
2899      'C': H5P.t('licenseC'),
2900    };
2901
2902    /**
2903     * Indicates if H5P is embedded on an external page using iframe.
2904     * @member {boolean} H5P.externalEmbed
2905     */
2906
2907    // Relay events to top window. This must be done before H5P.init
2908    // since events may be fired on initialization.
2909    if (H5P.isFramed && H5P.externalEmbed === false) {
2910      H5P.externalDispatcher.on('*', function (event) {
2911        window.parent.H5P.externalDispatcher.trigger.call(this, event);
2912      });
2913    }
2914
2915    /**
2916     * Prevent H5P Core from initializing. Must be overriden before document ready.
2917     * @member {boolean} H5P.preventInit
2918     */
2919    if (!H5P.preventInit) {
2920      // Note that this start script has to be an external resource for it to
2921      // load in correct order in IE9.
2922      H5P.init(document.body);
2923    }
2924
2925    if (H5PIntegration.saveFreq !== false) {
2926      // When was the last state stored
2927      var lastStoredOn = 0;
2928      // Store the current state of the H5P when leaving the page.
2929      var storeCurrentState = function () {
2930        // Make sure at least 250 ms has passed since last save
2931        var currentTime = new Date().getTime();
2932        if (currentTime - lastStoredOn > 250) {
2933          lastStoredOn = currentTime;
2934          for (var i = 0; i < H5P.instances.length; i++) {
2935            var instance = H5P.instances[i];
2936            if (instance.getCurrentState instanceof Function ||
2937                typeof instance.getCurrentState === 'function') {
2938              var state = instance.getCurrentState();
2939              if (state !== undefined) {
2940                // Async is not used to prevent the request from being cancelled.
2941                H5P.setUserData(instance.contentId, 'state', state, {deleteOnChange: true, async: false});
2942              }
2943            }
2944          }
2945        }
2946      };
2947      // iPad does not support beforeunload, therefore using unload
2948      H5P.$window.one('beforeunload unload', function () {
2949        // Only want to do this once
2950        H5P.$window.off('pagehide beforeunload unload');
2951        storeCurrentState();
2952      });
2953      // pagehide is used on iPad when tabs are switched
2954      H5P.$window.on('pagehide', storeCurrentState);
2955    }
2956  });
2957
2958})(H5P.jQuery);
2959;
2960var H5P = window.H5P = window.H5P || {};
2961
2962/**
2963 * The Event class for the EventDispatcher.
2964 *
2965 * @class
2966 * @param {string} type
2967 * @param {*} data
2968 * @param {Object} [extras]
2969 * @param {boolean} [extras.bubbles]
2970 * @param {boolean} [extras.external]
2971 */
2972H5P.Event = function (type, data, extras) {
2973  this.type = type;
2974  this.data = data;
2975  var bubbles = false;
2976
2977  // Is this an external event?
2978  var external = false;
2979
2980  // Is this event scheduled to be sent externally?
2981  var scheduledForExternal = false;
2982
2983  if (extras === undefined) {
2984    extras = {};
2985  }
2986  if (extras.bubbles === true) {
2987    bubbles = true;
2988  }
2989  if (extras.external === true) {
2990    external = true;
2991  }
2992
2993  /**
2994   * Prevent this event from bubbling up to parent
2995   */
2996  this.preventBubbling = function () {
2997    bubbles = false;
2998  };
2999
3000  /**
3001   * Get bubbling status
3002   *
3003   * @returns {boolean}
3004   *   true if bubbling false otherwise
3005   */
3006  this.getBubbles = function () {
3007    return bubbles;
3008  };
3009
3010  /**
3011   * Try to schedule an event for externalDispatcher
3012   *
3013   * @returns {boolean}
3014   *   true if external and not already scheduled, otherwise false
3015   */
3016  this.scheduleForExternal = function () {
3017    if (external && !scheduledForExternal) {
3018      scheduledForExternal = true;
3019      return true;
3020    }
3021    return false;
3022  };
3023};
3024
3025/**
3026 * Callback type for event listeners.
3027 *
3028 * @callback H5P.EventCallback
3029 * @param {H5P.Event} event
3030 */
3031
3032H5P.EventDispatcher = (function () {
3033
3034  /**
3035   * The base of the event system.
3036   * Inherit this class if you want your H5P to dispatch events.
3037   *
3038   * @class
3039   * @memberof H5P
3040   */
3041  function EventDispatcher() {
3042    var self = this;
3043
3044    /**
3045     * Keep track of listeners for each event.
3046     *
3047     * @private
3048     * @type {Object}
3049     */
3050    var triggers = {};
3051
3052    /**
3053     * Add new event listener.
3054     *
3055     * @throws {TypeError}
3056     *   listener must be a function
3057     * @param {string} type
3058     *   Event type
3059     * @param {H5P.EventCallback} listener
3060     *   Event listener
3061     * @param {Object} [thisArg]
3062     *   Optionally specify the this value when calling listener.
3063     */
3064    this.on = function (type, listener, thisArg) {
3065      if (typeof listener !== 'function') {
3066        throw TypeError('listener must be a function');
3067      }
3068
3069      // Trigger event before adding to avoid recursion
3070      self.trigger('newListener', {'type': type, 'listener': listener});
3071
3072      var trigger = {'listener': listener, 'thisArg': thisArg};
3073      if (!triggers[type]) {
3074        // First
3075        triggers[type] = [trigger];
3076      }
3077      else {
3078        // Append
3079        triggers[type].push(trigger);
3080      }
3081    };
3082
3083    /**
3084     * Add new event listener that will be fired only once.
3085     *
3086     * @throws {TypeError}
3087     *   listener must be a function
3088     * @param {string} type
3089     *   Event type
3090     * @param {H5P.EventCallback} listener
3091     *   Event listener
3092     * @param {Object} thisArg
3093     *   Optionally specify the this value when calling listener.
3094     */
3095    this.once = function (type, listener, thisArg) {
3096      if (!(listener instanceof Function)) {
3097        throw TypeError('listener must be a function');
3098      }
3099
3100      var once = function (event) {
3101        self.off(event.type, once);
3102        listener.call(this, event);
3103      };
3104
3105      self.on(type, once, thisArg);
3106    };
3107
3108    /**
3109     * Remove event listener.
3110     * If no listener is specified, all listeners will be removed.
3111     *
3112     * @throws {TypeError}
3113     *   listener must be a function
3114     * @param {string} type
3115     *   Event type
3116     * @param {H5P.EventCallback} listener
3117     *   Event listener
3118     */
3119    this.off = function (type, listener) {
3120      if (listener !== undefined && !(listener instanceof Function)) {
3121        throw TypeError('listener must be a function');
3122      }
3123
3124      if (triggers[type] === undefined) {
3125        return;
3126      }
3127
3128      if (listener === undefined) {
3129        // Remove all listeners
3130        delete triggers[type];
3131        self.trigger('removeListener', type);
3132        return;
3133      }
3134
3135      // Find specific listener
3136      for (var i = 0; i < triggers[type].length; i++) {
3137        if (triggers[type][i].listener === listener) {
3138          triggers[type].splice(i, 1);
3139          self.trigger('removeListener', type, {'listener': listener});
3140          break;
3141        }
3142      }
3143
3144      // Clean up empty arrays
3145      if (!triggers[type].length) {
3146        delete triggers[type];
3147      }
3148    };
3149
3150    /**
3151     * Try to call all event listeners for the given event type.
3152     *
3153     * @private
3154     * @param {string} Event type
3155     */
3156    var call = function (type, event) {
3157      if (triggers[type] === undefined) {
3158        return;
3159      }
3160
3161      // Clone array (prevents triggers from being modified during the event)
3162      var handlers = triggers[type].slice();
3163
3164      // Call all listeners
3165      for (var i = 0; i < handlers.length; i++) {
3166        var trigger = handlers[i];
3167        var thisArg = (trigger.thisArg ? trigger.thisArg : this);
3168        trigger.listener.call(thisArg, event);
3169      }
3170    };
3171
3172    /**
3173     * Dispatch event.
3174     *
3175     * @param {string|H5P.Event} event
3176     *   Event object or event type as string
3177     * @param {*} [eventData]
3178     *   Custom event data(used when event type as string is used as first
3179     *   argument).
3180     * @param {Object} [extras]
3181     * @param {boolean} [extras.bubbles]
3182     * @param {boolean} [extras.external]
3183     */
3184    this.trigger = function (event, eventData, extras) {
3185      if (event === undefined) {
3186        return;
3187      }
3188      if (event instanceof String || typeof event === 'string') {
3189        event = new H5P.Event(event, eventData, extras);
3190      }
3191      else if (eventData !== undefined) {
3192        event.data = eventData;
3193      }
3194
3195      // Check to see if this event should go externally after all triggering and bubbling is done
3196      var scheduledForExternal = event.scheduleForExternal();
3197
3198      // Call all listeners
3199      call.call(this, event.type, event);
3200
3201      // Call all * listeners
3202      call.call(this, '*', event);
3203
3204      // Bubble
3205      if (event.getBubbles() && self.parent instanceof H5P.EventDispatcher &&
3206          (self.parent.trigger instanceof Function || typeof self.parent.trigger === 'function')) {
3207        self.parent.trigger(event);
3208      }
3209
3210      if (scheduledForExternal) {
3211        H5P.externalDispatcher.trigger.call(this, event);
3212      }
3213    };
3214  }
3215
3216  return EventDispatcher;
3217})();
3218;
3219var H5P = window.H5P = window.H5P || {};
3220
3221/**
3222 * Used for xAPI events.
3223 *
3224 * @class
3225 * @extends H5P.Event
3226 */
3227H5P.XAPIEvent = function () {
3228  H5P.Event.call(this, 'xAPI', {'statement': {}}, {bubbles: true, external: true});
3229};
3230
3231H5P.XAPIEvent.prototype = Object.create(H5P.Event.prototype);
3232H5P.XAPIEvent.prototype.constructor = H5P.XAPIEvent;
3233
3234/**
3235 * Set scored result statements.
3236 *
3237 * @param {number} score
3238 * @param {number} maxScore
3239 * @param {object} instance
3240 * @param {boolean} completion
3241 * @param {boolean} success
3242 */
3243H5P.XAPIEvent.prototype.setScoredResult = function (score, maxScore, instance, completion, success) {
3244  this.data.statement.result = {};
3245
3246  if (typeof score !== 'undefined') {
3247    if (typeof maxScore === 'undefined') {
3248      this.data.statement.result.score = {'raw': score};
3249    }
3250    else {
3251      this.data.statement.result.score = {
3252        'min': 0,
3253        'max': maxScore,
3254        'raw': score
3255      };
3256      if (maxScore > 0) {
3257        this.data.statement.result.score.scaled = Math.round(score / maxScore * 10000) / 10000;
3258      }
3259    }
3260  }
3261
3262  if (typeof completion === 'undefined') {
3263    this.data.statement.result.completion = (this.getVerb() === 'completed' || this.getVerb() === 'answered');
3264  }
3265  else {
3266    this.data.statement.result.completion = completion;
3267  }
3268
3269  if (typeof success !== 'undefined') {
3270    this.data.statement.result.success = success;
3271  }
3272
3273  if (instance && instance.activityStartTime) {
3274    var duration = Math.round((Date.now() - instance.activityStartTime ) / 10) / 100;
3275    // xAPI spec allows a precision of 0.01 seconds
3276
3277    this.data.statement.result.duration = 'PT' + duration + 'S';
3278  }
3279};
3280
3281/**
3282 * Set a verb.
3283 *
3284 * @param {string} verb
3285 *   Verb in short form, one of the verbs defined at
3286 *   {@link http://adlnet.gov/expapi/verbs/|ADL xAPI Vocabulary}
3287 *
3288 */
3289H5P.XAPIEvent.prototype.setVerb = function (verb) {
3290  if (H5P.jQuery.inArray(verb, H5P.XAPIEvent.allowedXAPIVerbs) !== -1) {
3291    this.data.statement.verb = {
3292      'id': 'http://adlnet.gov/expapi/verbs/' + verb,
3293      'display': {
3294        'en-US': verb
3295      }
3296    };
3297  }
3298  else if (verb.id !== undefined) {
3299    this.data.statement.verb = verb;
3300  }
3301};
3302
3303/**
3304 * Get the statements verb id.
3305 *
3306 * @param {boolean} full
3307 *   if true the full verb id prefixed by http://adlnet.gov/expapi/verbs/
3308 *   will be returned
3309 * @returns {string}
3310 *   Verb or null if no verb with an id has been defined
3311 */
3312H5P.XAPIEvent.prototype.getVerb = function (full) {
3313  var statement = this.data.statement;
3314  if ('verb' in statement) {
3315    if (full === true) {
3316      return statement.verb;
3317    }
3318    return statement.verb.id.slice(31);
3319  }
3320  else {
3321    return null;
3322  }
3323};
3324
3325/**
3326 * Set the object part of the statement.
3327 *
3328 * The id is found automatically (the url to the content)
3329 *
3330 * @param {Object} instance
3331 *   The H5P instance
3332 */
3333H5P.XAPIEvent.prototype.setObject = function (instance) {
3334  if (instance.contentId) {
3335    this.data.statement.object = {
3336      'id': this.getContentXAPIId(instance),
3337      'objectType': 'Activity',
3338      'definition': {
3339        'extensions': {
3340          'http://h5p.org/x-api/h5p-local-content-id': instance.contentId
3341        }
3342      }
3343    };
3344    if (instance.subContentId) {
3345      this.data.statement.object.definition.extensions['http://h5p.org/x-api/h5p-subContentId'] = instance.subContentId;
3346      // Don't set titles on main content, title should come from publishing platform
3347      if (typeof instance.getTitle === 'function') {
3348        this.data.statement.object.definition.name = {
3349          "en-US": instance.getTitle()
3350        };
3351      }
3352    }
3353    else {
3354      var content = H5P.getContentForInstance(instance.contentId);
3355      if (content && content.metadata && content.metadata.title) {
3356        this.data.statement.object.definition.name = {
3357          "en-US": H5P.createTitle(content.metadata.title)
3358        };
3359      }
3360    }
3361  }
3362  else {
3363    // Content types view always expect to have a contentId when they are displayed.
3364    // This is not the case if they are displayed in the editor as part of a preview.
3365    // The fix is to set an empty object with definition for the xAPI event, so all
3366    // the content types that rely on this does not have to handle it. This means
3367    // that content types that are being previewed will send xAPI completed events,
3368    // but since there are no scripts that catch these events in the editor,
3369    // this is not a problem.
3370    this.data.statement.object = {
3371      definition: {}
3372    };
3373  }
3374};
3375
3376/**
3377 * Set the context part of the statement.
3378 *
3379 * @param {Object} instance
3380 *   The H5P instance
3381 */
3382H5P.XAPIEvent.prototype.setContext = function (instance) {
3383  if (instance.parent && (instance.parent.contentId || instance.parent.subContentId)) {
3384    this.data.statement.context = {
3385      "contextActivities": {
3386        "parent": [
3387          {
3388            "id": this.getContentXAPIId(instance.parent),
3389            "objectType": "Activity"
3390          }
3391        ]
3392      }
3393    };
3394  }
3395  if (instance.libraryInfo) {
3396    if (this.data.statement.context === undefined) {
3397      this.data.statement.context = {"contextActivities":{}};
3398    }
3399    this.data.statement.context.contextActivities.category = [
3400      {
3401        "id": "http://h5p.org/libraries/" + instance.libraryInfo.versionedNameNoSpaces,
3402        "objectType": "Activity"
3403      }
3404    ];
3405  }
3406};
3407
3408/**
3409 * Set the actor. Email and name will be added automatically.
3410 */
3411H5P.XAPIEvent.prototype.setActor = function () {
3412  if (H5PIntegration.user !== undefined) {
3413    this.data.statement.actor = {
3414      'name': H5PIntegration.user.name,
3415      'mbox': 'mailto:' + H5PIntegration.user.mail,
3416      'objectType': 'Agent'
3417    };
3418  }
3419  else {
3420    var uuid;
3421    try {
3422      if (localStorage.H5PUserUUID) {
3423        uuid = localStorage.H5PUserUUID;
3424      }
3425      else {
3426        uuid = H5P.createUUID();
3427        localStorage.H5PUserUUID = uuid;
3428      }
3429    }
3430    catch (err) {
3431      // LocalStorage and Cookies are probably disabled. Do not track the user.
3432      uuid = 'not-trackable-' + H5P.createUUID();
3433    }
3434    this.data.statement.actor = {
3435      'account': {
3436        'name': uuid,
3437        'homePage': H5PIntegration.siteUrl
3438      },
3439      'objectType': 'Agent'
3440    };
3441  }
3442};
3443
3444/**
3445 * Get the max value of the result - score part of the statement
3446 *
3447 * @returns {number}
3448 *   The max score, or null if not defined
3449 */
3450H5P.XAPIEvent.prototype.getMaxScore = function () {
3451  return this.getVerifiedStatementValue(['result', 'score', 'max']);
3452};
3453
3454/**
3455 * Get the raw value of the result - score part of the statement
3456 *
3457 * @returns {number}
3458 *   The score, or null if not defined
3459 */
3460H5P.XAPIEvent.prototype.getScore = function () {
3461  return this.getVerifiedStatementValue(['result', 'score', 'raw']);
3462};
3463
3464/**
3465 * Get content xAPI ID.
3466 *
3467 * @param {Object} instance
3468 *   The H5P instance
3469 */
3470H5P.XAPIEvent.prototype.getContentXAPIId = function (instance) {
3471  var xAPIId;
3472  if (instance.contentId && H5PIntegration && H5PIntegration.contents && H5PIntegration.contents['cid-' + instance.contentId]) {
3473    xAPIId =  H5PIntegration.contents['cid-' + instance.contentId].url;
3474    if (instance.subContentId) {
3475      xAPIId += '?subContentId=' +  instance.subContentId;
3476    }
3477  }
3478  return xAPIId;
3479};
3480
3481/**
3482 * Check if this event is sent from a child (i.e not from grandchild)
3483 *
3484 * @return {Boolean}
3485 */
3486H5P.XAPIEvent.prototype.isFromChild = function () {
3487  var parentId = this.getVerifiedStatementValue(['context', 'contextActivities', 'parent', 0, 'id']);
3488  return !parentId || parentId.indexOf('subContentId') === -1;
3489};
3490
3491/**
3492 * Figure out if a property exists in the statement and return it
3493 *
3494 * @param {string[]} keys
3495 *   List describing the property we're looking for. For instance
3496 *   ['result', 'score', 'raw'] for result.score.raw
3497 * @returns {*}
3498 *   The value of the property if it is set, null otherwise.
3499 */
3500H5P.XAPIEvent.prototype.getVerifiedStatementValue = function (keys) {
3501  var val = this.data.statement;
3502  for (var i = 0; i < keys.length; i++) {
3503    if (val[keys[i]] === undefined) {
3504      return null;
3505    }
3506    val = val[keys[i]];
3507  }
3508  return val;
3509};
3510
3511/**
3512 * List of verbs defined at {@link http://adlnet.gov/expapi/verbs/|ADL xAPI Vocabulary}
3513 *
3514 * @type Array
3515 */
3516H5P.XAPIEvent.allowedXAPIVerbs = [
3517  'answered',
3518  'asked',
3519  'attempted',
3520  'attended',
3521  'commented',
3522  'completed',
3523  'exited',
3524  'experienced',
3525  'failed',
3526  'imported',
3527  'initialized',
3528  'interacted',
3529  'launched',
3530  'mastered',
3531  'passed',
3532  'preferred',
3533  'progressed',
3534  'registered',
3535  'responded',
3536  'resumed',
3537  'scored',
3538  'shared',
3539  'suspended',
3540  'terminated',
3541  'voided',
3542
3543  // Custom verbs used for action toolbar below content
3544  'downloaded',
3545  'copied',
3546  'accessed-reuse',
3547  'accessed-embed',
3548  'accessed-copyright'
3549];
3550;
3551var H5P = window.H5P = window.H5P || {};
3552
3553/**
3554 * The external event dispatcher. Others, outside of H5P may register and
3555 * listen for H5P Events here.
3556 *
3557 * @type {H5P.EventDispatcher}
3558 */
3559H5P.externalDispatcher = new H5P.EventDispatcher();
3560
3561// EventDispatcher extensions
3562
3563/**
3564 * Helper function for triggering xAPI added to the EventDispatcher.
3565 *
3566 * @param {string} verb
3567 *   The short id of the verb we want to trigger
3568 * @param {Oject} [extra]
3569 *   Extra properties for the xAPI statement
3570 */
3571H5P.EventDispatcher.prototype.triggerXAPI = function (verb, extra) {
3572  this.trigger(this.createXAPIEventTemplate(verb, extra));
3573};
3574
3575/**
3576 * Helper function to create event templates added to the EventDispatcher.
3577 *
3578 * Will in the future be used to add representations of the questions to the
3579 * statements.
3580 *
3581 * @param {string} verb
3582 *   Verb id in short form
3583 * @param {Object} [extra]
3584 *   Extra values to be added to the statement
3585 * @returns {H5P.XAPIEvent}
3586 *   Instance
3587 */
3588H5P.EventDispatcher.prototype.createXAPIEventTemplate = function (verb, extra) {
3589  var event = new H5P.XAPIEvent();
3590
3591  event.setActor();
3592  event.setVerb(verb);
3593  if (extra !== undefined) {
3594    for (var i in extra) {
3595      event.data.statement[i] = extra[i];
3596    }
3597  }
3598  if (!('object' in event.data.statement)) {
3599    event.setObject(this);
3600  }
3601  if (!('context' in event.data.statement)) {
3602    event.setContext(this);
3603  }
3604  return event;
3605};
3606
3607/**
3608 * Helper function to create xAPI completed events
3609 *
3610 * DEPRECATED - USE triggerXAPIScored instead
3611 *
3612 * @deprecated
3613 *   since 1.5, use triggerXAPIScored instead.
3614 * @param {number} score
3615 *   Will be set as the 'raw' value of the score object
3616 * @param {number} maxScore
3617 *   will be set as the "max" value of the score object
3618 * @param {boolean} success
3619 *   will be set as the "success" value of the result object
3620 */
3621H5P.EventDispatcher.prototype.triggerXAPICompleted = function (score, maxScore, success) {
3622  this.triggerXAPIScored(score, maxScore, 'completed', true, success);
3623};
3624
3625/**
3626 * Helper function to create scored xAPI events
3627 *
3628 * @param {number} score
3629 *   Will be set as the 'raw' value of the score object
3630 * @param {number} maxScore
3631 *   Will be set as the "max" value of the score object
3632 * @param {string} verb
3633 *   Short form of adl verb
3634 * @param {boolean} completion
3635 *   Is this a statement from a completed activity?
3636 * @param {boolean} success
3637 *   Is this a statement from an activity that was done successfully?
3638 */
3639H5P.EventDispatcher.prototype.triggerXAPIScored = function (score, maxScore, verb, completion, success) {
3640  var event = this.createXAPIEventTemplate(verb);
3641  event.setScoredResult(score, maxScore, this, completion, success);
3642  this.trigger(event);
3643};
3644
3645H5P.EventDispatcher.prototype.setActivityStarted = function () {
3646  if (this.activityStartTime === undefined) {
3647    // Don't trigger xAPI events in the editor
3648    if (this.contentId !== undefined &&
3649        H5PIntegration.contents !== undefined &&
3650        H5PIntegration.contents['cid-' + this.contentId] !== undefined) {
3651      this.triggerXAPI('attempted');
3652    }
3653    this.activityStartTime = Date.now();
3654  }
3655};
3656
3657/**
3658 * Internal H5P function listening for xAPI completed events and stores scores
3659 *
3660 * @param {H5P.XAPIEvent} event
3661 */
3662H5P.xAPICompletedListener = function (event) {
3663  if ((event.getVerb() === 'completed' || event.getVerb() === 'answered') && !event.getVerifiedStatementValue(['context', 'contextActivities', 'parent'])) {
3664    var score = event.getScore();
3665    var maxScore = event.getMaxScore();
3666    var contentId = event.getVerifiedStatementValue(['object', 'definition', 'extensions', 'http://h5p.org/x-api/h5p-local-content-id']);
3667    H5P.setFinished(contentId, score, maxScore);
3668  }
3669};
3670;
3671/**
3672 * H5P.ContentType is a base class for all content types. Used by newRunnable()
3673 *
3674 * Functions here may be overridable by the libraries. In special cases,
3675 * it is also possible to override H5P.ContentType on a global level.
3676 *
3677 * NOTE that this doesn't actually 'extend' the event dispatcher but instead
3678 * it creates a single instance which all content types shares as their base
3679 * prototype. (in some cases this may be the root of strange event behavior)
3680 *
3681 * @class
3682 * @augments H5P.EventDispatcher
3683 */
3684H5P.ContentType = function (isRootLibrary) {
3685
3686  function ContentType() {}
3687
3688  // Inherit from EventDispatcher.
3689  ContentType.prototype = new H5P.EventDispatcher();
3690
3691  /**
3692   * Is library standalone or not? Not beeing standalone, means it is
3693   * included in another library
3694   *
3695   * @return {Boolean}
3696   */
3697  ContentType.prototype.isRoot = function () {
3698    return isRootLibrary;
3699  };
3700
3701  /**
3702   * Returns the file path of a file in the current library
3703   * @param  {string} filePath The path to the file relative to the library folder
3704   * @return {string} The full path to the file
3705   */
3706  ContentType.prototype.getLibraryFilePath = function (filePath) {
3707    return H5P.getLibraryPath(this.libraryInfo.versionedNameNoSpaces) + '/' + filePath;
3708  };
3709
3710  return ContentType;
3711};
3712;
3713/* eslint-disable no-param-reassign */
3714H5P.ConfirmationDialog = (function (EventDispatcher) {
3715  /**
3716   * Create a confirmation dialog
3717   *
3718   * @param [options] Options for confirmation dialog
3719   * @param [options.instance] Instance that uses confirmation dialog
3720   * @param [options.headerText] Header text
3721   * @param [options.dialogText] Dialog text
3722   * @param [options.cancelText] Cancel dialog button text
3723   * @param [options.confirmText] Confirm dialog button text
3724   * @param [options.hideCancel] Hide cancel button
3725   * @param [options.hideExit] Hide exit button
3726   * @param [options.skipRestoreFocus] Skip restoring focus when hiding the dialog
3727   * @param [options.classes] Extra classes for popup
3728   * @param [options.theme] Whether to use the new theme (true) or the old design (false)
3729   * @constructor
3730   */
3731  function ConfirmationDialog(options) {
3732    EventDispatcher.call(this);
3733    const self = this;
3734
3735    // Make sure confirmation dialogs have unique id
3736    H5P.ConfirmationDialog.uniqueId += 1;
3737    const { uniqueId } = H5P.ConfirmationDialog;
3738
3739    // Default options
3740    options = options || {};
3741    options.headerText = options.headerText || H5P.t('confirmDialogHeader');
3742    options.dialogText = options.dialogText || H5P.t('confirmDialogBody');
3743    options.cancelText = options.cancelText || H5P.t('cancelLabel');
3744    options.closeText = options.closeText || H5P.t('close');
3745    options.confirmText = options.confirmText || H5P.t('confirmLabel');
3746
3747    /**
3748     * Handle confirming event
3749     * @param {Event} e
3750     */
3751    function dialogConfirmed(e) {
3752      self.hide();
3753      self.trigger('confirmed');
3754      e.preventDefault();
3755    }
3756
3757    /**
3758     * Handle dialog canceled
3759     * @param {Event} e Event.
3760     * @param {object} [options] Options.
3761     * @param {boolean} [options.wasExplicitChoice] 
3762     * True if user chose cancel explicitly, otherwise (close, esc) false.
3763     */
3764    function dialogCanceled(e, options = {}) {
3765      self.hide();
3766      self.trigger('canceled', { wasExplicitChoice: options.wasExplicitChoice ?? false });
3767      e.preventDefault();
3768    }
3769
3770    /**
3771     * Handle tabbing through buttons with focus trapping.
3772     * @param {KeyboardEvent} event Keyboard event.
3773     */
3774    function handleTabbing(event) {
3775      if (event.key !== 'Tab') {
3776        return;
3777      }
3778
3779      event.preventDefault();
3780
3781      const currentIndex = focusableButtons.indexOf(event.target);
3782      const offset = event.shiftKey ? -1 : 1;
3783      const nextIndex = (currentIndex + offset + focusableButtons.length) % focusableButtons.length;
3784
3785      focusableButtons[nextIndex].focus();
3786    }
3787
3788    // Offset of exit button
3789    const exitButtonOffset = 2 * 16;
3790    const shadowOffset = 8;
3791
3792    // Determine if we are too large for our container and must resize
3793    let resizeIFrame = false;
3794
3795    // Create background
3796    const popupBackground = document.createElement('div');
3797    popupBackground.classList
3798      .add('h5p-confirmation-dialog-background', 'hidden', 'hiding');
3799
3800    if (options.theme) {
3801      popupBackground.classList.add('h5p-theme');
3802    }
3803
3804    if (window.H5PEditor) {
3805      popupBackground.classList.add('h5peditor');
3806
3807      if (H5PIntegration.theme?.density) {
3808        popupBackground.classList.add(`h5p-${H5PIntegration.theme.density}`);
3809      }
3810    }
3811
3812    // Create outer popup
3813    const popup = document.createElement('div');
3814    popup.classList.add('h5p-confirmation-dialog-popup', 'hidden');
3815    if (options.classes) {
3816      options.classes.forEach((popupClass) => {
3817        popup.classList.add(popupClass);
3818      });
3819    }
3820
3821    popup.setAttribute('role', 'alertdialog');
3822    popup.setAttribute('aria-modal', 'true');
3823    popup.setAttribute('aria-labelledby', `h5p-confirmation-dialog-header-text-${uniqueId}`);
3824    popup.setAttribute('aria-describedby', `h5p-confirmation-dialog-text-${uniqueId}`);
3825    popupBackground.appendChild(popup);
3826    popup.addEventListener('keydown', (e) => {
3827      if (e.key === 'Escape') { // Esc key
3828        // Exit dialog
3829        dialogCanceled(e);
3830      }
3831    });
3832    popup.addEventListener('keydown', (event) => {
3833      handleTabbing(event);
3834    });
3835
3836    // Popup header
3837    const header = document.createElement('div');
3838    header.classList.add('h5p-confirmation-dialog-header');
3839    popup.appendChild(header);
3840
3841    // Header text
3842    const headerText = document.createElement('div');
3843    headerText.classList.add('h5p-confirmation-dialog-header-text');
3844    headerText.id = `h5p-confirmation-dialog-dialog-header-text-${uniqueId}`;
3845    headerText.innerHTML = options.headerText;
3846    header.appendChild(headerText);
3847
3848    // Popup body
3849    const body = document.createElement('div');
3850    body.classList.add('h5p-confirmation-dialog-body');
3851    popup.appendChild(body);
3852
3853    // Popup text
3854    const text = document.createElement('div');
3855    text.classList.add('h5p-confirmation-dialog-text');
3856    text.innerHTML = options.dialogText;
3857    text.id = `h5p-confirmation-dialog-dialog-text-${uniqueId}`;
3858    body.appendChild(text);
3859
3860    // Popup buttons
3861    const buttons = document.createElement('div');
3862    buttons.classList.add('h5p-confirmation-dialog-buttons');
3863    body.appendChild(buttons);
3864
3865    // Cancel button
3866    if (!options.hideCancel) {
3867      const cancelButton = document.createElement('button');
3868      if (!options.theme) {
3869        cancelButton.classList.add('h5p-core-cancel-button');
3870      }
3871      else {
3872        cancelButton.classList.add('h5p-theme-button', 'h5p-theme-secondary-cta');
3873        cancelButton.classList.add('h5p-theme-cancel');
3874      }
3875      const cancelText = document.createElement('span');
3876      cancelText.textContent = options.cancelText;
3877      cancelButton.appendChild(cancelText);
3878      cancelButton.addEventListener('click', (event) => {
3879        dialogCanceled(event, { wasExplicitChoice: true });
3880      });
3881      buttons.appendChild(cancelButton);
3882    }
3883    else {
3884      // Center remaining buttons
3885      buttons.classList.add('center');
3886    }
3887
3888    // Confirm button
3889    const confirmButton = document.createElement('button');
3890    if (!options.theme) {
3891      confirmButton.classList.add('h5p-core-button');
3892    }
3893    // confirmButton.classList.add('h5p-confirmation-dialog-confirm-button');
3894    confirmButton.setAttribute('aria-label', options.confirmText);
3895
3896    if (options.theme) {
3897      confirmButton.classList.add('h5p-theme-button', 'h5p-theme-primary-cta');
3898      confirmButton.classList.add('h5p-theme-check');
3899    }
3900
3901    confirmButton.addEventListener('click', dialogConfirmed);
3902    const confirmText = document.createElement('span');
3903    confirmText.textContent = options.confirmText;
3904    confirmButton.appendChild(confirmText);
3905    buttons.appendChild(confirmButton);
3906
3907    let focusableButtons = [...buttons.childNodes];
3908
3909    // Exit button
3910    if (!options.hideExit) {
3911      const exitButton = document.createElement('button');
3912      exitButton.classList.add('h5p-confirmation-dialog-exit');
3913      exitButton.setAttribute('aria-label', options.closeText);
3914      exitButton.addEventListener('click', dialogCanceled);
3915      if (options.theme) {
3916        header.appendChild(exitButton);
3917      }
3918      else {
3919        popup.appendChild(exitButton);
3920      }
3921      focusableButtons.push(exitButton);
3922    }
3923
3924    // Wrapper element
3925    let wrapperElement;
3926
3927    // Maintains hidden state of elements
3928    let wrapperSiblingsHidden = [];
3929    let popupSiblingsHidden = [];
3930
3931    // Element with focus before dialog
3932    let previouslyFocused;
3933
3934    /**
3935     * Set parent of confirmation dialog
3936     * @param {HTMLElement} wrapper
3937     * @returns {H5P.ConfirmationDialog}
3938     */
3939    this.appendTo = function (wrapper) {
3940      wrapperElement = wrapper;
3941      return this;
3942    };
3943
3944    /**
3945     * Hide siblings of element from assistive technology
3946     *
3947     * @param {HTMLElement} element
3948     * @returns {Array} The previous hidden state of all siblings
3949     */
3950    const hideSiblings = function (element) {
3951      const hiddenSiblings = [];
3952      const siblings = element.parentNode.children;
3953      let i;
3954      for (i = 0; i < siblings.length; i += 1) {
3955        // Preserve hidden state
3956        hiddenSiblings[i] = !!siblings[i].getAttribute('aria-hidden');
3957
3958        if (siblings[i] !== element) {
3959          if (siblings[i].getAttribute('aria-live')) {
3960            siblings[i].setAttribute('aria-busy', true);
3961          }
3962          else {
3963            siblings[i].setAttribute('aria-hidden', true);
3964          }
3965        }
3966      }
3967      return hiddenSiblings;
3968    };
3969
3970    /**
3971     * Restores assistive technology state of element's siblings
3972     *
3973     * @param {HTMLElement} element
3974     * @param {Array} hiddenSiblings Hidden state of all siblings
3975     */
3976    const restoreSiblings = function (element, hiddenSiblings) {
3977      const siblings = element.parentNode.children;
3978      let i;
3979      for (i = 0; i < siblings.length; i += 1) {
3980        if (siblings[i] !== element && !hiddenSiblings[i]) {
3981          if (siblings[i].getAttribute('aria-live')) {
3982            siblings[i].setAttribute('aria-busy', false);
3983          }
3984          else {
3985            siblings[i].removeAttribute('aria-hidden');
3986          }
3987        }
3988      }
3989    };
3990
3991    /**
3992     * Hide siblings in underlay from assistive technologies
3993     */
3994    const disableUnderlay = function () {
3995      wrapperSiblingsHidden = hideSiblings(wrapperElement);
3996      popupSiblingsHidden = hideSiblings(popupBackground);
3997    };
3998
3999    /**
4000     * Restore state of underlay for assistive technologies
4001     */
4002    const restoreUnderlay = function () {
4003      restoreSiblings(wrapperElement, wrapperSiblingsHidden);
4004      restoreSiblings(popupBackground, popupSiblingsHidden);
4005    };
4006
4007    /**
4008     * Fit popup to container. Makes sure it doesn't overflow.
4009     * @params {number} [offsetTop] Offset of popup
4010     */
4011    const fitToContainer = function (offsetTop) {
4012      let popupOffsetTop = parseInt(popup.style.top, 10);
4013      if (offsetTop !== undefined) {
4014        popupOffsetTop = offsetTop;
4015      }
4016
4017      if (!popupOffsetTop) {
4018        popupOffsetTop = 0;
4019      }
4020
4021      // Overflows height
4022      if (popupOffsetTop + popup.offsetHeight > wrapperElement.offsetHeight) {
4023        popupOffsetTop = wrapperElement.offsetHeight - popup.offsetHeight - shadowOffset;
4024      }
4025
4026      if (popupOffsetTop - exitButtonOffset <= 0) {
4027        popupOffsetTop = exitButtonOffset + shadowOffset;
4028
4029        // We are too big and must resize
4030        resizeIFrame = true;
4031      }
4032      popup.style.top = `${popupOffsetTop}px`;
4033    };
4034
4035    /**
4036     * Show confirmation dialog
4037     * @params {number} offsetTop Offset top
4038     * @returns {H5P.ConfirmationDialog}
4039     */
4040    this.show = function (offsetTop) {
4041      // Capture focused item
4042      previouslyFocused = document.activeElement;
4043      wrapperElement.appendChild(popupBackground);
4044      popupBackground.classList.remove('hidden');
4045      fitToContainer(offsetTop);
4046      popup.classList.remove('hidden');
4047      popupBackground.addEventListener('transitionend', () => {
4048        buttons.firstChild.focus();
4049      }, { once: true });
4050      popupBackground.classList.remove('hiding');
4051      disableUnderlay();
4052
4053      // Resize iFrame if necessary
4054      if (resizeIFrame && options.instance) {
4055        const minHeight = parseInt(popup.offsetHeight, 10)
4056          + exitButtonOffset + (2 * shadowOffset);
4057        self.setViewPortMinimumHeight(minHeight);
4058        options.instance.trigger('resize');
4059        resizeIFrame = false;
4060      }
4061
4062      // Detect if the user prefers reduced motion, because in that case
4063      // we cannot rely on transitionend triggering and we need to manually
4064      // focus the buttons. It should also be checked for each show, since a
4065      // user may change this setting at any time.
4066      const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
4067      if (prefersReducedMotion.matches) {
4068        buttons.firstChild.focus();
4069      }
4070
4071      return this;
4072    };
4073
4074    /**
4075     * Hide confirmation dialog
4076     * @returns {H5P.ConfirmationDialog}
4077     */
4078    this.hide = function () {
4079      restoreUnderlay();
4080      popupBackground.classList.add('hiding');
4081      popup.classList.add('hidden');
4082
4083      // Restore focus
4084      if (!options.skipRestoreFocus) {
4085        previouslyFocused.focus();
4086      }
4087      popupBackground.classList.add('hidden');
4088      wrapperElement.removeChild(popupBackground);
4089      self.setViewPortMinimumHeight(null);
4090
4091      return this;
4092    };
4093
4094    /**
4095     * Retrieve element
4096     *
4097     * @return {HTMLElement}
4098     */
4099    this.getElement = function () {
4100      return popup;
4101    };
4102
4103    /**
4104     * Get previously focused element
4105     * @return {HTMLElement}
4106     */
4107    this.getPreviouslyFocused = function () {
4108      return previouslyFocused;
4109    };
4110
4111    /**
4112     * Sets the minimum height of the view port
4113     *
4114     * @param {number|null} minHeight
4115     */
4116    this.setViewPortMinimumHeight = function (minHeight) {
4117      const container = document.querySelector('.h5p-container') || document.body;
4118      container.style.minHeight = (typeof minHeight === 'number') ? (`${minHeight}px`) : minHeight;
4119    };
4120  }
4121
4122  ConfirmationDialog.prototype = Object.create(EventDispatcher.prototype);
4123  ConfirmationDialog.prototype.constructor = ConfirmationDialog;
4124
4125  return ConfirmationDialog;
4126}(H5P.EventDispatcher));
4127
4128H5P.ConfirmationDialog.uniqueId = -1;
4129;
4130/**
4131 * @class
4132 * @augments H5P.EventDispatcher
4133 * @param {Object} displayOptions
4134 * @param {boolean} displayOptions.export Triggers the display of the 'Download' button
4135 * @param {boolean} displayOptions.copyright Triggers the display of the 'Copyright' button
4136 * @param {boolean} displayOptions.embed Triggers the display of the 'Embed' button
4137 * @param {boolean} displayOptions.icon Triggers the display of the 'H5P icon' link
4138 */
4139H5P.ActionBar = (function ($, EventDispatcher) {
4140  "use strict";
4141
4142  function ActionBar(displayOptions) {
4143    EventDispatcher.call(this);
4144
4145    /** @alias H5P.ActionBar# */
4146    var self = this;
4147
4148    var hasActions = false;
4149
4150    // Create action bar
4151    var $actions = H5P.jQuery('<ul class="h5p-actions"></ul>');
4152
4153    /**
4154     * Helper for creating action bar buttons.
4155     *
4156     * @private
4157     * @param {string} type
4158     * @param {string} customClass Instead of type class
4159     */
4160    var addActionButton = function (type, customClass) {
4161      /**
4162       * Handles selection of action
4163       */
4164      var handler = function () {
4165        self.trigger(type);
4166      };
4167
4168      const $actionList = H5P.jQuery('<li/>', {
4169        'class': 'h5p-button h5p-noselect h5p-' + (customClass ? customClass : type),
4170        appendTo: $actions
4171      });
4172
4173      const $actionButton = H5P.jQuery('<button/>', {
4174        tabindex: 0,
4175        'aria-label': H5P.t(type + 'Description'),
4176        html: H5P.t(type),
4177        on: {
4178          click: handler,
4179          keypress: function (e) {
4180            if (e.which === 32) {
4181              handler();
4182              e.preventDefault(); // (since return false will block other inputs)
4183            }
4184          }
4185        },
4186        appendTo: $actionList
4187      });
4188
4189      H5P.Tooltip($actionButton.get(0));
4190
4191      hasActions = true;
4192    };
4193
4194    // Register action bar buttons
4195    if (displayOptions.export || displayOptions.copy) {
4196      // Add export button
4197      addActionButton('reuse', 'export');
4198    }
4199    if (displayOptions.copyright) {
4200      addActionButton('copyrights');
4201    }
4202    if (displayOptions.embed) {
4203      addActionButton('embed');
4204    }
4205    if (displayOptions.icon) {
4206      // Add about H5P button icon
4207      const $h5pLogo = H5P.jQuery('<li><a class="h5p-link" href="http://h5p.org" target="_blank" aria-label="' + H5P.t('h5pDescription') + '"></a></li>').appendTo($actions);
4208      H5P.Tooltip($h5pLogo.find('.h5p-link').get(0));
4209      hasActions = true;
4210    }
4211
4212    /**
4213     * Returns a reference to the dom element
4214     *
4215     * @return {H5P.jQuery}
4216     */
4217    self.getDOMElement = function () {
4218      return $actions;
4219    };
4220
4221    /**
4222     * Does the actionbar contain actions?
4223     *
4224     * @return {Boolean}
4225     */
4226    self.hasActions = function () {
4227      return hasActions;
4228    };
4229  }
4230
4231  ActionBar.prototype = Object.create(EventDispatcher.prototype);
4232  ActionBar.prototype.constructor = ActionBar;
4233
4234  return ActionBar;
4235
4236})(H5P.jQuery, H5P.EventDispatcher);
4237;
4238/**
4239 * Queue requests and handle them at your convenience
4240 *
4241 * @type {RequestQueue}
4242 */
4243H5P.RequestQueue = (function ($, EventDispatcher) {
4244  /**
4245   * A queue for requests, will be automatically processed when regaining connection
4246   *
4247   * @param {boolean} [options.showToast] Show toast when losing or regaining connection
4248   * @constructor
4249   */
4250  const RequestQueue = function (options) {
4251    EventDispatcher.call(this);
4252    this.processingQueue = false;
4253    options = options || {};
4254
4255    this.showToast = options.showToast;
4256    this.itemName = 'requestQueue';
4257  };
4258
4259  /**
4260   * Add request to queue. Only supports posts currently.
4261   *
4262   * @param {string} url
4263   * @param {Object} data
4264   * @returns {boolean}
4265   */
4266  RequestQueue.prototype.add = function (url, data) {
4267    if (!window.localStorage) {
4268      return false;
4269    }
4270
4271    let storedStatements = this.getStoredRequests();
4272    if (!storedStatements) {
4273      storedStatements = [];
4274    }
4275
4276    storedStatements.push({
4277      url: url,
4278      data: data,
4279    });
4280
4281    window.localStorage.setItem(this.itemName, JSON.stringify(storedStatements));
4282
4283    this.trigger('requestQueued', {
4284      storedStatements: storedStatements,
4285      processingQueue: this.processingQueue,
4286    });
4287    return true;
4288  };
4289
4290  /**
4291   * Get stored requests
4292   *
4293   * @returns {boolean|Array} Stored requests
4294   */
4295  RequestQueue.prototype.getStoredRequests = function () {
4296    if (!window.localStorage) {
4297      return false;
4298    }
4299
4300    const item = window.localStorage.getItem(this.itemName);
4301    if (!item) {
4302      return [];
4303    }
4304
4305    return JSON.parse(item);
4306  };
4307
4308  /**
4309   * Clear stored requests
4310   *
4311   * @returns {boolean} True if the storage was successfully cleared
4312   */
4313  RequestQueue.prototype.clearQueue = function () {
4314    if (!window.localStorage) {
4315      return false;
4316    }
4317
4318    window.localStorage.removeItem(this.itemName);
4319    return true;
4320  };
4321
4322  /**
4323   * Start processing of requests queue
4324   *
4325   * @return {boolean} Returns false if it was not possible to resume processing queue
4326   */
4327  RequestQueue.prototype.resumeQueue = function () {
4328    // Not supported
4329    if (!H5PIntegration || !window.navigator || !window.localStorage) {
4330      return false;
4331    }
4332
4333    // Already processing
4334    if (this.processingQueue) {
4335      return false;
4336    }
4337
4338    // Attempt to send queued requests
4339    const queue = this.getStoredRequests();
4340    const queueLength = queue.length;
4341
4342    // Clear storage, failed requests will be re-added
4343    this.clearQueue();
4344
4345    // No items left in queue
4346    if (!queueLength) {
4347      this.trigger('emptiedQueue', queue);
4348      return true;
4349    }
4350
4351    // Make sure requests are not changed while they're being handled
4352    this.processingQueue = true;
4353
4354    // Process queue in original order
4355    this.processQueue(queue);
4356    return true
4357  };
4358
4359  /**
4360   * Process first item in the request queue
4361   *
4362   * @param {Array} queue Request queue
4363   */
4364  RequestQueue.prototype.processQueue = function (queue) {
4365    if (!queue.length) {
4366      return;
4367    }
4368
4369    this.trigger('processingQueue');
4370
4371    // Make sure the requests are processed in a FIFO order
4372    const request = queue.shift();
4373
4374    const self = this;
4375    $.post(request.url, request.data)
4376      .fail(self.onQueuedRequestFail.bind(self, request))
4377      .always(self.onQueuedRequestProcessed.bind(self, queue))
4378  };
4379
4380  /**
4381   * Request fail handler
4382   *
4383   * @param {Object} request
4384   */
4385  RequestQueue.prototype.onQueuedRequestFail = function (request) {
4386    // Queue the failed request again if we're offline
4387    if (!window.navigator.onLine) {
4388      this.add(request.url, request.data);
4389    }
4390  };
4391
4392  /**
4393   * An item in the queue was processed
4394   *
4395   * @param {Array} queue Queue that was processed
4396   */
4397  RequestQueue.prototype.onQueuedRequestProcessed = function (queue) {
4398    if (queue.length) {
4399      this.processQueue(queue);
4400      return;
4401    }
4402
4403    // Finished processing this queue
4404    this.processingQueue = false;
4405
4406    // Run empty queue callback with next request queue
4407    const requestQueue = this.getStoredRequests();
4408    this.trigger('queueEmptied', requestQueue);
4409  };
4410
4411  /**
4412   * Display toast message on the first content of current page
4413   *
4414   * @param {string} msg Message to display
4415   * @param {boolean} [forceShow] Force override showing the toast
4416   * @param {Object} [configOverride] Override toast message config
4417   */
4418  RequestQueue.prototype.displayToastMessage = function (msg, forceShow, configOverride) {
4419    if (!this.showToast && !forceShow) {
4420      return;
4421    }
4422
4423    const config = H5P.jQuery.extend(true, {}, {
4424      position: {
4425        horizontal : 'centered',
4426        vertical: 'centered',
4427        noOverflowX: true,
4428      }
4429    }, configOverride);
4430
4431    H5P.attachToastTo(H5P.jQuery('.h5p-content:first')[0], msg, config);
4432  };
4433
4434  return RequestQueue;
4435})(H5P.jQuery, H5P.EventDispatcher);
4436
4437/**
4438 * Request queue for retrying failing requests, will automatically retry them when you come online
4439 *
4440 * @type {offlineRequestQueue}
4441 */
4442H5P.OfflineRequestQueue = (function (RequestQueue, Dialog) {
4443
4444  /**
4445   * Constructor
4446   *
4447   * @param {Object} [options] Options for offline request queue
4448   * @param {Object} [options.instance] The H5P instance which UI components are placed within
4449   */
4450  const offlineRequestQueue = function (options) {
4451    const requestQueue = new RequestQueue();
4452
4453    // We could handle requests from previous pages here, but instead we throw them away
4454    requestQueue.clearQueue();
4455
4456    let startTime = null;
4457    const retryIntervals = [10, 20, 40, 60, 120, 300, 600];
4458    let intervalIndex = -1;
4459    let currentInterval = null;
4460    let isAttached = false;
4461    let isShowing = false;
4462    let isLoading = false;
4463    const instance = options.instance;
4464
4465    const offlineDialog = new Dialog({
4466      headerText: H5P.t('offlineDialogHeader'),
4467      dialogText: H5P.t('offlineDialogBody'),
4468      confirmText: H5P.t('offlineDialogRetryButtonLabel'),
4469      hideCancel: true,
4470      hideExit: true,
4471      classes: ['offline'],
4472      instance: instance,
4473      skipRestoreFocus: true,
4474    });
4475
4476    const dialog = offlineDialog.getElement();
4477
4478    // Add retry text to body
4479    const countDownText = document.createElement('div');
4480    countDownText.classList.add('count-down');
4481    countDownText.innerHTML = H5P.t('offlineDialogRetryMessage')
4482      .replace(':num', '<span class="count-down-num">0</span>');
4483
4484    dialog.querySelector('.h5p-confirmation-dialog-text').appendChild(countDownText);
4485    const countDownNum = countDownText.querySelector('.count-down-num');
4486
4487    // Create throbber
4488    const throbberWrapper = document.createElement('div');
4489    throbberWrapper.classList.add('throbber-wrapper');
4490    const throbber = document.createElement('div');
4491    throbber.classList.add('sending-requests-throbber');
4492    throbberWrapper.appendChild(throbber);
4493
4494    requestQueue.on('requestQueued', function (e) {
4495      // Already processing queue, wait until queue has finished processing before showing dialog
4496      if (e.data && e.data.processingQueue) {
4497        return;
4498      }
4499
4500      if (!isAttached) {
4501        const rootContent = document.body.querySelector('.h5p-content');
4502        if (!rootContent) {
4503          return;
4504        }
4505        offlineDialog.appendTo(rootContent);
4506        rootContent.appendChild(throbberWrapper);
4507        isAttached = true;
4508      }
4509
4510      startCountDown();
4511    }.bind(this));
4512
4513    requestQueue.on('queueEmptied', function (e) {
4514      if (e.data && e.data.length) {
4515        // New requests were added while processing queue or requests failed again. Re-queue requests.
4516        startCountDown(true);
4517        return;
4518      }
4519
4520      // Successfully emptied queue
4521      clearInterval(currentInterval);
4522      toggleThrobber(false);
4523      intervalIndex = -1;
4524      if (isShowing) {
4525        offlineDialog.hide();
4526        isShowing = false;
4527      }
4528
4529      requestQueue.displayToastMessage(
4530        H5P.t('offlineSuccessfulSubmit'),
4531        true,
4532        {
4533          position: {
4534            vertical: 'top',
4535            offsetVertical: '100',
4536          }
4537        }
4538      );
4539
4540    }.bind(this));
4541
4542    offlineDialog.on('confirmed', function () {
4543      // Show dialog on next render in case it is being hidden by the 'confirm' button
4544      isShowing = false;
4545      setTimeout(function () {
4546        retryRequests();
4547      }, 100);
4548    }.bind(this));
4549
4550    // Initialize listener for when requests are added to queue
4551    window.addEventListener('online', function () {
4552      retryRequests();
4553    }.bind(this));
4554
4555    // Listen for queued requests outside the iframe
4556    window.addEventListener('message', function (event) {
4557      const isValidQueueEvent = window.parent === event.source
4558        && event.data.context === 'h5p'
4559        && event.data.action === 'queueRequest';
4560
4561      if (!isValidQueueEvent) {
4562        return;
4563      }
4564
4565      this.add(event.data.url, event.data.data);
4566    }.bind(this));
4567
4568    /**
4569     * Toggle throbber visibility
4570     *
4571     * @param {boolean} [forceShow] Will force throbber visibility if set
4572     */
4573    const toggleThrobber = function (forceShow) {
4574      isLoading = !isLoading;
4575      if (forceShow !== undefined) {
4576        isLoading = forceShow;
4577      }
4578
4579      if (isLoading && isShowing) {
4580        offlineDialog.hide();
4581        isShowing = false;
4582      }
4583
4584      if (isLoading) {
4585        throbberWrapper.classList.add('show');
4586      }
4587      else {
4588        throbberWrapper.classList.remove('show');
4589      }
4590    };
4591    /**
4592     * Retries the failed requests
4593     */
4594    const retryRequests = function () {
4595      clearInterval(currentInterval);
4596      toggleThrobber(true);
4597      requestQueue.resumeQueue();
4598    };
4599
4600    /**
4601     * Increments retry interval
4602     */
4603    const incrementRetryInterval = function () {
4604      intervalIndex += 1;
4605      if (intervalIndex >= retryIntervals.length) {
4606        intervalIndex = retryIntervals.length - 1;
4607      }
4608    };
4609
4610    /**
4611     * Starts counting down to retrying queued requests.
4612     *
4613     * @param forceDelayedShow
4614     */
4615    const startCountDown = function (forceDelayedShow) {
4616      // Already showing, wait for retry
4617      if (isShowing) {
4618        return;
4619      }
4620
4621      toggleThrobber(false);
4622      if (!isShowing) {
4623        if (forceDelayedShow) {
4624          // Must force delayed show since dialog may be hiding, and confirmation dialog does not
4625          //  support this.
4626          setTimeout(function () {
4627            offlineDialog.show(0);
4628          }, 100);
4629        }
4630        else {
4631          offlineDialog.show(0);
4632        }
4633      }
4634      isShowing = true;
4635      startTime = new Date().getTime();
4636      incrementRetryInterval();
4637      clearInterval(currentInterval);
4638      currentInterval = setInterval(updateCountDown, 100);
4639    };
4640
4641    /**
4642     * Updates the count down timer. Retries requests when time expires.
4643     */
4644    const updateCountDown = function () {
4645      const time = new Date().getTime();
4646      const timeElapsed = Math.floor((time - startTime) / 1000);
4647      const timeLeft = retryIntervals[intervalIndex] - timeElapsed;
4648      countDownNum.textContent = timeLeft.toString();
4649
4650      // Retry interval reached, retry requests
4651      if (timeLeft <= 0) {
4652        retryRequests();
4653      }
4654    };
4655
4656    /**
4657     * Add request to offline request queue. Only supports posts for now.
4658     *
4659     * @param {string} url The request url
4660     * @param {Object} data The request data
4661     */
4662    this.add = function (url, data) {
4663      // Only queue request if it failed because we are offline
4664      if (window.navigator.onLine) {
4665        return false;
4666      }
4667
4668      requestQueue.add(url, data);
4669    };
4670  };
4671
4672  return offlineRequestQueue;
4673})(H5P.RequestQueue, H5P.ConfirmationDialog);
4674;
4675/* global H5P */
4676H5P.Tooltip = (function () {
4677  // Position (allowed and default)
4678  const Position = {
4679    allowed: ['top', 'bottom', 'left', 'right'],
4680    default: 'top',
4681  };
4682
4683  /** {number} DELAY_SHOW_MS Delay before tooltip is shown */
4684  const DELAY_SHOW_MS = 500;
4685
4686  /** {number} DELAY_HIDE_MS Delay before tooltip is hidden */
4687  const DELAY_HIDE_MS = 500;
4688
4689  /**
4690   * Strips html tags and converts special characters.
4691   * Example: "<div>Me &amp; you</div>" is converted to "Me & you".
4692   *
4693   * @param {String} text The text to be parsed
4694   * @returns {String} The parsed text
4695   */
4696  function parseString(text) {
4697    if (text === null || text === undefined) {
4698      return '';
4699    }
4700    const div = document.createElement('div');
4701    div.innerHTML = text;
4702    return div.textContent;
4703  }
4704
4705  /**
4706   * Keep track of whether the user is using their mouse or keyboard to
4707   * navigate. Will determine whether tooltip should be shown on focus.
4708   */
4709  let usingMouse;
4710
4711  function debounce(callback, delay) {
4712    let timeout = null;
4713
4714    return function (...args) {
4715      clearTimeout(timeout);
4716      timeout = setTimeout(() => {
4717        callback(...args);
4718      }, delay);
4719    };
4720  }
4721
4722  // The mousemove listener is debounced for performance reasons
4723  document.addEventListener('mousemove', debounce(() => usingMouse = true, 100));
4724  document.addEventListener('mousedown', () => usingMouse = true);
4725  document.addEventListener('keydown', () => usingMouse = false);
4726
4727  /**
4728   * Create an accessible tooltip
4729   *
4730   * @param {HTMLElement} triggeringElement The element that should trigger the tooltip
4731   * @param {Object} options Options for tooltip
4732   * @param {String} options.text The text to be displayed in the tooltip
4733   *  If not set, will attempt to set text = options.tooltipSource of triggeringElement
4734   * @param {String[]} options.classes Extra css classes for the tooltip
4735   * @param {Boolean} options.ariaHidden Whether the hover should be read by screen readers or not (default: true)
4736   * @param {String} options.position Where the tooltip should appear in relation to the
4737   *  triggeringElement. Accepted positions are "top" (default), "left", "right" and "bottom"
4738   * @param {String} options.tooltipSource
4739   *
4740   * @returns {object} returns all the public functions
4741   *
4742   * @constructor
4743   */
4744
4745  function Tooltip(triggeringElement, options) {
4746    // Make sure tooltips have unique id
4747    H5P.Tooltip.uniqueId += 1;
4748    const tooltipId = `h5p-tooltip-${H5P.Tooltip.uniqueId}`;
4749
4750    // Default options
4751    options = options || {};
4752    options.classes = options.classes || [];
4753    options.ariaHidden = options.ariaHidden || true;
4754    options.tooltipSource = options.tooltipSource || 'aria-label';
4755    options.position = (options.position && Position.allowed.includes(options.position))
4756      ? options.position
4757      : Position.default;
4758
4759    // Add our internal classes
4760    options.classes.push('h5p-tooltip');
4761    if (options.position === 'left' || options.position === 'right') {
4762      options.classes.push('h5p-tooltip-narrow');
4763    }
4764
4765    // Initiate state
4766    let hover = false;
4767    let focus = false;
4768
4769    // Function used by the escape listener
4770    const hideOnEscape = function (event) {
4771      if (event.key === 'Escape') {
4772        tooltip.classList.remove('h5p-tooltip-visible');
4773      }
4774    };
4775
4776    // Create element
4777    const tooltip = document.createElement('div');
4778    tooltip.id = tooltipId;
4779    tooltip.role = 'tooltip';
4780    tooltip.textContent = parseString(options.text || triggeringElement.getAttribute(options.tooltipSource) || '');
4781    tooltip.setAttribute('aria-hidden', options.ariaHidden);
4782    tooltip.classList.add(...options.classes);
4783
4784    document.body.appendChild(tooltip);
4785
4786    // Aria-describedby will override aria-hidden
4787    if (!options.ariaHidden) {
4788      triggeringElement.setAttribute('aria-describedby', tooltipId);
4789    }
4790
4791    // Use a mutation observer to listen for options.tooltipSource being
4792    // changed for the triggering element. If so, update the tooltip.
4793    // Mutation observer will be used even if the original elements
4794    // doesn't have any options.tooltipSource.
4795    this.observer = new MutationObserver((mutations) => {
4796      const updatedText = mutations[0].target.getAttribute(options.tooltipSource);
4797
4798      if (tooltip.parentNode === null) {
4799        triggeringElement.appendChild(tooltip);
4800      }
4801
4802      tooltip.textContent = parseString(options.text || updatedText);
4803
4804      if (tooltip.textContent.trim().length === 0 && tooltip.classList.contains('h5p-tooltip-visible')) {
4805        tooltip.classList.remove('h5p-tooltip-visible');
4806      }
4807    });
4808    this.observer.observe(triggeringElement, {
4809      attributes: true,
4810      attributeFilter: [options.tooltipSource, 'class'],
4811    });
4812
4813    // A reference to the H5P container (if any). If null, it means
4814    // this tooltip is not whithin an H5P.
4815    let h5pContainer;
4816
4817    // Timer responsible for displaying the tooltip x ms after it has been
4818    // triggered (either by mouseenter or focusin)
4819    let showTooltipTimer;
4820
4821    // Timer responsible for hiding the tooltip x ms after it has been untriggered
4822    let hideTooltipTimer;
4823
4824    // This timer makes sure the tooltip is not hidden when the mouse
4825    // moves from the trigger to the tooltip.
4826    let triggerMouseLeaveTimer;
4827
4828    /**
4829     * Makes the tooltip visible and activates it's functionality
4830     *
4831     * @param {UIEvent} event The triggering event
4832     */
4833    const showTooltip = function (event, wait = true) {
4834      if (!event.target || event.target.disabled || event.target.getAttribute('aria-disabled') === 'true') {
4835        return;
4836      }
4837
4838      clearTimeout(hideTooltipTimer); // Prevent from hiding while supposed to show
4839
4840      if (wait === true) {
4841        // We don't want to show the tooltip right away.
4842        // Adding a 300 ms waiting period here.
4843        clearTimeout(showTooltipTimer);
4844        showTooltipTimer = setTimeout(() => {
4845          showTooltip(event, false);
4846        }, DELAY_SHOW_MS);
4847        return;
4848      }
4849
4850      // Don't show tooltip if it is empty
4851      if (tooltip.textContent.trim().length === 0) {
4852        return;
4853      }
4854
4855      if (event.type === 'mouseenter') {
4856        hover = true;
4857      }
4858      else {
4859        focus = true;
4860      }
4861
4862      // Reset placement
4863      tooltip.style.left = '';
4864      tooltip.style.top = '';
4865
4866      tooltip.classList.add('h5p-tooltip-visible');
4867
4868      // Add listener to iframe body, as esc keypress would not be detected otherwise
4869      document.body.addEventListener('keydown', hideOnEscape, true);
4870
4871      // The section below makes sure the tooltip is completely visible
4872
4873      // H5P.Tooltip can be used both from within an H5P and elsewhere.
4874      // The below code is for figuring out the containing element.
4875      // h5pContainer has to be looked up the first time we show the tooltip,
4876      // since it might not be added to the DOM when H5P.Tooltip is invoked.
4877      if (h5pContainer === undefined) {
4878        // After the below, h5pContainer is either null or a reference to the
4879        // DOM element
4880        h5pContainer = triggeringElement.closest('.h5p-container');
4881      }
4882      const rootRect = h5pContainer ? h5pContainer.getBoundingClientRect() : document.documentElement.getBoundingClientRect();
4883      const triggerRect = triggeringElement.getBoundingClientRect();
4884      let tooltipRect = tooltip.getBoundingClientRect();
4885
4886      if (options.position === 'top') {
4887        // Places it centered above
4888        tooltip.style.left = `${triggerRect.left + (triggerRect.width / 2) - (tooltipRect.width / 2)}px`;
4889        tooltip.style.top = `${triggerRect.top - tooltipRect.height}px`;
4890      }
4891      else if (options.position === 'bottom') {
4892        // Places it centered below
4893        tooltip.style.left = `${triggerRect.left + (triggerRect.width / 2) - (tooltipRect.width / 2)}px`;
4894        tooltip.style.top = `${triggerRect.bottom}px`;
4895      }
4896      else if (options.position === 'left') {
4897        tooltip.style.left = `${triggerRect.left - tooltipRect.width}px`;
4898        tooltip.style.top = `${triggerRect.top + (triggerRect.height - tooltipRect.height) / 2}px`;
4899        // We trust this option makes the tooltip being shown
4900        return;
4901      }
4902      else if (options.position === 'right') {
4903        tooltip.style.left = `${triggerRect.right}px`;
4904        tooltip.style.top = `${triggerRect.top + (triggerRect.height - tooltipRect.height) / 2}px`;
4905        // We trust this option makes the tooltip being shown
4906        return;
4907      }
4908
4909      tooltipRect = tooltip.getBoundingClientRect();
4910      const isVisible = tooltipRect.left >= 0
4911        && tooltipRect.top >= 0
4912        && tooltipRect.right <= rootRect.width
4913        && tooltipRect.bottom <= rootRect.height;
4914
4915      if (!isVisible) {
4916        // The tooltip placement needs to be adjusted. This logic will move the
4917        // tooltip either left or right if it's placed outside the root element
4918        tooltipRect = tooltip.getBoundingClientRect();
4919        if (tooltipRect.left < 0) {
4920          tooltip.style.left = 0;
4921        }
4922        else if (tooltipRect.right > rootRect.width) {
4923          tooltip.style.left = '';
4924          tooltip.style.right = 0;
4925        }
4926      }
4927    };
4928
4929    /**
4930     * Hides the tooltip and removes listeners
4931     *
4932     * @param {UIEvent} event The triggering event
4933     */
4934    const hideTooltip = function (event) {
4935      let hide = false;
4936      let wait = false;
4937
4938      if (event.type === 'click') {
4939        hide = true;
4940      }
4941      else {
4942        if (event.type === 'mouseleave') {
4943          wait = true; // Tooltip should not disappear right away
4944          hover = false;
4945        }
4946        else {
4947          focus = false;
4948        }
4949
4950        hide = (!hover && !focus);
4951      }
4952
4953      // Only hide tooltip if neither hovered nor focused
4954      if (hide) {
4955        clearTimeout(showTooltipTimer); // Prevent from showing while supposed to hide
4956
4957        const cleanupTooltip = () => {
4958          tooltip.classList.remove('h5p-tooltip-visible');
4959          document.body.removeEventListener('keydown', hideOnEscape, true); // Remove iframe body listener
4960        };
4961
4962        if (wait) {
4963          clearTimeout(hideTooltipTimer);
4964          hideTooltipTimer = setTimeout(() => {
4965            cleanupTooltip();
4966          }, DELAY_HIDE_MS);
4967        }
4968        else {
4969          cleanupTooltip();
4970        }
4971      }
4972    };
4973
4974    // Add event listeners to triggeringElement
4975    triggeringElement.addEventListener('mouseenter', showTooltip);
4976    triggeringElement.addEventListener('mouseleave', (event) => {
4977      triggerMouseLeaveTimer = setTimeout(() => {
4978        hideTooltip(event);
4979      }, 1);
4980    });
4981    triggeringElement.addEventListener('focusin', (event) => {
4982      if (!usingMouse) {
4983        showTooltip(event);
4984      }
4985    });
4986    triggeringElement.addEventListener('focusout', hideTooltip);
4987    triggeringElement.addEventListener('click', hideTooltip);
4988    tooltip.addEventListener('mouseenter', () => {
4989      clearTimeout(triggerMouseLeaveTimer);
4990    });
4991    tooltip.addEventListener('mouseleave', hideTooltip);
4992
4993    tooltip.addEventListener('click', (event) => {
4994      // Prevent clicks on the tooltip from triggering click
4995      // listeners on the triggering element
4996      event.stopPropagation();
4997      event.preventDefault();
4998
4999      // Hide the tooltip when it is clicked
5000      hideTooltip(event);
5001    });
5002
5003    /**
5004     * Change the text displayed by the tooltip
5005     *
5006     * @param {String} text The new text to be displayed
5007     *  Set to null to use options.tooltipSource of triggeringElement instead
5008     */
5009    this.setText = function (text) {
5010      options.text = text;
5011      tooltip.textContent = parseString(options.text || triggeringElement.getAttribute(options.tooltipSource) || '');
5012    };
5013
5014    /**
5015     * Hide the tooltip
5016     */
5017    this.hide = function () {
5018      hover = focus = false;
5019      tooltip.classList.remove('h5p-tooltip-visible');
5020    };
5021
5022    /**
5023     * Retrieve tooltip
5024     *
5025     * @return {HTMLElement}
5026     */
5027    this.getElement = function () {
5028      return tooltip;
5029    };
5030
5031    /**
5032     * Remove tooltip
5033     */
5034    this.remove = function () {
5035      this.observer?.disconnect();
5036      tooltip.remove();
5037    };
5038
5039    return {
5040      setText: this.setText,
5041      hide: this.hide,
5042      getElement: this.getElement,
5043      remove: this.remove,
5044      observer: this.observer,
5045    };
5046  }
5047
5048  return Tooltip;
5049}());
5050
5051H5P.Tooltip.uniqueId = -1;
5052;

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